svn commit: r257393 - in head/sys: boot/fdt/dts dev/usb/controller

Ian Lepore ian at FreeBSD.org
Wed Oct 30 18:26:20 UTC 2013


Author: ian
Date: Wed Oct 30 18:26:18 2013
New Revision: 257393
URL: http://svnweb.freebsd.org/changeset/base/257393

Log:
  Rework the imx ehci driver so that it's four separate ehci units rather
  than one unit with four busses attached to it.  This allows us to use
  existing fdt data which describes separate devices with separate resources.
  It also allows any combination of the units to be en/disabled in the
  board dts files.
  
  Adjust our dts code to match what's used by linux and u-boot now that
  we're structured to do so.
  
  Document lots of interesting stuff learned whiling doing this with a big
  comment block in the driver, so I don't have to re-learn it for the next
  round of changes.

Modified:
  head/sys/boot/fdt/dts/digi-ccwmx53.dts
  head/sys/boot/fdt/dts/efikamx.dts
  head/sys/boot/fdt/dts/imx51x.dtsi
  head/sys/boot/fdt/dts/imx53-qsb.dts
  head/sys/boot/fdt/dts/imx53x.dtsi
  head/sys/dev/usb/controller/ehci_imx.c

Modified: head/sys/boot/fdt/dts/digi-ccwmx53.dts
==============================================================================
--- head/sys/boot/fdt/dts/digi-ccwmx53.dts	Wed Oct 30 17:55:31 2013	(r257392)
+++ head/sys/boot/fdt/dts/digi-ccwmx53.dts	Wed Oct 30 18:26:18 2013	(r257393)
@@ -90,6 +90,12 @@
 			gpio at 53f90000 {
 				status = "okay";
 			};
+			usb at 53f80000 /* OTG */ {
+				status = "okay";
+			};
+			usb at 53f80200 /* Host 1 */ {
+				status = "okay";
+			};
 			wdog at 53f98000 {
 				status = "okay";
 			};

Modified: head/sys/boot/fdt/dts/efikamx.dts
==============================================================================
--- head/sys/boot/fdt/dts/efikamx.dts	Wed Oct 30 17:55:31 2013	(r257392)
+++ head/sys/boot/fdt/dts/efikamx.dts	Wed Oct 30 18:26:18 2013	(r257393)
@@ -89,6 +89,12 @@
 			gpio at 73f90000 {
 				status = "okay";
 			};
+			usb at 73f80000 /* OTG */ {
+				status = "okay";
+			};
+			usb at 73f80200 /* Host 1 */ {
+				status = "okay";
+			};
 			wdog at 73f98000 {
 				status = "okay";
 			};

Modified: head/sys/boot/fdt/dts/imx51x.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/imx51x.dtsi	Wed Oct 30 17:55:31 2013	(r257392)
+++ head/sys/boot/fdt/dts/imx51x.dtsi	Wed Oct 30 18:26:18 2013	(r257393)
@@ -246,16 +246,48 @@
 				/* 7003C000 0x4000 SPBA */
 			};
 
