svn commit: r275950 - in head/sys: arm/ti dev/sdhci
Ian Lepore
ian at FreeBSD.org
Sat Dec 20 01:13:15 UTC 2014
Author: ian
Date: Sat Dec 20 01:13:13 2014
New Revision: 275950
URL: https://svnweb.freebsd.org/changeset/base/275950
Log:
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.
The zero is apparently from a stale snapshot of the internal state presented
in the interface register, and leads to a false indication that the reset
is complete when it either hasn't started yet or is in-progress. The
workaround is to first loop until the bit is seen as asserted, then do the
normal loop waiting to see it de-asserted.
Submitted by: Michal Meloun <meloun at miracle.cz>
Modified:
head/sys/arm/ti/ti_sdhci.c
head/sys/dev/sdhci/sdhci.c
head/sys/dev/sdhci/sdhci.h
Modified: head/sys/arm/ti/ti_sdhci.c
==============================================================================
--- head/sys/arm/ti/ti_sdhci.c Sat Dec 20 00:37:56 2014 (r275949)
+++ head/sys/arm/ti/ti_sdhci.c Sat Dec 20 01:13:13 2014 (r275950)
@@ -413,9 +413,27 @@ ti_sdhci_hw_init(device_t dev)
DELAY(100);
}
- /* Reset both the command and data state machines */
+ /*
+ * Reset the command and data state machines and also other aspects of
+ * the controller such as bus clock and power.
+ *
+ * If we read the software reset register too fast after writing it we
+ * can get back a zero that means the reset hasn't started yet rather
+ * than that the reset is complete. Per TI recommendations, work around
+ * it by reading until we see the reset bit asserted, then read until
+ * it's clear. We also set the SDHCI_QUIRK_WAITFOR_RESET_ASSERTED quirk
+ * so that the main sdhci driver uses this same logic in its resets.
+ */
ti_sdhci_write_1(dev, NULL, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
- timeout = 1000;
+ timeout = 10000;
+ while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
+ SDHCI_RESET_ALL) != SDHCI_RESET_ALL) {
+ if (--timeout == 0) {
+ break;
+ }
+ DELAY(1);
+ }
+ timeout = 10000;
while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
SDHCI_RESET_ALL)) {
if (--timeout == 0) {
@@ -583,6 +601,12 @@ ti_sdhci_attach(device_t dev)
sc->slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE;
/*
+ * Reset bits are broken, have to wait to see the bits asserted
+ * before waiting to see them de-asserted.
+ */
+ sc->slot.quirks |= SDHCI_QUIRK_WAITFOR_RESET_ASSERTED;
+
+ /*
* DMA is not really broken, I just haven't implemented it yet.
*/
sc->slot.quirks |= SDHCI_QUIRK_BROKEN_DMA;
Modified: head/sys/dev/sdhci/sdhci.c
==============================================================================
--- head/sys/dev/sdhci/sdhci.c Sat Dec 20 00:37:56 2014 (r275949)
+++ head/sys/dev/sdhci/sdhci.c Sat Dec 20 01:13:13 2014 (r275950)
@@ -149,7 +149,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) &
@@ -168,26 +167,43 @@ sdhci_reset(struct sdhci_slot *slot, uin
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);
}
}
Modified: head/sys/dev/sdhci/sdhci.h
==============================================================================
--- head/sys/dev/sdhci/sdhci.h Sat Dec 20 00:37:56 2014 (r275949)
+++ head/sys/dev/sdhci/sdhci.h Sat Dec 20 01:13:13 2014 (r275950)
@@ -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
More information about the svn-src-all
mailing list