Subject: [PATCH 0/7] EHCI and OHCI for bcma and ssb

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:20 +0100

This patch series adds an EHCI and an OHCI driver for bcma and ssb 
based SoCs. These SoCs provide one device with two address spaces one 
for the OHCI and one for the EHCI part. The USB controllers are using 
the same interface as an PCI controller. This patch series is based on 
the mips Linux tree. It was in OpenWrt for some time and it was 
reviewed by George Kashperko recently.

Hauke Mehrtens (7):
  bcma: scan for extra address space
  bcma: add function to check every 10 us if a reg is set
  USB: OHCI: Add a generic platform device driver
  USB: EHCI: Add a generic platform device driver
  USB: Add driver for the bcma bus
  USB: Add driver for the ssb bus
  USB: OHCI: remove old SSB OHCI driver

 drivers/bcma/core.c              |   52 ++++---
 drivers/bcma/scan.c              |   18 ++-
 drivers/usb/host/Kconfig         |   57 ++++++--
 drivers/usb/host/Makefile        |    2 +
 drivers/usb/host/bcma-hcd.c      |  307 ++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ehci-hcd.c      |    5 +
 drivers/usb/host/ehci-platform.c |  211 ++++++++++++++++++++++++++
 drivers/usb/host/ohci-hcd.c      |   26 +---
 drivers/usb/host/ohci-platform.c |  193 ++++++++++++++++++++++++
 drivers/usb/host/ohci-ssb.c      |  260 --------------------------------
 drivers/usb/host/ssb-hcd.c       |  270 +++++++++++++++++++++++++++++++++
 include/linux/bcma/bcma.h        |    4 +
 12 files changed, 1089 insertions(+), 316 deletions(-)
 create mode 100644 drivers/usb/host/bcma-hcd.c
 create mode 100644 drivers/usb/host/ehci-platform.c
 create mode 100644 drivers/usb/host/ohci-platform.c
 delete mode 100644 drivers/usb/host/ohci-ssb.c
 create mode 100644 drivers/usb/host/ssb-hcd.c

-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:21 +0100

Some cores like the USB core have two address spaces. In the USB host
controller one address space is used for the OHCI and the other for the
EHCI controller interface. The USB controller is the only core I found
with two address spaces. This code is based on the AI scan function
ai_scan() in shared/aiutils.c i the Broadcom SDK.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/scan.c       |   18 +++++++++++++++++-
 include/linux/bcma/bcma.h |    1 +
 2 files changed, 18 insertions(+), 1 deletions(-)

diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c
index cad9948..c12a7fe 100644
--- a/drivers/bcma/scan.c
+++ b/drivers/bcma/scan.c
@@ -286,6 +286,22 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
 			return -EILSEQ;
 	}
 
+
+	/* First Slave Address Descriptor should be port 0:
+	 * the main register space for the core
+	 */
+	tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0);
+	if (tmp <= 0) {
+		/* Try again to see if it is a bridge */
+		tmp = bcma_erom_get_addr_desc(bus, eromptr,
+					      SCAN_ADDR_TYPE_BRIDGE, 0);
+		if (tmp > 0) {
+			pr_info("found bridge\n");
+			return -ENXIO;
+		}
+	}
+	core->addr = tmp;
+
 	/* get & parse slave ports */
 	for (i = 0; i < ports[1]; i++) {
 		for (j = 0; ; j++) {
@@ -298,7 +314,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
 				break;
 			} else {
 				if (i == 0 && j == 0)
-					core->addr = tmp;
+					core->addr1 = tmp;
 			}
 		}
 	}
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 83c209f..7fe41e1 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -138,6 +138,7 @@ struct bcma_device {
 	u8 core_index;
 
 	u32 addr;
+	u32 addr1;
 	u32 wrap;
 
 	void __iomem *io_addr;
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:22 +0100

This function checks if a reg get set or cleared every 10 microseconds.
It is used in bcma_core_set_clockmode() and bcma_core_pll_ctl() to
reduce code duplication. In addition it is needed in the USB host
driver.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/bcma/core.c       |   52 ++++++++++++++++++++++++++-------------------
 include/linux/bcma/bcma.h |    3 ++
 2 files changed, 33 insertions(+), 22 deletions(-)

diff --git a/drivers/bcma/core.c b/drivers/bcma/core.c
index 893f6e0..f7b7358 100644
--- a/drivers/bcma/core.c
+++ b/drivers/bcma/core.c
@@ -52,11 +52,36 @@ int bcma_core_enable(struct bcma_device *core, u32 flags)
 }
 EXPORT_SYMBOL_GPL(bcma_core_enable);
 
