/*
* 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;
/*
* 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
- 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;
+ }
}
}
- /* 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);
+ }
@@ -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));
/*
@@ -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);
}