#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/sdmmc/sdmmcchip.h>
#include <dev/sdmmc/sdmmcvar.h>
#include <armv7/omap/prcmvar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/fdt.h>
#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 1
#define SDHC_BUFFER_TIMEOUT 1
#define SDHC_TRANSFER_TIMEOUT 1
int ommmc_match(struct device *, void *, void *);
void ommmc_attach(struct device *, struct device *, void *);
struct ommmc_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
int sc_node;
void *sc_ih;
uint32_t sc_flags;
#define FL_HSS (1 << 0)
uint32_t sc_gpio[4];
struct device *sdmmc;
int clockbit;
uint32_t clkbase;
int maxblklen;
int flags;
uint32_t ocr;
uint32_t intr_status;
uint32_t intr_error_status;
};
void ommmc_power(int, void *);
void ommmc_shutdown(void *);
int ommmc_intr(void *);
#define MMC_RESET_DAT 1
#define MMC_RESET_CMD 2
#define MMC_RESET_ALL (MMC_RESET_CMD|MMC_RESET_DAT)
#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, int);
int ommmc_bus_width(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);
#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 = {
ommmc_host_reset,
ommmc_host_ocr,
ommmc_host_maxblklen,
ommmc_card_detect,
ommmc_bus_power,
ommmc_bus_clock,
ommmc_bus_width,
ommmc_exec_command,
ommmc_card_intr_mask,
ommmc_card_intr_ack
};
struct cfdriver ommmc_cd = {
NULL, "ommmc", DV_DULL
};
const struct cfattach ommmc_ca = {
sizeof(struct ommmc_softc), ommmc_match, ommmc_attach
};
int
ommmc_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return (OF_is_compatible(faa->fa_node, "ti,omap3-hsmmc") ||
OF_is_compatible(faa->fa_node, "ti,omap4-hsmmc") ||
OF_is_compatible(faa->fa_node, "ti,am335-sdhci"));
}
void
ommmc_attach(struct device *parent, struct device *self, void *aux)
{
struct ommmc_softc *sc = (struct ommmc_softc *) self;
struct fdt_attach_args *faa = aux;
struct sdmmcbus_attach_args saa;
uint32_t caps, width;
uint32_t addr, size;
int len, unit;
char hwmods[128];
if (faa->fa_nreg < 1)
return;
if (faa->fa_reg[0].size <= 0x100)
return;
if (OF_is_compatible(faa->fa_node, "ti,omap4-hsmmc") ||
OF_is_compatible(faa->fa_node, "ti,am335-sdhci")) {
addr = faa->fa_reg[0].addr + 0x100;
size = faa->fa_reg[0].size - 0x100;
} else {
addr = faa->fa_reg[0].addr;
size = faa->fa_reg[0].size;
}
unit = -1;
if ((len = OF_getprop(faa->fa_node, "ti,hwmods", hwmods,
sizeof(hwmods))) == 5) {
if (!strncmp(hwmods, "mmc", 3) &&
(hwmods[3] > '0') && (hwmods[3] <= '9'))
unit = hwmods[3] - '1';
}
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh))
panic("%s: bus_space_map failed!", __func__);
sc->sc_node = faa->fa_node;
printf("\n");
pinctrl_byname(faa->fa_node, "default");
if (unit != -1)
prcm_enablemodule(PRCM_MMC0 + unit);
sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_SDMMC,
ommmc_intr, sc, DEVNAME(sc));
if (sc->sc_ih == NULL) {
printf("%s: cannot map interrupt\n", DEVNAME(sc));
goto err;
}
OF_getpropintarray(faa->fa_node, "cd-gpios", sc->sc_gpio,
sizeof(sc->sc_gpio));
if (sc->sc_gpio[0])
gpio_controller_config_pin(sc->sc_gpio, GPIO_CONFIG_INPUT);
HSET4(sc, MMCHS_CAPA, MMCHS_CAPA_VS18 | MMCHS_CAPA_VS30);
#ifdef SDHC_DEBUG
ommmc_dump_regs(sc);
#endif
ommmc_host_reset(sc);
caps = HREAD4(sc, MMCHS_CAPA);
#if 0
if (usedma && ISSET(caps, SDHC_DMA_SUPPORT))
SET(sc->flags, SHF_USE_DMA);
#endif
sc->clkbase = 96 * 1000;
#if 0
if (SDHC_BASE_FREQ_KHZ(caps) != 0)
sc->clkbase = SDHC_BASE_FREQ_KHZ(caps);
#endif
if (sc->clkbase == 0) {
printf("%s: base clock frequency unknown\n", DEVNAME(sc));
goto err;
} else if (sc->clkbase < 10000 || sc->clkbase > 96000) {
printf("%s: base clock frequency out of range: %u MHz\n",
DEVNAME(sc), sc->clkbase / 1000);
goto err;
}
if (caps & MMCHS_CAPA_VS18)
SET(sc->ocr, MMC_OCR_1_65V_1_95V);
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);
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));
}
sc->maxblklen = 512;
bzero(&saa, sizeof(saa));
saa.saa_busname = "sdmmc";
saa.sct = &ommmc_functions;
saa.sch = sc;
if (OF_getproplen(faa->fa_node, "ti,needs-special-hs-handling") == 0 &&
(caps & MMCHS_CAPA_HSS)) {
sc->sc_flags |= FL_HSS;
saa.caps |= SMC_CAPS_MMC_HIGHSPEED | SMC_CAPS_SD_HIGHSPEED;
}
width = OF_getpropint(faa->fa_node, "bus-width", 1);
if (unit > 0)
width = 1;
if (width >= 8)
saa.caps |= SMC_CAPS_8BIT_MODE;
if (width >= 4)
saa.caps |= SMC_CAPS_4BIT_MODE;
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, size);
}
void
ommmc_power(int why, void *arg)
{
#if 0
struct ommmc_softc *sc = arg;
int n, i;
#endif
switch(why) {
case DVACT_SUSPEND:
#if 0
for (i = 0; i < sizeof sc->regs; i++)
sc->regs[i] = HREAD1(sc, i);
#endif
break;
case DVACT_RESUME:
#if 0
(void)ommmc_host_reset(sc);
for (i = 0; i < sizeof sc->regs; i++)
HWRITE1(sc, i, sc->regs[i]);
#endif
break;
}
}
void
ommmc_shutdown(void *arg)
{
struct ommmc_softc *sc = arg;
(void)ommmc_host_reset(sc);
}
int
ommmc_host_reset(sdmmc_chipset_handle_t sch)
{
struct ommmc_softc *sc = sch;
uint32_t imask;
int error;
int s;
s = splsdmmc();
HWRITE4(sc, MMCHS_IE, 0);
HWRITE4(sc, MMCHS_ISE, 0);
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);
HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CC);
HCLR4(sc, MMCHS_CON, MMCHS_CON_INIT);
HWRITE4(sc, MMCHS_STAT, ~0);
#endif
HSET4(sc, MMCHS_SYSCTL, 0xe << MMCHS_SYSCTL_DTO_SH);
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);
HCLR4(sc, MMCHS_CON, MMCHS_CON_DW8);
HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW);
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);
}
int
ommmc_card_detect(sdmmc_chipset_handle_t sch)
{
struct ommmc_softc *sc = sch;
if (OF_getproplen(sc->sc_node, "non-removable") == 0)
return 1;
if (sc->sc_gpio[0]) {
int inverted, val;
val = gpio_controller_get_pin(sc->sc_gpio);
inverted = (OF_getproplen(sc->sc_node, "cd-inverted") == 0);
return inverted ? !val : val;
}
return !ISSET(HREAD4(sc, MMCHS_SYSTEST), MMCHS_SYSTEST_SDCD) ?
1 : 0;
}
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();
HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP);
if (ocr == 0) {
splx(s);
(void)ommmc_host_reset(sc);
return (0);
}
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_65V_1_95V))
vdd = MMCHS_HCTL_SDVS_V18;
else {
splx(s);
return (EINVAL);
}
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);
if (!ISSET(HREAD4(sc, MMCHS_HCTL), MMCHS_HCTL_SDBP)) {
splx(s);
return (ENXIO);
}
splx(s);
return (0);
}
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");
return (-1);
}
int
ommmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
int error = 0;
struct ommmc_softc *sc = sch;
uint32_t reg;
int s;
int div;
int timo;
s = splsdmmc();
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;
}
HCLR4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN);
if (freq == SDMMC_SDCLK_OFF)
goto ret;
if ((div = ommmc_clock_divisor(sc, freq)) < 0) {
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);
if ((timing == SDMMC_TIMING_HIGHSPEED) && (sc->sc_flags & FL_HSS))
HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_HSPE);
else
HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_HSPE);
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;
}
HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN);
ret:
splx(s);
return (error);
}
int
ommmc_bus_width(sdmmc_chipset_handle_t sch, int width)
{
struct ommmc_softc *sc = sch;
int s;
if (width != 1 && width != 4 && width != 8)
return (1);
s = splsdmmc();
if (width == 8)
HSET4(sc, MMCHS_CON, MMCHS_CON_DW8);
else
HCLR4(sc, MMCHS_CON, MMCHS_CON_DW8);
if (width == 4)
HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW);
else if (width == 1)
HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW);
splx(s);
return (0);
}
void
ommmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
{
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;
error = ommmc_start_command(sc, cmd);
if (error != 0) {
cmd->c_error = error;
SET(cmd->c_flags, SCF_ITSDONE);
return;
}
if (!ommmc_wait_intr(sc, MMCHS_STAT_CC, SDHC_COMMAND_TIMEOUT)) {
cmd->c_error = ETIMEDOUT;
SET(cmd->c_flags, SCF_ITSDONE);
return;
}
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 (cmd->c_error == 0 && cmd->c_data != NULL)
ommmc_transfer_data(sc, cmd);
#if 0
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\n",
DEVNAME(sc), cmd->c_opcode, cmd->c_arg, cmd->c_data,
cmd->c_datalen, cmd->c_flags));
if (cmd->c_datalen > 0) {
blksize = MIN(cmd->c_datalen, cmd->c_blklen);
blkcount = cmd->c_datalen / blksize;
if (cmd->c_datalen % blksize > 0) {
printf("%s: data not a multiple of %d bytes\n",
DEVNAME(sc), blksize);
return (EINVAL);
}
}
if (blkcount > MMCHS_BLK_NBLK_MAX) {
printf("%s: too much data\n", DEVNAME(sc));
return (EINVAL);
}
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;
command |= MMCHS_CMD_ACEN;
}
}
#ifdef notyet
if (ISSET(sc->flags, SHF_USE_DMA))
command |= MMCHS_CMD_DE;
#endif
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;
if ((error = ommmc_wait_state(sc, MMCHS_PSTATE_CMDI, 0)) != 0)
return (error);
s = splsdmmc();
#if 0
HSET1(sc, SDHC_HOST_CTL, SDHC_LED_ON);
#endif
DPRINTF(1,("%s: cmd=%#x blksize=%d blkcount=%d\n",
DEVNAME(sc), command, blksize, blkcount));
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);
}
}
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);
for (timo = 1000; timo > 0; timo--) {
if (ISSET(HREAD4(sc, MMCHS_SYSCTL), mask))
break;
delay(1);
}
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 sec)
{
int status;
int s;
mask |= MMCHS_STAT_ERRI;
s = splsdmmc();
status = sc->intr_status & mask;
while (status == 0) {
if (tsleep_nsec(&sc->intr_status, PWAIT, "hcintr",
SEC_TO_NSEC(sec)) == 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));
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);
}
int
ommmc_intr(void *arg)
{
struct ommmc_softc *sc = arg;
uint32_t status;
status = HREAD4(sc, MMCHS_STAT);
HWRITE4(sc, MMCHS_STAT, status);
DPRINTF(2,("%s: interrupt status=%b\n", DEVNAME(sc),
status, MMCHS_STAT_FMT));
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
if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION))
ommmc_needs_discover(sc->sdmmc);
#endif
if (ISSET(status, MMCHS_STAT_BRR|
MMCHS_STAT_BWR|MMCHS_STAT_TC|
MMCHS_STAT_CC)) {
sc->intr_status |= status;
wakeup(&sc->intr_status);
}
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;
}
#ifdef SDHC_DEBUG
void
ommmc_dump_regs(struct ommmc_softc *sc)
{
}
#endif