code:
/*	$OpenBSD: ommmc.c,v 1.14 2015/05/30 02:17:36 jsg Exp $	*/

/*
 * Copyright (c) 2009 Dale Rahn <drahn@openbsd.org>
 * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* Omap SD/MMC support derived from /sys/dev/sdmmc/sdhc.c */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/systm.h>
#include <machine/bus.h>

#include <dev/sdmmc/sdmmcchip.h>
#include <dev/sdmmc/sdmmcvar.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/omap/prcmvar.h>

/*
 * NOTE: on OMAP4430/AM335x these registers skew by 0x100
 * this is handled by mapping at base address + 0x100
 */
/* registers */
#define MMCHS_SYSCONFIG	0x010
#define MMCHS_SYSSTATUS	0x014
#define MMCHS_CSRE	0x024
#define MMCHS_SYSTEST	0x028
#define  MMCHS_SYSTEST_SDCD	(1 << 15)
#define MMCHS_CON	0x02C
#define  MMCHS_CON_INIT	(1<<1)
#define  MMCHS_CON_DW8	(1<<5)
#define  MMCHS_CON_OD	(1<<0)
#define MMCHS_PWCNT	0x030
#define MMCHS_BLK	0x104
#define  MMCHS_BLK_NBLK_MAX	0xffff
#define  MMCHS_BLK_NBLK_SHIFT	16
#define  MMCHS_BLK_NBLK_MASK	(MMCHS_BLK_NBLK_MAX<<MMCHS_BLK_NBLK_SHIFT)
#define  MMCHS_BLK_BLEN_MAX	0x400
#define  MMCHS_BLK_BLEN_SHIFT	0
#define  MMCHS_BLK_BLEN_MASK	(MMCHS_BLK_BLEN_MAX<<MMCHS_BLK_BLEN_SHIFT)
#define MMCHS_ARG	0x108
#define MMCHS_CMD	0x10C
#define  MMCHS_CMD_INDX_SHIFT		24
#define  MMCHS_CMD_INDX_SHIFT_MASK	(0x3f << MMCHS_CMD_INDX_SHIFT)
#define	 MMCHS_CMD_CMD_TYPE_SHIFT	22
#define	 MMCHS_CMD_DP_SHIFT		21
#define	 MMCHS_CMD_DP			(1 << MMCHS_CMD_DP_SHIFT)
#define	 MMCHS_CMD_CICE_SHIFT		20
#define	 MMCHS_CMD_CICE			(1 << MMCHS_CMD_CICE_SHIFT)
#define	 MMCHS_CMD_CCCE_SHIFT		19
#define	 MMCHS_CMD_CCCE			(1 << MMCHS_CMD_CCCE_SHIFT)
#define	 MMCHS_CMD_RSP_TYPE_SHIFT	16
#define  MMCHS_CMD_RESP_NONE		(0x0 << MMCHS_CMD_RSP_TYPE_SHIFT)
#define  MMCHS_CMD_RESP136		(0x1 << MMCHS_CMD_RSP_TYPE_SHIFT)
#define  MMCHS_CMD_RESP48		(0x2 << MMCHS_CMD_RSP_TYPE_SHIFT)
#define  MMCHS_CMD_RESP48B		(0x3 << MMCHS_CMD_RSP_TYPE_SHIFT)
#define  MMCHS_CMD_MSBS			(1 << 5)
#define  MMCHS_CMD_DDIR			(1 << 4)
#define  MMCHS_CMD_ACEN			(1 << 2)
#define  MMCHS_CMD_BCE			(1 << 1)
#define  MMCHS_CMD_DE			(1 << 0)
#define MMCHS_RSP10	0x110
#define MMCHS_RSP32	0x114
#define MMCHS_RSP54	0x118
#define MMCHS_RSP76	0x11C
#define MMCHS_DATA	0x120
#define MMCHS_PSTATE	0x124
#define  MMCHS_PSTATE_CLEV	(1<<24)
#define  MMCHS_PSTATE_DLEV_SH	20
#define  MMCHS_PSTATE_DLEV_M	(0xf << MMCHS_PSTATE_DLEV_SH)
#define  MMCHS_PSTATE_BRE	(1<<11)
#define  MMCHS_PSTATE_BWE	(1<<10)
#define  MMCHS_PSTATE_RTA	(1<<9)
#define  MMCHS_PSTATE_WTA	(1<<8)
#define  MMCHS_PSTATE_DLA	(1<<2)
#define  MMCHS_PSTATE_DATI	(1<<1)
#define  MMCHS_PSTATE_CMDI	(1<<0)
#define  MMCHS_PSTATE_FMT "\20" \
    "\x098_CLEV" \
    "\x08b_BRE" \
    "\x08a_BWE" \
    "\x089_RTA" \
    "\x088_WTA" \
    "\x082_DLA" \
    "\x081_DATI" \
    "\x080_CMDI"
#define MMCHS_HCTL	0x128
#define  MMCHS_HCTL_SDVS_SHIFT	9
#define  MMCHS_HCTL_SDVS_MASK	(0x7<<MMCHS_HCTL_SDVS_SHIFT)
#define  MMCHS_HCTL_SDVS_V18	(0x5<<MMCHS_HCTL_SDVS_SHIFT)
#define  MMCHS_HCTL_SDVS_V30	(0x6<<MMCHS_HCTL_SDVS_SHIFT)
#define  MMCHS_HCTL_SDVS_V33	(0x7<<MMCHS_HCTL_SDVS_SHIFT)
#define  MMCHS_HCTL_SDBP	(1<<8)
#define  MMCHS_HCTL_HSPE	(1<<2)
#define  MMCHS_HCTL_DTW		(1<<1)
#define MMCHS_SYSCTL	0x12C
#define  MMCHS_SYSCTL_SRD	(1<<26)
#define  MMCHS_SYSCTL_SRC	(1<<25)
#define  MMCHS_SYSCTL_SRA	(1<<24)
#define  MMCHS_SYSCTL_DTO_SH	16
#define  MMCHS_SYSCTL_DTO_MASK	0x000f0000
#define  MMCHS_SYSCTL_CLKD_SH	6
#define  MMCHS_SYSCTL_CLKD_MASK	0x0000ffc0
#define  MMCHS_SYSCTL_CEN	(1<<2)
#define  MMCHS_SYSCTL_ICS	(1<<1)
#define  MMCHS_SYSCTL_ICE	(1<<0)
#define MMCHS_STAT	0x130
#define  MMCHS_STAT_BADA	(1<<29)
#define  MMCHS_STAT_CERR	(1<<28)
#define  MMCHS_STAT_ACE		(1<<24)
#define  MMCHS_STAT_DEB		(1<<22)
#define  MMCHS_STAT_DCRC	(1<<21)
#define  MMCHS_STAT_DTO		(1<<20)
#define  MMCHS_STAT_CIE		(1<<19)
#define  MMCHS_STAT_CEB		(1<<18)
#define  MMCHS_STAT_CCRC	(1<<17)
#define  MMCHS_STAT_CTO		(1<<16)
#define  MMCHS_STAT_ERRI	(1<<15)
#define  MMCHS_STAT_OBI		(1<<9)
#define  MMCHS_STAT_CIRQ	(1<<8)
#define  MMCHS_STAT_BRR		(1<<5)
#define  MMCHS_STAT_BWR		(1<<4)
#define  MMCHS_STAT_BGE		(1<<2)
#define  MMCHS_STAT_TC		(1<<1)
#define  MMCHS_STAT_CC		(1<<0)
#define  MMCHS_STAT_FMT "\20" \
    "\x09d_BADA" \
    "\x09c_CERR" \
    "\x098_ACE" \
    "\x096_DEB" \
    "\x095_DCRC" \
    "\x094_DTO" \
    "\x093_CIE" \
    "\x092_CEB" \
    "\x091_CCRC" \
    "\x090_CTO" \
    "\x08f_ERRI" \
    "\x089_OBI" \
    "\x088_CIRQ" \
    "\x085_BRR" \
    "\x084_BWR" \
    "\x082_BGE" \
    "\x081_TC" \
    "\x080_CC"
#define MMCHS_IE	0x134
#define MMCHS_ISE	0x138
#define MMCHS_AC12	0x13C
#define MMCHS_CAPA	0x140
#define  MMCHS_CAPA_VS18	(1 << 26)
#define  MMCHS_CAPA_VS30	(1 << 25)
#define  MMCHS_CAPA_VS33	(1 << 24)
#define  MMCHS_CAPA_SRS		(1 << 23)
#define  MMCHS_CAPA_DS		(1 << 22)
#define  MMCHS_CAPA_HSS		(1 << 21)
#define  MMCHS_CAPA_MBL_SHIFT	16
#define  MMCHS_CAPA_MBL_MASK	(3 << MMCHS_CAPA_MBL_SHIFT)
#define MMCHS_CUR_CAPA	0x148
#define MMCHS_REV	0x1fc

#define SDHC_COMMAND_TIMEOUT	hz
#define SDHC_BUFFER_TIMEOUT	hz
#define SDHC_TRANSFER_TIMEOUT	hz

void ommmc_attach(struct device *parent, struct device *self, void *args);

struct ommmc_softc {
	struct device sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
	void			*sc_ih; /* Interrupt handler */
	uint32_t		sc_flags;

	struct device *sdmmc;		/* generic SD/MMC device */
	int clockbit;			/* clock control bit */
	uint32_t clkbase;		/* base clock frequency in KHz */
	int maxblklen;			/* maximum block length */
	int flags;			/* flags for this host */
	uint32_t ocr;			/* OCR value from capabilities */
	uint32_t intr_status;		/* soft interrupt status */
	uint32_t intr_error_status;	/*  */
};

/* Host controller functions called by the attachment driver. */
int	ommmc_host_found(struct ommmc_softc *, bus_space_tag_t,
	    bus_space_handle_t, bus_size_t, int);
void	ommmc_power(int, void *);
void	ommmc_shutdown(void *);
int	ommmc_intr(void *);

/* RESET MODES */
#define MMC_RESET_DAT	1
#define MMC_RESET_CMD	2
#define MMC_RESET_ALL	(MMC_RESET_CMD|MMC_RESET_DAT)

/* flag values */
#define SHF_USE_DMA		0x0001

#define HREAD4(sc, reg)							\
	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
#define HWRITE4(sc, reg, val)						\
	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define HSET4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
#define HCLR4(sc, reg, bits)						\
	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))

int	ommmc_host_reset(sdmmc_chipset_handle_t);
uint32_t ommmc_host_ocr(sdmmc_chipset_handle_t);
int	ommmc_host_maxblklen(sdmmc_chipset_handle_t);
int	ommmc_card_detect(sdmmc_chipset_handle_t);
int	ommmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
int	ommmc_bus_clock(sdmmc_chipset_handle_t, int);
void	ommmc_card_intr_mask(sdmmc_chipset_handle_t, int);
void	ommmc_card_intr_ack(sdmmc_chipset_handle_t);
void	ommmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
int	ommmc_start_command(struct ommmc_softc *, struct sdmmc_command *);
int	ommmc_wait_state(struct ommmc_softc *, uint32_t, uint32_t);
int	ommmc_soft_reset(struct ommmc_softc *, int);
int	ommmc_wait_intr(struct ommmc_softc *, int, int);
void	ommmc_transfer_data(struct ommmc_softc *, struct sdmmc_command *);
void	ommmc_read_data(struct ommmc_softc *, uint8_t *, int);
void	ommmc_write_data(struct ommmc_softc *, uint8_t *, int);

/* #define SDHC_DEBUG */
#ifdef SDHC_DEBUG
int ommmcdebug = 20;
#define DPRINTF(n,s)	do { if ((n) <= ommmcdebug) printf s; } while (0)
void	ommmc_dump_regs(struct ommmc_softc *);
#else
#define DPRINTF(n,s)	do {} while(0)
#endif

struct sdmmc_chip_functions ommmc_functions = {
	/* host controller reset */
	ommmc_host_reset,
	/* host controller capabilities */
	ommmc_host_ocr,
	ommmc_host_maxblklen,
	/* card detection */
	ommmc_card_detect,
	/* bus power and clock frequency */
	ommmc_bus_power,
	ommmc_bus_clock,
	/* command execution */
	ommmc_exec_command,
	/* card interrupt */
	ommmc_card_intr_mask,
	ommmc_card_intr_ack
};

struct cfdriver ommmc_cd = {
	NULL, "ommmc", DV_DULL
};

struct cfattach ommmc_ca = {
	sizeof(struct ommmc_softc), NULL, ommmc_attach
};

void
ommmc_attach(struct device *parent, struct device *self, void *args)
{
	struct ommmc_softc		*sc = (struct ommmc_softc *) self;
	struct armv7_attach_args	*aa = args;
	struct sdmmcbus_attach_args	 saa;
	uint32_t			 caps;

	sc->sc_iot = aa->aa_iot;
	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
		panic("%s: bus_space_map failed!", __func__);

	printf("\n");

	/* Enable ICLKEN, FCLKEN? */
	prcm_enablemodule(PRCM_MMC0 + aa->aa_dev->unit);

	sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_SDMMC,
	    ommmc_intr, sc, DEVNAME(sc));
	if (sc->sc_ih == NULL) {
		printf("%s: cannot map interrupt\n", DEVNAME(sc));
		goto err;
	}

	/* Controller Voltage Capabilities Initialization */
	HSET4(sc, MMCHS_CAPA, MMCHS_CAPA_VS18 | MMCHS_CAPA_VS30);

#ifdef SDHC_DEBUG
	ommmc_dump_regs(sc);
#endif

	/*
	 * Reset the host controller and enable interrupts.
	 */
	ommmc_host_reset(sc);

	/* Determine host capabilities. */
	caps = HREAD4(sc, MMCHS_CAPA);

#if 0
	/* we want this !! */
	/* Use DMA if the host system and the controller support it. */
	if (usedma && ISSET(caps, SDHC_DMA_SUPPORT))
		SET(sc->flags, SHF_USE_DMA);
#endif

	/*
	 * Determine the base clock frequency. (2.2.24)
	 */

	sc->clkbase = 96 * 1000;
#if 0
	if (SDHC_BASE_FREQ_KHZ(caps) != 0)
		sc->clkbase = SDHC_BASE_FREQ_KHZ(caps);
		sc->clkbase = SDHC_BASE_FREQ_KHZ(caps);
