From 3714a75ff6acdc831c3cfb013448773772a26daf Mon Sep 17 00:00:00 2001 From: ian Date: Sat, 27 Dec 2014 04:54:36 +0000 Subject: MFC r275944, r275946, r275949, r275950: Add code to set and reset open-drain mode on the bus when requested. When command and data interrupts have been aggregated together, don't do the data-completed processing if a command-error interrupt is also asserted. Add a new sdhci quirk, SDHCI_QUIRK_WAITFOR_RESET_ASSERTED, to work around TI OMAP controllers which will return the reset-in-progress bit as zero if you read the status register too fast after setting the reset bit. --- sys/dev/sdhci/sdhci.c | 44 +++++++++++++++++++++++++++++++++----------- sys/dev/sdhci/sdhci.h | 7 ++++++- 2 files changed, 39 insertions(+), 12 deletions(-) (limited to 'sys/dev/sdhci') diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c index 1434d22..503cbf3 100644 --- a/sys/dev/sdhci/sdhci.c +++ b/sys/dev/sdhci/sdhci.c @@ -150,7 +150,6 @@ static void sdhci_reset(struct sdhci_slot *slot, uint8_t mask) { int timeout; - uint8_t res; if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { if (!(RD4(slot, SDHCI_PRESENT_STATE) & @@ -169,26 +168,43 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask) sdhci_set_clock(slot, clock); } - WR1(slot, SDHCI_SOFTWARE_RESET, mask); - if (mask & SDHCI_RESET_ALL) { slot->clock = 0; slot->power = 0; } + WR1(slot, SDHCI_SOFTWARE_RESET, mask); + + if (slot->quirks & SDHCI_QUIRK_WAITFOR_RESET_ASSERTED) { + /* + * Resets on TI OMAPs and AM335x are incompatible with SDHCI + * specification. The reset bit has internal propagation delay, + * so a fast read after write returns 0 even if reset process is + * in progress. The workaround is to poll for 1 before polling + * for 0. In the worst case, if we miss seeing it asserted the + * time we spent waiting is enough to ensure the reset finishes. + */ + timeout = 10000; + while ((RD1(slot, SDHCI_SOFTWARE_RESET) & mask) != mask) { + if (timeout <= 0) + break; + timeout--; + DELAY(1); + } + } + /* Wait max 100 ms */ - timeout = 100; + timeout = 10000; /* Controller clears the bits when it's done */ - while ((res = RD1(slot, SDHCI_SOFTWARE_RESET)) & mask) { - if (timeout == 0) { - slot_printf(slot, - "Reset 0x%x never completed - 0x%x.\n", - (int)mask, (int)res); + while (RD1(slot, SDHCI_SOFTWARE_RESET) & mask) { + if (timeout <= 0) { + slot_printf(slot, "Reset 0x%x never completed.\n", + mask); sdhci_dumpregs(slot); return; } timeout--; - DELAY(1000); + DELAY(10); } } @@ -714,9 +730,13 @@ sdhci_timeout(void *arg) struct sdhci_slot *slot = arg; if (slot->curcmd != NULL) { + slot_printf(slot, " Controller timeout\n"); + sdhci_dumpregs(slot); sdhci_reset(slot, SDHCI_RESET_CMD|SDHCI_RESET_DATA); slot->curcmd->error = MMC_ERR_TIMEOUT; sdhci_req_done(slot); + } else { + slot_printf(slot, " Spurious timeout - no active command\n"); } } @@ -1275,7 +1295,9 @@ sdhci_generic_intr(struct sdhci_slot *slot) /* Handle data interrupts. */ if (intmask & SDHCI_INT_DATA_MASK) { WR4(slot, SDHCI_INT_STATUS, intmask & SDHCI_INT_DATA_MASK); - sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK); + /* Dont call data_irq in case of errored command */ + if ((intmask & SDHCI_INT_CMD_ERROR_MASK) == 0) + sdhci_data_irq(slot, intmask & SDHCI_INT_DATA_MASK); } /* Handle AutoCMD12 error interrupt. */ if (intmask & SDHCI_INT_ACMD12ERR) { diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h index 5cde2b0..ff1576e 100644 --- a/sys/dev/sdhci/sdhci.h +++ b/sys/dev/sdhci/sdhci.h @@ -59,6 +59,8 @@ #define SDHCI_QUIRK_MISSING_CAPS (1<<12) /* Hardware shifts the 136-bit response, don't do it in software. */ #define SDHCI_QUIRK_DONT_SHIFT_RESPONSE (1<<13) +/* Wait to see reset bit asserted before waiting for de-asserted */ +#define SDHCI_QUIRK_WAITFOR_RESET_ASSERTED (1<<14) /* * Controller registers @@ -182,8 +184,11 @@ #define SDHCI_INT_NORMAL_MASK 0x00007FFF #define SDHCI_INT_ERROR_MASK 0xFFFF8000 -#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ +#define SDHCI_INT_CMD_ERROR_MASK (SDHCI_INT_TIMEOUT | \ SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) + +#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_CMD_ERROR_MASK) + #define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ -- cgit v1.1