summaryrefslogtreecommitdiffstats
path: root/sys/dev/sdhci
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2014-12-27 04:54:36 +0000
committerian <ian@FreeBSD.org>2014-12-27 04:54:36 +0000
commit3714a75ff6acdc831c3cfb013448773772a26daf (patch)
tree1e2a4745617d5c61749a425871c36817b39af903 /sys/dev/sdhci
parent02a7e72b74f75d8811be3a70490fb55f159db4c5 (diff)
downloadFreeBSD-src-3714a75ff6acdc831c3cfb013448773772a26daf.zip
FreeBSD-src-3714a75ff6acdc831c3cfb013448773772a26daf.tar.gz
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.
Diffstat (limited to 'sys/dev/sdhci')
-rw-r--r--sys/dev/sdhci/sdhci.c44
-rw-r--r--sys/dev/sdhci/sdhci.h7
2 files changed, 39 insertions, 12 deletions
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 | \
OpenPOWER on IntegriCloud