Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 15 May 2014 22:35:05 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r266200 - in stable/10/sys: arm/freescale arm/freescale/imx boot/fdt/dts dev/fdt dev/mmc dev/sdhci
Message-ID:  <201405152235.s4FMZ5l0045983@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Thu May 15 22:35:04 2014
New Revision: 266200
URL: http://svnweb.freebsd.org/changeset/base/266200

Log:
  MFC r261938, r261939, r261940, r261944, r261945, r261946, r261947, r261956, r261957, r261983, r261094,
      r261955, r261958,
  
    Add a driver to provide access to imx6 on-chip one-time-programmble data.
  
    Make it possible to access the ocotp registers before the ocotp device
    is attached, by establishing a temporary mapping of the registers when
    necessary.
  
    It turns out Freescale cleverly made the ocotp device compatible across
    several different families of SoCs, so move it to the freescale directory
    and prefix everything with fsl rather than imx6.
  
    Convert the imx6 sdhci "R1B fix" from a busy-loop in the interrupt handler
    to a callout.
  
    Increase the wait time for acquiring the SD bus from 10 to 250ms.
  
    If no compatible cards were found after probing the SD bus, say so.
  
    Add timeout logic to sdhci, separate from the timeouts done by the hardware.
  
    After a timeout, reset the controller using SDHCI_RESET_CMD|SDHCI_RESET_DATA
    rather than SDHCI_RESET_ALL; the latter turns off clocks and power, removing
    any possibility of recovering from the error.
  
    Add a helper routine to depth-search the device tree for a node with a
    matching 'compatible' property.

Added:
  stable/10/sys/arm/freescale/fsl_ocotp.c
     - copied, changed from r261946, head/sys/arm/freescale/fsl_ocotp.c
  stable/10/sys/arm/freescale/fsl_ocotpreg.h
     - copied, changed from r261946, head/sys/arm/freescale/fsl_ocotpreg.h
  stable/10/sys/arm/freescale/fsl_ocotpvar.h
     - copied unchanged from r261946, head/sys/arm/freescale/fsl_ocotpvar.h
Modified:
  stable/10/sys/arm/freescale/imx/files.imx6
  stable/10/sys/arm/freescale/imx/imx_sdhci.c
  stable/10/sys/boot/fdt/dts/imx6.dtsi
  stable/10/sys/dev/fdt/fdt_common.c
  stable/10/sys/dev/fdt/fdt_common.h
  stable/10/sys/dev/mmc/mmc.c
  stable/10/sys/dev/sdhci/sdhci.c
  stable/10/sys/dev/sdhci/sdhci.h
Directory Properties:
  stable/10/   (props changed)

Copied and modified: stable/10/sys/arm/freescale/fsl_ocotp.c (from r261946, head/sys/arm/freescale/fsl_ocotp.c)
==============================================================================
--- head/sys/arm/freescale/fsl_ocotp.c	Sat Feb 15 21:21:57 2014	(r261946, copy source)
+++ stable/10/sys/arm/freescale/fsl_ocotp.c	Thu May 15 22:35:04 2014	(r266200)
@@ -46,6 +46,48 @@ __FBSDID("$FreeBSD$");
 #include <arm/freescale/fsl_ocotpreg.h>
 #include <arm/freescale/fsl_ocotpvar.h>
 
