This patch adds support for two PPC processors with embedded
OHCI implementations, the Freescale MPC5200 and the IBM STB04XXX.

Because of ambiguities in the OHCI specification, there are two quirks
that are needed for these chips.

Quirk 1: The IBM and Freescale implementations differ on which
half of the 32-bit hcca->frame_no field contains the frame number.
I created an inline ohci_frame_no function to centralize the handling
of this difference.

Quirk 2: On both big-endian controllers, the order that the hardware
accesses the td->hwPSW is strange.  The problem is that while td->hwPSW
is an array of u16, the OHCI spec (incorrectly, IMHO) describes it
as an array of u32, with each element containing 2 u16 fields in
little-endian order.  IBM and Freescale implemented according to
the spec and the result is an array of big-endian u16 elements which
the OHCI hardware uses in this order: hwPSW[1], hwPSW[0], hwPSW[3],
hwPSW[2], etc.  Since we only use one of the hwPSW array elements,
we need to allocate two because the hardware accesses hwPSW[1].
I centralized the handling of this quirk in the ohci_hwPSW function.

Note that I previously submitted a similar patch that used the
PPC-specific OCP driver interface.  Here, I have changed to the
platform_device driver interface.

Thanks,
-Dale

Signed-off-by: Dale Farnsworth <[EMAIL PROTECTED]>

diff -Nru a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
--- a/drivers/usb/host/Kconfig  2004-12-02 15:54:46 -07:00
+++ b/drivers/usb/host/Kconfig  2004-12-02 15:54:46 -07:00
@@ -14,6 +14,8 @@
        default y if ARCH_OMAP
        default y if ARCH_LH7A404
        default y if PXA27x
+       default y if STB03xxx
+       default y if PPC_MPC52xx
        default PCI
 
 #
@@ -83,6 +85,32 @@
 
          To compile this driver as a module, choose M here: the
          module will be called ohci-hcd.
+
+config USB_OHCI_HCD_PPC_ON_CHIP
+         bool "OHCI support for on-chip PPC USB controller"
+         depends on USB_OHCI_HCD && PPC
+         default y
+         ---help---
+         Enables support for the USB controller on the Processor chip
+         If unsure, say Y.
+
+config USB_OHCI_HCD_PCI
+         bool "OHCI support for PCI-bus USB controllers"
+         depends on USB_OHCI_HCD && PCI && USB_OHCI_HCD_PPC_ON_CHIP
+         default y
+         ---help---
+         Enables support for PCI-bus plug-in USB controller cards
+         If unsure, say Y.
+
+config USB_OHCI_BIG_ENDIAN
+         bool
+         depends on USB_OHCI_HCD_PPC_ON_CHIP
+         default y
+
+config USB_OHCI_LITTLE_ENDIAN
+         bool
+         depends on USB_OHCI_HCD_PCI
+         default y
 
 config USB_UHCI_HCD
        tristate "UHCI HCD (most Intel and VIA) support"
diff -Nru a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
--- a/drivers/usb/host/ohci.h   2004-12-02 15:54:46 -07:00
+++ b/drivers/usb/host/ohci.h   2004-12-02 15:54:46 -07:00
@@ -112,7 +112,11 @@
        __hc32          hwBE;           /* Memory Buffer End Pointer */
 
        /* PSW is only for ISO */
-#define MAXPSW 1               /* hardware allows 8 */
+/*
+ * Only 1 PSW entry is used, but on some big-endian hardware, it is
+ * the second entry, so we need space for 2.
+ */
+#define MAXPSW (1+1)           /* hardware allows 8 */
        __hc16          hwPSW [MAXPSW];
 
        /* rest are purely for the driver's use */
@@ -191,8 +195,6 @@
        u8      what [4];               /* spec only identifies 252 bytes :) */
 } __attribute__ ((aligned(256)));
 