#endif
	if (sc->clkbase == 0) {
		/* The attachment driver must tell us. */
		printf("%s: base clock frequency unknown\n", DEVNAME(sc));
		goto err;
	} else if (sc->clkbase < 10000 || sc->clkbase > 96000) {
		/* SDHC 1.0 supports only 10-63 MHz. */
		printf("%s: base clock frequency out of range: %u MHz\n",
		    DEVNAME(sc), sc->clkbase / 1000);
		goto err;
	}

	/*
	 * XXX Set the data timeout counter value according to
	 * capabilities. (2.2.15)
	 */

	/*
	 * Determine SD bus voltage levels supported by the controller.
	 */
	if (caps & MMCHS_CAPA_VS18)
		SET(sc->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V);
	if (caps & MMCHS_CAPA_VS30)
		SET(sc->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V);
	if (caps & MMCHS_CAPA_VS33)
		SET(sc->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V);

	/*
	 * Omap max block size is fixed (single buffer), could limit
	 * this to 512 for double buffering, but dont see the point.
	 */
	switch ((caps & MMCHS_CAPA_MBL_MASK) >> MMCHS_CAPA_MBL_SHIFT) {
	case 0:
		sc->maxblklen = 512;
		break;
	case 1:
		sc->maxblklen = 1024;
		break;
	case 2:
		sc->maxblklen = 2048;
		break;
	default:
		sc->maxblklen = 512;
		printf("invalid capability blocksize in capa %08x,"
		    " trying 512\n", HREAD4(sc, MMCHS_CAPA));
	}
	/*
	 * MMC does not support blksize > 512 yet
	 */
	sc->maxblklen = 512;
	/*
	 * Attach the generic SD/MMC bus driver.  (The bus driver must
	 * not invoke any chipset functions before it is attached.)
	 */
	bzero(&saa, sizeof(saa));
	saa.saa_busname = "sdmmc";
	saa.sct = &ommmc_functions;
	saa.sch = sc;
	saa.caps = 0;
	if (caps & MMCHS_CAPA_HSS)
		saa.caps |= SMC_CAPS_MMC_HIGHSPEED;

	sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL);
	if (sc->sdmmc == NULL) {
		printf("%s: can't attach sdmmc\n", DEVNAME(sc));
		goto err;
	}

	return;
err:
	if (sc->sc_ih != NULL)
		arm_intr_disestablish(sc->sc_ih);
	bus_space_unmap(sc->sc_iot, sc->sc_ioh, aa->aa_dev->mem[0].size);
}

/*
 * Power hook established by or called from attachment driver.
 */
void
ommmc_power(int why, void *arg)
{
#if 0
	struct ommmc_softc *sc = arg;
	int n, i;
#endif

	switch(why) {
	case DVACT_SUSPEND:
		/* XXX poll for command completion or suspend command
		 * in progress */

		/* Save the host controller state. */
#if 0
		for (i = 0; i < sizeof sc->regs; i++)
			sc->regs[i] = HREAD1(sc, i);
#endif
		break;

	case DVACT_RESUME:
		/* Restore the host controller state. */
#if 0
		(void)ommmc_host_reset(sc);
		for (i = 0; i < sizeof sc->regs; i++)
			HWRITE1(sc, i, sc->regs[i]);
#endif
		break;
	}
}

/*
 * Shutdown hook established by or called from attachment driver.
 */
void
ommmc_shutdown(void *arg)
{
	struct ommmc_softc *sc = arg;

	/* XXX chip locks up if we don't disable it before reboot. */
	(void)ommmc_host_reset(sc);
}

/*
 * Reset the host controller.  Called during initialization, when
 * cards are removed, upon resume, and during error recovery.
 */
int
ommmc_host_reset(sdmmc_chipset_handle_t sch)
{
	struct ommmc_softc *sc = sch;
	uint32_t imask;
	int error;
	int s;

	s = splsdmmc();

	/* Disable all interrupts. */
	HWRITE4(sc, MMCHS_IE, 0);
	HWRITE4(sc, MMCHS_ISE, 0);

	/*
	 * Reset the entire host controller and wait up to 100ms for
	 * the controller to clear the reset bit.
	 */
	if ((error = ommmc_soft_reset(sc, MMCHS_SYSCTL_SRA)) != 0) {
		splx(s);
		return (error);
	}

#if 0
	HSET4(sc, MMCHS_CON, MMCHS_CON_INIT);
	HWRITE4(sc, MMCHS_CMD, 0);
	delay(100); /* should delay 1ms */

	HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CC);
	HCLR4(sc, MMCHS_CON, MMCHS_CON_INIT);
	HWRITE4(sc, MMCHS_STAT, ~0);
#endif

	/* Set data timeout counter value to max for now. */
	HSET4(sc, MMCHS_SYSCTL, 0xe << MMCHS_SYSCTL_DTO_SH);

	/* Enable interrupts. */
	imask = MMCHS_STAT_BRR | MMCHS_STAT_BWR | MMCHS_STAT_BGE |
	    MMCHS_STAT_TC | MMCHS_STAT_CC;

	imask |= MMCHS_STAT_BADA | MMCHS_STAT_CERR | MMCHS_STAT_DEB |
	    MMCHS_STAT_DCRC | MMCHS_STAT_DTO | MMCHS_STAT_CIE |
	    MMCHS_STAT_CEB | MMCHS_STAT_CCRC | MMCHS_STAT_CTO;

	HWRITE4(sc, MMCHS_IE, imask);
	HWRITE4(sc, MMCHS_ISE, imask);

	splx(s);
	return (0);
}

uint32_t
ommmc_host_ocr(sdmmc_chipset_handle_t sch)
{
	struct ommmc_softc *sc = sch;
	return (sc->ocr);
}

int
ommmc_host_maxblklen(sdmmc_chipset_handle_t sch)
{
	struct ommmc_softc *sc = sch;
	return (sc->maxblklen);
}

/*
 * Return non-zero if the card is currently inserted.
 */
int
ommmc_card_detect(sdmmc_chipset_handle_t sch)
{
	struct ommmc_softc *sc = sch;
	return !ISSET(HREAD4(sc, MMCHS_SYSTEST), MMCHS_SYSTEST_SDCD) ?
	    1 : 0;
}

/*
 * Set or change SD bus voltage and enable or disable SD bus power.
 * Return zero on success.
 */
int
ommmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
{
	struct ommmc_softc *sc = sch;
	uint32_t vdd;
	uint32_t reg;
	int s;

	s = splsdmmc();

	/*
	 * Disable bus power before voltage change.
	 */
	HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP);

	/* If power is disabled, reset the host and return now. */
	if (ocr == 0) {
		splx(s);
		(void)ommmc_host_reset(sc);
		return (0);
	}

	/*
	 * Select the maximum voltage according to capabilities.
	 */
	ocr &= sc->ocr;

	if (ISSET(ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V))
		vdd = MMCHS_HCTL_SDVS_V33;
	else if (ISSET(ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V))
		vdd = MMCHS_HCTL_SDVS_V30;
	else if (ISSET(ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V))
		vdd = MMCHS_HCTL_SDVS_V18;
	else {
		/* Unsupported voltage level requested. */
		splx(s);
		return (EINVAL);
	}

	/*
	 * Enable bus power.  Wait at least 1 ms (or 74 clocks) plus
	 * voltage ramp until power rises.
	 */
	reg = HREAD4(sc, MMCHS_HCTL);
	reg &= ~MMCHS_HCTL_SDVS_MASK;
	reg |= vdd;
	HWRITE4(sc, MMCHS_HCTL, reg);

	HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP);
	delay(10000); /* XXX */

	/*
	 * The host system may not power the bus due to battery low,
	 * etc.  In that case, the host controller should clear the
	 * bus power bit.
	 */
	if (!ISSET(HREAD4(sc, MMCHS_HCTL), MMCHS_HCTL_SDBP)) {
		splx(s);
		return (ENXIO);
	}

	splx(s);
	return (0);
}

/*
 * Return the smallest possible base clock frequency divisor value
 * for the CLOCK_CTL register to produce `freq' (KHz).
 */
static int
ommmc_clock_divisor(struct ommmc_softc *sc, uint32_t freq)
{
	int div;
	uint32_t maxclk = MMCHS_SYSCTL_CLKD_MASK>>MMCHS_SYSCTL_CLKD_SH;

	for (div = 1; div <= maxclk; div++)
		if ((sc->clkbase / div) <= freq) {
			return (div);
		}

	printf("divisor failure\n");
	/* No divisor found. */
	return (-1);
}

/*
 * Set or change SDCLK frequency or disable the SD clock.
 * Return zero on success.
 */
int
ommmc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
{
	int error = 0;
	struct ommmc_softc *sc = sch;
	uint32_t reg;
	int s;
	int div;
	int timo;

	s = splsdmmc();

	/* Must not stop the clock if commands are in progress. */
	for (timo = 1000; timo > 0; timo--) {
		if (!ISSET(HREAD4(sc, MMCHS_PSTATE),
		    MMCHS_PSTATE_CMDI|MMCHS_PSTATE_DATI))
			break;
		delay(10);
	}
	if (timo == 0) {
		error = ETIMEDOUT;
		goto ret;
	}

	/*
	 * Stop SD clock before changing the frequency.
	 */
	HCLR4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN);
	if (freq == SDMMC_SDCLK_OFF)
		goto ret;

	/*
	 * Set the minimum base clock frequency divisor.
	 */
	if ((div = ommmc_clock_divisor(sc, freq)) < 0) {
		/* Invalid base clock frequency or `freq' value. */
		error = EINVAL;
		goto ret;
	}
	reg = HREAD4(sc, MMCHS_SYSCTL);
	reg &= ~MMCHS_SYSCTL_CLKD_MASK;
	reg |= div << MMCHS_SYSCTL_CLKD_SH;
	HWRITE4(sc, MMCHS_SYSCTL, reg);

	/*
	 * Start internal clock.  Wait 10ms for stabilization.
	 */
	HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_ICE);
	for (timo = 1000; timo > 0; timo--) {
		if (ISSET(HREAD4(sc, MMCHS_SYSCTL), MMCHS_SYSCTL_ICS))
			break;
		delay(10);
	}
	if (timo == 0) {
		error = ETIMEDOUT;
		goto ret;
	}

	/*
	 * Enable SD clock.
	 */
	HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN);
ret:
	splx(s);
	return (error);
}

void
ommmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
{
	/* - this is SDIO card interrupt */
	struct ommmc_softc *sc = sch;

	if (enable) {
		HSET4(sc, MMCHS_IE, MMCHS_STAT_CIRQ);
		HSET4(sc, MMCHS_ISE, MMCHS_STAT_CIRQ);
	} else {
		HCLR4(sc, MMCHS_IE, MMCHS_STAT_CIRQ);
		HCLR4(sc, MMCHS_ISE, MMCHS_STAT_CIRQ);
	}
}

void
ommmc_card_intr_ack(sdmmc_chipset_handle_t sch)
{
	struct ommmc_softc *sc = sch;

	HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CIRQ);
}

int
ommmc_wait_state(struct ommmc_softc *sc, uint32_t mask, uint32_t value)
{
	uint32_t state;
	int timeout;

	state = HREAD4(sc, MMCHS_PSTATE);
	DPRINTF(3,("%s: wait_state %x %x %x(state=%b)\n", DEVNAME(sc),
	    mask, value, state, state, MMCHS_PSTATE_FMT));
	for (timeout = 1000; timeout > 0; timeout--) {
		if (((state = HREAD4(sc, MMCHS_PSTATE)) & mask) == value)
			return (0);
		delay(10);
	}
	DPRINTF(0,("%s: timeout waiting for %x (state=%b)\n", DEVNAME(sc),
	    value, state, MMCHS_PSTATE_FMT));
	return (ETIMEDOUT);
}

void
ommmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd)
{
	struct ommmc_softc *sc = sch;
	int error;

	/*
	 * Start the MMC command, or mark `cmd' as failed and return.
	 */
	error = ommmc_start_command(sc, cmd);
	if (error != 0) {
		cmd->c_error = error;
		SET(cmd->c_flags, SCF_ITSDONE);
		return;
	}

	/*
	 * Wait until the command phase is done, or until the command
	 * is marked done for any other reason.
	 */
	if (!ommmc_wait_intr(sc, MMCHS_STAT_CC, SDHC_COMMAND_TIMEOUT)) {
		cmd->c_error = ETIMEDOUT;
		SET(cmd->c_flags, SCF_ITSDONE);
		return;
	}

	/*
	 * The host controller removes bits [0:7] from the response
	 * data (CRC) and we pass the data up unchanged to the bus
	 * driver (without padding).
	 */
	if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) {
		if (ISSET(cmd->c_flags, SCF_RSP_136)) {
			uint32_t v0,v1,v2,v3;
			v0 = HREAD4(sc, MMCHS_RSP10);
			v1 = HREAD4(sc, MMCHS_RSP32);
			v2 = HREAD4(sc, MMCHS_RSP54);
			v3 = HREAD4(sc, MMCHS_RSP76);

			cmd->c_resp[0] = (v0 >> 8) | ((v1 & 0xff)  << 24);
			cmd->c_resp[1] = (v1 >> 8) | ((v2 & 0xff)  << 24);
			cmd->c_resp[2] = (v2 >> 8) | ((v3 & 0xff)  << 24);
			cmd->c_resp[3] = v3 >> 8;
#ifdef SDHC_DEBUG
			printf("resp[0] 0x%08x\nresp[1] 0x%08x\nresp[2] 0x%08x\nresp[3] 0x%08x\n", cmd->c_resp[0], cmd->c_resp[1], cmd->c_resp[2], cmd->c_resp[3]);
#endif
		} else  {
			cmd->c_resp[0] = HREAD4(sc, MMCHS_RSP10);
#ifdef SDHC_DEBUG
			printf("resp[0] 0x%08x\n", cmd->c_resp[0]);
#endif
		}
	}

	/*
	 * If the command has data to transfer in any direction,
	 * execute the transfer now.
	 */
	if (cmd->c_error == 0 && cmd->c_data != NULL)
		ommmc_transfer_data(sc, cmd);

#if 0
	/* Turn off the LED. */
	HCLR1(sc, SDHC_HOST_CTL, SDHC_LED_ON);
#endif

	DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n",
	    DEVNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_error));
	SET(cmd->c_flags, SCF_ITSDONE);
}

