From 98770c509f0b9974959b983478aa5c9ad41138f2 Mon Sep 17 00:00:00 2001
From: riastradh <riastradh@NetBSD.org>
Date: Thu, 13 Dec 2012 06:43:37 +0000
Subject: [PATCH] Implement TI AM335x's SDHC reset quirk.  Beaglebone SDHC
 works now!

On the AM335x, we first must wait for the controller to acknowledge
the reset; then we can wait for the reset to complete.

I believe this quirk also applies to the OMAP4 ES, but I don't have
one of those to test and we don't seem to have an obvious conditional
for it anyway.

This quirk may work for controllers that don't require it too, but I
am nervous about doing it by default because if we miss the reset
acknowledgement, then we'll just time out even though everything is
really hunky-dory.

Also, for all sdhc, don't bother writing 0 in sdhc_soft_reset while
waiting for the reset to complete; there is no need.

ok matt
---
 sys/arch/arm/omap/omap3_sdhc.c |  7 +++++--
 sys/dev/sdmmc/sdhc.c           | 28 ++++++++++++++++++++++++----
 sys/dev/sdmmc/sdhcvar.h        |  3 ++-
 3 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/sys/arch/arm/omap/omap3_sdhc.c b/sys/arch/arm/omap/omap3_sdhc.c
index 1b66a70dea28..6f3f6e4fa188 100644
--- a/sys/arch/arm/omap/omap3_sdhc.c
+++ b/sys/arch/arm/omap/omap3_sdhc.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: omap3_sdhc.c,v 1.5 2012/12/12 15:19:53 matt Exp $	*/
+/*	$NetBSD: omap3_sdhc.c,v 1.6 2012/12/13 06:43:38 riastradh Exp $	*/
 /*-
  * Copyright (c) 2011 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -29,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: omap3_sdhc.c,v 1.5 2012/12/12 15:19:53 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: omap3_sdhc.c,v 1.6 2012/12/13 06:43:38 riastradh Exp $");
 
 #include "opt_omap.h"
 
@@ -150,6 +150,9 @@ obiosdhc_attach(device_t parent, device_t self, void *aux)
 	sc->sc.sc_flags |= SDHC_FLAG_NO_LED_ON;
 	sc->sc.sc_flags |= SDHC_FLAG_RSP136_CRC;
 	sc->sc.sc_flags |= SDHC_FLAG_SINGLE_ONLY;
+#ifdef TI_AM335X
+	sc->sc.sc_flags |= SDHC_FLAG_WAIT_RESET;
+#endif
 	sc->sc.sc_host = sc->sc_hosts;
 	sc->sc.sc_clkbase = 96000;	/* 96MHZ */
 	if (!prop_dictionary_get_uint32(prop, "clkmask", &sc->sc.sc_clkmsk))
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c
index bb449ba3f9e4..931e16b556f1 100644
--- a/sys/dev/sdmmc/sdhc.c
+++ b/sys/dev/sdmmc/sdhc.c
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdhc.c,v 1.34 2012/12/12 15:15:31 matt Exp $	*/
+/*	$NetBSD: sdhc.c,v 1.35 2012/12/13 06:43:37 riastradh Exp $	*/
 /*	$OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $	*/
 
 /*
@@ -23,7 +23,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.34 2012/12/12 15:15:31 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.35 2012/12/13 06:43:37 riastradh Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_sdmmc.h"
@@ -1518,17 +1518,37 @@ sdhc_soft_reset(struct sdhc_host *hp, int mask)
 
 	DPRINTF(1,("%s: software reset reg=%08x\n", HDEVNAME(hp), mask));
 
+	/* Request the reset.  */
 	HWRITE1(hp, SDHC_SOFTWARE_RESET, mask);
+
+	/*
+	 * If necessary, wait for the controller to set the bits to
+	 * acknowledge the reset.
+	 */
+	if (ISSET(hp->sc->sc_flags, SDHC_FLAG_WAIT_RESET) &&
+	    ISSET(mask, (SDHC_RESET_DAT | SDHC_RESET_CMD))) {
+		for (timo = 10000; timo > 0; timo--) {
+			if (ISSET(HREAD1(hp, SDHC_SOFTWARE_RESET), mask))
+				break;
+			/* Short delay because I worry we may miss it...  */
+			sdmmc_delay(1);
+		}
+		if (timo == 0)
+			return ETIMEDOUT;
+	}
+
+	/*
+	 * Wait for the controller to clear the bits to indicate that
+	 * the reset has completed.
+	 */
 	for (timo = 10; timo > 0; timo--) {
 		if (!ISSET(HREAD1(hp, SDHC_SOFTWARE_RESET), mask))
 			break;
 		sdmmc_delay(10000);
-		HWRITE1(hp, SDHC_SOFTWARE_RESET, 0);
 	}
 	if (timo == 0) {
 		DPRINTF(1,("%s: timeout reg=%08x\n", HDEVNAME(hp),
 		    HREAD1(hp, SDHC_SOFTWARE_RESET)));
-		HWRITE1(hp, SDHC_SOFTWARE_RESET, 0);
 		return ETIMEDOUT;
 	}
 
diff --git a/sys/dev/sdmmc/sdhcvar.h b/sys/dev/sdmmc/sdhcvar.h
index 4d945549819c..fd1441c349b1 100644
--- a/sys/dev/sdmmc/sdhcvar.h
+++ b/sys/dev/sdmmc/sdhcvar.h
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdhcvar.h,v 1.10 2012/12/12 15:15:31 matt Exp $	*/
+/*	$NetBSD: sdhcvar.h,v 1.11 2012/12/13 06:43:37 riastradh Exp $	*/
 /*	$OpenBSD: sdhcvar.h,v 1.3 2007/09/06 08:01:01 jsg Exp $	*/
 
 /*
@@ -47,6 +47,7 @@ struct sdhc_softc {
 #define	SDHC_FLAG_HOSTCAPS	0x0200	/* No device provided capabilities */
 #define	SDHC_FLAG_RSP136_CRC	0x0400	/* Resp 136 with CRC and end-bit */
 #define	SDHC_FLAG_SINGLE_ONLY	0x0800	/* Single transfer only */
+#define	SDHC_FLAG_WAIT_RESET	0x1000	/* Wait for soft resets to start */
 
 	uint32_t		sc_clkbase;
 	int			sc_clkmsk;	/* Mask for SDCLK */