+/* Wait for bitmask in a register to get set or cleared.
+ * timeout is in units of ten-microseconds.
+ */
+int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask, int timeout,
+		   int set)
+{
+	int i;
+	u32 val;
+
+	for (i = 0; i < timeout; i++) {
+		val = bcma_read32(dev, reg);
+		if (set) {
+			if ((val & bitmask) == bitmask)
+				return 0;
+		} else {
+			if (!(val & bitmask))
+				return 0;
+		}
+		udelay(10);
+	}
+	pr_err("Timeout waiting for bitmask %08X on register %04X to %s.\n",
+	       bitmask, reg, (set ? "set" : "clear"));
+
+	return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(bcma_wait_bits);
+
 void bcma_core_set_clockmode(struct bcma_device *core,
 			     enum bcma_clkmode clkmode)
 {
-	u16 i;
-
 	WARN_ON(core->id.id != BCMA_CORE_CHIPCOMMON &&
 		core->id.id != BCMA_CORE_PCIE &&
 		core->id.id != BCMA_CORE_80211);
@@ -65,15 +90,8 @@ void bcma_core_set_clockmode(struct bcma_device *core,
 	case BCMA_CLKMODE_FAST:
 		bcma_set32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT);
 		udelay(64);
-		for (i = 0; i < 1500; i++) {
-			if (bcma_read32(core, BCMA_CLKCTLST) &
-			    BCMA_CLKCTLST_HAVEHT) {
-				i = 0;
-				break;
-			}
-			udelay(10);
-		}
-		if (i)
+		if (bcma_wait_bits(core, BCMA_CLKCTLST, BCMA_CLKCTLST_HAVEHT,
+				   1500, 1))
 			pr_err("HT force timeout\n");
 		break;
 	case BCMA_CLKMODE_DYNAMIC:
@@ -85,22 +103,12 @@ EXPORT_SYMBOL_GPL(bcma_core_set_clockmode);
 
 void bcma_core_pll_ctl(struct bcma_device *core, u32 req, u32 status, bool on)
 {
-	u16 i;
-
 	WARN_ON(req & ~BCMA_CLKCTLST_EXTRESREQ);
 	WARN_ON(status & ~BCMA_CLKCTLST_EXTRESST);
 
 	if (on) {
 		bcma_set32(core, BCMA_CLKCTLST, req);
-		for (i = 0; i < 10000; i++) {
-			if ((bcma_read32(core, BCMA_CLKCTLST) & status) ==
-			    status) {
-				i = 0;
-				break;
-			}
-			udelay(10);
-		}
-		if (i)
+		if (bcma_wait_bits(core, BCMA_CLKCTLST, status, 10000, 1))
 			pr_err("PLL enable timeout\n");
 	} else {
 		pr_warn("Disabling PLL not supported yet!\n");
diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h
index 7fe41e1..ebff87c 100644
--- a/include/linux/bcma/bcma.h
+++ b/include/linux/bcma/bcma.h
@@ -283,6 +283,9 @@ static inline void bcma_maskset16(struct bcma_device *cc,
 	bcma_write16(cc, offset, (bcma_read16(cc, offset) & mask) | set);
 }
 
+extern int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask,
+			  int timeout, int set);
+
 extern bool bcma_core_is_enabled(struct bcma_device *core);
 extern void bcma_core_disable(struct bcma_device *core, u32 flags);
 extern int bcma_core_enable(struct bcma_device *core, u32 flags);
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:23 +0100

This adds a generic driver for platform devices. It works like the PCI
driver and is based on it. This is for devices which do not have an own
bus but their OHCI controller works like a PCI controller. It will be
used for the Broadcom bcma and ssb USB OHCI controller.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/usb/host/Kconfig         |   10 ++
 drivers/usb/host/ohci-hcd.c      |    5 +
 drivers/usb/host/ohci-platform.c |  193 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 208 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ohci-platform.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 91413ca..6651ed6 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -393,6 +393,16 @@ config USB_CNS3XXX_OHCI
 	  Enable support for the CNS3XXX SOC's on-chip OHCI controller.
 	  It is needed for low-speed USB 1.0 device support.
 
+config USB_OHCI_HCD_PLATFORM
+	bool "OHCI driver for a platform device"
+	depends on USB_OHCI_HCD && EXPERIMENTAL
+	default n
+	---help---
+	  Adds an OHCI host driver for a generic platform device, which
+	  provieds a memory space and an irq.
+
+	  If unsure, say N.
+
 config USB_OHCI_BIG_ENDIAN_DESC
 	bool
 	depends on USB_OHCI_HCD
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 34b9edd..50fbbf9 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1121,6 +1121,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		ohci_xls_driver
 #endif
 
+#ifdef CONFIG_USB_OHCI_HCD_PLATFORM
+#include "ohci-platform.c"
+#define PLATFORM_DRIVER		ohci_platform_driver
+#endif
+
 #if	!defined(PCI_DRIVER) &&		\
 	!defined(PLATFORM_DRIVER) &&	\
 	!defined(OMAP1_PLATFORM_DRIVER) &&	\
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
new file mode 100644
index 0000000..1b5fc73
--- /dev/null
+++ b/drivers/usb/host/ohci-platform.c
@@ -0,0 +1,193 @@
+/*
+ * Generic platform ohci driver
+ *
+ * Copyright 2007 Michael Buesch <[email-masked]>
+ * Copyright 2011 Hauke Mehrtens <[email-masked]>
+ *
+ * Derived from the OCHI-SSB driver
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/platform_device.h>
+
+static int ohci_platform_reset(struct usb_hcd *hcd)
+{
+	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+	int err;
+
+	ohci_hcd_init(ohci);
+	err = ohci_init(ohci);
+
+	return err;
+}
+
+static int ohci_platform_start(struct usb_hcd *hcd)
+{
+	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+	int err;
+
+	err = ohci_run(ohci);
+	if (err < 0) {
+		ohci_err(ohci, "can't start\n");
+		ohci_stop(hcd);
+	}
+
+	return err;
+}
+
+static const struct hc_driver ohci_platform_hc_driver = {
+	.description		= "platform-usb-ohci",
+	.product_desc		= "Generic Platform OHCI Controller",
+	.hcd_priv_size		= sizeof(struct ohci_hcd),
+
+	.irq			= ohci_irq,
+	.flags			= HCD_MEMORY | HCD_USB11,
+
+	.reset			= ohci_platform_reset,
+	.start			= ohci_platform_start,
+	.stop			= ohci_stop,
+	.shutdown		= ohci_shutdown,
+
+	.urb_enqueue		= ohci_urb_enqueue,
+	.urb_dequeue		= ohci_urb_dequeue,
+	.endpoint_disable	= ohci_endpoint_disable,
+
+	.get_frame_number	= ohci_get_frame,
+
+	.hub_status_data	= ohci_hub_status_data,
+	.hub_control		= ohci_hub_control,
+#ifdef	CONFIG_PM
+	.bus_suspend		= ohci_bus_suspend,
+	.bus_resume		= ohci_bus_resume,
+#endif
+
+	.start_port_reset	= ohci_start_port_reset,
+};
+
+static int ohci_platform_attach(struct platform_device *dev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res_irq, *res_mem;
+	int err = -ENOMEM;
+
+	hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
+			dev_name(&dev->dev));
+	if (!hcd)
+		goto err_return;
+
+	res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+	if (!res_irq) {
+		err = -ENXIO;
+		goto err_return;
+	}
+	res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!res_mem) {
+		err = -ENXIO;
+		goto err_return;
+	}
+	hcd->rsrc_start = res_mem->start;
+	hcd->rsrc_len = res_mem->end - res_mem->start + 1;
+
+	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs)
+		goto err_put_hcd;
+	err = usb_add_hcd(hcd, res_irq->start, IRQF_SHARED);
+	if (err)
+		goto err_iounmap;
+
+	platform_set_drvdata(dev, hcd);
+
+	return err;
+
+err_iounmap:
+	iounmap(hcd->regs);
+err_put_hcd:
+	usb_put_hcd(hcd);
+err_return:
+	return err;
+}
+
+static int ohci_platform_probe(struct platform_device *dev)
+{
+	int err;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	err = ohci_platform_attach(dev);
+
+	return err;
+}
+
+static int ohci_platform_remove(struct platform_device *dev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = platform_get_drvdata(dev);
+	if (!hcd)
+		return -ENODEV;
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static void ohci_platform_shutdown(struct platform_device *dev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = platform_get_drvdata(dev);
+	if (!hcd)
+		return;
+
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+
+#ifdef CONFIG_PM
+
+static int ohci_platform_suspend(struct platform_device *dev,
+				 pm_message_t state)
+{
+
+	return 0;
+}
+
+static int ohci_platform_resume(struct platform_device *dev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+	ohci_finish_controller_resume(hcd);
+	return 0;
+}
+
+#else /* !CONFIG_PM */
+#define ohci_platform_suspend	NULL
+#define ohci_platform_resume	NULL
+#endif /* CONFIG_PM */
+
+static const struct platform_device_id ohci_platform_table[] = {
+	{ "ohci-platform", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, ohci_platform_table);
+
+static struct platform_driver ohci_platform_driver = {
+	.id_table	= ohci_platform_table,
+	.probe		= ohci_platform_probe,
+	.remove		= ohci_platform_remove,
+	.shutdown	= ohci_platform_shutdown,
+	.suspend	= ohci_platform_suspend,
+	.resume		= ohci_platform_resume,
+	.driver		= {
+		.name	= "ohci-platform",
+	}
+};
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:24 +0100

This adds a generic driver for platform devices. It works like the PCI
driver and is based on it. This is for devices which do not have an own
bus but their EHCI controller works like a PCI controller. It will be
used for the Broadcom bcma and ssb USB EHCI controller.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/usb/host/Kconfig         |   10 ++
 drivers/usb/host/ehci-hcd.c      |    5 +
 drivers/usb/host/ehci-platform.c |  211 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 226 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ehci-platform.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 6651ed6..e7b5efe 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -403,6 +403,16 @@ config USB_OHCI_HCD_PLATFORM
 
 	  If unsure, say N.
 
+config USB_EHCI_HCD_PLATFORM
+	bool "Generic EHCI driver for a platform device"
+	depends on USB_EHCI_HCD && EXPERIMENTAL
+	default n
+	---help---
+	  Adds an EHCI host driver for a generic platform device, which
+	  provieds a memory space and an irq.
+
+	  If unsure, say N.
+
 config USB_OHCI_BIG_ENDIAN_DESC
 	bool
 	depends on USB_OHCI_HCD
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index a007a9f..afe0984 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1376,6 +1376,11 @@ MODULE_LICENSE ("GPL");
 #define        PLATFORM_DRIVER         ehci_mv_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_HCD_PLATFORM
+#include "ehci-platform.c"
+#define PLATFORM_DRIVER		ehci_platform_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
new file mode 100644
index 0000000..1c20c02
--- /dev/null
+++ b/drivers/usb/host/ehci-platform.c
@@ -0,0 +1,211 @@
+/*
+ * Generic platform ehci driver
+ *
+ * Copyright 2007 Steven Brown <[email-masked]>
+ * Copyright 2010-2011 Hauke Mehrtens <[email-masked]>
+ *
+ * Derived from the ohci-ssb driver
+ * Copyright 2007 Michael Buesch <[email-masked]>
+ *
+ * Derived from the EHCI-PCI driver
+ * Copyright (c) 2000-2004 by David Brownell
+ *
+ * Derived from the ohci-pci driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/platform_device.h>
+
+static int ehci_platform_reset(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int retval;
+
+	ehci->caps = hcd->regs;
+	ehci->regs = hcd->regs +
+		HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache this readonly data; minimize chip reads */
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+	retval = ehci_halt(ehci);
+	if (retval)
+		return retval;
+
+	/* data structure init */
+	retval = ehci_init(hcd);
+	if (retval)
+		return retval;
+
+	ehci_reset(ehci);
+
+	ehci_port_power(ehci, 1);
+
+	return retval;
+}
+
+static const struct hc_driver ehci_platform_hc_driver = {
+	.description		= "platform-usb-ehci",
+	.product_desc		= "Generic Platform EHCI Controller",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+
+	.irq			= ehci_irq,
+	.flags			= HCD_MEMORY | HCD_USB2,
+
+	.reset			= ehci_platform_reset,
+	.start			= ehci_run,
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+
+	.get_frame_number	= ehci_get_frame,
+
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+#if defined(CONFIG_PM)
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+#endif
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	.update_device		= ehci_update_device,
+
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_platform_attach(struct platform_device *dev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res_irq, *res_mem;
+	int err = -ENOMEM;
+
+	hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
+			     dev_name(&dev->dev));
+	if (!hcd)
+		goto err_return;
+
+	res_irq = platform_get_resource(dev, IORESOURCE_IRQ, 0);
+	if (!res_irq) {
+		err = -ENXIO;
+		goto err_return;
+	}
+	res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (!res_mem) {
+		err = -ENXIO;
+		goto err_return;
+	}
+	hcd->rsrc_start = res_mem->start;
+	hcd->rsrc_len = res_mem->end - res_mem->start + 1;
+
+	/*
+	 * start & size modified per sbutils.c
+	 */
+	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs)
+		goto err_put_hcd;
+	err = usb_add_hcd(hcd, res_irq->start, IRQF_SHARED);
+	if (err)
+		goto err_iounmap;
+
+	platform_set_drvdata(dev, hcd);
+
+	return err;
+
+err_iounmap:
+	iounmap(hcd->regs);
+err_put_hcd:
+	usb_put_hcd(hcd);
+err_return:
+	return err;
+}
+
+static int ehci_platform_probe(struct platform_device *dev)
+{
+	int err;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	err = ehci_platform_attach(dev);
+
+	return err;
+}
+
+static int ehci_platform_remove(struct platform_device *dev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = platform_get_drvdata(dev);
+	if (!hcd)
+		return -ENODEV;
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static void ehci_platform_shutdown(struct platform_device *dev)
+{
+	struct usb_hcd *hcd;
+
+	hcd = platform_get_drvdata(dev);
+	if (!hcd)
+		return;
+
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+
+#ifdef CONFIG_PM
+
+static int ehci_platform_suspend(struct platform_device *dev,
+				 pm_message_t state)
+{
+	return 0;
+}
+
+static int ehci_platform_resume(struct platform_device *dev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+	ehci_finish_controller_resume(hcd);
+	return 0;
+}
+
+#else /* !CONFIG_PM */
+#define ehci_platform_suspend	NULL
+#define ehci_platform_resume	NULL
+#endif /* CONFIG_PM */
+
+static const struct platform_device_id ehci_platform_table[] = {
+	{ "ehci-platform", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, ehci_platform_table);
+
+static struct platform_driver ehci_platform_driver = {
+	.id_table	= ehci_platform_table,
+	.probe		= ehci_platform_probe,
+	.remove		= ehci_platform_remove,
+	.shutdown	= ehci_platform_shutdown,
+	.suspend	= ehci_platform_suspend,
+	.resume		= ehci_platform_resume,
+	.driver		= {
+		.name	= "ehci-platform",
+	}
+};
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:25 +0100

This adds a USB driver using the generic platform device driver for the
USB controller found on the Broadcom bcma bus. The bcma bus just
exposes one device which serves the OHCI and the EHCI controller at the
same time. This driver probes for this USB controller and creates and
registers two new platform devices which will be probed by the new
generic platform device driver. This makes it possible to use the EHCI
and the OCHI controller on the bcma bus at the same time.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/usb/host/Kconfig    |   12 ++
 drivers/usb/host/Makefile   |    1 +
 drivers/usb/host/bcma-hcd.c |  307 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 320 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/bcma-hcd.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index e7b5efe..b15ce91 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -633,3 +633,15 @@ config USB_PXA168_EHCI
 	help
 	  Enable support for Marvell PXA168 SoC's on-chip EHCI
 	  host controller
+
+config USB_HCD_BCMA
+	tristate "BCMA usb host driver"
+	depends on BCMA && EXPERIMENTAL
+	select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+	select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
+	help
+	  Enbale support for the EHCI and OCHI host controller on an bcma bus.
+	  It converts the bcma driver into two platform device drivers
+	  for ehci and ohci.
+
+	  If unsure, say N.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 7ca290f..f2c2846 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -37,3 +37,4 @@ obj-$(CONFIG_USB_IMX21_HCD)	+= imx21-hcd.o
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= fsl-mph-dr-of.o
 obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o
 obj-$(CONFIG_MIPS_ALCHEMY)	+= alchemy-common.o
+obj-$(CONFIG_USB_HCD_BCMA)	+= bcma-hcd.o
diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c
new file mode 100644
index 0000000..ef46364
--- /dev/null
+++ b/drivers/usb/host/bcma-hcd.c
@@ -0,0 +1,307 @@
+/*
+ * Broadcom specific Advanced Microcontroller Bus
+ * Broadcom USB-core driver (BCMA bus glue)
+ *
+ * Copyright 2011 Hauke Mehrtens <[email-masked]>
+ *
+ * Based on ssb-ohci driver
+ * Copyright 2007 Michael Buesch <[email-masked]>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005-2011 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Common USB driver for BCMA Bus");
+MODULE_LICENSE("GPL");
+
+struct bcma_hcd_device {
+	struct platform_device *ehci_dev;
+	struct platform_device *ohci_dev;
+};
+
+static void __devinit bcma_hcd_4716wa(struct bcma_device *dev)
+{
+#ifdef CONFIG_BCMA_DRIVER_MIPS
+	/* Work around for 4716 failures. */
+	if (dev->bus->chipinfo.id == 0x4716) {
+		u32 tmp;
+
+		tmp = bcma_cpu_clock(&dev->bus->drv_mips);
+		if (tmp >= 480000000)
+			tmp = 0x1846b; /* set CDR to 0x11(fast) */
+		else if (tmp == 453000000)
+			tmp = 0x1046b; /* set CDR to 0x10(slow) */
+		else
+			tmp = 0;
+
+		/* Change Shim mdio control reg to fix host not acking at
+		 * high frequencies
+		 */
+		if (tmp) {
+			bcma_write32(dev, 0x524, 0x1); /* write sel to enable */
+			udelay(500);
+
+			bcma_write32(dev, 0x524, tmp);
+			udelay(500);
+			bcma_write32(dev, 0x524, 0x4ab);
+			udelay(500);
+			bcma_read32(dev, 0x528);
+			bcma_write32(dev, 0x528, 0x80000000);
+		}
+	}
+#endif /* CONFIG_BCMA_DRIVER_MIPS */
+}
+
+/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
+static void __devinit bcma_hcd_init_chip(struct bcma_device *dev)
+{
+	u32 tmp;
+
+	/*
+	 * USB 2.0 special considerations:
+	 *
+	 * 1. Since the core supports both OHCI and EHCI functions, it must
+	 *    only be reset once.
+	 *
+	 * 2. In addition to the standard SI reset sequence, the Host Control
+	 *    Register must be programmed to bring the USB core and various
+	 *    phy components out of reset.
+	 */
+	if (!bcma_core_is_enabled(dev)) {
+		bcma_core_enable(dev, 0);
+		mdelay(10);
+		if (dev->id.rev >= 5) {
+			/* Enable Misc PLL */
+			tmp = bcma_read32(dev, 0x1e0);
+			tmp |= 0x100;
+			bcma_write32(dev, 0x1e0, tmp);
+			if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100, 1))
+				printk(KERN_EMERG "Failed to enable misc PPL!\n");
+
+			/* Take out of resets */
+			bcma_write32(dev, 0x200, 0x4ff);
+			udelay(25);
+			bcma_write32(dev, 0x200, 0x6ff);
+			udelay(25);
+
+			/* Make sure digital and AFE are locked in USB PHY */
+			bcma_write32(dev, 0x524, 0x6b);
+			udelay(50);
+			tmp = bcma_read32(dev, 0x524);
+			udelay(50);
+			bcma_write32(dev, 0x524, 0xab);
+			udelay(50);
+			tmp = bcma_read32(dev, 0x524);
+			udelay(50);
+			bcma_write32(dev, 0x524, 0x2b);
+			udelay(50);
+			tmp = bcma_read32(dev, 0x524);
+			udelay(50);
+			bcma_write32(dev, 0x524, 0x10ab);
+			udelay(50);
+			tmp = bcma_read32(dev, 0x524);
+
+			if (bcma_wait_bits(dev, 0x528, 0xc000, 10000, 1)) {
+				tmp = bcma_read32(dev, 0x528);
+				printk(KERN_EMERG
+				       "USB20H mdio_rddata 0x%08x\n", tmp);
+			}
+			bcma_write32(dev, 0x528, 0x80000000);
+			tmp = bcma_read32(dev, 0x314);
+			udelay(265);
+			bcma_write32(dev, 0x200, 0x7ff);
+			udelay(10);
+
+			/* Take USB and HSIC out of non-driving modes */
+			bcma_write32(dev, 0x510, 0);
+		} else {
+			bcma_write32(dev, 0x200, 0x7ff);
+
+			udelay(1);
+		}
+
+		bcma_hcd_4716wa(dev);
+	}
+}
+
+static struct platform_device * __devinit
+bcma_hcd_create_pdev(struct bcma_device *dev, char *name, u32 addr)
+{
+	struct platform_device *hci_dev;
+	struct resource hci_res[2];
+	int ret = -ENOMEM;
+
+	memset(hci_res, 0, sizeof(hci_res));
+
+	hci_res[0].start = addr;
+	hci_res[0].end = hci_res[0].start + 0x1000 - 1;
+	hci_res[0].flags = IORESOURCE_MEM;
+
+	hci_res[1].start = dev->irq;
+	hci_res[1].flags = IORESOURCE_IRQ;
+
+	hci_dev = platform_device_alloc(name, 0);
+	if (!hci_dev)
+		goto err_alloc;
+
+	hci_dev->dev.parent = &dev->dev;
+	hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
+
+	ret = platform_device_add_resources(hci_dev, hci_res,
+					    ARRAY_SIZE(hci_res));
+	if (ret)
+		goto err_alloc;
+
+	ret = platform_device_add(hci_dev);
+	if (ret) {
+err_alloc:
+		platform_device_put(hci_dev);
+		return ERR_PTR(ret);
+	}
+
+	return hci_dev;
+}
+
+static int __devinit bcma_hcd_probe(struct bcma_device *dev)
+{
+	int err;
+	u16 chipid_top;
+	u32 ohci_addr;
+	struct bcma_hcd_device *usb_dev;
+	struct bcma_chipinfo *chipinfo;
+
+	chipinfo = &dev->bus->chipinfo;
+	/* USBcores are only connected on embedded devices. */
+	chipid_top = (chipinfo->id & 0xFF00);
+	if (chipid_top != 0x4700 && chipid_top != 0x5300)
+		return -ENODEV;
+
+	/* TODO: Probably need checks here; is the core connected? */
+
+	if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
+	    dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
+		return -EOPNOTSUPP;
+
+	usb_dev = kzalloc(sizeof(struct bcma_hcd_device), GFP_KERNEL);
+	if (!usb_dev)
+		return -ENOMEM;
+
+	bcma_hcd_init_chip(dev);
+
+	/* In AI chips EHCI is addrspace 0, OHCI is 1 */
+	ohci_addr = dev->addr1;
+	if ((chipinfo->id == 0x5357 || chipinfo->id == 0x4749)
+	    && chipinfo->rev == 0)
+		ohci_addr = 0x18009000;
+
+	usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, "ohci-platform",
+						 ohci_addr);
+	if (IS_ERR(usb_dev->ohci_dev)) {
+		err = PTR_ERR(usb_dev->ohci_dev);
+		goto err_free_usb_dev;
+	}
+
+	usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, "ehci-platform",
+						 dev->addr);
+	if (IS_ERR(usb_dev->ehci_dev)) {
+		err = PTR_ERR(usb_dev->ehci_dev);
+		goto err_unregister_ohci_dev;
+	}
+
+	bcma_set_drvdata(dev, usb_dev);
+	return 0;
+
+err_unregister_ohci_dev:
+	platform_device_unregister(usb_dev->ohci_dev);
+err_free_usb_dev:
+	kfree(usb_dev);
+	return err;
+}
+
+static void __devexit bcma_hcd_remove(struct bcma_device *dev)
+{
+	struct bcma_hcd_device *usb_dev;
+	struct platform_device *ohci_dev;
+	struct platform_device *ehci_dev;
+
+	usb_dev = bcma_get_drvdata(dev);
+	if (!usb_dev)
+		return;
+
+	ohci_dev = usb_dev->ohci_dev;
+	ehci_dev = usb_dev->ehci_dev;
+
+	if (ohci_dev)
+		platform_device_unregister(ohci_dev);
+	if (ehci_dev)
+		platform_device_unregister(ehci_dev);
+
+	bcma_core_disable(dev, 0);
+}
+
+static void bcma_hcd_shutdown(struct bcma_device *dev)
+{
+	bcma_core_disable(dev, 0);
+}
+
+#ifdef CONFIG_PM
+
+static int bcma_hcd_suspend(struct bcma_device *dev, pm_message_t state)
+{
+	bcma_core_disable(dev, 0);
+
+	return 0;
+}
+
+static int bcma_hcd_resume(struct bcma_device *dev)
+{
+	bcma_core_enable(dev, 0);
+
+	return 0;
+}
+
+#else /* !CONFIG_PM */
+#define bcma_hcd_suspend	NULL
+#define bcma_hcd_resume	NULL
+#endif /* CONFIG_PM */
+
+static const struct bcma_device_id bcma_hcd_table[] __devinitconst = {
+	BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS),
+	BCMA_CORETABLE_END
+};
+MODULE_DEVICE_TABLE(bcma, bcma_hcd_table);
+
+static struct bcma_driver bcma_hcd_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= bcma_hcd_table,
+	.probe		= bcma_hcd_probe,
+	.remove		= __devexit_p(bcma_hcd_remove),
+	.shutdown	= bcma_hcd_shutdown,
+	.suspend	= bcma_hcd_suspend,
+	.resume		= bcma_hcd_resume,
+};
+
+static int __init bcma_hcd_init(void)
+{
+	return bcma_driver_register(&bcma_hcd_driver);
+}
+module_init(bcma_hcd_init);
+
+static void __exit bcma_hcd_exit(void)
+{
+	bcma_driver_unregister(&bcma_hcd_driver);
+}
+module_exit(bcma_hcd_exit);
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:26 +0100