int
ommmc_start_command(struct ommmc_softc *sc, struct sdmmc_command *cmd)
{
	uint32_t blksize = 0;
	uint32_t blkcount = 0;
	uint32_t command;
	int error;
	int s;

	DPRINTF(1,("%s: start cmd %u arg=%#x data=%p dlen=%d flags=%#x "
	    "proc=\"%s\"\n", DEVNAME(sc), cmd->c_opcode, cmd->c_arg,
	    cmd->c_data, cmd->c_datalen, cmd->c_flags, curproc ?
	    curproc->p_comm : ""));

	/*
	 * The maximum block length for commands should be the minimum
	 * of the host buffer size and the card buffer size. (1.7.2)
	 */

	/* Fragment the data into proper blocks. */
	if (cmd->c_datalen > 0) {
		blksize = MIN(cmd->c_datalen, cmd->c_blklen);
		blkcount = cmd->c_datalen / blksize;
		if (cmd->c_datalen % blksize > 0) {
			/* XXX: Split this command. (1.7.4) */
			printf("%s: data not a multiple of %d bytes\n",
			    DEVNAME(sc), blksize);
			return (EINVAL);
		}
	}

	/* Check limit imposed by 9-bit block count. (1.7.2) */
	if (blkcount > MMCHS_BLK_NBLK_MAX) {
		printf("%s: too much data\n", DEVNAME(sc));
		return (EINVAL);
	}

	/* Prepare transfer mode register value. (2.2.5) */
	command = 0;
	if (ISSET(cmd->c_flags, SCF_CMD_READ))
		command |= MMCHS_CMD_DDIR;
	if (blkcount > 0) {
		command |= MMCHS_CMD_BCE;
		if (blkcount > 1) {
			command |= MMCHS_CMD_MSBS;
			/* XXX only for memory commands? */
			command |= MMCHS_CMD_ACEN;
		}
	}
#ifdef notyet
	if (ISSET(sc->flags, SHF_USE_DMA))
		command |= MMCHS_CMD_DE;
#endif

	/*
	 * Prepare command register value. (2.2.6)
	 */
	command |= (cmd->c_opcode << MMCHS_CMD_INDX_SHIFT) &
	   MMCHS_CMD_INDX_SHIFT_MASK;

	if (ISSET(cmd->c_flags, SCF_RSP_CRC))
		command |= MMCHS_CMD_CCCE;
	if (ISSET(cmd->c_flags, SCF_RSP_IDX))
		command |= MMCHS_CMD_CICE;
	if (cmd->c_data != NULL)
		command |= MMCHS_CMD_DP;

	if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT))
		command |= MMCHS_CMD_RESP_NONE;
	else if (ISSET(cmd->c_flags, SCF_RSP_136))
		command |= MMCHS_CMD_RESP136;
	else if (ISSET(cmd->c_flags, SCF_RSP_BSY))
		command |= MMCHS_CMD_RESP48B;
	else
		command |= MMCHS_CMD_RESP48;

	/* Wait until command and data inhibit bits are clear. (1.5) */
	if ((error = ommmc_wait_state(sc, MMCHS_PSTATE_CMDI, 0)) != 0)
		return (error);

	s = splsdmmc();

#if 0
	/* Alert the user not to remove the card. */
	HSET1(sc, SDHC_HOST_CTL, SDHC_LED_ON);
#endif

	/* XXX: Set DMA start address if SHF_USE_DMA is set. */

	DPRINTF(1,("%s: cmd=%#x blksize=%d blkcount=%d\n",
	    DEVNAME(sc), command, blksize, blkcount));

	/*
	 * Start a CPU data transfer.  Writing to the high order byte
	 * of the SDHC_COMMAND register triggers the SD command. (1.5)
	 */
	HWRITE4(sc, MMCHS_BLK, (blkcount << MMCHS_BLK_NBLK_SHIFT) |
	    (blksize << MMCHS_BLK_BLEN_SHIFT));
	HWRITE4(sc, MMCHS_ARG, cmd->c_arg);
	HWRITE4(sc, MMCHS_CMD, command);

	splx(s);
	return (0);
}

void
ommmc_transfer_data(struct ommmc_softc *sc, struct sdmmc_command *cmd)
{
	uint8_t *datap = cmd->c_data;
	int i, datalen;
	int mask;
	int error;

	mask = ISSET(cmd->c_flags, SCF_CMD_READ) ?
	    MMCHS_PSTATE_BRE : MMCHS_PSTATE_BWE;
	error = 0;
	datalen = cmd->c_datalen;

	DPRINTF(1,("%s: resp=%#x datalen=%d\n", DEVNAME(sc),
	    MMC_R1(cmd->c_resp), datalen));

	while (datalen > 0) {
		if (!ommmc_wait_intr(sc, MMCHS_STAT_BRR| MMCHS_STAT_BWR,
		    SDHC_BUFFER_TIMEOUT)) {
			error = ETIMEDOUT;
			break;
		}

		if ((error = ommmc_wait_state(sc, mask, mask)) != 0)
			break;

		i = MIN(datalen, cmd->c_blklen);
		if (ISSET(cmd->c_flags, SCF_CMD_READ))
			ommmc_read_data(sc, datap, i);
		else
			ommmc_write_data(sc, datap, i);

		datap += i;
		datalen -= i;
	}

	if (error == 0 && !ommmc_wait_intr(sc, MMCHS_STAT_TC,
	    SDHC_TRANSFER_TIMEOUT))
		error = ETIMEDOUT;

	if (error != 0)
		cmd->c_error = error;
	SET(cmd->c_flags, SCF_ITSDONE);

	DPRINTF(1,("%s: data transfer done (error=%d)\n",
	    DEVNAME(sc), cmd->c_error));
}

void
ommmc_read_data(struct ommmc_softc *sc, uint8_t *datap, int datalen)
{
	while (datalen > 3) {
		*(uint32_t *)datap = HREAD4(sc, MMCHS_DATA);
		datap += 4;
		datalen -= 4;
	}
	if (datalen > 0) {
		uint32_t rv = HREAD4(sc, MMCHS_DATA);
		do {
			*datap++ = rv & 0xff;
			rv = rv >> 8;
		} while (--datalen > 0);
	}
}

void
ommmc_write_data(struct ommmc_softc *sc, uint8_t *datap, int datalen)
{
	while (datalen > 3) {
		DPRINTF(3,("%08x\n", *(uint32_t *)datap));
		HWRITE4(sc, MMCHS_DATA, *((uint32_t *)datap));
		datap += 4;
		datalen -= 4;
	}
	if (datalen > 0) {
		uint32_t rv = *datap++;
		if (datalen > 1)
			rv |= *datap++ << 8;
		if (datalen > 2)
			rv |= *datap++ << 16;
		DPRINTF(3,("rv %08x\n", rv));
		HWRITE4(sc, MMCHS_DATA, rv);
	}
}

/* Prepare for another command. */
int
ommmc_soft_reset(struct ommmc_softc *sc, int mask)
{

	int timo;

	DPRINTF(1,("%s: software reset reg=%#x\n", DEVNAME(sc), mask));

	HSET4(sc, MMCHS_SYSCTL, mask);
	delay(10);
	for (timo = 1000; timo > 0; timo--) {
		if (!ISSET(HREAD4(sc, MMCHS_SYSCTL), mask))
			break;
		delay(10);
	}
	if (timo == 0) {
		DPRINTF(1,("%s: timeout reg=%#x\n", DEVNAME(sc),
		    HREAD4(sc, MMCHS_SYSCTL)));
		return (ETIMEDOUT);
	}

	return (0);
}

int
ommmc_wait_intr(struct ommmc_softc *sc, int mask, int timo)
{
	int status;
	int s;

	mask |= MMCHS_STAT_ERRI;

	s = splsdmmc();
	status = sc->intr_status & mask;
	while (status == 0) {
		if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo)
		    == EWOULDBLOCK) {
			status |= MMCHS_STAT_ERRI;
			break;
		}
		status = sc->intr_status & mask;
	}
	sc->intr_status &= ~status;

	DPRINTF(2,("%s: intr status %#x error %#x\n", DEVNAME(sc), status,
	    sc->intr_error_status));

	/* Command timeout has higher priority than command complete. */
	if (ISSET(status, MMCHS_STAT_ERRI)) {
		sc->intr_error_status = 0;
		(void)ommmc_soft_reset(sc, MMCHS_SYSCTL_SRC|MMCHS_SYSCTL_SRD);
		status = 0;
	}

	splx(s);
	return (status);
}

/*
 * Established by attachment driver at interrupt priority IPL_SDMMC.
 */
int
ommmc_intr(void *arg)
{
	struct ommmc_softc *sc = arg;

	uint32_t status;

	/* Find out which interrupts are pending. */
	status = HREAD4(sc, MMCHS_STAT);

	/* Acknowledge the interrupts we are about to handle. */
	HWRITE4(sc, MMCHS_STAT, status);
	DPRINTF(2,("%s: interrupt status=%b\n", DEVNAME(sc),
	    status, MMCHS_STAT_FMT));

	/*
	 * Service error interrupts.
	 */
	if (ISSET(status, MMCHS_STAT_ERRI)) {
		if (ISSET(status, MMCHS_STAT_CTO|
		    MMCHS_STAT_DTO)) {
			sc->intr_status |= status;
			sc->intr_error_status |= status & 0xffff0000;
			wakeup(&sc->intr_status);
		}
	}

#if 0
	/*
	 * Wake up the sdmmc event thread to scan for cards.
	 */
	if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION))
		ommmc_needs_discover(sc->sdmmc);
#endif

	/*
	 * Wake up the blocking process to service command
	 * related interrupt(s).
	 */
	if (ISSET(status, MMCHS_STAT_BRR|
	    MMCHS_STAT_BWR|MMCHS_STAT_TC|
	    MMCHS_STAT_CC)) {
		sc->intr_status |= status;
		wakeup(&sc->intr_status);
	}

	/*
	 * Service SD card interrupts.
	 */
	if (ISSET(status, MMCHS_STAT_CIRQ)) {
		DPRINTF(0,("%s: card interrupt\n", DEVNAME(sc)));
		HCLR4(sc, MMCHS_STAT, MMCHS_STAT_CIRQ);
		sdmmc_card_intr(sc->sdmmc);
	}
	return 1;
}


code:
/*	$OpenBSD: omohci.c,v 1.3 2014/05/19 13:11:31 mpi Exp $ */

/*
 * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/timeout.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>

#include <dev/usb/ohcireg.h>
#include <dev/usb/ohcivar.h>
#include <armv7/omap/prcmvar.h>

#define HOSTUEADDR		0x0E0
#define HOSTUESTATUS		0x0E4
#define HOSTTIMEOUTCTRL		0x0E8
#define HOSTREVISION		0x0EC
#define WHM_REVID_REGISTER	0x0F4
#define WHM_TEST_OBSV		0x0F8
#define WHM_TEST_CTL		0x0FC
#define HHC_TEST_CFG		0x100
#define HHC_TEST_CTL		0x104
#define HHC_TEST_OBSV		0x108
#define REVDEV			0x200 /* 16 bit */
#define EP_NUM			0x204 /* 16 bit */
#define DATA			0x208 /* 16 bit */
#define CTRL			0x20C /* 16 bit */
#define STAT_FLG		0x210 /* 16 bit */
#define RXFSTAT			0x214 /* 16 bit */
#define SYSCON1			0x218 /* 16 bit */
#define SYSCON2			0x21C /* 16 bit */
#define DEVSTAT			0x220 /* 16 bit */
#define SOFREG			0x224 /* 16 bit */
#define IRQ_EN			0x228 /* 16 bit */
#define DMA_IRQ_EN		0x22C /* 16 bit */
#define IRQ_SRC			0x230 /* 16 bit */
#define EPN_STAT		0x234 /* 16 bit */
#define DMAN_STAT		0x238 /* 16 bit */
#define RXDMA_CFG		0x240 /* 16 bit */
#define TXDMA_CFG		0x244 /* 16 bit */

#define TXDMA0			0x250
#define TXDMA1		0x254
#define TXDMA2		0x258
#define RXDMA0		0x260
#define RXDMA1		0x264
#define RXDMA2		0x268

#define EP0	 	0x280
#define EP_RX(x)	0x280 + (x * 4)
#define EP_TX(x)	0x2C0 + (x * 4)

#define OTG_REV		0x300
#define OTG_SYSCON_1	0x304
#define OTG_SYSCON_2	0x308
#define		OTG_SYSCON2_OTG_EN		0x80000000
#define		OTG_SYSCON2_UHOST_EN		0x00000100
#define		OTG_SYSCON2_MODE_DISABLED	0x00000000
#define		OTG_SYSCON2_MODE_CLIENT		0x00000001
#define		OTG_SYSCON2_MODE_HOST		0x00000004
#define OTG_CTRL	0x30C
#if 0
#define OTG_IRQ_EN	0x310 /* 16 bit */
#define OTG_IRQ_SRC	0x314 /* 16 bit */
#define OTG_OUTCTRL	0x318 /* 16 bit */
#define OTG_TEST	0x320 /* 16 bit */
#endif
#define OTG_VC		0x3FC

int	omohci_match(struct device *, void *, void *);
void	omohci_attach(struct device *, struct device *, void *);
int	omohci_detach(struct device *, int);
int	omohci_activate(struct device *, int);

struct omohci_softc {
	struct ohci_softc	sc;
	void			*sc_ihc0;
	void			*sc_ihc1;
	void			*sc_ihc2;
	void			*sc_ih0;
	void			*sc_ih1;
	void			*sc_ihotg;
};

void	omohci_enable(struct omohci_softc *);
void	omohci_disable(struct omohci_softc *);

struct cfattach omohci_ca = {
        sizeof (struct omohci_softc), omohci_match, omohci_attach,
	omohci_detach, omohci_detach
};

int
omohci_match(struct device *parent, void *match, void *aux)
{
#if 0
	if ((cputype & ~CPU_ID_XSCALE_COREREV_MASK) != CPU_ID_PXA27X)
		return (0);
#endif

	return (1);
}

