svn commit: r276287 - in stable/10/sys: arm/ti dev/sdhci

Ian Lepore ian at FreeBSD.org
Sat Dec 27 04:54:38 UTC 2014


Author: ian
Date: Sat Dec 27 04:54:36 2014
New Revision: 276287
URL: https://svnweb.freebsd.org/changeset/base/276287

Log:
  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.

Modified:
  stable/10/sys/arm/ti/ti_sdhci.c
  stable/10/sys/dev/sdhci/sdhci.c
  stable/10/sys/dev/sdhci/sdhci.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/arm/ti/ti_sdhci.c
==============================================================================
--- stable/10/sys/arm/ti/ti_sdhci.c	Sat Dec 27 04:51:21 2014	(r276286)
+++ stable/10/sys/arm/ti/ti_sdhci.c	Sat Dec 27 04:54:36 2014	(r276287)
@@ -112,6 +112,7 @@ static struct ofw_compat_data compat_dat
 #define	MMCHS_CON			0x02C
 #define	  MMCHS_CON_DW8			  (1 << 5)
 #define	  MMCHS_CON_DVAL_8_4MS		  (3 << 9)
+#define	  MMCHS_CON_OD			  (1 << 0)
 #define MMCHS_SYSCTL			0x12C
 #define   MMCHS_SYSCTL_CLKD_MASK	   0x3FF
 #define   MMCHS_SYSCTL_CLKD_SHIFT	   6
@@ -327,7 +328,7 @@ ti_sdhci_update_ios(device_t brdev, devi
 	struct ti_sdhci_softc *sc = device_get_softc(brdev);
 	struct sdhci_slot *slot;
 	struct mmc_ios *ios;
-	uint32_t val32;
+	uint32_t val32, newval32;
 
 	slot = device_get_ivars(reqdev);
 	ios = &slot->host.ios;
@@ -339,10 +340,20 @@ ti_sdhci_update_ios(device_t brdev, devi
 	 * requested, then let the standard driver handle everything else.
 	 */
 	val32 = ti_mmchs_read_4(sc, MMCHS_CON);
+	newval32  = val32;
+
 	if (ios->bus_width == bus_width_8)
-		ti_mmchs_write_4(sc, MMCHS_CON, val32 | MMCHS_CON_DW8); 
+		newval32 |= MMCHS_CON_DW8;
 	else
-		ti_mmchs_write_4(sc, MMCHS_CON, val32 & ~MMCHS_CON_DW8); 
+		newval32 &= ~MMCHS_CON_DW8;
+
+	if (ios->bus_mode == opendrain)
+		newval32 |= MMCHS_CON_OD;
+	else /* if (ios->bus_mode == pushpull) */
+		newval32 &= ~MMCHS_CON_OD;
+
+	if (newval32 != val32)
+		ti_mmchs_write_4(sc, MMCHS_CON, newval32);
 
 	return (sdhci_generic_update_ios(brdev, reqdev));
 }
@@ -392,20 +403,42 @@ ti_sdhci_hw_init(device_t dev)
 	/* Issue a softreset to the controller */
 	ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, MMCHS_SYSCONFIG_RESET);
 	timeout = 1000;
-	while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & MMCHS_SYSSTATUS_RESETDONE)) {
+	while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) &
+	    MMCHS_SYSSTATUS_RESETDONE)) {
 		if (--timeout == 0) {
-			device_printf(dev, "Error: Controller reset operation timed out\n");
+			device_printf(dev,
+			    "Error: Controller reset operation timed out\n");
 			break;
 		}
 		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;
-	while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL)) {
+	timeout = 10000;
+	while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
+	    SDHCI_RESET_ALL) != SDHCI_RESET_ALL) {
 		if (--timeout == 0) {
-			device_printf(dev, "Error: Software reset operation timed out\n");
+			break;
+		}
+		DELAY(1);
+	}
+	timeout = 10000;
+	while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
+	    SDHCI_RESET_ALL)) {
+		if (--timeout == 0) {
+			device_printf(dev,
+			    "Error: Software reset operation timed out\n");
 			break;
 		}
 		DELAY(100);
@@ -562,6 +595,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: stable/10/sys/dev/sdhci/sdhci.c
==============================================================================
--- stable/10/sys/dev/sdhci/sdhci.c	Sat Dec 27 04:51:21 2014	(r276286)
+++ stable/10/sys/dev/sdhci/sdhci.c	Sat Dec 27 04:54:36 2014	(r276287)
@@ -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, 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);
 	}
 }
 
@@ -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 *sl
 	/* 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) {

Modified: stable/10/sys/dev/sdhci/sdhci.h
==============================================================================
--- stable/10/sys/dev/sdhci/sdhci.h	Sat Dec 27 04:51:21 2014	(r276286)
+++ stable/10/sys/dev/sdhci/sdhci.h	Sat Dec 27 04:54:36 2014	(r276287)
@@ -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 | \


More information about the svn-src-all mailing list