This adds a USB driver using the generic platform device driver for the
USB controller found on the Broadcom ssb bus. The ssb bus just
exposes one device which serves the OHCI and the EHCI controller at the
same time. This driver probes for this USB controller and creates and
registers two new platform devices which will be probed by the new
generic platform device driver. This makes it possible to use the EHCI
and the OCHI controller on the ssb bus at the same time.

The old ssb OHCI USB driver will be removed in the next step as this
driver also provide an OHCI driver and an EHCI for the cores supporting
it.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/usb/host/Kconfig   |   12 ++
 drivers/usb/host/Makefile  |    1 +
 drivers/usb/host/ssb-hcd.c |  270 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 283 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/host/ssb-hcd.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index b15ce91..2825dc4 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -645,3 +645,15 @@ config USB_HCD_BCMA
 	  for ehci and ohci.
 
 	  If unsure, say N.
+
+config USB_HCD_SSB
+	tristate "SSB usb host driver"
+	depends on SSB && EXPERIMENTAL
+	select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+	select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
+	help
+	  Enbale support for the EHCI and OCHI host controller on an bcma bus.
+	  It converts the bcma driver into two platform device drivers
+	  for ehci and ohci.
+
+	  If unsure, say N.
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index f2c2846..9c52966 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= fsl-mph-dr-of.o
 obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o
 obj-$(CONFIG_MIPS_ALCHEMY)	+= alchemy-common.o
 obj-$(CONFIG_USB_HCD_BCMA)	+= bcma-hcd.o