+/*
+ * Find the physical address and size of the ocotp registers and devmap them,
+ * returning a pointer to the virtual address of the base.
+ *
+ * XXX This is temporary until we've worked out all the details of controlling
+ * the load order of devices.  In an ideal world this device would be up and
+ * running before anything that needs it.  When we're at a point to make that
+ * happen, this little block of code, and the few lines in fsl_ocotp_read_4()
+ * that refer to it can be deleted.
+ */
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <dev/fdt/fdt_common.h>
+#include <machine/devmap.h>
+
+static uint32_t   *ocotp_regs;
+static vm_size_t   ocotp_size;
+
+static void
+fsl_ocotp_devmap(void)
+{
+	phandle_t child, root;
+	u_long base, size;
+
+	if ((root = OF_finddevice("/")) == 0)
+		goto fatal;
+	if ((child = fdt_depth_search_compatible(root, "fsl,imx6q-ocotp", 0)) == 0)
+		goto fatal;
+	if (fdt_regsize(child, &base, &size) != 0)
+		goto fatal;
+
+	ocotp_size = (vm_size_t)size;
+
+	if ((ocotp_regs = pmap_mapdev((vm_offset_t)base, ocotp_size)) == NULL)
+		goto fatal;
+
+	return;
+fatal:
+	panic("cannot find/map the ocotp registers");
+}
+/* XXX end of temporary code */
+
 struct ocotp_softc {
 	device_t	dev;
 	struct resource	*mem_res;
@@ -56,23 +98,16 @@ static struct ocotp_softc *ocotp_sc;
 static inline uint32_t
 RD4(struct ocotp_softc *sc, bus_size_t off)
 {
+
 	return (bus_read_4(sc->mem_res, off));
 }
 
-
 static int
 ocotp_detach(device_t dev)
 {
-	struct ocotp_softc *sc;
 
-	sc = device_get_softc(dev);
-
-	if (sc->mem_res != NULL)
-		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
-
-	ocotp_sc = NULL;
-
-	return (0);
+	/* The ocotp registers are always accessible. */
+	return (EBUSY);
 }
 
 static int
@@ -95,6 +130,11 @@ ocotp_attach(device_t dev)
 	}
 
 	ocotp_sc = sc;
+
+	/* We're done with the temporary mapping now. */
+	if (ocotp_regs != NULL)
+		pmap_unmapdev((vm_offset_t)ocotp_regs, ocotp_size);
+
 	err = 0;
 
 out:
@@ -111,11 +151,11 @@ ocotp_probe(device_t dev)
 	if (!ofw_bus_status_okay(dev))
 		return (ENXIO);
 
-        if (ofw_bus_is_compatible(dev, "fsl,fslq-ocotp") == 0)
+	if (ofw_bus_is_compatible(dev, "fsl,imx6q-ocotp") == 0)
 		return (ENXIO);
 
 	device_set_desc(dev, 
-	    "Freescale i.MX6 On-Chip One-Time-Programmable Memory");
+	    "Freescale On-Chip One-Time-Programmable Memory");
 
 	return (BUS_PROBE_DEFAULT);
 }
