Index: sys/dev/sdmmc/sdhc.c =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/sdhc.c,v retrieving revision 1.10 diff -u -p -r1.10 sdhc.c --- sys/dev/sdmmc/sdhc.c 2 Feb 2012 22:49:17 -0000 1.10 +++ sys/dev/sdmmc/sdhc.c 24 May 2012 19:11:33 -0000 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhc.c,v 1.10 2012/02/02 22:49:17 nonaka Exp $ */ +/* $NetBSD: sdhc.c,v 1.11 2012/02/23 21:06:21 matt Exp $ */ /* $OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $ */ /* @@ -23,7 +23,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.10 2012/02/02 22:49:17 nonaka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.11 2012/02/23 21:06:21 matt Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -82,30 +82,80 @@ struct sdhc_host { uint32_t flags; /* flags for this host */ #define SHF_USE_DMA 0x0001 #define SHF_USE_4BIT_MODE 0x0002 +#define SHF_USE_8BIT_MODE 0x0004 }; #define HDEVNAME(hp) (device_xname((hp)->sc->sc_dev)) -#define HREAD1(hp, reg) \ - (bus_space_read_1((hp)->iot, (hp)->ioh, (reg))) -#define HREAD2(hp, reg) \ - (bus_space_read_2((hp)->iot, (hp)->ioh, (reg))) -#define HREAD4(hp, reg) \ +static uint8_t +hread1(struct sdhc_host *hp, bus_size_t reg) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) + return bus_space_read_1(hp->iot, hp->ioh, reg); + + return bus_space_read_4(hp->iot, hp->ioh, reg & -4) >> (8 * (reg & 3)); +} + +static uint16_t +hread2(struct sdhc_host *hp, bus_size_t reg) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) + return bus_space_read_2(hp->iot, hp->ioh, reg); + + return bus_space_read_4(hp->iot, hp->ioh, reg & -4) >> (8 * (reg & 2)); +} + +#define HREAD1(hp, reg) hread1(hp, reg) +#define HREAD2(hp, reg) hread2(hp, reg) +#define HREAD4(hp, reg) \ (bus_space_read_4((hp)->iot, (hp)->ioh, (reg))) -#define HWRITE1(hp, reg, val) \ - bus_space_write_1((hp)->iot, (hp)->ioh, (reg), (val)) -#define HWRITE2(hp, reg, val) \ - bus_space_write_2((hp)->iot, (hp)->ioh, (reg), (val)) + + +static void +hwrite1(struct sdhc_host *hp, bus_size_t o, uint8_t val) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + bus_space_write_1(hp->iot, hp->ioh, o, val); + } else { + const size_t shift = 8 * (o & 3); + o &= -4; + uint32_t tmp = bus_space_read_4(hp->iot, hp->ioh, o); + tmp = (val << shift) | (tmp & ~(0xff << shift)); + bus_space_write_4(hp->iot, hp->ioh, o, tmp); + } +} + +static void +hwrite2(struct sdhc_host *hp, bus_size_t o, uint16_t val) +{ + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + bus_space_write_2(hp->iot, hp->ioh, o, val); + } else { + const size_t shift = 8 * (o & 2); + o &= -4; + uint32_t tmp = bus_space_read_4(hp->iot, hp->ioh, o); + tmp = (val << shift) | (tmp & ~(0xffff << shift)); + bus_space_write_4(hp->iot, hp->ioh, o, tmp); + } +} + +#define HWRITE1(hp, reg, val) hwrite1(hp, reg, val) +#define HWRITE2(hp, reg, val) hwrite2(hp, reg, val) #define HWRITE4(hp, reg, val) \ bus_space_write_4((hp)->iot, (hp)->ioh, (reg), (val)) + #define HCLR1(hp, reg, bits) \ - HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits)) + do if (bits) HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits)); while (0) #define HCLR2(hp, reg, bits) \ - HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits)) + do if (bits) HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits)); while (0) +#define HCLR4(hp, reg, bits) \ + do if (bits) HWRITE4((hp), (reg), HREAD4((hp), (reg)) & ~(bits)); while (0) #define HSET1(hp, reg, bits) \ - HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits)) + do if (bits) HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits)); while (0) #define HSET2(hp, reg, bits) \ - HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)) + do if (bits) HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)); while (0) +#define HSET4(hp, reg, bits) \ + do if (bits) HWRITE4((hp), (reg), HREAD4((hp), (reg)) | (bits)); while (0) static int sdhc_host_reset(sdmmc_chipset_handle_t); static int sdhc_host_reset1(sdmmc_chipset_handle_t); @@ -128,8 +178,11 @@ static int sdhc_wait_intr(struct sdhc_ho static void sdhc_transfer_data(struct sdhc_host *, struct sdmmc_command *); static int sdhc_transfer_data_dma(struct sdhc_host *, struct sdmmc_command *); static int sdhc_transfer_data_pio(struct sdhc_host *, struct sdmmc_command *); -static void sdhc_read_data_pio(struct sdhc_host *, uint8_t *, int); -static void sdhc_write_data_pio(struct sdhc_host *, uint8_t *, int); +static void sdhc_read_data_pio(struct sdhc_host *, uint8_t *, u_int); +static void sdhc_write_data_pio(struct sdhc_host *, uint8_t *, u_int); +static void esdhc_read_data_pio(struct sdhc_host *, uint8_t *, u_int); +static void esdhc_write_data_pio(struct sdhc_host *, uint8_t *, u_int); + static struct sdmmc_chip_functions sdhc_functions = { /* host controller reset */ @@ -257,16 +310,23 @@ sdhc_host_found(struct sdhc_softc *sc, b * capabilities. (2.2.15) */ HWRITE1(hp, SDHC_TIMEOUT_CTL, SDHC_TIMEOUT_MAX); +#if 0 + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) + HWRITE4(hp, SDHC_NINTR_STATUS, SDHC_CMD_TIMEOUT_ERROR << 16); +#endif /* * Determine SD bus voltage levels supported by the controller. */ - if (ISSET(caps, SDHC_VOLTAGE_SUPP_1_8V)) + if (ISSET(caps, SDHC_VOLTAGE_SUPP_1_8V)) { SET(hp->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V); - if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_0V)) + } + if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_0V)) { SET(hp->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V); - if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_3V)) + } + if (ISSET(caps, SDHC_VOLTAGE_SUPP_3_3V)) { SET(hp->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V); + } /* * Determine the maximum block length supported by the host @@ -308,9 +368,15 @@ sdhc_host_found(struct sdhc_softc *sc, b saa.saa_dmat = hp->dmat; saa.saa_clkmin = hp->clkbase / 256; saa.saa_clkmax = hp->clkbase; - if (ISSET(sc->sc_flags, SDHC_FLAG_HAVE_DVS)) + if (ISSET(sc->sc_flags, SDHC_FLAG_HAVE_CGM)) + saa.saa_clkmin /= 2046; + else if (ISSET(sc->sc_flags, SDHC_FLAG_HAVE_DVS)) saa.saa_clkmin /= 16; saa.saa_caps = SMC_CAPS_4BIT_MODE|SMC_CAPS_AUTO_STOP; + if (ISSET(sc->sc_flags, SDHC_FLAG_8BIT_MODE)) + saa.saa_caps |= SMC_CAPS_8BIT_MODE; + if (ISSET(caps, SDHC_HIGH_SPEED_SUPP)) + saa.saa_caps |= SMC_CAPS_SD_HIGHSPEED; #if notyet if (ISSET(hp->flags, SHF_USE_DMA)) saa.saa_caps |= SMC_CAPS_DMA; @@ -353,16 +419,28 @@ sdhc_suspend(device_t dev, const pmf_qua { struct sdhc_softc *sc = device_private(dev); struct sdhc_host *hp; - int n, i; /* XXX poll for command completion or suspend command * in progress */ /* Save the host controller state. */ - for (n = 0; n < sc->sc_nhosts; n++) { + for (size_t n = 0; n < sc->sc_nhosts; n++) { hp = sc->sc_host[n]; - for (i = 0; i < sizeof hp->regs; i++) - hp->regs[i] = HREAD1(hp, i); + if (ISSET(sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + for (size_t i = 0; i < sizeof hp->regs; i += 4) { + uint32_t v = HREAD4(hp, i); + hp->regs[i + 0] = (v >> 0); + hp->regs[i + 1] = (v >> 8); + if (i + 3 < sizeof hp->regs) { + hp->regs[i + 2] = (v >> 16); + hp->regs[i + 3] = (v >> 24); + } + } + } else { + for (size_t i = 0; i < sizeof hp->regs; i++) { + hp->regs[i] = HREAD1(hp, i); + } + } } return true; } @@ -372,14 +450,30 @@ sdhc_resume(device_t dev, const pmf_qual { struct sdhc_softc *sc = device_private(dev); struct sdhc_host *hp; - int n, i; /* Restore the host controller state. */ - for (n = 0; n < sc->sc_nhosts; n++) { + for (size_t n = 0; n < sc->sc_nhosts; n++) { hp = sc->sc_host[n]; (void)sdhc_host_reset(hp); - for (i = 0; i < sizeof hp->regs; i++) - HWRITE1(hp, i, hp->regs[i]); + if (ISSET(sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + for (size_t i = 0; i < sizeof hp->regs; i += 4) { + if (i + 3 < sizeof hp->regs) { + HWRITE4(hp, i, + (hp->regs[i + 0] << 0) + | (hp->regs[i + 1] << 8) + | (hp->regs[i + 2] << 16) + | (hp->regs[i + 3] << 24)); + } else { + HWRITE4(hp, i, + (hp->regs[i + 0] << 0) + | (hp->regs[i + 1] << 8)); + } + } + } else { + for (size_t i = 0; i < sizeof hp->regs; i++) { + HWRITE1(hp, i, hp->regs[i]); + } + } } return true; } @@ -389,10 +483,9 @@ sdhc_shutdown(device_t dev, int flags) { struct sdhc_softc *sc = device_private(dev); struct sdhc_host *hp; - int i; /* XXX chip locks up if we don't disable it before reboot. */ - for (i = 0; i < sc->sc_nhosts; i++) { + for (size_t i = 0; i < sc->sc_nhosts; i++) { hp = sc->sc_host[i]; (void)sdhc_host_reset(hp); } @@ -407,13 +500,17 @@ static int sdhc_host_reset1(sdmmc_chipset_handle_t sch) { struct sdhc_host *hp = (struct sdhc_host *)sch; - uint16_t sdhcimask; + uint32_t sdhcimask; int error; /* Don't lock. */ /* Disable all interrupts. */ - HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, 0); + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + HWRITE4(hp, SDHC_NINTR_SIGNAL_EN, 0); + } else { + HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, 0); + } /* * Reset the entire host controller and wait up to 100ms for @@ -425,16 +522,30 @@ sdhc_host_reset1(sdmmc_chipset_handle_t /* Set data timeout counter value to max for now. */ HWRITE1(hp, SDHC_TIMEOUT_CTL, SDHC_TIMEOUT_MAX); +#if 0 + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) + HWRITE4(hp, SDHC_NINTR_STATUS, SDHC_CMD_TIMEOUT_ERROR << 16); +#endif /* Enable interrupts. */ sdhcimask = SDHC_CARD_REMOVAL | SDHC_CARD_INSERTION | SDHC_BUFFER_READ_READY | SDHC_BUFFER_WRITE_READY | SDHC_DMA_INTERRUPT | SDHC_BLOCK_GAP_EVENT | SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE; - HWRITE2(hp, SDHC_NINTR_STATUS_EN, sdhcimask); - HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK); - HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, sdhcimask); - HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, SDHC_EINTR_SIGNAL_MASK); + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + sdhcimask |= SDHC_EINTR_STATUS_MASK << 16; + HWRITE4(hp, SDHC_NINTR_STATUS_EN, sdhcimask); + sdhcimask ^= + (SDHC_EINTR_STATUS_MASK ^ SDHC_EINTR_SIGNAL_MASK) << 16; + sdhcimask ^= SDHC_BUFFER_READ_READY ^ SDHC_BUFFER_WRITE_READY; + HWRITE4(hp, SDHC_NINTR_SIGNAL_EN, sdhcimask); + } else { + HWRITE2(hp, SDHC_NINTR_STATUS_EN, sdhcimask); + HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK); + sdhcimask ^= SDHC_BUFFER_READ_READY ^ SDHC_BUFFER_WRITE_READY; + HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, sdhcimask); + HWRITE2(hp, SDHC_EINTR_SIGNAL_EN, SDHC_EINTR_SIGNAL_MASK); + } out: return error; @@ -482,9 +593,7 @@ sdhc_card_detect(sdmmc_chipset_handle_t r = ISSET(HREAD4(hp, SDHC_PRESENT_STATE), SDHC_CARD_INSERTED); mutex_exit(&hp->host_mtx); - if (r) - return 1; - return 0; + return r ? 1 : 0; } /* @@ -521,7 +630,8 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc /* * Disable bus power before voltage change. */ - if (!(hp->sc->sc_flags & SDHC_FLAG_NO_PWR0)) + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS) + && !ISSET(hp->sc->sc_flags, SDHC_FLAG_NO_PWR0)) HWRITE1(hp, SDHC_POWER_CTL, 0); /* If power is disabled, reset the host and return now. */ @@ -534,34 +644,36 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc * Select the lowest voltage according to capabilities. */ ocr &= hp->ocr; - if (ISSET(ocr, MMC_OCR_1_7V_1_8V|MMC_OCR_1_8V_1_9V)) + if (ISSET(ocr, MMC_OCR_1_7V_1_8V|MMC_OCR_1_8V_1_9V)) { vdd = SDHC_VOLTAGE_1_8V; - else if (ISSET(ocr, MMC_OCR_2_9V_3_0V|MMC_OCR_3_0V_3_1V)) + } else if (ISSET(ocr, MMC_OCR_2_9V_3_0V|MMC_OCR_3_0V_3_1V)) { vdd = SDHC_VOLTAGE_3_0V; - else if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V)) + } else if (ISSET(ocr, MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V)) { vdd = SDHC_VOLTAGE_3_3V; - else { + } else { /* Unsupported voltage level requested. */ error = EINVAL; goto out; } - /* - * Enable bus power. Wait at least 1 ms (or 74 clocks) plus - * voltage ramp until power rises. - */ - HWRITE1(hp, SDHC_POWER_CTL, - (vdd << SDHC_VOLTAGE_SHIFT) | SDHC_BUS_POWER); - sdmmc_delay(10000); + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + /* + * Enable bus power. Wait at least 1 ms (or 74 clocks) plus + * voltage ramp until power rises. + */ + HWRITE1(hp, SDHC_POWER_CTL, + (vdd << SDHC_VOLTAGE_SHIFT) | SDHC_BUS_POWER); + sdmmc_delay(10000); - /* - * 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(HREAD1(hp, SDHC_POWER_CTL), SDHC_BUS_POWER)) { - error = ENXIO; - goto out; + /* + * 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(HREAD1(hp, SDHC_POWER_CTL), SDHC_BUS_POWER)) { + error = ENXIO; + goto out; + } } out: @@ -574,30 +686,53 @@ out: * Return the smallest possible base clock frequency divisor value * for the CLOCK_CTL register to produce `freq' (KHz). */ -static int -sdhc_clock_divisor(struct sdhc_host *hp, u_int freq) +static bool +sdhc_clock_divisor(struct sdhc_host *hp, u_int freq, u_int *divp) { - int div; + u_int div; - if (hp->sc->sc_flags & SDHC_FLAG_HAVE_DVS) { - int dvs = (hp->clkbase + freq - 1) / freq; - div = 1; - for (div = 1; div <= 256; div <<= 1, dvs >>= 1) { - if (dvs <= 16) { - div <<= SDHC_SDCLK_DIV_SHIFT; - div |= (dvs - 1) << SDHC_SDCLK_DVS_SHIFT; - return div; + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_HAVE_CGM)) { + for (div = hp->clkbase / freq; div <= 0x3ff; div++) { + if ((hp->clkbase / div) <= freq) { + *divp = SDHC_SDCLK_CGM + | ((div & 0x300) << SDHC_SDCLK_XDIV_SHIFT) + | ((div & 0x0ff) << SDHC_SDCLK_DIV_SHIFT); + return true; + } + } + /* No divisor found. */ + return false; + } + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_HAVE_DVS)) { + u_int dvs = (hp->clkbase + freq - 1) / freq; + u_int roundup = dvs & 1; + for (dvs >>= 1, div = 1; div <= 256; div <<= 1, dvs >>= 1) { + if (dvs + roundup <= 16) { + dvs += roundup - 1; + *divp = (div << SDHC_SDCLK_DIV_SHIFT) + | (dvs << SDHC_SDCLK_DVS_SHIFT); + DPRINTF(2, + ("%s: divisor for freq %u is %u * %u\n", + HDEVNAME(hp), freq, div * 2, dvs + 1)); + return true; } + /* + * If we drop bits, we need to round up the divisor. + */ + roundup |= dvs & 1; } + panic("%s: can't find divisor for freq %u", HDEVNAME(hp), freq); } else { for (div = 1; div <= 256; div *= 2) { - if ((hp->clkbase / div) <= freq) - return (div / 2) << SDHC_SDCLK_DIV_SHIFT; + if ((hp->clkbase / div) <= freq) { + *divp = (div / 2) << SDHC_SDCLK_DIV_SHIFT; + return true; + } } } /* No divisor found. */ - return -1; + return false; } /* @@ -608,11 +743,11 @@ static int sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq) { struct sdhc_host *hp = (struct sdhc_host *)sch; - int div; - int timo; + u_int div; + u_int timo; int error = 0; #ifdef DIAGNOSTIC - int ispresent; + bool ispresent; #endif #ifdef DIAGNOSTIC @@ -631,43 +766,82 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sc /* * Stop SD clock before changing the frequency. */ - HWRITE2(hp, SDHC_CLOCK_CTL, 0); - if (freq == SDMMC_SDCLK_OFF) - goto out; + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + HCLR4(hp, SDHC_CLOCK_CTL, 0xfff8); + if (freq == SDMMC_SDCLK_OFF) { + HSET4(hp, SDHC_CLOCK_CTL, 0x80f0); + goto out; + } + } else { + HWRITE2(hp, SDHC_CLOCK_CTL, 0); + if (freq == SDMMC_SDCLK_OFF) + goto out; + } /* * Set the minimum base clock frequency divisor. */ - if ((div = sdhc_clock_divisor(hp, freq)) < 0) { + if (!sdhc_clock_divisor(hp, freq, &div)) { /* Invalid base clock frequency or `freq' value. */ error = EINVAL; goto out; } - HWRITE2(hp, SDHC_CLOCK_CTL, div); + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + HWRITE4(hp, SDHC_CLOCK_CTL, + div | (SDHC_TIMEOUT_MAX << 16)); + } else { + HWRITE2(hp, SDHC_CLOCK_CTL, div); + } /* * Start internal clock. Wait 10ms for stabilization. */ - HSET2(hp, SDHC_CLOCK_CTL, SDHC_INTCLK_ENABLE); - for (timo = 1000; timo > 0; timo--) { - if (ISSET(HREAD2(hp, SDHC_CLOCK_CTL), SDHC_INTCLK_STABLE)) - break; - sdmmc_delay(10); - } - if (timo == 0) { - error = ETIMEDOUT; - goto out; + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + sdmmc_delay(10000); + HSET4(hp, SDHC_CLOCK_CTL, 8|SDHC_INTCLK_ENABLE|SDHC_INTCLK_STABLE); + } else { + HSET2(hp, SDHC_CLOCK_CTL, SDHC_INTCLK_ENABLE); + for (timo = 1000; timo > 0; timo--) { + if (ISSET(HREAD2(hp, SDHC_CLOCK_CTL), SDHC_INTCLK_STABLE)) + break; + sdmmc_delay(10); + } + if (timo == 0) { + error = ETIMEDOUT; + goto out; + } } - /* - * Enable SD clock. - */ - HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE); + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + HSET1(hp, SDHC_SOFTWARE_RESET, SDHC_INIT_ACTIVE); + /* + * Sending 80 clocks at 400kHz takes 200us. + * So delay for that time + slop and then + * check a few times for completion. + */ + sdmmc_delay(210); + for (timo = 10; timo > 0; timo--) { + if (!ISSET(HREAD1(hp, SDHC_SOFTWARE_RESET), + SDHC_INIT_ACTIVE)) + break; + sdmmc_delay(10); + } + DPRINTF(2,("%s: %u init spins\n", __func__, 10 - timo)); + /* + * Enable SD clock. + */ + HSET4(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE); + } else { + /* + * Enable SD clock. + */ + HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE); - if (freq > 25000) - HSET1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED); - else - HCLR1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED); + if (freq > 25000) + HSET1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED); + else + HCLR1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED); + } out: mutex_exit(&hp->host_mtx); @@ -686,6 +860,10 @@ sdhc_bus_width(sdmmc_chipset_handle_t sc case 4: break; + case 8: + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_8BIT_MODE)) + break; + /* FALLTHROUGH */ default: DPRINTF(0,("%s: unsupported bus width (%d)\n", HDEVNAME(hp), width)); @@ -694,9 +872,17 @@ sdhc_bus_width(sdmmc_chipset_handle_t sc mutex_enter(&hp->host_mtx); reg = HREAD1(hp, SDHC_HOST_CTL); - reg &= ~SDHC_4BIT_MODE; - if (width == 4) - reg |= SDHC_4BIT_MODE; + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + reg &= ~(SDHC_4BIT_MODE|SDHC_8BIT_MODE); + if (width == 4) + reg |= SDHC_4BIT_MODE; + else if (width == 8) + reg |= SDHC_8BIT_MODE; + } else { + reg &= ~SDHC_4BIT_MODE; + if (width == 4) + reg |= SDHC_4BIT_MODE; + } HWRITE1(hp, SDHC_HOST_CTL, reg); mutex_exit(&hp->host_mtx); @@ -716,15 +902,17 @@ sdhc_card_enable_intr(sdmmc_chipset_hand { struct sdhc_host *hp = (struct sdhc_host *)sch; - mutex_enter(&hp->host_mtx); - if (enable) { - HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); - HSET2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); - } else { - HCLR2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); - HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + mutex_enter(&hp->host_mtx); + if (enable) { + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + HSET2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); + } else { + HCLR2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); + HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + } + mutex_exit(&hp->host_mtx); } - mutex_exit(&hp->host_mtx); } static void @@ -732,9 +920,11 @@ sdhc_card_intr_ack(sdmmc_chipset_handle_ { struct sdhc_host *hp = (struct sdhc_host *)sch; - mutex_enter(&hp->host_mtx); - HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); - mutex_exit(&hp->host_mtx); + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + mutex_enter(&hp->host_mtx); + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + mutex_exit(&hp->host_mtx); + } } static int @@ -759,6 +949,19 @@ sdhc_exec_command(sdmmc_chipset_handle_t struct sdhc_host *hp = (struct sdhc_host *)sch; int error; +#if 0 + if (cmd->c_data) { + const uint16_t ready = SDHC_BUFFER_READ_READY | SDHC_BUFFER_WRITE_READY; + if (ISSET(hp->flags, SHF_USE_DMA)) { + HCLR2(hp, SDHC_NINTR_SIGNAL_EN, ready); + HCLR2(hp, SDHC_NINTR_STATUS_EN, ready); + } else { + HSET2(hp, SDHC_NINTR_SIGNAL_EN, ready); + HSET2(hp, SDHC_NINTR_STATUS_EN, ready); + } + } +#endif + /* * Start the MMC command, or mark `cmd' as failed and return. */ @@ -805,10 +1008,25 @@ sdhc_exec_command(sdmmc_chipset_handle_t sdhc_transfer_data(hp, cmd); out: - mutex_enter(&hp->host_mtx); - /* Turn off the LED. */ - HCLR1(hp, SDHC_HOST_CTL, SDHC_LED_ON); - mutex_exit(&hp->host_mtx); +#if 0 + if (cmd->c_dmamap != NULL && cmd->c_error == 0 + && ISSET(hp->flags, SHF_USE_DMA) + && ISSET(cmd->c_flags, SCF_CMD_READ) { + if (((uintptr_t)cmd->c_data & PAGE_MASK) + cmd->c_datalen > PAGE_SIZE) { + memcpy(cmd->c_data, + (void *)hp->sc->dma_map->dm_segs[0].ds_addr, + cmd->c_datalen); + } + bus_dmamap_unload(hp->sc->dt, hp->sc->dma_map); + } +#endif + + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + mutex_enter(&hp->host_mtx); + /* Turn off the LED. */ + HCLR1(hp, SDHC_HOST_CTL, SDHC_LED_ON); + mutex_exit(&hp->host_mtx); + } SET(cmd->c_flags, SCF_ITSDONE); DPRINTF(1,("%s: cmd %d %s (flags=%08x error=%d)\n", HDEVNAME(hp), @@ -819,15 +1037,16 @@ out: static int sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd) { + struct sdhc_softc * const sc = hp->sc; uint16_t blksize = 0; uint16_t blkcount = 0; uint16_t mode; uint16_t command; int error; - DPRINTF(1,("%s: start cmd %d arg=%08x data=%p dlen=%d flags=%08x\n", + DPRINTF(1,("%s: start cmd %d arg=%08x data=%p dlen=%d flags=%08x, status=%#x\n", HDEVNAME(hp), cmd->c_opcode, cmd->c_arg, cmd->c_data, - cmd->c_datalen, cmd->c_flags)); + cmd->c_datalen, cmd->c_flags, HREAD4(hp, SDHC_NINTR_STATUS))); /* * The maximum block length for commands should be the minimum @@ -840,7 +1059,7 @@ sdhc_start_command(struct sdhc_host *hp, blkcount = cmd->c_datalen / blksize; if (cmd->c_datalen % blksize > 0) { /* XXX: Split this command. (1.7.4) */ - aprint_error_dev(hp->sc->sc_dev, + aprint_error_dev(sc->sc_dev, "data not a multiple of %u bytes\n", blksize); return EINVAL; } @@ -848,7 +1067,7 @@ sdhc_start_command(struct sdhc_host *hp, /* Check limit imposed by 9-bit block count. (1.7.2) */ if (blkcount > SDHC_BLOCK_COUNT_MAX) { - aprint_error_dev(hp->sc->sc_dev, "too much data\n"); + aprint_error_dev(sc->sc_dev, "too much data\n"); return EINVAL; } @@ -904,8 +1123,10 @@ sdhc_start_command(struct sdhc_host *hp, mutex_enter(&hp->host_mtx); - /* Alert the user not to remove the card. */ - HSET1(hp, SDHC_HOST_CTL, SDHC_LED_ON); + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + /* Alert the user not to remove the card. */ + HSET1(hp, SDHC_HOST_CTL, SDHC_LED_ON); + } /* Set DMA start address. */ if (ISSET(mode, SDHC_DMA_ENABLE)) @@ -915,12 +1136,18 @@ sdhc_start_command(struct sdhc_host *hp, * Start a CPU data transfer. Writing to the high order byte * of the SDHC_COMMAND register triggers the SD command. (1.5) */ - HWRITE2(hp, SDHC_TRANSFER_MODE, mode); - HWRITE2(hp, SDHC_BLOCK_SIZE, blksize); - if (blkcount > 1) - HWRITE2(hp, SDHC_BLOCK_COUNT, blkcount); - HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg); - HWRITE2(hp, SDHC_COMMAND, command); + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + HWRITE4(hp, SDHC_BLOCK_SIZE, blksize | (blkcount << 16)); + HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg); + HWRITE4(hp, SDHC_TRANSFER_MODE, mode | (command << 16)); + } else { + HWRITE2(hp, SDHC_TRANSFER_MODE, mode); + HWRITE2(hp, SDHC_BLOCK_SIZE, blksize); + if (blkcount > 1) + HWRITE2(hp, SDHC_BLOCK_COUNT, blkcount); + HWRITE4(hp, SDHC_ARGUMENT, cmd->c_arg); + HWRITE2(hp, SDHC_COMMAND, command); + } mutex_exit(&hp->host_mtx); @@ -966,6 +1193,11 @@ sdhc_transfer_data_dma(struct sdhc_host uint16_t remain; int error = 0; + KASSERT(HREAD2(hp, SDHC_NINTR_STATUS_EN) & SDHC_DMA_INTERRUPT); + KASSERT(HREAD2(hp, SDHC_NINTR_SIGNAL_EN) & SDHC_DMA_INTERRUPT); + KASSERT(HREAD2(hp, SDHC_NINTR_STATUS_EN) & SDHC_TRANSFER_COMPLETE); + KASSERT(HREAD2(hp, SDHC_NINTR_SIGNAL_EN) & SDHC_TRANSFER_COMPLETE); + for (;;) { if (!sdhc_wait_intr(hp, SDHC_DMA_INTERRUPT|SDHC_TRANSFER_COMPLETE, @@ -1000,31 +1232,56 @@ static int sdhc_transfer_data_pio(struct sdhc_host *hp, struct sdmmc_command *cmd) { uint8_t *data = cmd->c_data; - int len, datalen; - int mask; + u_int len, datalen; + u_int imask; + u_int pmask; int error = 0; + void (*pio_func)(struct sdhc_host *, uint8_t *, u_int); - mask = ISSET(cmd->c_flags, SCF_CMD_READ) ? - SDHC_BUFFER_READ_ENABLE : SDHC_BUFFER_WRITE_ENABLE; + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + imask = SDHC_BUFFER_READ_READY; + pmask = SDHC_BUFFER_READ_ENABLE; + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + pio_func = esdhc_read_data_pio; + } else { + pio_func = sdhc_read_data_pio; + } + } else { + imask = SDHC_BUFFER_WRITE_READY; + pmask = SDHC_BUFFER_WRITE_ENABLE; + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + pio_func = esdhc_write_data_pio; + } else { + pio_func = sdhc_write_data_pio; + } + } datalen = cmd->c_datalen; + KASSERT(HREAD2(hp, SDHC_NINTR_STATUS_EN) & imask); + KASSERT(HREAD2(hp, SDHC_NINTR_STATUS_EN) & SDHC_TRANSFER_COMPLETE); + KASSERT(HREAD2(hp, SDHC_NINTR_SIGNAL_EN) & SDHC_TRANSFER_COMPLETE); + while (datalen > 0) { - if (!sdhc_wait_intr(hp, - SDHC_BUFFER_READ_READY|SDHC_BUFFER_WRITE_READY, - SDHC_BUFFER_TIMEOUT)) { - error = ETIMEDOUT; - break; - } + if (!ISSET(HREAD4(hp, SDHC_PRESENT_STATE), imask)) { + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + HSET4(hp, SDHC_NINTR_SIGNAL_EN, imask); + } else { + HSET2(hp, SDHC_NINTR_SIGNAL_EN, imask); + } + if (!sdhc_wait_intr(hp, imask, SDHC_BUFFER_TIMEOUT)) { + error = ETIMEDOUT; + break; + } - error = sdhc_wait_state(hp, mask, mask); - if (error) - break; + error = sdhc_wait_state(hp, pmask, pmask); + if (error) + break; + } len = MIN(datalen, cmd->c_blklen); - if (ISSET(cmd->c_flags, SCF_CMD_READ)) - sdhc_read_data_pio(hp, data, len); - else - sdhc_write_data_pio(hp, data, len); + (*pio_func)(hp, data, len); + DPRINTF(2,("%s: pio data transfer %u @ %p\n", + HDEVNAME(hp), len, data)); data += len; datalen -= len; @@ -1038,7 +1295,7 @@ sdhc_transfer_data_pio(struct sdhc_host } static void -sdhc_read_data_pio(struct sdhc_host *hp, uint8_t *data, int datalen) +sdhc_read_data_pio(struct sdhc_host *hp, uint8_t *data, u_int datalen) { if (((__uintptr_t)data & 3) == 0) { @@ -1078,7 +1335,7 @@ sdhc_read_data_pio(struct sdhc_host *hp, } static void -sdhc_write_data_pio(struct sdhc_host *hp, uint8_t *data, int datalen) +sdhc_write_data_pio(struct sdhc_host *hp, uint8_t *data, u_int datalen) { if (((__uintptr_t)data & 3) == 0) { @@ -1117,6 +1374,49 @@ sdhc_write_data_pio(struct sdhc_host *hp } } + +static void +esdhc_read_data_pio(struct sdhc_host *hp, uint8_t *data, u_int datalen) +{ + uint16_t status = HREAD2(hp, SDHC_NINTR_STATUS); + while (datalen > 3 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + uint32_t v = HREAD4(hp, SDHC_DATA); + v = le32toh(v); + *(uint32_t *)data = v; + data += 4; + datalen -= 4; + status = HREAD2(hp, SDHC_NINTR_STATUS); + } + + if (datalen > 0 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + uint32_t v = HREAD4(hp, SDHC_DATA); + v = le32toh(v); + do { + *data++ = v; + v >>= 8; + } while (--datalen > 0); + } +} + +static void +esdhc_write_data_pio(struct sdhc_host *hp, uint8_t *data, u_int datalen) +{ + uint16_t status = HREAD2(hp, SDHC_NINTR_STATUS); + while (datalen > 3 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + uint32_t v = *(uint32_t *)data; + v = htole32(v); + HWRITE4(hp, SDHC_DATA, v); + data += 4; + datalen -= 4; + status = HREAD2(hp, SDHC_NINTR_STATUS); + } + if (datalen > 0 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + uint32_t v = *(uint32_t *)data; + v = htole32(v); + HWRITE4(hp, SDHC_DATA, v); + } +} + /* Prepare for another command. */ static int sdhc_soft_reset(struct sdhc_host *hp, int mask) @@ -1139,6 +1439,10 @@ sdhc_soft_reset(struct sdhc_host *hp, in return ETIMEDOUT; } + if (ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + HWRITE4(hp, SDHC_DMA_CTL, SDHC_DMA_SNOOP); + } + return 0; } @@ -1165,9 +1469,12 @@ sdhc_wait_intr(struct sdhc_host *hp, int hp->intr_error_status)); /* Command timeout has higher priority than command complete. */ - if (ISSET(status, SDHC_ERROR_INTERRUPT)) { + if (ISSET(status, SDHC_ERROR_INTERRUPT) || hp->intr_error_status) { hp->intr_error_status = 0; - (void)sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD); + hp->intr_status &= ~SDHC_ERROR_INTERRUPT; + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { + (void)sdhc_soft_reset(hp, SDHC_RESET_DAT|SDHC_RESET_CMD); + } status = 0; } mutex_exit(&hp->intr_mtx); @@ -1183,29 +1490,43 @@ sdhc_intr(void *arg) { struct sdhc_softc *sc = (struct sdhc_softc *)arg; struct sdhc_host *hp; - int host; int done = 0; uint16_t status; uint16_t error; /* We got an interrupt, but we don't know from which slot. */ - for (host = 0; host < sc->sc_nhosts; host++) { + for (size_t host = 0; host < sc->sc_nhosts; host++) { hp = sc->sc_host[host]; if (hp == NULL) continue; - /* Find out which interrupts are pending. */ - status = HREAD2(hp, SDHC_NINTR_STATUS); - if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) - continue; /* no interrupt for us */ - - /* Acknowledge the interrupts we are about to handle. */ - HWRITE2(hp, SDHC_NINTR_STATUS, status); - DPRINTF(2,("%s: interrupt status=%x\n", HDEVNAME(hp), - status)); - - if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) - continue; + if (ISSET(sc->sc_flags, SDHC_FLAG_32BIT_ACCESS)) { + /* Find out which interrupts are pending. */ + uint32_t xstatus = HREAD4(hp, SDHC_NINTR_STATUS); + status = xstatus; + error = xstatus >> 16; + status |= (error ? SDHC_ERROR_INTERRUPT : 0); + if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) + continue; /* no interrupt for us */ + /* Acknowledge the interrupts we are about to handle. */ + HWRITE4(hp, SDHC_NINTR_STATUS, xstatus); + } else { + /* Find out which interrupts are pending. */ + error = 0; + status = HREAD2(hp, SDHC_NINTR_STATUS); + if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) + continue; /* no interrupt for us */ + /* Acknowledge the interrupts we are about to handle. */ + HWRITE2(hp, SDHC_NINTR_STATUS, status); + if (ISSET(status, SDHC_ERROR_INTERRUPT)) { + /* Acknowledge error interrupts. */ + error = HREAD2(hp, SDHC_EINTR_STATUS); + HWRITE2(hp, SDHC_EINTR_STATUS, error); + } + } + + DPRINTF(2,("%s: interrupt status=%x error=%x\n", HDEVNAME(hp), + status, error)); /* Claim this interrupt. */ done = 1; @@ -1213,19 +1534,11 @@ sdhc_intr(void *arg) /* * Service error interrupts. */ - if (ISSET(status, SDHC_ERROR_INTERRUPT)) { - /* Acknowledge error interrupts. */ - error = HREAD2(hp, SDHC_EINTR_STATUS); - HWRITE2(hp, SDHC_EINTR_STATUS, error); - DPRINTF(2,("%s: error interrupt, status=%x\n", - HDEVNAME(hp), error)); - - if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR| - SDHC_DATA_TIMEOUT_ERROR)) { - hp->intr_error_status |= error; - hp->intr_status |= status; - cv_broadcast(&hp->intr_cv); - } + if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR| + SDHC_DATA_TIMEOUT_ERROR)) { + hp->intr_error_status |= error; + hp->intr_status |= status; + cv_broadcast(&hp->intr_cv); } /* @@ -1233,27 +1546,34 @@ sdhc_intr(void *arg) */ if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) { sdmmc_needs_discover(hp->sdmmc); -#if 0 - HCLR2(hp, SDHC_NINTR_STATUS_EN, - status & (SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)); -#endif + if (ISSET(sc->sc_flags, SDHC_FLAG_ENHANCED)) { + HCLR4(hp, SDHC_NINTR_STATUS_EN, + status & (SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)); + HCLR4(hp, SDHC_NINTR_SIGNAL_EN, + status & (SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)); + } } /* * Wake up the blocking process to service command * related interrupt(s). */ - if (ISSET(status, SDHC_BUFFER_READ_READY| - SDHC_BUFFER_WRITE_READY|SDHC_COMMAND_COMPLETE| + if (ISSET(status, SDHC_COMMAND_COMPLETE| + SDHC_BUFFER_READ_READY|SDHC_BUFFER_WRITE_READY| SDHC_TRANSFER_COMPLETE|SDHC_DMA_INTERRUPT)) { hp->intr_status |= status; + if (ISSET(sc->sc_flags, SDHC_FLAG_ENHANCED)) { + HCLR4(hp, SDHC_NINTR_SIGNAL_EN, + status & (SDHC_BUFFER_READ_READY|SDHC_BUFFER_WRITE_READY)); + } cv_broadcast(&hp->intr_cv); } /* * Service SD card interrupts. */ - if (ISSET(status, SDHC_CARD_INTERRUPT)) { + if (!ISSET(sc->sc_flags, SDHC_FLAG_ENHANCED) + && ISSET(status, SDHC_CARD_INTERRUPT)) { DPRINTF(0,("%s: card interrupt\n", HDEVNAME(hp))); HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); sdmmc_card_intr(hp->sdmmc); @@ -1270,8 +1590,9 @@ sdhc_dump_regs(struct sdhc_host *hp) printf("0x%02x PRESENT_STATE: %x\n", SDHC_PRESENT_STATE, HREAD4(hp, SDHC_PRESENT_STATE)); - printf("0x%02x POWER_CTL: %x\n", SDHC_POWER_CTL, - HREAD1(hp, SDHC_POWER_CTL)); + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) + printf("0x%02x POWER_CTL: %x\n", SDHC_POWER_CTL, + HREAD1(hp, SDHC_POWER_CTL)); printf("0x%02x NINTR_STATUS: %x\n", SDHC_NINTR_STATUS, HREAD2(hp, SDHC_NINTR_STATUS)); printf("0x%02x EINTR_STATUS: %x\n", SDHC_EINTR_STATUS,