+obj-$(CONFIG_USB_HCD_SSB)	+= ssb-hcd.o
diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c
new file mode 100644
index 0000000..889b425
--- /dev/null
+++ b/drivers/usb/host/ssb-hcd.c
@@ -0,0 +1,270 @@
+/*
+ * Sonics Silicon Backplane
+ * Broadcom USB-core driver  (SSB bus glue)
+ *
+ * Copyright 2011 Hauke Mehrtens <[email-masked]>
+ *
+ * Based on ssb-ohci driver
+ * Copyright 2007 Michael Buesch <[email-masked]>
+ *
+ * Derived from the OHCI-PCI driver
+ * Copyright 1999 Roman Weissgaerber
+ * Copyright 2000-2002 David Brownell
+ * Copyright 1999 Linus Torvalds
+ * Copyright 1999 Gregory P. Smith
+ *
+ * Derived from the USBcore related parts of Broadcom-SB
+ * Copyright 2005-2011 Broadcom Corporation
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+#include <linux/ssb/ssb.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+
+MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Common USB driver for SSB Bus");
+MODULE_LICENSE("GPL");
+
+#define SSB_HCD_TMSLOW_HOSTMODE	(1 << 29)
+
+struct ssb_hcd_device {
+	struct platform_device *ehci_dev;
+	struct platform_device *ohci_dev;
+
+	u32 enable_flags;
+};
+
+static void __devinit ssb_hcd_5354wa(struct ssb_device *dev)
+{
+#ifdef CONFIG_SSB_DRIVER_MIPS
+	/* Work around for 5354 failures */
+	if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
+		/* Change syn01 reg */
+		ssb_write32(dev, 0x894, 0x00fe00fe);
+
+		/* Change syn03 reg */
+		ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1);
+	}
+#endif
+}
+
+static void __devinit ssb_hcd_usb20wa(struct ssb_device *dev)
+{
+	if (dev->id.coreid == SSB_DEV_USB20_HOST) {
+		/*
+		 * USB 2.0 special considerations:
+		 *
+		 * In addition to the standard SSB reset sequence, the Host
+		 * Control Register must be programmed to bring the USB core
+		 * and various phy components out of reset.
+		 */
+		ssb_write32(dev, 0x200, 0x7ff);
+
+		/* Change Flush control reg */
+		ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8);
+		ssb_read32(dev, 0x400);
+
+		/* Change Shim control reg */
+		ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100);
+		ssb_read32(dev, 0x304);
+
+		udelay(1);
+
+		ssb_hcd_5354wa(dev);
+	}
+}
+
+/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
+static u32 __devinit ssb_hcd_init_chip(struct ssb_device *dev)
+{
+	u32 flags = 0;
+
+	if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
+		/* Put the device into host-mode. */
+		flags |= SSB_HCD_TMSLOW_HOSTMODE;
+
+	ssb_device_enable(dev, flags);
+
+	ssb_hcd_usb20wa(dev);
+
+	return flags;
+}
+
+static struct platform_device * __devinit
+ssb_hcd_create_pdev(struct ssb_device *dev, char *name, u32 addr, u32 len)
+{
+	struct platform_device *hci_dev;
+	struct resource hci_res[2];
+	int ret = -ENOMEM;
+
+	memset(hci_res, 0, sizeof(hci_res));
+
+	hci_res[0].start = addr;
+	hci_res[0].end = hci_res[0].start + len - 1;
+	hci_res[0].flags = IORESOURCE_MEM;
+
+	hci_res[1].start = dev->irq;
+	hci_res[1].flags = IORESOURCE_IRQ;
+
+	hci_dev = platform_device_alloc(name, 0);
+	if (!hci_dev)
+		goto err_alloc;
+
+	hci_dev->dev.parent = dev->dev;
+	hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
+
+	ret = platform_device_add_resources(hci_dev, hci_res, 2);
+	if (ret)
+		goto err_alloc;
+
+	ret = platform_device_add(hci_dev);
+	if (ret) {
+err_alloc:
+		platform_device_put(hci_dev);
+		return ERR_PTR(ret);
+	}
+
+	return hci_dev;
+}
+
+static int __devinit ssb_hcd_probe(struct ssb_device *dev,
+				   const struct ssb_device_id *id)
+{
+	int err, tmp;
+	int start, len;
+	u16 chipid_top;
+	struct ssb_hcd_device *usb_dev;
+
+	/* USBcores are only connected on embedded devices. */
+	chipid_top = (dev->bus->chip_id & 0xFF00);
+	if (chipid_top != 0x4700 && chipid_top != 0x5300)
+		return -ENODEV;
+
+	/* TODO: Probably need checks here; is the core connected? */
+
+	if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
+	    dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
+		return -EOPNOTSUPP;
+
+	usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL);
+	if (!usb_dev)
+		return -ENOMEM;
+
+	/* We currently always attach SSB_DEV_USB11_HOSTDEV
+	 * as HOST OHCI. If we want to attach it as Client device,
+	 * we must branch here and call into the (yet to
+	 * be written) Client mode driver. Same for remove(). */
+	usb_dev->enable_flags = ssb_hcd_init_chip(dev);
+
+	tmp = ssb_read32(dev, SSB_ADMATCH0);
+
+	start = ssb_admatch_base(tmp);
+	len = ssb_admatch_size(tmp);
+	usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, "ohci-platform", start,
+						len);
+	if (IS_ERR(usb_dev->ohci_dev)) {
+		err = PTR_ERR(usb_dev->ohci_dev);
+		goto err_free_usb_dev;
+	}
+
+	if (dev->id.coreid == SSB_DEV_USB20_HOST) {
+		start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
+		len = 0x100; /* ehci reg block size */
+		usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, "ehci-platform",
+							start, len);
+		if (IS_ERR(usb_dev->ehci_dev)) {
+			err = PTR_ERR(usb_dev->ehci_dev);
+			goto err_unregister_ohci_dev;
+		}
+	}
+
+	ssb_set_drvdata(dev, usb_dev);
+	return 0;
+
+err_unregister_ohci_dev:
+	platform_device_unregister(usb_dev->ohci_dev);
+err_free_usb_dev:
+	kfree(usb_dev);
+	return err;
+}
+
+static void __devexit ssb_hcd_remove(struct ssb_device *dev)
+{
+	struct ssb_hcd_device *usb_dev;
+	struct platform_device *ohci_dev;
+	struct platform_device *ehci_dev;
+
+	usb_dev = ssb_get_drvdata(dev);
+	if (!usb_dev)
+		return;
+
+	ohci_dev = usb_dev->ohci_dev;
+	ehci_dev = usb_dev->ehci_dev;
+
+	if (ohci_dev)
+		platform_device_unregister(ohci_dev);
+	if (ehci_dev)
+		platform_device_unregister(ehci_dev);
+
+	ssb_device_disable(dev, 0);
+}
+
+static void __devexit ssb_hcd_shutdown(struct ssb_device *dev)
+{
+	ssb_device_disable(dev, 0);
+}
+
+#ifdef CONFIG_PM
+
+static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state)
+{
+	ssb_device_disable(dev, 0);
+
+	return 0;
+}
+
+static int ssb_hcd_resume(struct ssb_device *dev)
+{
+	struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
+
+	ssb_device_enable(dev, usb_dev->enable_flags);
+
+	return 0;
+}
+
+#else /* !CONFIG_PM */
+#define ssb_hcd_suspend	NULL
+#define ssb_hcd_resume	NULL
+#endif /* CONFIG_PM */
+
+static const struct ssb_device_id ssb_hcd_table[] __devinitconst = {
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
+	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
+	SSB_DEVTABLE_END
+};
+MODULE_DEVICE_TABLE(ssb, ssb_hcd_table);
+
+static struct ssb_driver ssb_hcd_driver = {
+	.name		= KBUILD_MODNAME,
+	.id_table	= ssb_hcd_table,
+	.probe		= ssb_hcd_probe,
+	.remove		= __devexit_p(ssb_hcd_remove),
+	.shutdown	= ssb_hcd_shutdown,
+	.suspend	= ssb_hcd_suspend,
+	.resume		= ssb_hcd_resume,
+};
+
+static int __init ssb_hcd_init(void)
+{
+	return ssb_driver_register(&ssb_hcd_driver);
+}
+module_init(ssb_hcd_init);
+
+static void __exit ssb_hcd_exit(void)
+{
+	ssb_driver_unregister(&ssb_hcd_driver);
+}
+module_exit(ssb_hcd_exit);
-- 
1.7.5.4