void
omohci_attach(struct device *parent, struct device *self, void *aux)
{
	struct omohci_softc	*sc = (struct omohci_softc *)self;
        struct ahb_attach_args	*aa = aux;
	usbd_status		r;

	sc->sc.iot = aa->aa_iot;
	sc->sc.sc_bus.dmatag = aa->aa_dmat;
	sc->sc_ih0 = NULL;
	sc->sc_ih1 = NULL;
	sc->sc_ihc0 = NULL;
	sc->sc_ihc1 = NULL;
	sc->sc_ihc2 = NULL;
	sc->sc_ihotg = NULL;
	sc->sc.sc_size = 0;

	/* Map I/O space */
	if (bus_space_map(sc->sc.iot, aa->aa_addr, aa->aa_size, 0,
	    &sc->sc.ioh)) {
		printf(": cannot map mem space\n");
		return;
	}
	sc->sc.sc_size = aa->aa_size;

	/* XXX copied from ohci_pci.c. needed? */
	bus_space_barrier(sc->sc.iot, sc->sc.ioh, 0, sc->sc.sc_size,
	    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);

#if 0
	/* start the usb clock */
	pxa2x0_clkman_config(CKEN_USBHC, 1);
#endif
	omohci_enable(sc);

	/* Disable interrupts, so we don't get any spurious ones. */
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OHCI_INTERRUPT_DISABLE,
	    OHCI_MIE);

	sc->sc_ihc0 = arm_intr_establish(aa->aa_intr, IPL_USB,
	    ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
	sc->sc_ihc1 = arm_intr_establish(aa->aa_intr+1, IPL_USB,
	    ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
	sc->sc_ihc2 = arm_intr_establish(aa->aa_intr+2, IPL_USB,
	    ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
	sc->sc_ih0 = arm_intr_establish(aa->aa_intr+3, IPL_USB,
	    ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
	sc->sc_ih1 = arm_intr_establish(aa->aa_intr+4, IPL_USB,
	    ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
	sc->sc_ihotg = arm_intr_establish(aa->aa_intr+5, IPL_USB,
	    ohci_intr, &sc->sc, sc->sc.sc_bus.bdev.dv_xname);
	if (sc->sc_ih0 == NULL ||
	    sc->sc_ih1 == NULL ||
	    sc->sc_ihc0 == NULL ||
	    sc->sc_ihc1 == NULL ||
	    sc->sc_ihc2 == NULL ||
	    sc->sc_ihotg == NULL) {
		printf(": unable to establish interrupt\n");
		omohci_disable(sc);
#if 0
		pxa2x0_clkman_config(CKEN_USBHC, 0);
#endif
		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
		sc->sc.sc_size = 0;
		return;
	}

	prcm_enablemodule(PRCM_USB);

	bus_space_write_4(sc->sc.iot, sc->sc.ioh, OTG_SYSCON_2,
	    OTG_SYSCON2_UHOST_EN | OTG_SYSCON2_MODE_HOST);

	strlcpy(sc->sc.sc_vendor, "OMAP24xx", sizeof(sc->sc.sc_vendor));
	r = ohci_init(&sc->sc);
	if (r != USBD_NORMAL_COMPLETION) {
		printf("%s: init failed, error=%d\n",
		    sc->sc.sc_bus.bdev.dv_xname, r);
		arm_intr_disestablish(sc->sc_ih0);
		arm_intr_disestablish(sc->sc_ih1);
		arm_intr_disestablish(sc->sc_ihc0);
		arm_intr_disestablish(sc->sc_ihc1);
		arm_intr_disestablish(sc->sc_ihc2);
		arm_intr_disestablish(sc->sc_ihotg);
		sc->sc_ih0 = NULL;
		sc->sc_ih1 = NULL;
		sc->sc_ihc0 = NULL;
		sc->sc_ihc1 = NULL;
		sc->sc_ihc2 = NULL;
		sc->sc_ihotg = NULL;
		omohci_disable(sc);
#if 0
		pxa2x0_clkman_config(CKEN_USBHC, 0);
#endif
		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
		sc->sc.sc_size = 0;
		return;
	}

	config_found(self, &sc->sc.sc_bus, usbctlprint);
}

int
omohci_detach(struct device *self, int flags)
{
	struct omohci_softc		*sc = (struct omohci_softc *)self;
	int				rv;

	rv = ohci_detach(self, flags);
	if (rv)
		return (rv);

	if (sc->sc_ih0 != NULL) {
		arm_intr_disestablish(sc->sc_ih0);
		arm_intr_disestablish(sc->sc_ih1);
		arm_intr_disestablish(sc->sc_ihc0);
		arm_intr_disestablish(sc->sc_ihc1);
		arm_intr_disestablish(sc->sc_ihc2);
		arm_intr_disestablish(sc->sc_ihotg);
		sc->sc_ih0 = NULL;
		sc->sc_ih1 = NULL;
		sc->sc_ihc0 = NULL;
		sc->sc_ihc1 = NULL;
		sc->sc_ihc2 = NULL;
		sc->sc_ihotg = NULL;
	}

	omohci_disable(sc);

	/* stop clock */
#if 0
	pxa2x0_clkman_config(CKEN_USBHC, 0);
#endif

	if (sc->sc.sc_size) {
		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
		sc->sc.sc_size = 0;
	}

	return (0);
}

int
omohci_activate(struct device *self, int act)
{
	struct omohci_softc *sc = (struct omohci_softc *)self;

	switch (act) {
	case DVACT_SUSPEND:
		sc->sc.sc_bus.use_polling++;
		ohci_power(why, &sc->sc);
#if 0
		pxa2x0_clkman_config(CKEN_USBHC, 0);
#endif
		sc->sc.sc_bus.use_polling--;
		break;

	case DVACT_RESUME:
		sc->sc.sc_bus.use_polling++;
#if 0
		pxa2x0_clkman_config(CKEN_USBHC, 1);
#endif
		omohci_enable(sc);
		ohci_power(why, &sc->sc);
		sc->sc.sc_bus.use_polling--;
		break;
	}
	return 0;
}

void
omohci_enable(struct omohci_softc *sc)
{
#if 0
	u_int32_t			hr;

	/* Full host reset */
	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) | USBHC_HR_FHR);

	DELAY(USBHC_RST_WAIT);

	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR));

	/* Force system bus interface reset */
	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) | USBHC_HR_FSBIR);

	while (bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR) & \
	    USBHC_HR_FSBIR)
		DELAY(3);

	/* Enable the ports (physically only one, only enable that one?) */
	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) & ~(USBHC_HR_SSE));
	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) & ~(USBHC_HR_SSEP2));
#endif
}

void
omohci_disable(struct omohci_softc *sc)
{
#if 0
	u_int32_t			hr;

	/* Full host reset */
	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) | USBHC_HR_FHR);

	DELAY(USBHC_RST_WAIT);

	hr = bus_space_read_4(sc->sc.iot, sc->sc.ioh, USBHC_HR);
	bus_space_write_4(sc->sc.iot, sc->sc.ioh, USBHC_HR,
	    (hr & USBHC_HR_MASK) & ~(USBHC_HR_FHR));
#endif
}

license: unknown


code:
/* $OpenBSD: omusbtll.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */
/*
 * Copyright (c) 2010 Dale Rahn <drahn@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/evcount.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <armv7/armv7/armv7var.h>
#include <armv7/omap/prcmvar.h>

/* registers */
#define USBTLL_REVISION			0x0000
#define USBTLL_SYSCONFIG		0x0010
#define USBTLL_SYSSTATUS		0x0014
#define USBTLL_IRQSTATUS		0x0018
#define USBTLL_IRQENABLE		0x001C
#define USBTLL_SHARED_CONF			0x0030
#define  USBTLL_SHARED_CONF_USB_90D_DDR_EN	(1<<6)
#define  USBTLL_SHARED_CONF_USB_180D_SDR_EN	(1<<5)
#define  USBTLL_SHARED_CONF_USB_DIVRATIO_SH	2
#define  USBTLL_SHARED_CONF_FCLK_REQ		(1<<1)
#define  USBTLL_SHARED_CONF_FCLK_IS_ON		(1<<0)

#define USBTLL_CHANNEL_CONF_(i)		(0x0040 + (0x04 * (i)))
#define  USBTLL_CHANNEL_CONF_FSLSLINESTATE_SH       	28
#define  USBTLL_CHANNEL_CONF_FSLSMODE_SH   		24
#define  USBTLL_CHANNEL_CONF_TESTTXSE0   		(1<<20)
#define  USBTLL_CHANNEL_CONF_TESTTXDAT   		(1<<19)
#define  USBTLL_CHANNEL_CONF_TESTTXEN   		(1<<18)
#define  USBTLL_CHANNEL_CONF_TESTEN   			(1<<17)
#define  USBTLL_CHANNEL_CONF_DRVVBUS   			(1<<16)
#define  USBTLL_CHANNEL_CONF_CHRGVBUS   		(1<<15)
#define  USBTLL_CHANNEL_CONF_ULPINOBITSTUFF   		(1<<11)
#define  USBTLL_CHANNEL_CONF_ULPIAUTOIDLE   		(1<<10)
#define  USBTLL_CHANNEL_CONF_UTMIAUTOIDLE   		(1<<9)
#define  USBTLL_CHANNEL_CONF_ULPIDDRMODE     		(1<<8)
#define  USBTLL_CHANNEL_CONF_LPIOUTCLKMODE   		(1<<7)
#define  USBTLL_CHANNEL_CONF_TLLFULLSPEED   		(1<<6)
#define  USBTLL_CHANNEL_CONF_TLLCONNECT   		(1<<5)
#define  USBTLL_CHANNEL_CONF_TLLATTACH   		(1<<4)
#define  USBTLL_CHANNEL_CONF_UTMIISADEV       		(1<<3)
#define  USBTLL_CHANNEL_CONF_CHANMODE_SH    		1
#define  USBTLL_CHANNEL_CONF_CHANEN			(1<<0)

/*
ULPI_VENDOR_ID_LO_(i)		(0x0800 + (0x100 * (i)))
ULPI_VENDOR_ID_HI_(i)		(0x0801 + (0x100 * (i)))
ULPI_PRODUCT_ID_LO_(i)		(0x0802 + (0x100 * (i)))
ULPI_PRODUCT_ID_HI_(i)		(0x0803 + (0x100 * (i)))
ULPI_FUNCTION_CTRL_(i)		(0x0804 + (0x100 * (i)))
ULPI_FUNCTION_CTRL_SET_(i)	(0x0805 + (0x100 * (i)))
ULPI_FUNCTION_CTRL_CLR_(i)	(0x0806 + (0x100 * (i)))
ULPI_INTERFACE_CTRL_(i)		(0x0807 + (0x100 * (i)))
ULPI_INTERFACE_CTRL_SET_(i)	(0x0808 + (0x100 * (i)))
ULPI_INTERFACE_CTRL_CLR_(i)	(0x0809 + (0x100 * (i)))
ULPI_OTG_CTRL_(i)		(0x080A + (0x100 * (i)))
ULPI_OTG_CTRL_SET_(i)		(0x080B + (0x100 * (i)))
ULPI_OTG_CTRL_CLR_(i)		(0x080C + (0x100 * (i)))
ULPI_USB_INT_EN_RISE_(i)	(0x080D + (0x100 * (i)))
ULPI_USB_INT_EN_RISE_SET_(i)	(0x080E + (0x100 * (i)))
ULPI_USB_INT_EN_RISE_CLR_(i)	(0x080F + (0x100 * (i)))
ULPI_USB_INT_EN_FALL_(i)	(0x0810 + (0x100 * (i)))
ULPI_USB_INT_EN_FALL_SET_(i)	(0x0811 + (0x100 * (i)))
ULPI_USB_INT_EN_FALL_CLR_(i)	(0x0812 + (0x100 * (i)))
ULPI_USB_INT_STATUS_(i)		(0x0813 + (0x100 * (i)))
ULPI_USB_INT_LATCH_(i)		(0x0814 + (0x100 * (i)))
*/

struct omusbtll_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

void omusbtll_attach(struct device *parent, struct device *self, void *args);
void omusbtll_init(uint32_t channel_mask);

struct cfattach	omusbtll_ca = {
	sizeof (struct omusbtll_softc), NULL, omusbtll_attach
};

struct cfdriver omusbtll_cd = {
	NULL, "omusbtll", DV_DULL
};

struct omusbtll_softc *omusbtll_sc;
void
omusbtll_attach(struct device *parent, struct device *self, void *args)
{
	struct omusbtll_softc *sc = (struct omusbtll_softc *) self;
	struct armv7_attach_args *aa = args;
	u_int32_t rev;

	sc->sc_iot = aa->aa_iot;
	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) {
		printf("%s: bus_space_map failed!\n", __func__);
		return;
	}

#if 0
	prcm_enablemodule(PRCM_USBHOST1);
	prcm_enablemodule(PRCM_USBHOST2);
#endif
	prcm_enablemodule(PRCM_USBTLL);

	delay(10000);

	//return;
#if 1
	rev = bus_space_read_1(sc->sc_iot, sc->sc_ioh, USBTLL_SYSCONFIG);

	printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
#endif

	omusbtll_sc = sc;

	omusbtll_init(0x3);
}

void omusbtll_init(uint32_t channel_mask)
{
	int i;
	uint32_t val;
	/* global reacharound */
	struct omusbtll_softc *sc = omusbtll_sc;

	for(i = 0; i < 3; i++) {
		val = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
		    USBTLL_CHANNEL_CONF_(i));
		val &= ~(USBTLL_CHANNEL_CONF_ULPINOBITSTUFF |
		    USBTLL_CHANNEL_CONF_ULPIAUTOIDLE |
		    USBTLL_CHANNEL_CONF_ULPIDDRMODE);
		bus_space_write_4(sc->sc_iot, sc->sc_ioh,
		    USBTLL_CHANNEL_CONF_(i), val);
	}

	val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, USBTLL_SHARED_CONF);
	val |= (USBTLL_SHARED_CONF_USB_180D_SDR_EN |
	    (1 << USBTLL_SHARED_CONF_USB_DIVRATIO_SH) |
	    USBTLL_SHARED_CONF_FCLK_IS_ON);
	val &= ~(USBTLL_SHARED_CONF_USB_90D_DDR_EN);
	bus_space_write_4(sc->sc_iot, sc->sc_ioh, USBTLL_SHARED_CONF, val);

	for (i = 0; i < 3; i++) {
		if (channel_mask & (1<<i)) {
			val = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
			    USBTLL_CHANNEL_CONF_(i));

			val |= USBTLL_CHANNEL_CONF_CHANEN;
			bus_space_write_4(sc->sc_iot, sc->sc_ioh,
			    USBTLL_CHANNEL_CONF_(i), val);
		printf("usbtll enabling %d\n", i);
		}
	}
}


code:
/* $OpenBSD: prcm.c,v 1.9 2014/05/08 21:17:01 miod Exp $ */
/*
 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*-
 * Copyright (c) 2011
 *	Ben Gray <ben.r.gray@gmail.com>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Driver for the Power, Reset and Clock Management Module (PRCM).
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/time.h>
#include <sys/device.h>

#include <machine/bus.h>
#include <machine/intr.h>
#include <arm/cpufunc.h>
#include <armv7/armv7/armv7var.h>
#include <armv7/omap/prcmvar.h>

#include <armv7/omap/am335x_prcmreg.h>
#include <armv7/omap/omap3_prcmreg.h>
#include <armv7/omap/omap4_prcmreg.h>

#define PRCM_REVISION		0x0800
#define PRCM_SYSCONFIG		0x0810

uint32_t prcm_imask_mask[PRCM_REG_MAX];
uint32_t prcm_fmask_mask[PRCM_REG_MAX];
uint32_t prcm_imask_addr[PRCM_REG_MAX];
uint32_t prcm_fmask_addr[PRCM_REG_MAX];

#define SYS_CLK		13 /* SYS_CLK speed in MHz */

struct prcm_softc {
	struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_prcm;
	bus_space_handle_t	sc_cm1;
	bus_space_handle_t	sc_cm2;
	void (*sc_setup)(struct prcm_softc *sc);
	void (*sc_enablemodule)(struct prcm_softc *sc, int mod);
	void (*sc_setclock)(struct prcm_softc *sc,
	    int clock, int speed);
	uint32_t		cm1_avail;
	uint32_t		cm2_avail;
};