@@ -124,13 +164,23 @@ uint32_t
 fsl_ocotp_read_4(bus_size_t off)
 {
 
-	if (ocotp_sc == NULL)
-		panic("fsl_ocotp_read_4: softc not set!");
-
 	if (off > FSL_OCOTP_LAST_REG)
 		panic("fsl_ocotp_read_4: offset out of range");
 
-	return (RD4(ocotp_sc, off));
+	/* If we have a softcontext use the regular bus_space read. */
+	if (ocotp_sc != NULL)
+		return (RD4(ocotp_sc, off));
+
+	/*
+	 * Otherwise establish a tempory device mapping if necessary, and read
+	 * the device without any help from bus_space.
+	 *
+	 * XXX Eventually the code from there down can be deleted.
+	 */
+	if (ocotp_regs == NULL)
+		fsl_ocotp_devmap();
+
+	return (ocotp_regs[off / 4]);
 }
 
 static device_method_t ocotp_methods[] = {

Copied and modified: stable/10/sys/arm/freescale/fsl_ocotpreg.h (from r261946, head/sys/arm/freescale/fsl_ocotpreg.h)
==============================================================================
--- head/sys/arm/freescale/fsl_ocotpreg.h	Sat Feb 15 21:21:57 2014	(r261946, copy source)
+++ stable/10/sys/arm/freescale/fsl_ocotpreg.h	Thu May 15 22:35:04 2014	(r266200)
@@ -48,6 +48,13 @@
 #define	FSL_OCOTP_CFG1				0x420
 #define	FSL_OCOTP_CFG2				0x430
 #define	FSL_OCOTP_CFG3				0x440
+#define	  FSL_OCOTP_CFG3_SPEED_SHIFT		  16
+#define	  FSL_OCOTP_CFG3_SPEED_MASK		  \
+    (0x03 << FSL_OCOTP_CFG3_SPEED_SHIFT)
+#define	  FSL_OCOTP_CFG3_SPEED_792MHZ		  0
+#define	  FSL_OCOTP_CFG3_SPEED_852MHZ		  1
+#define	  FSL_OCOTP_CFG3_SPEED_996MHZ		  2
+#define	  FSL_OCOTP_CFG3_SPEED_1200MHZ		  3
 #define	FSL_OCOTP_CFG4				0x450
 #define	FSL_OCOTP_CFG5				0x460
 #define	FSL_OCOTP_CFG6				0x470

Copied: stable/10/sys/arm/freescale/fsl_ocotpvar.h (from r261946, head/sys/arm/freescale/fsl_ocotpvar.h)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/10/sys/arm/freescale/fsl_ocotpvar.h	Thu May 15 22:35:04 2014	(r266200, copy of r261946, head/sys/arm/freescale/fsl_ocotpvar.h)
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2014 Steven Lawrance <stl@koffein.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	FSL_OCOTPVAR_H
+#define	FSL_OCOTPVAR_H
+
+uint32_t fsl_ocotp_read_4(bus_size_t _offset);
+
+#endif

Modified: stable/10/sys/arm/freescale/imx/files.imx6
==============================================================================
--- stable/10/sys/arm/freescale/imx/files.imx6	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/arm/freescale/imx/files.imx6	Thu May 15 22:35:04 2014	(r266200)
@@ -17,6 +17,7 @@ kern/kern_clocksource.c			standard
 arm/arm/gic.c				standard
 arm/arm/pl310.c				standard
 arm/freescale/imx/bus_space.c		standard
+arm/freescale/fsl_ocotp.c		standard
 arm/freescale/imx/common.c		standard
 arm/freescale/imx/imx6_anatop.c		standard
 arm/freescale/imx/imx6_ccm.c		standard

Modified: stable/10/sys/arm/freescale/imx/imx_sdhci.c
==============================================================================
--- stable/10/sys/arm/freescale/imx/imx_sdhci.c	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/arm/freescale/imx/imx_sdhci.c	Thu May 15 22:35:04 2014	(r266200)
@@ -35,13 +35,18 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/types.h>
 #include <sys/bus.h>
+#include <sys/callout.h>
 #include <sys/kernel.h>
+#include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/module.h>
+#include <sys/mutex.h>
 #include <sys/resource.h>
 #include <sys/rman.h>
 #include <sys/taskqueue.h>
+#include <sys/time.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -65,6 +70,8 @@ struct imx_sdhci_softc {
 	struct resource *	irq_res;
 	void *			intr_cookie;
 	struct sdhci_slot	slot;
+	struct callout		r1bfix_callout;
+	sbintime_t		r1bfix_timeout_at;
 	uint32_t		baseclk_hz;
 	uint32_t		sdclockreg_freq_bits;
 	uint32_t		cmd_and_mode;
@@ -130,6 +137,8 @@ struct imx_sdhci_softc {
 #define	 SDHC_PROT_CDTL		(1 << 6)
 #define	 SDHC_PROT_CDSS		(1 << 7)
 
+#define	SDHC_INT_STATUS		0x30
+
 #define	SDHC_CLK_IPGEN		(1 << 0)
 #define	SDHC_CLK_HCKEN		(1 << 1)
 #define	SDHC_CLK_PEREN		(1 << 2)
@@ -147,6 +156,7 @@ static struct ofw_compat_data compat_dat
 };;
 
 static void imx_sdhc_set_clock(struct imx_sdhci_softc *sc, int enable);
+static void imx_sdhci_r1bfix_func(void *arg);
 
 static inline uint32_t
 RD4(struct imx_sdhci_softc *sc, bus_size_t off)
@@ -532,46 +542,107 @@ imx_sdhc_set_clock(struct imx_sdhci_soft
 	    RD4(sc, SDHC_VEND_SPEC) | SDHC_VEND_FRC_SDCLK_ON);
 }
 