---

From: Hauke Mehrtens <[email-masked]>
Date: Sat, 21 Jan 2012 23:19:27 +0100

This is now replaced by the new ssb USB driver, which also supports
devices with an EHCI controller.

Signed-off-by: Hauke Mehrtens <[email-masked]>
---
 drivers/usb/host/Kconfig    |   13 --
 drivers/usb/host/ohci-hcd.c |   21 +----
 drivers/usb/host/ohci-ssb.c |  260 -------------------------------------------
 3 files changed, 1 insertions(+), 293 deletions(-)
 delete mode 100644 drivers/usb/host/ohci-ssb.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 2825dc4..3649510 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -360,19 +360,6 @@ config USB_OHCI_HCD_PCI
 	  Enables support for PCI-bus plug-in USB controller cards.
 	  If unsure, say Y.
 
-config USB_OHCI_HCD_SSB
-	bool "OHCI support for Broadcom SSB OHCI core"
-	depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD) && EXPERIMENTAL
-	default n
-	---help---
-	  Support for the Sonics Silicon Backplane (SSB) attached
-	  Broadcom USB OHCI core.
-
-	  This device is present in some embedded devices with
-	  Broadcom based SSB bus.
-
-	  If unsure, say N.
-
 config USB_OHCI_SH
 	bool "OHCI support for SuperH USB controller"
 	depends on USB_OHCI_HCD && SUPERH
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 50fbbf9..a2f4d4e 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1081,11 +1081,6 @@ MODULE_LICENSE ("GPL");
 #define PS3_SYSTEM_BUS_DRIVER	ps3_ohci_driver
 #endif
 