int	prcm_match(struct device *, void *, void *);
void	prcm_attach(struct device *, struct device *, void *);
int	prcm_setup_dpll5(struct prcm_softc *);
uint32_t prcm_v3_bit(int mod);
uint32_t prcm_am335x_clkctrl(int mod);

void prcm_am335x_enablemodule(struct prcm_softc *, int);
void prcm_am335x_setclock(struct prcm_softc *, int, int);

void prcm_v3_setup(struct prcm_softc *);
void prcm_v3_enablemodule(struct prcm_softc *, int);
void prcm_v3_setclock(struct prcm_softc *, int, int);

void prcm_v4_enablemodule(struct prcm_softc *, int);
int prcm_v4_hsusbhost_activate(int);
int prcm_v4_hsusbhost_set_source(int, int);

struct cfattach	prcm_ca = {
	sizeof (struct prcm_softc), NULL, prcm_attach
};

struct cfdriver prcm_cd = {
	NULL, "prcm", DV_DULL
};

void
prcm_attach(struct device *parent, struct device *self, void *args)
{
	struct armv7_attach_args *aa = args;
	struct prcm_softc *sc = (struct prcm_softc *) self;
	u_int32_t reg;

	sc->sc_iot = aa->aa_iot;

	switch (board_id) {
	case BOARD_ID_AM335X_BEAGLEBONE:
		sc->sc_setup = NULL;
		sc->sc_enablemodule = prcm_am335x_enablemodule;
		sc->sc_setclock = prcm_am335x_setclock;
		break;
	case BOARD_ID_OMAP3_BEAGLE:
	case BOARD_ID_OMAP3_OVERO:
		sc->sc_setup = prcm_v3_setup;
		sc->sc_enablemodule = prcm_v3_enablemodule;
		sc->sc_setclock = prcm_v3_setclock;
		break;
	case BOARD_ID_OMAP4_PANDA:
		sc->sc_setup = NULL;
		sc->sc_enablemodule = prcm_v4_enablemodule;
		sc->sc_setclock = NULL;
		sc->cm1_avail = 1;
		sc->cm2_avail = 1;
		break;
	}

	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_prcm))
		panic("prcm_attach: bus_space_map failed!");

	if (sc->cm1_avail &&
	    bus_space_map(sc->sc_iot, aa->aa_dev->mem[1].addr,
	    aa->aa_dev->mem[1].size, 0, &sc->sc_cm1))
		panic("prcm_attach: bus_space_map failed!");

	if (sc->cm2_avail &&
	    bus_space_map(sc->sc_iot, aa->aa_dev->mem[2].addr,
	    aa->aa_dev->mem[2].size, 0, &sc->sc_cm2))
		panic("prcm_attach: bus_space_map failed!");

	reg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, PRCM_REVISION);
	printf(" rev %d.%d\n", reg >> 4 & 0xf, reg & 0xf);

	if (sc->sc_setup != NULL)
		sc->sc_setup(sc);
}

void
prcm_v3_setup(struct prcm_softc *sc)
{
	/* Setup the 120MHZ DPLL5 clock, to be used by USB. */
	prcm_setup_dpll5(sc);

	prcm_fmask_mask[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_FMASK;
	prcm_imask_mask[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_IMASK;
	prcm_fmask_addr[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_FADDR;
	prcm_imask_addr[PRCM_REG_CORE_CLK1] = PRCM_REG_CORE_CLK1_IADDR;

	prcm_fmask_mask[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_FMASK;
	prcm_imask_mask[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_IMASK;
	prcm_fmask_addr[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_FADDR;
	prcm_imask_addr[PRCM_REG_CORE_CLK2] = PRCM_REG_CORE_CLK2_IADDR;

	prcm_fmask_mask[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_FMASK;
	prcm_imask_mask[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_IMASK;
	prcm_fmask_addr[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_FADDR;
	prcm_imask_addr[PRCM_REG_CORE_CLK3] = PRCM_REG_CORE_CLK3_IADDR;

	prcm_fmask_mask[PRCM_REG_WKUP] = PRCM_REG_WKUP_FMASK;
	prcm_imask_mask[PRCM_REG_WKUP] = PRCM_REG_WKUP_IMASK;
	prcm_fmask_addr[PRCM_REG_WKUP] = PRCM_REG_WKUP_FADDR;
	prcm_imask_addr[PRCM_REG_WKUP] = PRCM_REG_WKUP_IADDR;

	prcm_fmask_mask[PRCM_REG_PER] = PRCM_REG_PER_FMASK;
	prcm_imask_mask[PRCM_REG_PER] = PRCM_REG_PER_IMASK;
	prcm_fmask_addr[PRCM_REG_PER] = PRCM_REG_PER_FADDR;
	prcm_imask_addr[PRCM_REG_PER] = PRCM_REG_PER_IADDR;

	prcm_fmask_mask[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_FMASK;
	prcm_imask_mask[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_IMASK;
	prcm_fmask_addr[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_FADDR;
	prcm_imask_addr[PRCM_REG_USBHOST] = PRCM_REG_USBHOST_IADDR;
}

void
prcm_setclock(int clock, int speed)
{
	struct prcm_softc *sc = prcm_cd.cd_devs[0];

	if (!sc->sc_setclock)
		panic("%s: not initialised!", __func__);

	sc->sc_setclock(sc, clock, speed);
}

void
prcm_am335x_setclock(struct prcm_softc *sc, int clock, int speed)
{
	u_int32_t oreg, reg, mask;

	/* set CLKSEL register */
	if (clock == 1) {
		oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm,
		    PRCM_AM335X_CLKSEL_TIMER2_CLK);
		mask = 3;
		reg = oreg & ~mask;
		reg |=0x02;
		bus_space_write_4(sc->sc_iot, sc->sc_prcm,
		    PRCM_AM335X_CLKSEL_TIMER2_CLK, reg);
	} else if (clock == 2) {
		oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm,
		    PRCM_AM335X_CLKSEL_TIMER3_CLK);
		mask = 3;
		reg = oreg & ~mask;
		reg |=0x02;
		bus_space_write_4(sc->sc_iot, sc->sc_prcm,
		    PRCM_AM335X_CLKSEL_TIMER3_CLK, reg);
	}
}

void
prcm_v3_setclock(struct prcm_softc *sc, int clock, int speed)
{
	u_int32_t oreg, reg, mask;

	if (clock == 1) {
		oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_WKUP);
		mask = 1;
		reg = (oreg &~mask) | (speed & mask);
		bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_WKUP, reg);
	} else if (clock >= 2 && clock <= 9) {
		int shift =  (clock-2);
		oreg = bus_space_read_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_PER);
		mask = 1 << (shift);
		reg =  (oreg & ~mask) | ( (speed << shift) & mask);
		bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL_PER, reg);
	} else
		panic("%s: invalid clock %d", __func__, clock);
}

uint32_t
prcm_v3_bit(int mod)
{
	switch(mod) {
	case PRCM_MMC0:
		return PRCM_CLK_EN_MMC1;
	case PRCM_MMC1:
		return PRCM_CLK_EN_MMC2;
	case PRCM_MMC2:
		return PRCM_CLK_EN_MMC3;
	case PRCM_USB:
		return PRCM_CLK_EN_USB;
	case PRCM_GPIO0:
		return PRCM_CLK_EN_GPIO1;
	case PRCM_GPIO1:
		return PRCM_CLK_EN_GPIO2;
	case PRCM_GPIO2:
		return PRCM_CLK_EN_GPIO3;
	case PRCM_GPIO3:
		return PRCM_CLK_EN_GPIO4;
	case PRCM_GPIO4:
		return PRCM_CLK_EN_GPIO5;
	case PRCM_GPIO5:
		return PRCM_CLK_EN_GPIO6;
	default:
		panic("%s: module not found\n", __func__);
	}
}

uint32_t
prcm_am335x_clkctrl(int mod)
{
	switch(mod) {
	case PRCM_TIMER2:
		return PRCM_AM335X_TIMER2_CLKCTRL;
	case PRCM_TIMER3:
		return PRCM_AM335X_TIMER3_CLKCTRL;
	case PRCM_MMC0:
		return PRCM_AM335X_MMC0_CLKCTRL;
	case PRCM_MMC1:
		return PRCM_AM335X_MMC1_CLKCTRL;
	case PRCM_MMC2:
		return PRCM_AM335X_MMC2_CLKCTRL;
	case PRCM_USB:
		return PRCM_AM335X_USB0_CLKCTRL;
	case PRCM_GPIO0:
		return PRCM_AM335X_GPIO0_CLKCTRL;
	case PRCM_GPIO1:
		return PRCM_AM335X_GPIO1_CLKCTRL;
	case PRCM_GPIO2:
		return PRCM_AM335X_GPIO2_CLKCTRL;
	case PRCM_GPIO3:
		return PRCM_AM335X_GPIO3_CLKCTRL;
	case PRCM_TPCC:
		return PRCM_AM335X_TPCC_CLKCTRL;
	case PRCM_TPTC0:
		return PRCM_AM335X_TPTC0_CLKCTRL;
	case PRCM_TPTC1:
		return PRCM_AM335X_TPTC1_CLKCTRL;
	case PRCM_TPTC2:
		return PRCM_AM335X_TPTC2_CLKCTRL;
	case PRCM_I2C0:
		return PRCM_AM335X_I2C0_CLKCTRL;
	case PRCM_I2C1:
		return PRCM_AM335X_I2C1_CLKCTRL;
	case PRCM_I2C2:
		return PRCM_AM335X_I2C2_CLKCTRL;
	default:
		panic("%s: module not found\n", __func__);
	}
}

void
prcm_enablemodule(int mod)
{
	struct prcm_softc *sc = prcm_cd.cd_devs[0];

	if (!sc->sc_enablemodule)
		panic("%s: not initialised!", __func__);

	sc->sc_enablemodule(sc, mod);
}

void
prcm_am335x_enablemodule(struct prcm_softc *sc, int mod)
{
	uint32_t clkctrl;
	int reg;

	/*set enable bits in CLKCTRL register */
	reg = prcm_am335x_clkctrl(mod);
	clkctrl = bus_space_read_4(sc->sc_iot, sc->sc_prcm, reg);
	clkctrl &=~AM335X_CLKCTRL_MODULEMODE_MASK;
	clkctrl |= AM335X_CLKCTRL_MODULEMODE_ENABLE;
	bus_space_write_4(sc->sc_iot, sc->sc_prcm, reg, clkctrl);

	/* wait until module is enabled */
	while (bus_space_read_4(sc->sc_iot, sc->sc_prcm, reg) & 0x30000)
		;
}

void
prcm_v3_enablemodule(struct prcm_softc *sc, int mod)
{
	uint32_t bit;
	uint32_t fclk, iclk, fmask, imask, mbit;
	int freg, ireg, reg;

	bit = prcm_v3_bit(mod);
	reg = bit >> 5;

	freg = prcm_fmask_addr[reg];
	ireg = prcm_imask_addr[reg];
	fmask = prcm_fmask_mask[reg];
	imask = prcm_imask_mask[reg];

	mbit = 1 << (bit & 0x1f);
	if (fmask & mbit) { /* dont access the register if bit isn't present */
		fclk = bus_space_read_4(sc->sc_iot, sc->sc_prcm, freg);
		bus_space_write_4(sc->sc_iot, sc->sc_prcm, freg, fclk | mbit);
	}
	if (imask & mbit) { /* dont access the register if bit isn't present */
		iclk = bus_space_read_4(sc->sc_iot, sc->sc_prcm, ireg);
		bus_space_write_4(sc->sc_iot, sc->sc_prcm, ireg, iclk | mbit);
	}
	printf("\n");
}

void
prcm_v4_enablemodule(struct prcm_softc *sc, int mod)
{
	switch (mod) {
		case PRCM_MMC0:
			break;
		case PRCM_USBP1_PHY:
		case PRCM_USBP2_PHY:
			prcm_v4_hsusbhost_set_source(mod, 0);
		case PRCM_USB:
		case PRCM_USBTLL:
		case PRCM_USBP1_UTMI:
		case PRCM_USBP1_HSIC:
		case PRCM_USBP2_UTMI:
		case PRCM_USBP2_HSIC:
			prcm_v4_hsusbhost_activate(mod);
			return;
		case  PRCM_GPIO0:
		case  PRCM_GPIO1:
		case  PRCM_GPIO2:
		case  PRCM_GPIO3:
		case  PRCM_GPIO4:
		case  PRCM_GPIO5:
			/* XXX */
			break;
	default:
		panic("%s: module not found\n", __func__);
	}
}

int
prcm_v4_hsusbhost_activate(int type)
{
	struct prcm_softc *sc = prcm_cd.cd_devs[0];
	uint32_t i;
	uint32_t clksel_reg_off;
	uint32_t clksel, oclksel;

	switch (type) {
		case PRCM_USB:
		case PRCM_USBP1_PHY:
		case PRCM_USBP2_PHY:
			/* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
			clksel_reg_off = O4_L3INIT_CM2_OFFSET + 0x58;
			clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off);
			oclksel = clksel;
			/* Enable the module and also enable the optional func clocks */
			if (type == PRCM_USB) {
				clksel &= ~O4_CLKCTRL_MODULEMODE_MASK;
				clksel |=  /*O4_CLKCTRL_MODULEMODE_ENABLE*/2;

				clksel |= (0x1 << 15); /* USB-HOST clock control: FUNC48MCLK */
			}

			break;

		default:
			panic("%s: invalid type %d", __func__, type);
			return (EINVAL);
	}
	bus_space_write_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off, clksel);

	/* Try MAX_MODULE_ENABLE_WAIT number of times to check if enabled */
	for (i = 0; i < O4_MAX_MODULE_ENABLE_WAIT; i++) {
		clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off);
		if ((clksel & O4_CLKCTRL_IDLEST_MASK) == O4_CLKCTRL_IDLEST_ENABLED)
			break;
	}

	/* Check the enabled state */
	if ((clksel & O4_CLKCTRL_IDLEST_MASK) != O4_CLKCTRL_IDLEST_ENABLED) {
		printf("Error: HERE failed to enable module with clock %d\n", type);
		printf("Error: 0x%08x => 0x%08x\n", clksel_reg_off, clksel);
		return (ETIMEDOUT);
	}

	return (0);
}

int
prcm_v4_hsusbhost_set_source(int clk, int clksrc)
{
	struct prcm_softc *sc = prcm_cd.cd_devs[0];
	uint32_t clksel_reg_off;
	uint32_t clksel;
	unsigned int bit;

	if (clk == PRCM_USBP1_PHY)
		bit = 24;
	else if (clk != PRCM_USBP2_PHY)
		bit = 25;
	else
		return (-EINVAL);

	/* We need the CM_L3INIT_HSUSBHOST_CLKCTRL register in CM2 register set */
	clksel_reg_off = O4_L3INIT_CM2_OFFSET + 0x58;
	clksel = bus_space_read_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off);

	/* XXX: Set the clock source to either external or internal */
	if (clksrc == 0)
		clksel |= (0x1 << bit);
	else
		clksel &= ~(0x1 << bit);

	bus_space_write_4(sc->sc_iot, sc->sc_cm2, clksel_reg_off, clksel);

	return (0);
}

/*
 * OMAP35xx Power, Reset, and Clock Management Reference Guide
 * (sprufa5.pdf) and AM/DM37x Multimedia Device Technical Reference
 * Manual (sprugn4h.pdf) note that DPLL5 provides a 120MHz clock for
 * peripheral domain modules (page 107 and page 302).
 * The reference clock for DPLL5 is DPLL5_ALWON_FCLK which is
 * SYS_CLK, running at 13MHz.
 */
int
prcm_setup_dpll5(struct prcm_softc *sc)
{
	uint32_t val;

	/*
	 * We need to set the multiplier and divider values for PLL.
	 * To end up with 120MHz we take SYS_CLK, divide by it and multiply
	 * with 120 (sprugn4h.pdf, 13.4.11.4.1 SSC Configuration)
	 */
	val = ((120 & 0x7ff) << 8) | ((SYS_CLK - 1) & 0x7f);
	bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL4_PLL, val);

	/* Clock divider from the PLL to the 120MHz clock. */
	bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKSEL5_PLL, val);

	/*
	 * spruf98o.pdf, page 2319:
	 * PERIPH2_DPLL_FREQSEL is 0x7 1.75MHz to 2.1MHz
	 * EN_PERIPH2_DPLL is 0x7
	 */
	val = (7 << 4) | (7 << 0);
	bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_CLKEN2_PLL, val);

	/* Disable the interconnect clock auto-idle. */
	bus_space_write_4(sc->sc_iot, sc->sc_prcm, CM_AUTOIDLE2_PLL, 0x0);

	/* Wait until DPLL5 is locked and there's clock activity. */
	while ((val = bus_space_read_4(sc->sc_iot, sc->sc_prcm,
	    CM_IDLEST_CKGEN) & 0x01) == 0x00) {
#ifdef DIAGNOSTIC
		printf("CM_IDLEST_PLL = 0x%08x\n", val);
#endif
	}

	return 0;
}