+static boolean_t
+imx_sdhci_r1bfix_is_wait_done(struct imx_sdhci_softc *sc)
+{
+	uint32_t inhibit;
+
+	mtx_assert(&sc->slot.mtx, MA_OWNED);
+
+	/*
+	 * Check the DAT0 line status using both the DLA (data line active) and
+	 * CDIHB (data inhibit) bits in the present state register.  In theory
+	 * just DLA should do the trick,  but in practice it takes both.  If the
+	 * DAT0 line is still being held and we're not yet beyond the timeout
+	 * point, just schedule another callout to check again later.
+	 */
+	inhibit = RD4(sc, SDHC_PRES_STATE) & (SDHC_PRES_DLA | SDHC_PRES_CDIHB);
+
+	if (inhibit && getsbinuptime() < sc->r1bfix_timeout_at) {
+		callout_reset_sbt(&sc->r1bfix_callout, SBT_1MS, 0, 
+		    imx_sdhci_r1bfix_func, sc, 0);
+		return (false);
+	}
+
+	/*
+	 * If we reach this point with the inhibit bits still set, we've got a
+	 * timeout, synthesize a DATA_TIMEOUT interrupt.  Otherwise the DAT0
+	 * line has been released, and we synthesize a DATA_END, and if the type
+	 * of fix needed was on a command-without-data we also now add in the
+	 * original INT_RESPONSE that we suppressed earlier.
+	 */
+	if (inhibit)
+		sc->r1bfix_intmask |= SDHCI_INT_DATA_TIMEOUT;
+	else {
+		sc->r1bfix_intmask |= SDHCI_INT_DATA_END;
+		if (sc->r1bfix_type == R1BFIX_NODATA)
+			sc->r1bfix_intmask |= SDHCI_INT_RESPONSE;
+	}
+
+	sc->r1bfix_type = R1BFIX_NONE;
+	return (true);
+}
+
+static void
+imx_sdhci_r1bfix_func(void * arg)
+{
+	struct imx_sdhci_softc *sc = arg;
+	boolean_t r1bwait_done;
+
+	mtx_lock(&sc->slot.mtx);
+	r1bwait_done = imx_sdhci_r1bfix_is_wait_done(sc);
+	mtx_unlock(&sc->slot.mtx);
+	if (r1bwait_done)
+		sdhci_generic_intr(&sc->slot);
+}
+
 static void
 imx_sdhci_intr(void *arg)
 {
 	struct imx_sdhci_softc *sc = arg;
 	uint32_t intmask;
 
-	intmask = RD4(sc, SDHCI_INT_STATUS);
+	mtx_lock(&sc->slot.mtx);
 
 	/*
 	 * Manually check the DAT0 line for R1B response types that the
-	 * controller fails to handle properly.
+	 * controller fails to handle properly.  The controller asserts the done
+	 * interrupt while the card is still asserting busy with the DAT0 line.
 	 *
-	 * To do the NODATA fix, when the RESPONSE (COMMAND_COMPLETE) interrupt
-	 * occurs, we have to wait for the DAT0 line to be released, then
-	 * synthesize a DATA_END (TRANSFER_COMPLETE) interrupt, which we do by
-	 * storing SDHCI_INT_DATA_END into a variable that gets ORed into the
-	 * return value when the SDHCI_INT_STATUS register is read.
+	 * We check DAT0 immediately because most of the time, especially on a
+	 * read, the card will actually be done by time we get here.  If it's
+	 * not, then the wait_done routine will schedule a callout to re-check
+	 * periodically until it is done.  In that case we clear the interrupt
+	 * out of the hardware now so that we can present it later when the DAT0
+	 * line is released.
 	 *
-	 * For the AC12 fix, when the DATA_END interrupt occurs we wait for the
-	 * DAT0 line to be released, and the waiting is all the fix we need.
-	 */
-	if ((sc->r1bfix_type == R1BFIX_NODATA && 
-	    (intmask & SDHCI_INT_RESPONSE)) || 
-	    (sc->r1bfix_type == R1BFIX_AC12 && 
-	    (intmask & SDHCI_INT_DATA_END))) {
-		uint32_t count;
-		count = 0;
-		/* XXX use a callout or something instead of busy-waiting. */
-		while (count < 250000 && 
-		   (RD4(sc, SDHC_PRES_STATE) & SDHC_PRES_DLA)) {
-			++count;
-			DELAY(1);
+	 * If we need to wait for the the DAT0 line to be released, we set up a
+	 * timeout point 250ms in the future.  This number comes from the SD
+	 * spec, which allows a command to take that long.  In the real world,
+	 * cards tend to take 10-20ms for a long-running command such as a write
+	 * or erase that spans two pages.
+	 */
+	switch (sc->r1bfix_type) {
+	case R1BFIX_NODATA:
+		intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_RESPONSE;
+		break;
+	case R1BFIX_AC12:
+		intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_DATA_END;
+		break;
+	default:
+		intmask = 0;
+		break;
+	}
+	if (intmask) {
+		sc->r1bfix_timeout_at = getsbinuptime() + 250 * SBT_1MS;
+		if (!imx_sdhci_r1bfix_is_wait_done(sc)) {
+			WR4(sc, SDHC_INT_STATUS, intmask);
+			bus_barrier(sc->mem_res, SDHC_INT_STATUS, 4, 
+			    BUS_SPACE_BARRIER_WRITE);
 		}
-		if (count >= 250000)
-			sc->r1bfix_intmask = SDHCI_INT_DATA_TIMEOUT;
-		else if (sc->r1bfix_type == R1BFIX_NODATA)
-			sc->r1bfix_intmask = SDHCI_INT_DATA_END;
-		sc->r1bfix_type = R1BFIX_NONE;
 	}
 
+	mtx_unlock(&sc->slot.mtx);
 	sdhci_generic_intr(&sc->slot);
 }
 
@@ -660,6 +731,7 @@ imx_sdhci_attach(device_t dev)
 	}
 
 	sdhci_init_slot(dev, &sc->slot, 0);
+	callout_init(&sc->r1bfix_callout, true);
 
 	/*
 	 * If the slot is flagged with the non-removable property, set our flag
@@ -670,7 +742,7 @@ imx_sdhci_attach(device_t dev)
 	 * We don't have gpio support yet.  If there's a cd-gpios property just
 	 * force the SDHCI_CARD_PRESENT bit on for now.  If there isn't really a
 	 * card there it will fail to probe at the mmc layer and nothing bad
-	 * happens except instantiating a /dev/mmcN device for an empty slot.
+	 * happens except instantiating an mmcN device for an empty slot.
 	 */
 	node = ofw_bus_get_node(dev);
 	if (OF_hasprop(node, "non-removable"))

Modified: stable/10/sys/boot/fdt/dts/imx6.dtsi
==============================================================================
--- stable/10/sys/boot/fdt/dts/imx6.dtsi	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/boot/fdt/dts/imx6.dtsi	Thu May 15 22:35:04 2014	(r266200)
@@ -337,6 +337,11 @@
 				bus-width = <0x4>;
 				status ="disabled";
 			};