-#ifdef CONFIG_USB_OHCI_HCD_SSB
-#include "ohci-ssb.c"
-#define SSB_OHCI_DRIVER		ssb_ohci_driver
-#endif
-
 #ifdef CONFIG_MFD_SM501
 #include "ohci-sm501.c"
 #define SM501_OHCI_DRIVER	ohci_hcd_sm501_driver
@@ -1134,8 +1129,7 @@ MODULE_LICENSE ("GPL");
 	!defined(SA1111_DRIVER) &&	\
 	!defined(PS3_SYSTEM_BUS_DRIVER) && \
 	!defined(SM501_OHCI_DRIVER) && \
-	!defined(TMIO_OHCI_DRIVER) && \
-	!defined(SSB_OHCI_DRIVER)
+	!defined(TMIO_OHCI_DRIVER)
 #error "missing bus glue for ohci-hcd"
 #endif
 
@@ -1201,12 +1195,6 @@ static int __init ohci_hcd_mod_init(void)
 		goto error_pci;
 #endif
 
-#ifdef SSB_OHCI_DRIVER
-	retval = ssb_driver_register(&SSB_OHCI_DRIVER);
-	if (retval)
-		goto error_ssb;
-#endif
-
 #ifdef SM501_OHCI_DRIVER
 	retval = platform_driver_register(&SM501_OHCI_DRIVER);
 	if (retval < 0)