code:
/* $OpenBSD: prcmvar.h,v 1.5 2014/03/18 07:34:17 syl Exp $ */
/*
 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

void prcm_setclock(int clock, int speed);
void prcm_enablemodule(int mod);
void prcm_disablemodule(int mod);

#define PRCM_CLK_SPEED_32	0
#define PRCM_CLK_SPEED_SYS	1

enum PRCM_MODULES {
	PRCM_TIMER0,
	PRCM_TIMER1,
	PRCM_TIMER2,
	PRCM_TIMER3,
	PRCM_GPIO0,
	PRCM_GPIO1,
	PRCM_GPIO2,
	PRCM_GPIO3,
	PRCM_GPIO4,
	PRCM_GPIO5,
	PRCM_TPCC,
	PRCM_TPTC0,
	PRCM_TPTC1,
	PRCM_TPTC2,
	PRCM_MMC0,
	PRCM_MMC1,
	PRCM_MMC2,
	PRCM_USB,
	PRCM_USBTLL,
	PRCM_USBP1_PHY,
	PRCM_USBP1_UTMI,
	PRCM_USBP1_HSIC,
	PRCM_USBP2_PHY,
	PRCM_USBP2_UTMI,
	PRCM_USBP2_HSIC,
	PRCM_I2C0,
	PRCM_I2C1,
	PRCM_I2C2,
};

#define PRCM_REG_MAX	6
/* need interface for CM_AUTOIDLE */


code:
/* $OpenBSD: sitara_cm.c,v 1.2 2013/11/06 19:03:07 syl Exp $ */
/* $NetBSD: sitara_cm.c,v 1.1 2013/04/17 14:31:02 bouyer Exp $ */
/*
 * Copyright (c) 2010
 *	Ben Gray <ben.r.gray@gmail.com>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Ben Gray.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * SCM - System Control Module
 *
 * Hopefully in the end this module will contain a bunch of utility functions
 * for configuring and querying the general system control registers, but for
 * now it only does pin(pad) multiplexing.
 *
 * This is different from the GPIO module in that it is used to configure the
 * pins between modules not just GPIO input/output.
 *
 * This file contains the generic top level driver, however it relies on chip
 * specific settings and therefore expects an array of sitara_cm_padconf structs
 * call ti_padconf_devmap to be located somewhere in the kernel.
 *
 */
#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <machine/bus.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/omap/sitara_cm.h>
#include <armv7/omap/sitara_cmreg.h>

void sitara_cm_attach(struct device *parent, struct device *self, void *aux);

struct sitara_cm_softc {
        struct device		sc_dev;
	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;
};

struct cfattach sitaracm_ca = {
	sizeof (struct sitara_cm_softc), NULL, sitara_cm_attach
};

struct cfdriver sitaracm_cd = {
	NULL, "sitaracm", DV_DULL
};

static struct sitara_cm_softc *sitara_cm_sc = NULL;

#define	sitara_cm_read_2(sc, reg)		\
    bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg))
#define	sitara_cm_write_2(sc, reg, val)		\
    bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define	sitara_cm_read_4(sc, reg)		\
    bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
#define	sitara_cm_write_4(sc, reg, val)		\
    bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))

/**
 *	ti_padconf_devmap - Array of pins, should be defined one per SoC
 *
 *	This array is typically defined in one of the targeted *_scm_pinumx.c
 *	files and is specific to the given SoC platform. Each entry in the array
 *	corresponds to an individual pin.
 */
extern const struct sitara_cm_device sitara_cm_dev;

/**
 *	sitara_cm_padconf_from_name - searches the list of pads and returns entry
 *	                             with matching ball name.
 *	@ballname: the name of the ball
 *
 *	RETURNS:
 *	A pointer to the matching padconf or NULL if the ball wasn't found.
 */
static const struct sitara_cm_padconf*
sitara_cm_padconf_from_name(const char *ballname)
{
	const struct sitara_cm_padconf *padconf;

	padconf = sitara_cm_dev.padconf;
	while (padconf->ballname != NULL) {
		if (strcmp(ballname, padconf->ballname) == 0)
			return(padconf);
		padconf++;
	}

	return (NULL);
}

/**
 *	sitara_cm_padconf_set_internal - sets the muxmode and state for a pad/pin
 *	@padconf: pointer to the pad structure
 *	@muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
 *	@state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
 *
 *
 *	LOCKING:
 *	Internally locks it's own context.
 *
 *	RETURNS:
 *	0 on success.
 *	EINVAL if pin requested is outside valid range or already in use.
 */
static int
sitara_cm_padconf_set_internal(struct sitara_cm_softc *sc,
    const struct sitara_cm_padconf *padconf,
    const char *muxmode, unsigned int state)
{
	unsigned int mode;
	uint16_t reg_val;

	/* populate the new value for the PADCONF register */
	reg_val = (uint16_t)(state & sitara_cm_dev.padconf_sate_mask);

	/* find the new mode requested */
	for (mode = 0; mode < 8; mode++) {
		if ((padconf->muxmodes[mode] != NULL) &&
		    (strcmp(padconf->muxmodes[mode], muxmode) == 0)) {
			break;
		}
	}

	/* couldn't find the mux mode */
	if (mode >= 8) {
		printf("%s: Invalid mux mode \"%s\"\n", __func__, muxmode);
		return (EINVAL);
	}

	/* set the mux mode */
	reg_val |= (uint16_t)(mode & sitara_cm_dev.padconf_muxmode_mask);

	/* write the register value (16-bit writes) */
	sitara_cm_write_2(sc, padconf->reg_off, reg_val);

	return (0);
}

/**
 *	sitara_cm_padconf_set - sets the muxmode and state for a pad/pin
 *	@padname: the name of the pad, i.e. "c12"
 *	@muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
 *	@state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
 *
 *
 *	LOCKING:
 *	Internally locks it's own context.
 *
 *	RETURNS:
 *	0 on success.
 *	EINVAL if pin requested is outside valid range or already in use.
 */
int
sitara_cm_padconf_set(const char *padname, const char *muxmode, unsigned int state)
{
	const struct sitara_cm_padconf *padconf;

	if (!sitara_cm_sc)
		return (ENXIO);

	/* find the pin in the devmap */
	padconf = sitara_cm_padconf_from_name(padname);
	if (padconf == NULL)
		return (EINVAL);

	return (
	    sitara_cm_padconf_set_internal(sitara_cm_sc, padconf, muxmode, state)
	    );
}

/**
 *	sitara_cm_padconf_get - gets the muxmode and state for a pad/pin
 *	@padname: the name of the pad, i.e. "c12"
 *	@muxmode: upon return will contain the name of the muxmode of the pin
 *	@state: upon return will contain the state of the pad/pin
 *
 *
 *	LOCKING:
 *	Internally locks it's own context.
 *
 *	RETURNS:
 *	0 on success.
 *	EINVAL if pin requested is outside valid range or already in use.
 */
int
sitara_cm_padconf_get(const char *padname, const char **muxmode,
    unsigned int *state)
{
	const struct sitara_cm_padconf *padconf;
	uint16_t reg_val;

	if (!sitara_cm_sc)
		return (ENXIO);

	/* find the pin in the devmap */
	padconf = sitara_cm_padconf_from_name(padname);
	if (padconf == NULL)
		return (EINVAL);

	/* read the register value (16-bit reads) */
	reg_val = sitara_cm_read_2(sitara_cm_sc, padconf->reg_off);

	/* save the state */
	if (state)
		*state = (reg_val & sitara_cm_dev.padconf_sate_mask);

	/* save the mode */
	if (muxmode) {
		*muxmode = padconf->muxmodes[
		    (reg_val & sitara_cm_dev.padconf_muxmode_mask)
		    ];
	}

	return (0);
}

/**
 *	sitara_cm_padconf_set_gpiomode - converts a pad to GPIO mode.
 *	@gpio: the GPIO pin number (0-195)
 *	@state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
 *
 *
 *
 *	LOCKING:
 *	Internally locks it's own context.
 *
 *	RETURNS:
 *	0 on success.
 *	EINVAL if pin requested is outside valid range or already in use.
 */
int
sitara_cm_padconf_set_gpiomode(uint32_t gpio, unsigned int state)
{
	const struct sitara_cm_padconf *padconf;
	uint16_t reg_val;

	if (!sitara_cm_sc)
		return (ENXIO);

	/* find the gpio pin in the padconf array */
	padconf = sitara_cm_dev.padconf;
	while (padconf->ballname != NULL) {
		if (padconf->gpio_pin == gpio)
			break;
		padconf++;
	}
	if (padconf->ballname == NULL)
		return (EINVAL);

	/* populate the new value for the PADCONF register */
	reg_val = (uint16_t)(state & sitara_cm_dev.padconf_sate_mask);

	/* set the mux mode */
	reg_val |=
	    (uint16_t)(padconf->gpio_mode & sitara_cm_dev.padconf_muxmode_mask);

	/* write the register value (16-bit writes) */
	sitara_cm_write_2(sitara_cm_sc, padconf->reg_off, reg_val);

	return (0);
}

/**
 *	sitara_cm_padconf_get_gpiomode - gets the current GPIO mode of the pin
 *	@gpio: the GPIO pin number (0-195)
 *	@state: upon return will contain the state
 *
 *
 *
 *	LOCKING:
 *	Internally locks it's own context.
 *
 *	RETURNS:
 *	0 on success.
 *	EINVAL if pin requested is outside valid range or not configured as GPIO.
 */
int
sitara_cm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state)
{
	const struct sitara_cm_padconf *padconf;
	uint16_t reg_val;

	if (!sitara_cm_sc)
		return (ENXIO);

	/* find the gpio pin in the padconf array */
	padconf = sitara_cm_dev.padconf;
	while (padconf->ballname != NULL) {
		if (padconf->gpio_pin == gpio)
			break;
		padconf++;
	}
	if (padconf->ballname == NULL)
		return (EINVAL);

	/* read the current register settings */
	reg_val = sitara_cm_read_2(sitara_cm_sc, padconf->reg_off);

	/*
	 * check to make sure the pins is configured as GPIO in the
	 * first state
	 */
	if ((reg_val & sitara_cm_dev.padconf_muxmode_mask) !=
	    padconf->gpio_mode)
		return (EINVAL);

	/*
	 * read and store the reset of the state,
	 * i.e. pull-up, pull-down, etc
	 */
	if (state)
		*state = (reg_val & sitara_cm_dev.padconf_sate_mask);

	return (0);
}

int
sitara_cm_reg_read_4(uint32_t reg, uint32_t *val)
{
	if (!sitara_cm_sc)
		return (ENXIO);

	*val = sitara_cm_read_4(sitara_cm_sc, reg);
	return (0);
}

int
sitara_cm_reg_write_4(uint32_t reg, uint32_t val)
{
	if (!sitara_cm_sc)
		return (ENXIO);

	sitara_cm_write_4(sitara_cm_sc, reg, val);
	return (0);
}

void
sitara_cm_attach(struct device *parent, struct device *self, void *aux)
{
	struct sitara_cm_softc *sc = (struct sitara_cm_softc *)self;
	struct armv7_attach_args *aa = aux;
	uint32_t rev;

	if (sitara_cm_sc)
		panic("sitara_cm_attach: already attached");

	sc->sc_iot = aa->aa_iot;

	if (bus_space_map(aa->aa_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh) != 0)
		panic("%s: bus_space_map failed!\n", __func__);

	sitara_cm_sc = sc;

	if (sitara_cm_reg_read_4(OMAP2SCM_REVISION, &rev) != 0)
		panic("sitara_cm_attach: read revision");
	printf(": control module, rev %d.%d\n",
	    SCM_REVISION_MAJOR(rev), SCM_REVISION_MINOR(rev));
}


code:
/* $OpenBSD: sitara_cm.h,v 1.1 2013/09/04 14:38:32 patrick Exp $ */
/* $NetBSD: sitara_cm.h,v 1.1 2013/04/17 14:31:02 bouyer Exp $ */
/*
 * Copyright (c) 2010
 *	Ben Gray <ben.r.gray@gmail.com>.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Ben Gray.
 * 4. The name of the company nor the name of the author may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/**
 *	Functions to configure the PIN multiplexing on the chip.
 *
 *	This is different from the GPIO module in that it is used to configure the
 *	pins between modules not just GPIO input output.
 *
 */