-#define ohci_frame_no(ohci) ((u16)hc32_to_cpup(ohci,&(ohci)->hcca->frame_no))
-  
 /*
  * This is the structure of the OHCI controller's memory mapped I/O region.
  * You must use readl() and writel() (in <asm/io.h>) to access these fields!!
@@ -547,6 +549,37 @@
 static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
 {
        return big_endian(ohci) ? be32_to_cpup((__force __be32 *)x) : 
le32_to_cpup((__force __le32 *)x);
+}
+
+#ifdef CONFIG_STB03xxx
+#define OHCI_BE_FRAME_NO_SHIFT 16
+#else
+#define OHCI_BE_FRAME_NO_SHIFT 0
+#endif
+
+static inline u16 ohci_frame_no(const struct ohci_hcd *ohci)
+{
+       u32 tmp;
+       if (big_endian(ohci)) {
+               tmp = be32_to_cpup((__force __be32 *)&ohci->hcca->frame_no);
+               tmp >>= OHCI_BE_FRAME_NO_SHIFT;
+       } else
+               tmp = le32_to_cpup((__force __le32 *)&ohci->hcca->frame_no);
+
+       return (u16)tmp;
+}
+
+static inline u16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
+                                 const struct td *td, int index)
+{
+       return (__hc16 *)(big_endian(ohci) ?
+                       &td->hwPSW[index ^ 1] : &td->hwPSW[index]);
+}
+
+static inline u16 ohci_hwPSW(const struct ohci_hcd *ohci,
+                               const struct td *td, int index)
+{
+       return hc16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
 }
 
 /*-------------------------------------------------------------------------*/
diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
--- a/drivers/usb/host/ohci-dbg.c       2004-12-02 15:54:46 -07:00
+++ b/drivers/usb/host/ohci-dbg.c       2004-12-02 15:54:46 -07:00
@@ -328,7 +328,7 @@
                        hc32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
                        hc32_to_cpup (ohci, &td->hwBE));
                for (i = 0; i < MAXPSW; i++) {
-                       u16     psw = hc16_to_cpup (ohci, &td->hwPSW [i]);
+                       u16     psw = ohci_hwPSW (ohci, td, i);
                        int     cc = (psw >> 12) & 0x0f;
                        ohci_dbg (ohci, "    psw [%d] = %2x, CC=%x %s=%d\n", i,
                                psw, cc,
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c 2004-12-02 15:54:46 -07:00
+++ b/drivers/usb/host/ohci-q.c 2004-12-02 15:54:46 -07:00
@@ -553,7 +553,8 @@
        td->hwINFO = cpu_to_hc32 (ohci, info);
        if (is_iso) {
                td->hwCBP = cpu_to_hc32 (ohci, data & 0xFFFFF000);
-               td->hwPSW [0] = cpu_to_hc16 (ohci, (data & 0x0FFF) | 0xE000);
+               *ohci_hwPSWp(ohci, td, 0) = cpu_to_hc16 (ohci,
+                                               (data & 0x0FFF) | 0xE000);
                td->ed->last_iso = info & 0xffff;
        } else {
                td->hwCBP = cpu_to_hc32 (ohci, data); 
@@ -725,7 +726,7 @@
 
        /* ISO ... drivers see per-TD length/status */
        if (tdINFO & TD_ISO) {
-               u16     tdPSW = hc16_to_cpu (ohci, td->hwPSW [0]);
+               u16     tdPSW = ohci_hwPSW( ohci, td, 0);
                int     dlen = 0;
 
                /* NOTE:  assumes FC in tdINFO == 0 (and MAXPSW == 1) */
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c       2004-12-02 15:54:46 -07:00
+++ b/drivers/usb/host/ohci-hcd.c       2004-12-02 15:54:46 -07:00
@@ -900,11 +900,16 @@
 #include "ohci-pxa27x.c"
 #endif
 
+#ifdef CONFIG_USB_OHCI_HCD_PPC_ON_CHIP
+#include "ohci-ppc-on-chip.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_ARCH_OMAP) \
       || defined (CONFIG_ARCH_LH7A404) \
       || defined (CONFIG_PXA27x) \
+      || defined (CONFIG_USB_OHCI_HCD_PPC_ON_CHIP) \
        )
 #error "missing bus glue for ohci-hcd"
 #endif
diff -Nru a/drivers/usb/host/ohci-ppc-on-chip.c 
b/drivers/usb/host/ohci-ppc-on-chip.c
--- /dev/null   Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/host/ohci-ppc-on-chip.c       2004-12-02 15:54:46 -07:00
@@ -0,0 +1,298 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber <[EMAIL PROTECTED]>
+ * (C) Copyright 2000-2002 David Brownell <[EMAIL PROTECTED]>
+ * (C) Copyright 2002 Hewlett-Packard Company
+ * (C) Copyright 2003-2004 MontaVista Software Inc.
+ * 
+ * Bus Glue for PPC On-Chip OHCI driver
+ *
+ * Modified by Dale Farnsworth <[EMAIL PROTECTED]> from ohci-sa1111.c
+ *
+ * This file is licenced under the GPL.
+ */
+
+static void usb_hcd_on_chip_remove(struct usb_hcd *, struct platform_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_on_chip_probe - initialize On-Chip HCDs
+ * Context: !in_interrupt()
+ *
+ * Allocates basic resources for this USB host controller, and
+ * then invokes the start() method for the HCD associated with it
+ * through the hotplug entry's driver_data.
+ *
+ * Store this function in the HCD's struct pci_driver as probe().
+ */
+static int usb_hcd_on_chip_probe(const struct hc_driver *driver,
+                         struct usb_hcd **hcd_out,
+                         struct platform_device *pdev)
+{
+       int retval;
+       struct usb_hcd *hcd = 0;
+       struct ohci_hcd *ohci;
+       struct resource *res;
+       int irq;
+       struct device *dev;
+
+       pr_debug("starting On-Chip OHCI USB Controller\n");
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               pr_debug(__FILE__ ": no irq\n");
+               return -ENODEV;
+       }
+       irq = res->start;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               pr_debug(__FILE__ ": no reg addr\n");
+               return -ENODEV;
+       }
+       if (!request_mem_region(res->start, res->end - res->start + 1,
+                                                       driver->description)) {
+               pr_debug(__FILE__ ": request_mem_region failed\n");
+               return -EBUSY;
+       }
+
+       hcd = driver->hcd_alloc();
+       if (!hcd){
+               pr_debug(__FILE__ ": hcd_alloc failed\n");
+               retval = -ENOMEM;
+               goto err0;
+       }
+
+       ohci = hcd_to_ohci(hcd);
+
+       ohci->flags |= OHCI_BIG_ENDIAN;
+
+       hcd->driver = (struct hc_driver *) driver;
+       hcd->description = driver->description;
+       hcd->irq = irq;
+       hcd->regs = (struct ohci_regs *) ioremap(res->start,
+                                               res->end - res->start + 1);
+       if (!hcd->regs) {
+               pr_debug(__FILE__ ": ioremap failed\n");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       dev = &pdev->dev;
+       if (!dev->dma_mask) {
+               static u64 dma_mask = 0xffffffffULL;
+               dev->dma_mask = &dma_mask;
+               dev->coherent_dma_mask = 0xffffffffULL;
+       }
+       hcd->self.controller = dev;
+
+       retval = hcd_buffer_create(hcd);
+       if (retval) {
+               pr_debug(__FILE__ ": pool alloc fail\n");
+               goto err2;
+       }
+
+       retval = request_irq(hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+                                                     hcd->description, hcd);
+       if (retval) {
+               pr_debug(__FILE__ ": request_irq failed, returned %d\n",
+                                                               retval);
+               retval = -EBUSY;
+               goto err3;
+       }
+
+       pr_debug("%s (On-Chip OHCI) at 0x%p, irq %d\n",
+             hcd->description, hcd->regs, hcd->irq);
+
+       usb_bus_init(&hcd->self);
+       hcd->self.op = &usb_hcd_operations;
+       hcd->self.release = &usb_hcd_release;
+       hcd->self.hcpriv = (void *) hcd;
+       hcd->self.bus_name = "on_chip_ohci";
+       hcd->product_desc = "On-Chip OHCI";
+
+       INIT_LIST_HEAD(&hcd->dev_list);
+
+       usb_register_bus(&hcd->self);
+
+       if ((retval = driver->start(hcd)) < 0) {
+               usb_hcd_on_chip_remove(hcd, pdev);
+               return retval;
+       }
+
+       *hcd_out = hcd;
+       return 0;
+
+ err3:
+       hcd_buffer_destroy(hcd);
+ err2:
+       iounmap(hcd->regs);
+ err1:
+       kfree(hcd);
+ err0:
+       pr_debug("stopping On-Chip OHCI USB Controller\n");
+       release_mem_region(res->start, res->end - res->start + 1);
+       return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_on_chip_remove - shutdown processing for On-Chip HCDs
+ * @pdev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_on_chip_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+static void usb_hcd_on_chip_remove(struct usb_hcd *hcd,
+                                               struct platform_device *pdev)
+{
+       struct resource *res;
+
+       pr_debug(__FILE__ ": remove: %s, state %x\n", hcd->self.bus_name,
+                                                               hcd->state);
+       if (in_interrupt())
+               BUG();
+
+       hcd->state = USB_STATE_QUIESCING;
+
+       pr_debug("%s: roothub graceful disconnect\n", hcd->self.bus_name);
+       usb_disconnect(&hcd->self.root_hub);
+
+       hcd->driver->stop(hcd);
+       hcd->state = USB_STATE_HALT;
+
+       free_irq(hcd->irq, hcd);
+       hcd_buffer_destroy(hcd);
+
+       usb_deregister_bus(&hcd->self);
+
+       iounmap(hcd->regs);
+       kfree(hcd);
+
+       pr_debug("stopping On-Chip OHCI USB Controller\n");
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, res->end - res->start + 1);
+}
+
+static int __devinit ohci_on_chip_start(struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       int             ret;
+
+       if ((ret = ohci_init(ohci)) < 0)
+               return ret;
+
+       if ((ret = ohci_run(ohci)) < 0) {
+               err("can't start %s", ohci->hcd.self.bus_name);
+               ohci_stop(hcd);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct hc_driver ohci_on_chip_hc_driver = {
+       .description =          "on_chip_ohci",
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  ohci_irq,
+       .flags =                HCD_USB11,
+
+       /*
+        * basic lifecycle operations
+        */
+       .start =                ohci_on_chip_start,
+#ifdef CONFIG_PM
+       /* suspend:             ohci_on_chip_suspend,  -- tbd */
+       /* resume:              ohci_on_chip_resume,   -- tbd */
+#endif
+       .stop =                 ohci_stop,
+
+       /*
+        * memory lifecycle (except per-request)
+        */
+       .hcd_alloc =            ohci_hcd_alloc,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          ohci_urb_enqueue,
+       .urb_dequeue =          ohci_urb_dequeue,
+       .endpoint_disable =     ohci_endpoint_disable,
+
+       /*
+        * scheduling support
+        */
+       .get_frame_number =     ohci_get_frame,
+
+       /*
+        * root hub support
+        */
+       .hub_status_data =      ohci_hub_status_data,
+       .hub_control =          ohci_hub_control,
+};
+
+static int ohci_hcd_on_chip_drv_probe(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = NULL;
+       int ret;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       ret = usb_hcd_on_chip_probe(&ohci_on_chip_hc_driver, &hcd, pdev);
+
+       if (ret == 0)
+               dev_set_drvdata(dev, hcd);
+
+       return ret;
+}
+
+static int ohci_hcd_on_chip_drv_remove(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+
+       usb_hcd_on_chip_remove(hcd, pdev);
+
+       dev_set_drvdata(dev, NULL);
+       return 0;
+}
+
+static struct device_driver ohci_hcd_on_chip_driver = {
+       .name           = "on_chip_ohci",
+       .bus            = &platform_bus_type,
+       .probe          = ohci_hcd_on_chip_drv_probe,
+       .remove         = ohci_hcd_on_chip_drv_remove,
+       /*.suspend      = ohci_hcd_on_chip_drv_suspend,*/
+       /*.resume       = ohci_hcd_on_chip_drv_resume,*/
+};
+
+static int __init ohci_hcd_on_chip_init(void)
+{
+       pr_debug(DRIVER_INFO " (ON-CHIP)\n");
+       pr_debug("block sizes: ed %d td %d\n", sizeof(struct ed),
+                                                       sizeof(struct td));
+
+       return driver_register(&ohci_hcd_on_chip_driver);
+}
+
+static void __exit ohci_hcd_on_chip_cleanup(void)
+{
+       driver_unregister(&ohci_hcd_on_chip_driver);
+}
+
+module_init(ohci_hcd_on_chip_init);
+module_exit(ohci_hcd_on_chip_cleanup);


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now. 
http://productguide.itmanagersjournal.com/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to