@@ -1230,10 +1218,6 @@ static int __init ohci_hcd_mod_init(void)
 	platform_driver_unregister(&SM501_OHCI_DRIVER);
  error_sm501:
 #endif
-#ifdef SSB_OHCI_DRIVER
-	ssb_driver_unregister(&SSB_OHCI_DRIVER);
- error_ssb:
-#endif
 #ifdef PCI_DRIVER
 	pci_unregister_driver(&PCI_DRIVER);
  error_pci:
@@ -1281,9 +1265,6 @@ static void __exit ohci_hcd_mod_exit(void)
 #ifdef SM501_OHCI_DRIVER
 	platform_driver_unregister(&SM501_OHCI_DRIVER);
 #endif
-#ifdef SSB_OHCI_DRIVER
-	ssb_driver_unregister(&SSB_OHCI_DRIVER);
-#endif
 #ifdef PCI_DRIVER
 	pci_unregister_driver(&PCI_DRIVER);
 #endif
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c
deleted file mode 100644
index 5ba1859..0000000
--- a/drivers/usb/host/ohci-ssb.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Sonics Silicon Backplane
- * Broadcom USB-core OHCI driver
- *
- * Copyright 2007 Michael Buesch <[email-masked]>
- *
- * Derived from the OHCI-PCI driver
- * Copyright 1999 Roman Weissgaerber
- * Copyright 2000-2002 David Brownell
- * Copyright 1999 Linus Torvalds
- * Copyright 1999 Gregory P. Smith
- *
- * Derived from the USBcore related parts of Broadcom-SB
- * Copyright 2005 Broadcom Corporation
- *
- * Licensed under the GNU/GPL. See COPYING for details.
- */
-#include <linux/ssb/ssb.h>
-
-
-#define SSB_OHCI_TMSLOW_HOSTMODE	(1 << 29)
-
-struct ssb_ohci_device {
-	struct ohci_hcd ohci; /* _must_ be at the beginning. */
-
-	u32 enable_flags;
-};
-
-static inline
-struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd)
-{
-	return (struct ssb_ohci_device *)(hcd->hcd_priv);
-}
-
-
-static int ssb_ohci_reset(struct usb_hcd *hcd)
-{
-	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
-	struct ohci_hcd *ohci = &ohcidev->ohci;
-	int err;
-
-	ohci_hcd_init(ohci);
-	err = ohci_init(ohci);
-
-	return err;
-}
-
-static int ssb_ohci_start(struct usb_hcd *hcd)
-{
-	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
-	struct ohci_hcd *ohci = &ohcidev->ohci;
-	int err;
-
-	err = ohci_run(ohci);
-	if (err < 0) {
-		ohci_err(ohci, "can't start\n");
-		ohci_stop(hcd);
-	}
-
-	return err;
-}
-
-static const struct hc_driver ssb_ohci_hc_driver = {
-	.description		= "ssb-usb-ohci",
-	.product_desc		= "SSB OHCI Controller",
-	.hcd_priv_size		= sizeof(struct ssb_ohci_device),
-
-	.irq			= ohci_irq,
-	.flags			= HCD_MEMORY | HCD_USB11,
-
-	.reset			= ssb_ohci_reset,
-	.start			= ssb_ohci_start,
-	.stop			= ohci_stop,
-	.shutdown		= ohci_shutdown,
-
-	.urb_enqueue		= ohci_urb_enqueue,
-	.urb_dequeue		= ohci_urb_dequeue,
-	.endpoint_disable	= ohci_endpoint_disable,
-
-	.get_frame_number	= ohci_get_frame,
-
-	.hub_status_data	= ohci_hub_status_data,
-	.hub_control		= ohci_hub_control,
-#ifdef	CONFIG_PM
-	.bus_suspend		= ohci_bus_suspend,
-	.bus_resume		= ohci_bus_resume,
-#endif
-
-	.start_port_reset	= ohci_start_port_reset,
-};
-
-static void ssb_ohci_detach(struct ssb_device *dev)
-{
-	struct usb_hcd *hcd = ssb_get_drvdata(dev);
-
-	if (hcd->driver->shutdown)
-		hcd->driver->shutdown(hcd);
-	usb_remove_hcd(hcd);
-	iounmap(hcd->regs);
-	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-	usb_put_hcd(hcd);
-	ssb_device_disable(dev, 0);
-}
-
-static int ssb_ohci_attach(struct ssb_device *dev)
-{
-	struct ssb_ohci_device *ohcidev;
-	struct usb_hcd *hcd;
-	int err = -ENOMEM;
-	u32 tmp, flags = 0;
-
-	if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
-	    dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
-		return -EOPNOTSUPP;
-
-	if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV) {
-		/* Put the device into host-mode. */
-		flags |= SSB_OHCI_TMSLOW_HOSTMODE;
-		ssb_device_enable(dev, flags);
-	} else if (dev->id.coreid == SSB_DEV_USB20_HOST) {
-		/*
-		 * USB 2.0 special considerations:
-		 *
-		 * In addition to the standard SSB reset sequence, the Host
-		 * Control Register must be programmed to bring the USB core
-		 * and various phy components out of reset.
-		 */
-		ssb_device_enable(dev, 0);
-		ssb_write32(dev, 0x200, 0x7ff);
-
-		/* Change Flush control reg */
-		tmp = ssb_read32(dev, 0x400);
-		tmp &= ~8;
-		ssb_write32(dev, 0x400, tmp);
-		tmp = ssb_read32(dev, 0x400);
-
-		/* Change Shim control reg */
-		tmp = ssb_read32(dev, 0x304);
-		tmp &= ~0x100;
-		ssb_write32(dev, 0x304, tmp);
-		tmp = ssb_read32(dev, 0x304);
-
-		udelay(1);
-
-		/* Work around for 5354 failures */
-		if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
-			/* Change syn01 reg */
-			tmp = 0x00fe00fe;
-			ssb_write32(dev, 0x894, tmp);
-
-			/* Change syn03 reg */
-			tmp = ssb_read32(dev, 0x89c);
-			tmp |= 0x1;
-			ssb_write32(dev, 0x89c, tmp);
-		}
-	} else
-		ssb_device_enable(dev, 0);
-
-	hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
-			dev_name(dev->dev));
-	if (!hcd)
-		goto err_dev_disable;
-	ohcidev = hcd_to_ssb_ohci(hcd);
-	ohcidev->enable_flags = flags;
-
-	tmp = ssb_read32(dev, SSB_ADMATCH0);
-	hcd->rsrc_start = ssb_admatch_base(tmp);
-	hcd->rsrc_len = ssb_admatch_size(tmp);
-	hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
-	if (!hcd->regs)
-		goto err_put_hcd;
-	err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
-	if (err)
-		goto err_iounmap;
-
-	ssb_set_drvdata(dev, hcd);
-
-	return err;
-
-err_iounmap:
-	iounmap(hcd->regs);
-err_put_hcd:
-	usb_put_hcd(hcd);
-err_dev_disable:
-	ssb_device_disable(dev, flags);
-	return err;
-}
-
-static int ssb_ohci_probe(struct ssb_device *dev,
-		const struct ssb_device_id *id)
-{
-	int err;
-	u16 chipid_top;
-
-	/* USBcores are only connected on embedded devices. */
-	chipid_top = (dev->bus->chip_id & 0xFF00);
-	if (chipid_top != 0x4700 && chipid_top != 0x5300)
-		return -ENODEV;
-
-	/* TODO: Probably need checks here; is the core connected? */
-
-	if (usb_disabled())
-		return -ENODEV;
-
-	/* We currently always attach SSB_DEV_USB11_HOSTDEV
-	 * as HOST OHCI. If we want to attach it as Client device,
-	 * we must branch here and call into the (yet to
-	 * be written) Client mode driver. Same for remove(). */
-
-	err = ssb_ohci_attach(dev);
-
-	return err;
-}
-
-static void ssb_ohci_remove(struct ssb_device *dev)
-{
-	ssb_ohci_detach(dev);
-}
-
-#ifdef CONFIG_PM
-
-static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state)
-{
-	ssb_device_disable(dev, 0);
-
-	return 0;
-}
-
-static int ssb_ohci_resume(struct ssb_device *dev)
-{
-	struct usb_hcd *hcd = ssb_get_drvdata(dev);
-	struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
-
-	ssb_device_enable(dev, ohcidev->enable_flags);
-
-	ohci_finish_controller_resume(hcd);
-	return 0;
-}
-
-#else /* !CONFIG_PM */
-#define ssb_ohci_suspend	NULL
-#define ssb_ohci_resume	NULL
-#endif /* CONFIG_PM */
-
-static const struct ssb_device_id ssb_ohci_table[] = {
-	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
-	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
-	SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
-	SSB_DEVTABLE_END
-};
-MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
-
-static struct ssb_driver ssb_ohci_driver = {
-	.name		= KBUILD_MODNAME,
-	.id_table	= ssb_ohci_table,
-	.probe		= ssb_ohci_probe,
-	.remove		= ssb_ohci_remove,
-	.suspend	= ssb_ohci_suspend,
-	.resume		= ssb_ohci_resume,
-};
-- 
1.7.5.4