#ifndef _OMAP_SCM_H_
#define _OMAP_SCM_H_

struct sitara_cm_padconf {
	uint16_t    reg_off;
	uint16_t    gpio_pin;
	uint16_t    gpio_mode;
	const char  *ballname;
	const char  *muxmodes[8];
};

struct sitara_cm_padstate {
	const char  *state;
	uint16_t    reg;
};

struct sitara_cm_device {
	uint16_t			padconf_muxmode_mask;
	uint16_t			padconf_sate_mask;
	struct sitara_cm_padstate	*padstate;
	struct sitara_cm_padconf		*padconf;
};

int sitara_cm_padconf_set(const char *padname, const char *muxmode,
    unsigned int state);
int sitara_cm_padconf_get(const char *padname, const char **muxmode,
    unsigned int *state);
int sitara_cm_padconf_set_gpiomode(uint32_t gpio, unsigned int state);
int sitara_cm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state);
int sitara_cm_padconf_set_gpioflags(uint32_t gpio, uint32_t flags);
void sitara_cm_padconf_get_gpioflags(uint32_t gpio, uint32_t *flags);
int sitara_cm_reg_read_4(uint32_t reg, uint32_t *val);
int sitara_cm_reg_write_4(uint32_t reg, uint32_t val);

#endif /* _OMAP_SCM_H_ */


code:
/* $OpenBSD: sitara_cmreg.h,v 1.1 2013/09/04 14:38:32 patrick Exp $ */
/*	$NetBSD: sitara_cmreg.h,v 1.1 2013/04/17 15:04:39 bouyer Exp $	*/

/*
 * Copyright (c) 2013 Manuel Bouyer.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* register definitions for the Control module found in the
 * Texas Instrument AM335x SOC
 */

#ifndef _OMAP2SCMREG_H
#define _OMAP2SCMREG_H

#define OMAP2SCM_REVISION	0x0000
#define SCM_REVISION_SCHEME(x)	(((x) & 0xc0000000) >> 30)
#define SCM_REVISION_FUNC(x)	(((x) & 0x0fff0000) >> 16)
#define SCM_REVISION_RTL(x)	(((x) & 0x0000f800) >> 11)
#define SCM_REVISION_MAJOR(x)	(((x) & 0x00000700) >>  8)
#define SCM_REVISION_CUSTOM(x)	(((x) & 0x000000c0) >>  6)
#define SCM_REVISION_MINOR(x)	(((x) & 0x0000001f) >>  0)

#define OMAP2SCM_MAC_ID0_LO	0x630
#define OMAP2SCM_MAC_ID0_HI	0x634

#endif /* _OMAP2SCMREG_H */


code:
/*	$OpenBSD: ti_iic.c,v 1.2 2014/03/18 14:23:52 rapha Exp $	*/
/* $NetBSD: ti_iic.c,v 1.4 2013/04/25 13:04:27 rkujawa Exp $ */

/*
 * Copyright (c) 2013 Manuel Bouyer.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*-
 * Copyright (c) 2012 Jared D. McNeill <jmcneill@invisible.ca>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/rwlock.h>

#include <machine/bus.h>
#include <machine/intr.h>

#include <dev/i2c/i2cvar.h>

#include <armv7/armv7/armv7var.h>
#include <armv7/omap/prcmvar.h>
#include <armv7/omap/sitara_cm.h>
#include <armv7/omap/sitara_cmreg.h>
#include <armv7/omap/ti_iicreg.h>

#ifndef AM335X_I2C_SLAVE_ADDR
#define AM335X_I2C_SLAVE_ADDR	0x01
#endif

#ifdef I2CDEBUG
#define DPRINTF(args)	printf args
#else
#define DPRINTF(args)
#endif

/* operation in progress */
typedef enum {
	TI_I2CREAD,
	TI_I2CWRITE,
	TI_I2CDONE,
	TI_I2CERROR
} ti_i2cop_t;

struct ti_iic_softc {
	struct device		sc_dev;
	struct i2c_controller	sc_ic;
	struct rwlock		sc_buslock;
	struct device		*sc_i2cdev;

	bus_space_tag_t		sc_iot;
	bus_space_handle_t	sc_ioh;

	void			*sc_ih;
	ti_i2cop_t		sc_op;
	int			sc_buflen;
	int			sc_bufidx;
	char			*sc_buf;

	int			sc_rxthres;
	int			sc_txthres;
};

#define I2C_READ_REG(sc, reg)		\
	bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg))
#define I2C_READ_DATA(sc)		\
	bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, AM335X_I2C_DATA);
#define I2C_WRITE_REG(sc, reg, val)	\
	bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define I2C_WRITE_DATA(sc, val)		\
	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, AM335X_I2C_DATA, (val))

#define DEVNAME(sc)	((sc)->sc_dev.dv_xname)

static void	ti_iic_attach(struct device *, struct device *, void *);
static int	ti_iic_intr(void *);

static int	ti_iic_acquire_bus(void *, int);
static void	ti_iic_release_bus(void *, int);
static int	ti_iic_exec(void *, i2c_op_t, i2c_addr_t, const void *,
		    size_t, void *, size_t, int);

static int	ti_iic_reset(struct ti_iic_softc *);
static int	ti_iic_op(struct ti_iic_softc *, i2c_addr_t, ti_i2cop_t,
		    uint8_t *, size_t, int);
static void	ti_iic_handle_intr(struct ti_iic_softc *, uint32_t);
static void	ti_iic_do_read(struct ti_iic_softc *, uint32_t);
static void	ti_iic_do_write(struct ti_iic_softc *, uint32_t);

static int	ti_iic_wait(struct ti_iic_softc *, uint16_t, uint16_t, int);
static uint32_t	ti_iic_stat(struct ti_iic_softc *, uint32_t);
static int	ti_iic_flush(struct ti_iic_softc *);

struct cfattach tiiic_ca = {
	sizeof (struct ti_iic_softc), NULL, ti_iic_attach
};

struct cfdriver tiiic_cd = {
	NULL, "tiiic", DV_DULL
};

static void
ti_iic_attach(struct device *parent, struct device *self, void *args)
{
	struct ti_iic_softc *sc = (struct ti_iic_softc *)self;
	struct armv7_attach_args *aa = args;
	struct i2cbus_attach_args iba;
	uint16_t rev;
	const char *mode;
	u_int state;
	char buf[20];
	char *pin;
	/* BBB specific pin names */
	char *pins[6] = {"I2C0_SDA", "I2C0_SCL",
			"SPIO_D1", "SPI0_CS0",
			"UART1_CTSn", "UART1_RTSn"};

	sc->sc_iot = aa->aa_iot;
	rw_init(&sc->sc_buslock, "tiiilk");

	sc->sc_rxthres = sc->sc_txthres = 4;

	if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr,
	    aa->aa_dev->mem[0].size, 0, &sc->sc_ioh))
		panic("%s: bus_space_map failed!");

	sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_NET,
	    ti_iic_intr, sc, DEVNAME(sc));

	prcm_enablemodule(PRCM_I2C0 + aa->aa_dev->unit);

	if (board_id == BOARD_ID_AM335X_BEAGLEBONE) {
		pin = pins[aa->aa_dev->unit * 2];
		snprintf(buf, sizeof buf, "I2C%d_SDA", aa->aa_dev->unit);
		if (sitara_cm_padconf_set(pin, buf,
		    (0x01 << 4) | (0x01 << 5) | (0x01 << 6)) != 0) {
			printf(": can't switch %s pad\n", buf);
			return;
		}
		if (sitara_cm_padconf_get(pin, &mode, &state) == 0) {
			printf(": %s state %d ", mode, state);
		}

		pin = pins[aa->aa_dev->unit * 2 + 1];
		snprintf(buf, sizeof buf, "I2C%d_SCL", aa->aa_dev->unit);
		if (sitara_cm_padconf_set(pin, buf,
		    (0x01 << 4) | (0x01 << 5) | (0x01 << 6)) != 0) {
			printf(": can't switch %s pad\n", buf);
			return;
		}
		if (sitara_cm_padconf_get(pin, &mode, &state) == 0) {
			printf(": %s state %d ", mode, state);
		}
	}

	rev = I2C_READ_REG(sc, AM335X_I2C_REVNB_LO);
	printf(" rev %d.%d\n",
	    (int)I2C_REVNB_LO_MAJOR(rev),
	    (int)I2C_REVNB_LO_MINOR(rev));

	ti_iic_reset(sc);
	ti_iic_flush(sc);

	sc->sc_ic.ic_cookie = sc;
	sc->sc_ic.ic_acquire_bus = ti_iic_acquire_bus;
	sc->sc_ic.ic_release_bus = ti_iic_release_bus;
	sc->sc_ic.ic_exec = ti_iic_exec;

	bzero(&iba, sizeof iba);
	iba.iba_name = "iic";
	iba.iba_tag = &sc->sc_ic;
	(void) config_found(&sc->sc_dev, &iba, iicbus_print);
}

static int
ti_iic_intr(void *arg)
{
	struct ti_iic_softc *sc = arg;
	uint32_t stat;

	DPRINTF(("ti_iic_intr\n"));
	stat = I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS);
	I2C_WRITE_REG(sc, AM335X_I2C_IRQSTATUS, stat);
	DPRINTF(("ti_iic_intr pre handle sc->sc_op eq %#x\n", sc->sc_op));

	ti_iic_handle_intr(sc, stat);

	if (sc->sc_op == TI_I2CERROR || sc->sc_op == TI_I2CDONE) {
		DPRINTF(("ti_iic_intr post handle sc->sc_op %#x\n", sc->sc_op));
		wakeup(&sc->sc_dev);
	}

	DPRINTF(("ti_iic_intr status 0x%x\n", stat));

	return 1;
}

static int
ti_iic_acquire_bus(void *opaque, int flags)
{
	struct ti_iic_softc *sc = opaque;

	if (flags & I2C_F_POLL)
		return 0;

	return (rw_enter(&sc->sc_buslock, RW_WRITE));
}

static void
ti_iic_release_bus(void *opaque, int flags)
{
	struct ti_iic_softc *sc = opaque;

	if (flags & I2C_F_POLL)
		return;

	rw_exit(&sc->sc_buslock);
}

static int
ti_iic_exec(void *opaque, i2c_op_t op, i2c_addr_t addr,
    const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
	struct ti_iic_softc *sc = opaque;
	int err;

	DPRINTF(("ti_iic_exec: op 0x%x cmdlen %zd len %zd flags 0x%x\n",
	    op, cmdlen, len, flags));

#define __UNCONST(a)  ((void *)(unsigned long)(const void *)(a))
	if (cmdlen > 0) {
		err = ti_iic_op(sc, addr, TI_I2CWRITE, __UNCONST(cmdbuf),
		    cmdlen, (I2C_OP_READ_P(op) ? 0 : I2C_F_STOP) | flags);
		if (err)
			goto done;
	}
	if (I2C_OP_STOP_P(op))
		flags |= I2C_F_STOP;

	/*
	 * I2C controller doesn't allow for zero-byte transfers.
	 */
	if (len == 0)
		goto done;

	if (I2C_OP_READ_P(op))
		err = ti_iic_op(sc, addr, TI_I2CREAD, buf, len, flags);
	else
		err = ti_iic_op(sc, addr, TI_I2CWRITE, buf, len, flags);

done:
	if (err)
		ti_iic_reset(sc);

	ti_iic_flush(sc);

	DPRINTF(("ti_iic_exec: done %d\n", err));
	return err;
}

static int
ti_iic_reset(struct ti_iic_softc *sc)
{
	uint32_t psc, scll, sclh;
	int i;

	DPRINTF(("ti_iic_reset\n"));

	/* Disable */
	I2C_WRITE_REG(sc, AM335X_I2C_CON, 0);
	/* Soft reset */
	I2C_WRITE_REG(sc, AM335X_I2C_SYSC, I2C_SYSC_SRST);
	delay(1000);
	/* enable so that we can check for reset complete */
	I2C_WRITE_REG(sc, AM335X_I2C_CON, I2C_CON_EN);
	delay(1000);
	for (i = 0; i < 1000; i++) { /* 1s delay for reset */
		if (I2C_READ_REG(sc, AM335X_I2C_SYSS) & I2C_SYSS_RDONE)
			break;
	}
	/* Disable again */
	I2C_WRITE_REG(sc, AM335X_I2C_CON, 0);
	delay(50000);

	if (i >= 1000) {
		printf("%s: couldn't reset module\n", DEVNAME(sc));
		return 1;
	}

	/* XXX standard speed only */
	psc = 3;
	scll = 53;
	sclh = 55;

	/* Clocks */
	I2C_WRITE_REG(sc, AM335X_I2C_PSC, psc);
	I2C_WRITE_REG(sc, AM335X_I2C_SCLL, scll);
	I2C_WRITE_REG(sc, AM335X_I2C_SCLH, sclh);

	/* Own I2C address */
	I2C_WRITE_REG(sc, AM335X_I2C_OA, AM335X_I2C_SLAVE_ADDR);

	/* 5 bytes fifo */
	I2C_WRITE_REG(sc, AM335X_I2C_BUF,
	    I2C_BUF_RXTRSH(sc->sc_rxthres) | I2C_BUF_TXTRSH(sc->sc_txthres));

	/* Enable */
	I2C_WRITE_REG(sc, AM335X_I2C_CON, I2C_CON_EN);

	return 0;
}