+
+			ocotp0: ocotp@021bc000 {
+				compatible = "fsl,imx6q-ocotp";
+				reg = <0x021bc000 0x4000>;
+			}
 		};
 	};
 };

Modified: stable/10/sys/dev/fdt/fdt_common.c
==============================================================================
--- stable/10/sys/dev/fdt/fdt_common.c	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/dev/fdt/fdt_common.c	Thu May 15 22:35:04 2014	(r266200)
@@ -223,6 +223,27 @@ fdt_find_compatible(phandle_t start, con
 	return (0);
 }
 
+phandle_t
+fdt_depth_search_compatible(phandle_t start, const char *compat, int strict)
+{
+	phandle_t child, node;
+
+	/*
+	 * Depth-search all descendants of 'start' node, and find first with
+	 * matching 'compatible' property.
+	 */
+	for (node = OF_child(start); node != 0; node = OF_peer(node)) {
+		if (fdt_is_compatible(node, compat) && 
+		    (strict == 0 || fdt_is_compatible_strict(node, compat))) {
+			return (node);
+		}
+		child = fdt_depth_search_compatible(node, compat, strict);
+		if (child != 0)
+			return (child);
+	}
+	return (0);
+}
+
 int
 fdt_is_enabled(phandle_t node)
 {
@@ -483,11 +504,23 @@ fdt_intr_to_rl(device_t dev, phandle_t n
 	nintr = OF_getencprop_alloc(node, "interrupts",  sizeof(*intr),
 	    (void **)&intr);
 	if (nintr > 0) {
-		iparent = 0;
-		OF_searchencprop(node, "interrupt-parent", &iparent,
-		    sizeof(iparent));
-		OF_searchencprop(OF_xref_phandle(iparent), "#interrupt-cells",
-		    &icells, sizeof(icells));
+		if (OF_searchencprop(node, "interrupt-parent", &iparent,
+		    sizeof(iparent)) == -1) {
+			device_printf(dev, "No interrupt-parent found, "
+			    "assuming direct parent\n");
+			iparent = OF_parent(node);
+		}
+		if (OF_searchencprop(OF_xref_phandle(iparent), 
+		    "#interrupt-cells", &icells, sizeof(icells)) == -1) {
+			device_printf(dev, "Missing #interrupt-cells property, "
+			    "assuming <1>\n");
+			icells = 1;
+		}
+		if (icells < 1 || icells > nintr) {
+			device_printf(dev, "Invalid #interrupt-cells property "
+			    "value <%d>, assuming <1>\n", icells);
+			icells = 1;
+		}
 		for (i = 0, k = 0; i < nintr; i += icells, k++) {
 			intr[i] = ofw_bus_map_intr(dev, iparent, icells,
 			    &intr[i]);

Modified: stable/10/sys/dev/fdt/fdt_common.h
==============================================================================
--- stable/10/sys/dev/fdt/fdt_common.h	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/dev/fdt/fdt_common.h	Thu May 15 22:35:04 2014	(r266200)
@@ -81,6 +81,7 @@ u_long fdt_data_get(void *, int);
 int fdt_data_to_res(pcell_t *, int, int, u_long *, u_long *);
 int fdt_data_verify(void *, int);
 phandle_t fdt_find_compatible(phandle_t, const char *, int);
+phandle_t fdt_depth_search_compatible(phandle_t, const char *, int);
 int fdt_get_mem_regions(struct mem_region *, int *, uint32_t *);
 int fdt_get_reserved_regions(struct mem_region *, int *);
 int fdt_get_phyaddr(phandle_t, device_t, int *, void **);

Modified: stable/10/sys/dev/mmc/mmc.c
==============================================================================
--- stable/10/sys/dev/mmc/mmc.c	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/dev/mmc/mmc.c	Thu May 15 22:35:04 2014	(r266200)
@@ -1572,6 +1572,7 @@ mmc_go_discovery(struct mmc_softc *sc)
 	if (bootverbose || mmc_debug)
 		device_printf(sc->dev, "Current OCR: 0x%08x\n", mmcbr_get_ocr(dev));
 	if (mmcbr_get_ocr(dev) == 0) {
+		device_printf(sc->dev, "No compatible cards found on bus\n");
 		mmc_delete_cards(sc);
 		mmc_power_down(sc);
 		return;

Modified: stable/10/sys/dev/sdhci/sdhci.c
==============================================================================
--- stable/10/sys/dev/sdhci/sdhci.c	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/dev/sdhci/sdhci.c	Thu May 15 22:35:04 2014	(r266200)
@@ -29,6 +29,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
+#include <sys/callout.h>
 #include <sys/conf.h>
 #include <sys/kernel.h>
 #include <sys/lock.h>
@@ -609,6 +610,7 @@ sdhci_init_slot(device_t dev, struct sdh
 	
 	TASK_INIT(&slot->card_task, 0, sdhci_card_task, slot);
 	callout_init(&slot->card_callout, 1);
+	callout_init_mtx(&slot->timeout_callout, &slot->mtx, 0);
 	return (0);
 }
 
@@ -623,6 +625,7 @@ sdhci_cleanup_slot(struct sdhci_slot *sl
 {
 	device_t d;
 
+	callout_drain(&slot->timeout_callout);
 	callout_drain(&slot->card_callout);
 	taskqueue_drain(taskqueue_swi_giant, &slot->card_task);
 
@@ -702,6 +705,32 @@ sdhci_generic_update_ios(device_t brdev,
 	return (0);
 }
 
+static void 
+sdhci_req_done(struct sdhci_slot *slot)
+{
+	struct mmc_request *req;
+
+	if (slot->req != NULL && slot->curcmd != NULL) {
+		callout_stop(&slot->timeout_callout);
+		req = slot->req;
+		slot->req = NULL;
+		slot->curcmd = NULL;
+		req->done(req);
+	}
+}
+ 
+static void 
+sdhci_timeout(void *arg)
+{
+	struct sdhci_slot *slot = arg;
+
+	if (slot->curcmd != NULL) {
+		sdhci_reset(slot, SDHCI_RESET_CMD|SDHCI_RESET_DATA);
+		slot->curcmd->error = MMC_ERR_TIMEOUT;
+		sdhci_req_done(slot);
+	}
+}
+ 
 static void
 sdhci_set_transfer_mode(struct sdhci_slot *slot,
 	struct mmc_data *data)
@@ -727,7 +756,6 @@ sdhci_set_transfer_mode(struct sdhci_slo
 static void
 sdhci_start_command(struct sdhci_slot *slot, struct mmc_command *cmd)
 {
-	struct mmc_request *req = slot->req;
 	int flags, timeout;
 	uint32_t mask, state;
 
@@ -740,9 +768,7 @@ sdhci_start_command(struct sdhci_slot *s
 	if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
 		slot_printf(slot, "Unsupported response type!\n");
 		cmd->error = MMC_ERR_FAILED;
-		slot->req = NULL;
-		slot->curcmd = NULL;
-		req->done(req);
+		sdhci_req_done(slot);
 		return;
 	}
 
@@ -754,9 +780,7 @@ sdhci_start_command(struct sdhci_slot *s
 	    slot->power == 0 ||
 	    slot->clock == 0) {
 		cmd->error = MMC_ERR_FAILED;
-		slot->req = NULL;
-		slot->curcmd = NULL;
-		req->done(req);
+		sdhci_req_done(slot);
 		return;
 	}
 	/* Always wait for free CMD bus. */
@@ -767,17 +791,24 @@ sdhci_start_command(struct sdhci_slot *s
 	/* We shouldn't wait for DAT for stop commands. */
 	if (cmd == slot->req->stop)
 		mask &= ~SDHCI_DAT_INHIBIT;
-	/* Wait for bus no more then 10 ms. */
-	timeout = 10;
+	/*
+	 *  Wait for bus no more then 250 ms.  Typically there will be no wait
+	 *  here at all, but when writing a crash dump we may be bypassing the
+	 *  host platform's interrupt handler, and in some cases that handler
+	 *  may be working around hardware quirks such as not respecting r1b
+	 *  busy indications.  In those cases, this wait-loop serves the purpose
+	 *  of waiting for the prior command and data transfers to be done, and
+	 *  SD cards are allowed to take up to 250ms for write and erase ops.
+	 *  (It's usually more like 20-30ms in the real world.)
+	 */
+	timeout = 250;
 	while (state & mask) {
 		if (timeout == 0) {
 			slot_printf(slot, "Controller never released "
 			    "inhibit bit(s).\n");
 			sdhci_dumpregs(slot);
 			cmd->error = MMC_ERR_FAILED;
-			slot->req = NULL;
-			slot->curcmd = NULL;
-			req->done(req);
+			sdhci_req_done(slot);
 			return;
 		}
 		timeout--;
@@ -819,6 +850,8 @@ sdhci_start_command(struct sdhci_slot *s
 	sdhci_set_transfer_mode(slot, cmd->data);
 	/* Start command. */
 	WR2(slot, SDHCI_COMMAND_FLAGS, (cmd->opcode << 8) | (flags & 0xff));
+	/* Start timeout callout. */
+	callout_reset(&slot->timeout_callout, 2*hz, sdhci_timeout, slot);
 }
 
 static void
@@ -1004,10 +1037,7 @@ sdhci_start(struct sdhci_slot *slot)
 		sdhci_reset(slot, SDHCI_RESET_DATA);
 	}
 
-	/* We must be done -- bad idea to do this while locked? */
-	slot->req = NULL;
-	slot->curcmd = NULL;
-	req->done(req);
+	sdhci_req_done(slot);
 }
 
 int

Modified: stable/10/sys/dev/sdhci/sdhci.h
==============================================================================
--- stable/10/sys/dev/sdhci/sdhci.h	Thu May 15 22:11:48 2014	(r266199)
+++ stable/10/sys/dev/sdhci/sdhci.h	Thu May 15 22:35:04 2014	(r266200)
@@ -241,6 +241,7 @@ struct sdhci_slot {
 	bus_addr_t	paddr;		/* DMA buffer address */
 	struct task	card_task;	/* Card presence check task */
 	struct callout	card_callout;	/* Card insert delay callout */
+	struct callout	timeout_callout;/* Card command/data response timeout */
 	struct mmc_host host;		/* Host parameters */
 	struct mmc_request *req;	/* Current request */
 	struct mmc_command *curcmd;	/* Current command of current request */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201405152235.s4FMZ5l0045983>