---

From: Alan Stern <[email-masked]>
Date: Sat, 21 Jan 2012 22:41:26 -0500 (EST)

> used for the Broadcom bcma and ssb USB EHCI controller.

Before adding a generic platform driver for EHCI, you should give some
to thought to how it might be generalized.  There are a lot of EHCI
platform drivers, all differing in various major or minor respects.  
It should be possible to replace a lot of them with the generic driver, 
but first it will need some way to cope with a few minor quirks.

Please consider this, and think about which of the existing drivers 
could be replaced.

> +++ b/drivers/usb/host/ehci-platform.c

Most of this routine should be replaced with a call to ehci_setup.

Alan Stern

---

From: Hauke Mehrtens <[email-masked]>
Date: Sun, 22 Jan 2012 16:02:13 +0100

> Please consider this, and think about which of the existing drivers 

For now I just build this for bcma and ssb based SoCs. Yes there are
some drivers which could be replaced with this one, but most (all ??) of
them do something special in the device probing and this have to be
moved to somewhere else e.g. where the platform device is created.
I could rename it so it would not be generic any more, but I think it is
the wrong approach. ;-)
I am not able and do not have the time to convert all EHCI platform
drivers, where it is possible  to this generic platform driver, as I do
not have the devices to test this and time is limited.

If someone else wants to improve something on these "generic" platform
drivers to make them work with an other device I am totally fine with it.
> Most of this routine should be replaced with a call to ehci_setup.
Thanks for the info, this is changed.
> Alan Stern

Hauke

---

From: Greg KH <[email-masked]>
Date: Sun, 22 Jan 2012 14:23:46 -0500

> not have the devices to test this and time is limited.

Time is not limited for us, sorry, this seems like the correct thing to
do, and because of that, we (well I at least) will not accept this patch
as-is.

Please go rework it to be as Alan suggested.

> drivers to make them work with an other device I am totally fine with it.

I think that someone just became you :)

Yes, this isn't fair, but it's how Linux kernel development works,
sorry.

thanks,

greg k-h

---

From: Alan Stern <[email-masked]>
Date: Mon, 23 Jan 2012 11:29:43 -0500 (EST)

> Yes, this isn't fair, but it's how Linux kernel development works,

The work doesn't have to be all done right away.  Still, I think it 
makes sense to separate out the "generic platform" drivers from the 
rest of this patch series.

Some platform drivers require additional storage for things like
pointers to clocks or OTG transceivers.  For example see ehci-mv.c,
which allocates its own ehci_hcd_mv structure along with ehci_hcd.  
Some other drivers even define their own private version of ehci_hcd,
such as ehci-fsl.c.  If you can figure out a good way to expend the
ehci_hcd structure so that it can accomodate the extra fields needed by
all these drivers in a generic way, that would be an excellent start
and well worth merging.  Maybe just adding a "void *platform_data"
field would be enough.

As for special activities during device probing...  Many of these can 
be handled later on, easily enough, by adding appropriate quirk flags.  
We don't have to worry about that part right now.

Alan Stern

---

From: Hauke Mehrtens <[email-masked]>
Date: Mon, 23 Jan 2012 21:57:56 +0100

> rest of this patch series.
Ok, but how should these patches being merged as my plan is to get them
all into 3.4 in some way? The bcma hcd driver depends on changes in
drivers/bcma and will also depend on these generic platform driver.

> and well worth merging.  Maybe just adding a "void *platform_data"
Yes I will have a look at most/all these drivers and will come up with a
new version of the generic platform driver in some days. Thanks for
these examples you mentioned.

> We don't have to worry about that part right now.
Yes I also thought about such flags.

Hauke

---

From: Alan Stern <[email-masked]>
Date: Mon, 23 Jan 2012 16:34:02 -0500 (EST)

> drivers/bcma and will also depend on these generic platform driver.

Like I said, we don't need a fully general generic platform driver
right away.  If you make a small number of additions so that it is
clear how to use the two generic drivers instead of some of the
existing platform drivers, and then submit the generic drivers as a
separate standalone patch series, that should be good enough.  Greg
will probably be very happy to merge it, if it includes an explicit
plan for replacing some existing drivers.

Then the bcma stuff can be added on top, as a separate patch series.  
Once that's done, we can start making an effort to assimilate the
platform drivers into the generic drivers.

Alan Stern