static int
ti_iic_op(struct ti_iic_softc *sc, i2c_addr_t addr, ti_i2cop_t op,
    uint8_t *buf, size_t buflen, int flags)
{
	uint16_t con, stat, mask;
	int err, retry;

	KASSERT(op == TI_I2CREAD || op == TI_I2CWRITE);
	DPRINTF(("ti_iic_op: addr %#x op %#x buf %p buflen %#x flags %#x\n",
	    addr, op, buf, (unsigned int) buflen, flags));

	mask = I2C_IRQSTATUS_ARDY | I2C_IRQSTATUS_NACK | I2C_IRQSTATUS_AL;
	if (op == TI_I2CREAD)
		mask |= I2C_IRQSTATUS_RDR | I2C_IRQSTATUS_RRDY;
	else
		mask |= I2C_IRQSTATUS_XDR | I2C_IRQSTATUS_XRDY;

	err = ti_iic_wait(sc, I2C_IRQSTATUS_BB, 0, flags);
	if (err) {
		DPRINTF(("ti_iic_op: wait error %d\n", err));
		return err;
	}

	con = I2C_CON_EN;
	con |= I2C_CON_MST;
	con |= I2C_CON_STT;
	if (flags & I2C_F_STOP)
		con |= I2C_CON_STP;
	if (addr & ~0x7f)
		con |= I2C_CON_XSA;
	if (op == TI_I2CWRITE)
		con |= I2C_CON_TRX;

	sc->sc_op = op;
	sc->sc_buf = buf;
	sc->sc_buflen = buflen;
	sc->sc_bufidx = 0;

	I2C_WRITE_REG(sc,
	    AM335X_I2C_CON, I2C_CON_EN | I2C_CON_MST | I2C_CON_STP);
	DPRINTF(("ti_iic_op: op %d con 0x%x ", op, con));
	I2C_WRITE_REG(sc, AM335X_I2C_CNT, buflen);
	I2C_WRITE_REG(sc, AM335X_I2C_SA, (addr & I2C_SA_MASK));
	DPRINTF(("SA 0x%x len %d\n",
	    I2C_READ_REG(sc, AM335X_I2C_SA), I2C_READ_REG(sc, AM335X_I2C_CNT)));

	if ((flags & I2C_F_POLL) == 0) {
		/* clear any pending interrupt */
		I2C_WRITE_REG(sc, AM335X_I2C_IRQSTATUS,
		    I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS));
		/* and enable */
		I2C_WRITE_REG(sc, AM335X_I2C_IRQENABLE_SET, mask);
	}
	/* start transfer */
	I2C_WRITE_REG(sc, AM335X_I2C_CON, con);

	if ((flags & I2C_F_POLL) == 0) {
		/* and wait for completion */
		DPRINTF(("ti_iic_op waiting, op %#x\n", sc->sc_op));
		while (sc->sc_op == op) {
			if (tsleep(&sc->sc_dev, PWAIT, "tiiic", 500)
			    == EWOULDBLOCK) {
				/* timeout */
				op = TI_I2CERROR;
			}
		}
		DPRINTF(("ti_iic_op waiting done, op %#x\n", sc->sc_op));

		/* disable interrupts */
		I2C_WRITE_REG(sc, AM335X_I2C_IRQENABLE_CLR, 0xffff);
	} else {
		/* poll for completion */
		DPRINTF(("ti_iic_op polling, op %x\n", sc->sc_op));
		while (sc->sc_op == op) {
			stat = ti_iic_stat(sc, mask);
			DPRINTF(("ti_iic_op stat 0x%x\n", stat));
			if (stat == 0) /* timeout */
				sc->sc_op = TI_I2CERROR;
			else
				ti_iic_handle_intr(sc, stat);
			I2C_WRITE_REG(sc, AM335X_I2C_IRQSTATUS, stat);
		}
		DPRINTF(("ti_iic_op polling done, op now %x\n", sc->sc_op));
	}
	retry = 10000;
	I2C_WRITE_REG(sc, AM335X_I2C_CON, 0);
	while (I2C_READ_REG(sc, AM335X_I2C_CON) & I2C_CON_MST) {
		delay(100);
		if (--retry == 0)
			break;
	}

	return (sc->sc_op == TI_I2CDONE) ? 0 : EIO;
}

static void
ti_iic_handle_intr(struct ti_iic_softc *sc, uint32_t stat)
{
	KASSERT(stat != 0);
	DPRINTF(("ti_iic_handle_intr stat %#x\n", stat));

	if (stat & (I2C_IRQSTATUS_NACK|I2C_IRQSTATUS_AL)) {
		sc->sc_op = TI_I2CERROR;
		return;
	}
	if (stat & I2C_IRQSTATUS_ARDY) {
		sc->sc_op = TI_I2CDONE;
		return;
	}
	if (sc->sc_op == TI_I2CREAD)
		ti_iic_do_read(sc, stat);
	else if (sc->sc_op == TI_I2CWRITE)
		ti_iic_do_write(sc, stat);
	else
		return;
}
void
ti_iic_do_read(struct ti_iic_softc *sc, uint32_t stat)
{
	int len = 0;

	DPRINTF(("ti_iic_do_read stat %#x\n", stat));
	if (stat & I2C_IRQSTATUS_RDR) {
		len = I2C_READ_REG(sc, AM335X_I2C_BUFSTAT);
		len = I2C_BUFSTAT_RXSTAT(len);
		DPRINTF(("ti_iic_do_read receive drain len %d left %d\n",
		    len, I2C_READ_REG(sc, AM335X_I2C_CNT)));
	} else if (stat & I2C_IRQSTATUS_RRDY) {
		len = sc->sc_rxthres + 1;
		DPRINTF(("ti_iic_do_read receive len %d left %d\n",
		    len, I2C_READ_REG(sc, AM335X_I2C_CNT)));
	}
	for (;
	    sc->sc_bufidx < sc->sc_buflen && len > 0;
	    sc->sc_bufidx++, len--) {
		sc->sc_buf[sc->sc_bufidx] = I2C_READ_DATA(sc);
		DPRINTF(("ti_iic_do_read got b[%d]=0x%x\n", sc->sc_bufidx,
		    sc->sc_buf[sc->sc_bufidx]));
	}
	DPRINTF(("ti_iic_do_read done\n"));
}

void
ti_iic_do_write(struct ti_iic_softc *sc, uint32_t stat)
{
	int len = 0;

	DPRINTF(("ti_iic_do_write stat %#x\n", stat));

	if (stat & I2C_IRQSTATUS_XDR) {
		len = I2C_READ_REG(sc, AM335X_I2C_BUFSTAT);
		len = I2C_BUFSTAT_TXSTAT(len);
		DPRINTF(("ti_iic_do_write xmit drain len %d left %d\n",
		    len, I2C_READ_REG(sc, AM335X_I2C_CNT)));
	} else if (stat & I2C_IRQSTATUS_XRDY) {
		len = sc->sc_txthres + 1;
		DPRINTF(("ti_iic_do_write xmit len %d left %d\n",
		    len, I2C_READ_REG(sc, AM335X_I2C_CNT)));
	}
	for (;
	    sc->sc_bufidx < sc->sc_buflen && len > 0;
	    sc->sc_bufidx++, len--) {
		DPRINTF(("ti_iic_do_write send b[%d]=0x%x\n",
		    sc->sc_bufidx, sc->sc_buf[sc->sc_bufidx]));
		I2C_WRITE_DATA(sc, sc->sc_buf[sc->sc_bufidx]);
	}
	DPRINTF(("ti_iic_do_write done\n"));
}

static int
ti_iic_wait(struct ti_iic_softc *sc, uint16_t mask, uint16_t val, int flags)
{
	int retry = 10;
	uint16_t v;
	DPRINTF(("ti_iic_wait mask %#x val %#x flags %#x\n", mask, val, flags));

	while (((v = I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS_RAW)) & mask) != val) {
		--retry;
		if (retry == 0) {
			printf("%s: wait timeout, mask=%#x val=%#x stat=%#x\n",
			    DEVNAME(sc), mask, val, v);
			return EBUSY;
		}
		if (flags & I2C_F_POLL)
			delay(50000);
		else
			tsleep(&sc->sc_dev, PWAIT, "tiiic", 50);
	}
	DPRINTF(("ti_iic_wait done retry %#x\n", retry));

	return 0;
}

static uint32_t
ti_iic_stat(struct ti_iic_softc *sc, uint32_t mask)
{
	uint32_t v;
	int retry = 500;
	DPRINTF(("ti_iic_wait mask %#x\n", mask));
	while (--retry > 0) {
		v = I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS_RAW) & mask;
		if (v != 0)
			break;
		delay(100);
	}
	DPRINTF(("ti_iic_wait done retry %#x\n", retry));
	return v;
}

static int
ti_iic_flush(struct ti_iic_softc *sc)
{
	DPRINTF(("ti_iic_flush\n"));
#if 0
	int retry = 1000;
	uint16_t v;

	while ((v =
	    I2C_READ_REG(sc, AM335X_I2C_IRQSTATUS_RAW)) & I2C_IRQSTATUS_RRDY) {
		if (--retry == 0) {
			printf("%s: flush timeout, stat = %#x\n", DEVNAME(sc), v);
			return EBUSY;
		}
		(void)I2C_READ_DATA(sc);
		delay(1000);
	}
#endif

	I2C_WRITE_REG(sc, AM335X_I2C_CNT, 0);
	return 0;
}


code:
/*	$OpenBSD: ti_iicreg.h,v 1.1 2013/11/24 15:00:22 rapha Exp $	*/
/*	$NetBSD: ti_iicreg.h,v 1.1 2013/04/17 14:33:06 bouyer Exp $	*/

/*
 * Copyright (c) 2013 Manuel Bouyer.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* register definitions for the i2c controller found in the
 * Texas Instrument AM335x SOC
 */

#ifndef _AM335XIICREG_H
#define _AM335XIICREG_H

#define AM335X_I2C_REVNB_LO		0x00
#define		I2C_REVNB_LO_RTL(x)		(((x) >> 11) & 0x01f)
#define		I2C_REVNB_LO_MAJOR(x)		(((x) >>  8) & 0x007)
#define		I2C_REVNB_LO_CUSTOM(x)		(((x) >>  6) & 0x003)
#define		I2C_REVNB_LO_MINOR(x)		(((x) >>  0) & 0x01f)
#define AM335X_I2C_REVNB_HI		0x04
#define		I2C_REVNB_HI_SCHEME(x)		(((x) >> 14) & 0x003)
#define		I2C_REVNB_HI_FUNC(x)		(((x) >>  0) & 0xfff)
#define AM335X_I2C_SYSC			0x10
#define		I2C_SYSC_CLKACTIVITY_OCP	0x0010
#define		I2C_SYSC_CLKACTIVITY_SYSTEM	0x0020
#define		I2C_SYSC_IDLE_MASK		0x0018
#define		I2C_SYSC_IDLE_FORCE		0x0000
#define		I2C_SYSC_IDLE_SMART		0x0010
#define		I2C_SYSC_IDLE_NONE		0x0008
#define		I2C_SYSC_ENAWAKEUP		0x0004
#define		I2C_SYSC_SRST			0x0002
#define		I2C_SYSC_AUTOIDLE		0x0001
#define AM335X_I2C_IRQSTATUS_RAW		0x24
#define AM335X_I2C_IRQSTATUS		0x28
#define AM335X_I2C_IRQENABLE_SET		0x2C
#define AM335X_I2C_IRQENABLE_CLR		0x30
#define AM335X_I2C_WE			0x34
#define		I2C_IRQSTATUS_XDR		0x4000
#define		I2C_IRQSTATUS_RDR		0x2000
#define		I2C_IRQSTATUS_BB		0x1000
#define		I2C_IRQSTATUS_ROVR		0x0800
#define		I2C_IRQSTATUS_XUDF		0x0400
#define		I2C_IRQSTATUS_AAS		0x0200
#define		I2C_IRQSTATUS_BF		0x0100
#define		I2C_IRQSTATUS_AERR		0x0080
#define		I2C_IRQSTATUS_STC		0x0040
#define		I2C_IRQSTATUS_GC		0x0020
#define		I2C_IRQSTATUS_XRDY		0x0010
#define		I2C_IRQSTATUS_RRDY		0x0008
#define		I2C_IRQSTATUS_ARDY		0x0004
#define		I2C_IRQSTATUS_NACK		0x0002
#define		I2C_IRQSTATUS_AL		0x0001
#define AM335X_I2C_DMARXENABLE_SET	0x38
#define AM335X_I2C_DMATXENABLE_SET	0x3C
#define AM335X_I2C_DMARXENABLE_CLR	0x40
#define		I2C_DMARXENABLE			0x0001
#define AM335X_I2C_DMATXENABLE_CLR	0x44
#define		I2C_DMATXENABLE			0x0001
#define AM335X_I2C_DMARXWAKE_EN		0x48
	/* use same bits as IRQ */
#define AM335X_I2C_DMATXWAKE_EN		0x4C
	/* use same bits as IRQ */
#define AM335X_I2C_SYSS			0x90
#define		I2C_SYSS_RDONE			0x0001
#define AM335X_I2C_BUF			0x94
#define		I2C_BUF_RDMA_EN			0x8000
#define		I2C_BUF_RXFIFO_CLR		0x4000
#define		I2C_BUF_RXTRSH_MASK		0x3f00
#define		I2C_BUF_RXTRSH(x)		((x) << 8)
#define		I2C_BUF_XDMA_EN			0x0080
#define		I2C_BUF_TXFIFO_CLR		0x0040
#define		I2C_BUF_TXTRSH_MASK		0x003f
#define		I2C_BUF_TXTRSH(x)		((x) << 0)
#define AM335X_I2C_CNT			0x98
#define		I2C_CNT_MASK			0xffff
#define AM335X_I2C_DATA			0x9C
#define		I2C_DATA_MASK			0x00ff
#define AM335X_I2C_CON			0xA4
#define		I2C_CON_EN			0x8000
#define		I2C_CON_STB			0x0800
#define		I2C_CON_MST			0x0400
#define		I2C_CON_TRX			0x0200
#define		I2C_CON_XSA			0x0100
#define		I2C_CON_XOA0			0x0080
#define		I2C_CON_XOA1			0x0040
#define		I2C_CON_XOA2			0x0020
#define		I2C_CON_XOA3			0x0010
#define		I2C_CON_STP			0x0002
#define		I2C_CON_STT			0x0001
#define AM335X_I2C_OA			0xA8
#define		I2C_OA_MASK			0x03ff
#define AM335X_I2C_SA			0xAC
#define		I2C_SA_MASK			0x03ff
#define AM335X_I2C_PSC			0xB0
#define		I2C_PSC_MASK			0x000f
#define AM335X_I2C_SCLL			0xB4
#define		I2C_SCLL_MASK			0x000f
#define AM335X_I2C_SCLH			0xB8
#define		I2C_SCLH_MASK			0x000f
#define AM335X_I2C_SYSTEST		0xBC
#define AM335X_I2C_BUFSTAT		0xC0
#define		I2C_BUFSTAT_FIFODEPTH(x)	(((x) >> 14) & 0x03)
#define		I2C_BUFSTAT_RXSTAT(x)		(((x) >>  8) & 0x3f)
#define		I2C_BUFSTAT_TXSTAT(x)		(((x) >>  0) & 0x3f)
#define AM335X_I2C_OA1			0xC4
#define AM335X_I2C_OA2			0xC8
#define AM335X_I2C_OA3			0xCC
	/* same bits as I2C_OA */
#define AM335X_I2C_ACTOA			0xD0
#define AM335X_I2C_SBLOCK		0xD4
#define		I2C_ACTOA_OA3_ACT		0x0008
#define		I2C_ACTOA_OA2_ACT		0x0004
#define		I2C_ACTOA_OA1_ACT		0x0002
#define		I2C_ACTOA_OA0_ACT		0x0001

#endif /* _AM335XIICREG_H */