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-head mailing list