-			/* 73F80000 0x4000 USBOH3 */
-			/* irq14 USBOH3 USB Host 1 */
-			/* irq16 USBOH3 USB Host 2 */
-			/* irq17 USBOH3 USB Host 3 */
-			/* irq18 USBOH3 USB OTG */
-			usb1: usb at 73F80000 {
-				compatible = "fsl,usb-4core";
-				reg = <0x73f80000 0x4000>;
-				interrupt-parent = <&tzic>;
-				interrupts = <18 14 16 17>;
+			usbphy0: usbphy at 0 {
+				compatible = "usb-nop-xceiv";
+				status = "okay";
+			};
+
+			usbotg: usb at 73f80000 {
+				compatible = "fsl,imx51-usb", "fsl,imx27-usb";
+				reg = <0x73f80000 0x0200>;
+				interrupts = <18>;
+				fsl,usbmisc = <&usbmisc 0>;
+				fsl,usbphy = <&usbphy0>;
+				status = "disabled";
+			};
+
+			usbh1: usb at 73f80200 {
+				compatible = "fsl,imx51-usb", "fsl,imx27-usb";
+				reg = <0x73f80200 0x0200>;
+				interrupts = <14>;
+				fsl,usbmisc = <&usbmisc 1>;
+				status = "disabled";
+			};
+
+			usbh2: usb at 73f80400 {
+				compatible = "fsl,imx51-usb", "fsl,imx27-usb";
+				reg = <0x73f80400 0x0200>;
+				interrupts = <16>;
+				fsl,usbmisc = <&usbmisc 2>;
+				status = "disabled";
+			};
+
+			usbh3: usb at 73f80600 {
+				compatible = "fsl,imx51-usb", "fsl,imx27-usb";
+				reg = <0x73f80600 0x0200>;
+				interrupts = <17>;
+				fsl,usbmisc = <&usbmisc 3>;
+				status = "disabled";
+			};
+
+			usbmisc: usbmisc at 73f80800 {
+				#index-cells = <1>;
+				compatible = "fsl,imx51-usbmisc";
+				reg = <0x73f80800 0x200>;
 			};
 
 			/* 73F98000 0x4000 WDOG1 */

Modified: head/sys/boot/fdt/dts/imx53-qsb.dts
==============================================================================
--- head/sys/boot/fdt/dts/imx53-qsb.dts	Wed Oct 30 17:55:31 2013	(r257392)
+++ head/sys/boot/fdt/dts/imx53-qsb.dts	Wed Oct 30 18:26:18 2013	(r257393)
@@ -28,6 +28,7 @@
  * SUCH DAMAGE.
  *
  * Freescale i.MX53 Quick Start Board
+ * In u-boot, this board is known as "MX53LOCO" for some reason.
  *
  * $FreeBSD$
  */
@@ -90,6 +91,12 @@
 			gpio at 53f90000 {
 				status = "okay";
 			};
+			usb at 53f80000 /* OTG */ {
+				status = "okay";
+			};
+			usb at 53f80200 /* Host 1 */ {
+				status = "okay";
+			};
 			wdog at 53f98000 {
 				status = "okay";
 			};

Modified: head/sys/boot/fdt/dts/imx53x.dtsi
==============================================================================
--- head/sys/boot/fdt/dts/imx53x.dtsi	Wed Oct 30 17:55:31 2013	(r257392)
+++ head/sys/boot/fdt/dts/imx53x.dtsi	Wed Oct 30 18:26:18 2013	(r257393)
@@ -290,16 +290,50 @@
 				/* 5003C000 0x4000 SPBA */
 			};
 
-			/* 73F80000 0x4000 USBOH3 */
-			/* irq14 USBOH3 USB Host 1 */
-			/* irq16 USBOH3 USB Host 2 */
-			/* irq17 USBOH3 USB Host 3 */
-			/* irq18 USBOH3 USB OTG */
-			usb1: usb at 53F80000 {
-				compatible = "fsl,usb-4core";
-				reg = <0x53f80000 0x4000>;
-				interrupt-parent = <&tzic>;
-				interrupts = <18 14 16 17>;
+			usbphy0: usbphy at 0 {
+				compatible = "usb-nop-xceiv";
+				status = "okay";
+			};
+
+			usbphy1: usbphy at 1 {
+				compatible = "usb-nop-xceiv";
+				status = "okay";
+			};
+
+			usbotg: usb at 53f80000 {
+				compatible = "fsl,imx53-usb", "fsl,imx27-usb";
+				reg = <0x53f80000 0x0200>;
+				interrupts = <18>;
+				fsl,usbphy = <&usbphy0>;
+				status = "disabled";
+			};
+
+			usbh1: usb at 53f80200 {
+				compatible = "fsl,imx53-usb", "fsl,imx27-usb";
+				reg = <0x53f80200 0x0200>;
+				interrupts = <14>;
+				fsl,usbphy = <&usbphy1>;
+				status = "disabled";
+			};
+
+			usbh2: usb at 53f80400 {
+				compatible = "fsl,imx53-usb", "fsl,imx27-usb";
+				reg = <0x53f80400 0x0200>;
+				interrupts = <16>;
+				status = "disabled";
+			};
+
+			usbh3: usb at 53f80600 {
+				compatible = "fsl,imx53-usb", "fsl,imx27-usb";
+				reg = <0x53f80600 0x0200>;
+				interrupts = <17>;
+				status = "disabled";
+			};
+
+			usbmisc: usbmisc at 53f80800 {
+				#index-cells = <1>;
+				compatible = "fsl,imx53-usbmisc";
+				reg = <0x53f80800 0x200>;
 			};
 
 			/* 53F98000 0x4000 WDOG1 */
@@ -419,18 +453,6 @@
 				status = "disabled";
 			};
 
