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 <[email protected]>

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
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to