-
-
-			/* 53FC4000 0x4000 USBOH3 */
-			/* NOTYET
-			usb at 53fc4000 {
-				compatible = "fsl,imx53-otg";
-				reg = <0x53fc4000 0x4000>;
-				interrupt-parent = <&tzic>;
-				interrupts = <>;
-				status = "disabled";
-			};
-			*/
 			/* 53FD0000 0x4000 SRC */
 			reset at 53fd0000 {
 				compatible = "fsl,imx53-src";

Modified: head/sys/dev/usb/controller/ehci_imx.c
==============================================================================
--- head/sys/dev/usb/controller/ehci_imx.c	Wed Oct 30 17:55:31 2013	(r257392)
+++ head/sys/dev/usb/controller/ehci_imx.c	Wed Oct 30 18:26:18 2013	(r257393)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (c) 2010-2012 Semihalf
  * Copyright (c) 2012 The FreeBSD Foundation
+ * Copyright (c) 2013 Ian Lepore <ian at freebsd.org>
  * All rights reserved.
  *
  * Portions of this software were developed by Oleksandr Rybalko
@@ -31,7 +32,9 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
-#include "opt_bus.h"
+/*
+ * EHCI driver for Freescale i.MX SoCs which incorporate the USBOH3 controller.
+ */
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -52,231 +55,278 @@ __FBSDID("$FreeBSD$");
 #include <dev/usb/usb_bus.h>
 #include <dev/usb/controller/ehci.h>
 #include <dev/usb/controller/ehcireg.h>
+#include "usbdevs.h"
 
 #include <machine/bus.h>
 #include <machine/resource.h>
 
-#include "opt_platform.h"
+#include <arm/freescale/imx/imx_machdep.h>
 
-#define	FSL_EHCI_COUNT		4
-#define FSL_EHCI_REG_OFF	0x100
-#define FSL_EHCI_REG_SIZE	0x100
-#define	FSL_EHCI_REG_STEP	0x200
+#include "opt_platform.h"
 
-struct imx_ehci_softc {
-	ehci_softc_t		ehci[FSL_EHCI_COUNT];
-	/* MEM + 4 interrupts */
-	struct resource		*sc_res[1 + FSL_EHCI_COUNT];
-};
+/*
+ * Notes on the hardware and related FDT data seen in the wild.
+ *
+ * There are two sets of registers in the USBOH3 implementation; documentation
+ * refers to them as "core" and "non-core" registers.  A set of core register
+ * exists for each OTG or EHCI device.  There is a single set of non-core
+ * registers per USBOH3, and they control aspects of operation not directly
+ * related to the USB specs, such as whether interrupts from each of the core
+ * devices are able to generate a SoC wakeup event.
+ *
+ * In the FreeBSD universe we might be inclined to describe the core and
+ * non-core registers by using a pair of resource address/size values (two
+ * entries in the reg property for each core).  However, we have to work with
+ * existing FDT data (which mostly comes from the linux universe), and the way
+ * they've chosen to represent this is with an entry for a "usbmisc" device
+ * whose reg property describes the non-core registers. The way we handle FDT
+ * data, this means that the resources (memory-mapped register range) for the
+ * non-core registers belongs to a device other than the echi devices.
+ *
+ * At the moment we have no need to access the non-core registers, so all of
+ * this amounts to documenting what's known.  The following compat strings have
+ * been seen in existing FDT data:
+ *   - "fsl,imx25-usbmisc"
+ *   - "fsl,imx51-usbmisc";
+ *   - "fsl,imx6q-usbmisc";
+ *
+ * In addition to the single usbmisc device, the existing FDT data defines a
+ * separate device for each of the OTG or EHCI cores within the USBOH3.  Each of
+ * those devices has a set of core registers described by the reg property.
+ *
+ * The core registers for each of the four cores in the USBOH3 are divided into
+ * two parts: a set of imx-specific registers at an offset of 0 from the
+ * beginning of the register range, and the standard USB (EHCI or OTG) registers
+ * at an offset of 0x100 from the beginning of the register range.  The FreeBSD
+ * way of dealing with this might be to map out two ranges in the reg property,
+ * but that's not what the alternate universe has done.  To work with existing
+ * FDT data, we acquire the resource that maps all the core registers, then use
+ * bus_space_subregion() to create another resource that maps just the standard
+ * USB registers, which we provide to the standard USB code in the ehci_softc.
+ *
+ * The following compat strings have been seen for the OTG and EHCI cores.  The
+ * FDT compat table in this driver contains all these strings, but as of this
+ * writing, not all of these SoCs have been tested with the driver.  The fact
+ * that imx27 is common to all of them gives some hope that the driver will work
+ * on all these SoCs.
+ *   - "fsl,imx23-usb", "fsl,imx27-usb";
+ *   - "fsl,imx25-usb", "fsl,imx27-usb";
+ *   - "fsl,imx28-usb", "fsl,imx27-usb";
+ *   - "fsl,imx51-usb", "fsl,imx27-usb";
+ *   - "fsl,imx53-usb", "fsl,imx27-usb";
+ *   - "fsl,imx6q-usb", "fsl,imx27-usb";
+ *
+ * The FDT data for some SoCs contains the following properties, which we don't
+ * currently do anything with:
+ *   - fsl,usbmisc = <&usbmisc 0>;
+ *   - fsl,usbphy = <&usbphy0>;
+ *
+ * Some imx SoCs have FDT data related to USB PHY, some don't.  We have separate
+ * usbphy drivers where needed; this data is mentioned here just to keep all the
+ * imx-FDT-usb-related info in one place.  Here are the usbphy compat strings
+ * known to exist:
+ *   - "nop-usbphy"
+ *   - "usb-nop-xceiv";
+ *   - "fsl,imx23-usbphy" 
+ *   - "fsl,imx28-usbphy", "fsl,imx23-usbphy";
+ *   - "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
+ *
+ */
 
-/* i.MX515 have 4 EHCI inside USB core */
-/* TODO: we can get number of EHCIs by IRQ allocation */
-static struct resource_spec imx_ehci_spec[] = {
-	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
-	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
-	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
-	{ SYS_RES_IRQ,		2,	RF_ACTIVE },
-	/* RF_OPTIONAL will allow to use driver for systems with 3 EHCIs */
-	{ SYS_RES_IRQ,		3,	RF_ACTIVE | RF_OPTIONAL },
-	{ -1, 0 }
+static struct ofw_compat_data compat_data[] = {
+	{"fsl,imx6q-usb",	1},
+	{"fsl,imx53-usb",	1},
+	{"fsl,imx51-usb",	1},
+	{"fsl,imx28-usb",	1},
+	{"fsl,imx27-usb",	1},
+	{"fsl,imx25-usb",	1},
+	{"fsl,imx23-usb",	1},
+	{NULL,		 	0},
 };
 
-/* Forward declarations */
-static int	fsl_ehci_attach(device_t self);
-static int	fsl_ehci_detach(device_t self);
-static int	fsl_ehci_probe(device_t self);
-
-static device_method_t ehci_methods[] = {
-	/* Device interface */
-	DEVMETHOD(device_probe, fsl_ehci_probe),
-	DEVMETHOD(device_attach, fsl_ehci_attach),
-	DEVMETHOD(device_detach, fsl_ehci_detach),
-	DEVMETHOD(device_suspend, bus_generic_suspend),
-	DEVMETHOD(device_resume, bus_generic_resume),
-	DEVMETHOD(device_shutdown, bus_generic_shutdown),
-
-	/* Bus interface */
-	DEVMETHOD(bus_print_child, bus_generic_print_child),
-
-	{ 0, 0 }
-};
+/*
+ * Each EHCI device in the SoC has some SoC-specific per-device registers at an
+ * offset of 0, then the standard EHCI registers begin at an offset of 0x100.
+ */
+#define	IMX_EHCI_REG_OFF	0x100
+#define	IMX_EHCI_REG_SIZE	0x100
 
-/* kobj_class definition */
-static driver_t ehci_driver = {
-	"ehci",
-	ehci_methods,
-	sizeof(struct imx_ehci_softc)
+struct imx_ehci_softc {
+	ehci_softc_t	ehci_softc;
+	struct resource	*ehci_mem_res;	/* EHCI core regs. */
+	struct resource	*ehci_irq_res;	/* EHCI core IRQ. */ 
 };
 
-static devclass_t ehci_devclass;
+static int
+imx_ehci_probe(device_t dev)
+{
 
-DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
-MODULE_DEPEND(ehci, usb, 1, 1, 1);
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+		device_set_desc(dev, "Freescale i.MX integrated USB controller");
+		return (BUS_PROBE_DEFAULT);
+	}
+	return (ENXIO);
+}
 
-/*
- * Public methods
- */
 static int
-fsl_ehci_probe(device_t dev)
+imx_ehci_detach(device_t dev)
 {
+	struct imx_ehci_softc *sc;
+	ehci_softc_t *esc;
 
-	if (ofw_bus_is_compatible(dev, "fsl,usb-4core") == 0)
-		return (ENXIO);
+	sc = device_get_softc(dev);
 
-	device_set_desc(dev, "Freescale integrated USB controller");
+	esc = &sc->ehci_softc;
 
-	return (BUS_PROBE_DEFAULT);
+	if (esc->sc_bus.bdev != NULL)
+		device_delete_child(dev, esc->sc_bus.bdev);
+	if (esc->sc_flags & EHCI_SCFLG_DONEINIT)
+		ehci_detach(esc);
+	if (esc->sc_intr_hdl != NULL)
+		bus_teardown_intr(dev, esc->sc_irq_res, 
+		    esc->sc_intr_hdl);
+	if (sc->ehci_irq_res != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, 
+		    sc->ehci_irq_res);
+	if (sc->ehci_mem_res != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0,
+		    sc->ehci_mem_res);
+
+	usb_bus_mem_free_all(&esc->sc_bus, &ehci_iterate_hw_softc);
+
+	/* During module unload there are lots of children leftover */
+	device_delete_children(dev);
+
+	return (0);
 }
 
 static int
-fsl_ehci_attach(device_t self)
+imx_ehci_attach(device_t dev)
 {
 	struct imx_ehci_softc *sc;
-	bus_space_tag_t iot;
 	ehci_softc_t *esc;
-	int err, i, rid;
+	int err, rid;
+
+	sc = device_get_softc(dev);
+	esc = &sc->ehci_softc;
+	err = 0;
 
-	sc = device_get_softc(self);
+	/* Allocate bus_space resources. */
 	rid = 0;
+	sc->ehci_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (sc->ehci_mem_res == NULL) {
+		device_printf(dev, "Cannot allocate memory resources\n");
+		err = ENXIO;
+		goto out;
+	}
 
-	/* Allocate io resource for EHCI */
-	if (bus_alloc_resources(self, imx_ehci_spec, sc->sc_res)) {
-		device_printf(self, "could not allocate resources\n");
-		return (ENXIO);
+	rid = 0;
+	sc->ehci_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (sc->ehci_irq_res == NULL) {
+		device_printf(dev, "Cannot allocate IRQ resources\n");
+		err = ENXIO;
+		goto out;
 	}
-	iot = rman_get_bustag(sc->sc_res[0]);
 
-	/* TODO: Power/clock enable */
-	/* TODO: basic init */
+	esc->sc_io_tag = rman_get_bustag(sc->ehci_mem_res);
+	esc->sc_bus.parent = dev;
+	esc->sc_bus.devices = esc->sc_devices;
+	esc->sc_bus.devices_max = EHCI_MAX_DEVICES;
+
+	if (usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(dev),
+	    &ehci_iterate_hw_softc) != 0) {
+		device_printf(dev, "usb_bus_mem_alloc_all() failed\n");
+		err = ENOMEM;
+		goto out;
+	}
 
-	for (i = 0; i < FSL_EHCI_COUNT; i ++) {
-		/* No interrupt - no driver */
-		if (sc->sc_res[1 + i] == NULL)
-			continue;
-
-		esc = &sc->ehci[i];
-		esc->sc_io_tag = iot;
-		esc->sc_bus.parent = self;
-		esc->sc_bus.devices = esc->sc_devices;
-		esc->sc_bus.devices_max = EHCI_MAX_DEVICES;
-
-		if (usb_bus_mem_alloc_all(&esc->sc_bus, USB_GET_DMA_TAG(self),
-		    &ehci_iterate_hw_softc))
-			continue;
-
-		/*
-		 * Set handle to USB related registers subregion used by
-		 * generic EHCI driver.
-		 */
-		err = bus_space_subregion(iot,
-		    rman_get_bushandle(sc->sc_res[0]),
-		    FSL_EHCI_REG_OFF + (i * FSL_EHCI_REG_STEP),
-		    FSL_EHCI_REG_SIZE, &esc->sc_io_hdl);
-		if (err != 0)
-			continue;
-
-		/* Setup interrupt handler */
-		err = bus_setup_intr(self, sc->sc_res[1 + i], INTR_TYPE_BIO,
-		    NULL, (driver_intr_t *)ehci_interrupt, esc,
-		    &esc->sc_intr_hdl);
-		if (err) {
-			device_printf(self, "Could not setup irq, "
-			    "for EHCI%d %d\n", i, err);
-			continue;
-		}
-
-		/* Add USB device */
-		esc->sc_bus.bdev = device_add_child(self, "usbus", -1);
-		if (!esc->sc_bus.bdev) {
-			device_printf(self, "Could not add USB device\n");
-			err = bus_teardown_intr(self, esc->sc_irq_res,
-			    esc->sc_intr_hdl);
-			if (err)
-				device_printf(self, "Could not tear down irq,"
-				    " %d\n", err);
-			continue;
-		}
-		device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus);
-
-		esc->sc_id_vendor = 0x1234;
-		strlcpy(esc->sc_vendor, "Freescale", sizeof(esc->sc_vendor));
-
-		/* Set flags */
-		esc->sc_flags |= EHCI_SCFLG_DONTRESET | EHCI_SCFLG_NORESTERM;
-
-		err = ehci_init(esc);
-		if (!err) {
-			esc->sc_flags |= EHCI_SCFLG_DONEINIT;
-			err = device_probe_and_attach(esc->sc_bus.bdev);
-		} else {
-			device_printf(self, "USB init failed err=%d\n", err);
-
-			device_delete_child(self, esc->sc_bus.bdev);
-			esc->sc_bus.bdev = NULL;
-
-			err = bus_teardown_intr(self, esc->sc_irq_res,
-			    esc->sc_intr_hdl);
-			if (err)
-				device_printf(self, "Could not tear down irq,"
-				    " %d\n", err);
+	/*
+	 * Set handle to USB related registers subregion used by
+	 * generic EHCI driver.
+	 */
+	err = bus_space_subregion(esc->sc_io_tag, 
+	    rman_get_bushandle(sc->ehci_mem_res),
+	    IMX_EHCI_REG_OFF, IMX_EHCI_REG_SIZE, &esc->sc_io_hdl);
+	if (err != 0) {
+		device_printf(dev, "bus_space_subregion() failed\n");
+		err = ENXIO;
+		goto out;
+	}
 
-			continue;
-		}
+	/* Setup interrupt handler. */
+	err = bus_setup_intr(dev, sc->ehci_irq_res, INTR_TYPE_BIO, NULL, 
+	    (driver_intr_t *)ehci_interrupt, esc, &esc->sc_intr_hdl);
+	if (err != 0) {
+		device_printf(dev, "Could not setup IRQ\n");
+		goto out;
 	}
-	return (0);
-}
 
-static int
-fsl_ehci_detach(device_t self)
-{
-	struct imx_ehci_softc *sc;
-	ehci_softc_t *esc;
-	int err, i;
+	/* Turn on clocks. */
+	imx_ccm_usb_enable(dev);
+
+	/* Add USB bus device. */
+	esc->sc_bus.bdev = device_add_child(dev, "usbus", -1);
+	if (esc->sc_bus.bdev == NULL) {
+		device_printf(dev, "Could not add USB device\n");
+		goto out;
+	}
+	device_set_ivars(esc->sc_bus.bdev, &esc->sc_bus);
 
-	sc = device_get_softc(self);
+	esc->sc_id_vendor = USB_VENDOR_FREESCALE;
+	strlcpy(esc->sc_vendor, "Freescale", sizeof(esc->sc_vendor));
 
-	for (i = 0; i < FSL_EHCI_COUNT; i ++) {
-		esc = &sc->ehci[i];
-		if (esc->sc_flags & EHCI_SCFLG_DONEINIT)
-			continue;
-		/*
-		 * only call ehci_detach() after ehci_init()
-		 */
-		if (esc->sc_flags & EHCI_SCFLG_DONEINIT) {
-			ehci_detach(esc);
-			esc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
-		}
-
-		/*
-		 * Disable interrupts that might have been switched on in
-		 * ehci_init.
-		 */
-		if (esc->sc_io_tag && esc->sc_io_hdl)
-			bus_space_write_4(esc->sc_io_tag, esc->sc_io_hdl,
-			    EHCI_USBINTR, 0);
-
-		if (esc->sc_irq_res && esc->sc_intr_hdl) {
-			err = bus_teardown_intr(self, esc->sc_irq_res,
-			    esc->sc_intr_hdl);
-			if (err) {
-				device_printf(self, "Could not tear down irq,"
-				    " %d\n", err);
-				return (err);
-			}
-			esc->sc_intr_hdl = NULL;
-		}
-
-		if (esc->sc_bus.bdev) {
-			device_delete_child(self, esc->sc_bus.bdev);
-			esc->sc_bus.bdev = NULL;
-		}
+	/* Set flags that affect ehci_init() behavior. */
+	esc->sc_flags |= EHCI_SCFLG_DONTRESET | EHCI_SCFLG_NORESTERM;
+	err = ehci_init(esc);
+	if (err != 0) {
+		device_printf(dev, "USB init failed, usb_err_t=%d\n", 
+		    err);
+		goto out;
 	}
+	esc->sc_flags |= EHCI_SCFLG_DONEINIT;
 
-	/* During module unload there are lots of children leftover */
-	device_delete_children(self);
+	/* Probe the bus. */
+	err = device_probe_and_attach(esc->sc_bus.bdev);
+	if (err != 0) {
+		device_printf(dev,
+		    "device_probe_and_attach() failed\n");
+		goto out;
+	}
 
-	if (sc->sc_res[0])
-		bus_release_resources(self, imx_ehci_spec, sc->sc_res);
+	err = 0;
 
-	return (0);
+out:
+
+	if (err != 0)
+		imx_ehci_detach(dev);
+
+	return (err);
 }
+
+static device_method_t ehci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, imx_ehci_probe),
+	DEVMETHOD(device_attach, imx_ehci_attach),
+	DEVMETHOD(device_detach, imx_ehci_detach),
+	DEVMETHOD(device_suspend, bus_generic_suspend),
+	DEVMETHOD(device_resume, bus_generic_resume),
+	DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+	/* Bus interface */
+	DEVMETHOD(bus_print_child, bus_generic_print_child),
+
+	DEVMETHOD_END
+};
+
+static driver_t ehci_driver = {
+	"ehci",
+	ehci_methods,
+	sizeof(struct imx_ehci_softc)
+};
+
+static devclass_t ehci_devclass;
+
+DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0);
+MODULE_DEPEND(ehci, usb, 1, 1, 1);


More information about the svn-src-all mailing list