Patch 3 of 3.

Adds support for the ohci controllers found on the IBM STB04xxx and
Freescale MPC52xx embedded chips.  In addition to the low level hcd
support added in drivers/usb/host/ohci-ocp.c, it contains code for
2 quirks of these OHCI implementations.

Quirk 1: The IBM and Freescale implementations differ on 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 quirk.

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.

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

diff -Nru a/drivers/usb/Kconfig b/drivers/usb/Kconfig
--- a/drivers/usb/Kconfig       2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/Kconfig       2004-10-12 10:40:05 -07:00
@@ -7,7 +7,7 @@
 # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
 config USB
        tristate "Support for Host-side USB"
-       depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404
+       depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || 
STB03xxx || PPC_MPC52xx
        ---help---
          Universal Serial Bus (USB) is a specification for a serial bus
          subsystem which offers higher speeds and more features than the
diff -Nru a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
--- a/drivers/usb/host/Kconfig  2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/host/Kconfig  2004-10-12 10:40:05 -07:00
@@ -66,6 +66,32 @@
          To compile this driver as a module, choose M here: the
          module will be called ohci-hcd.
 
+config USB_OHCI_HCD_OCP
+        bool "OHCI support for on-chip USB controller"
+        depends on USB_OHCI_HCD && PPC_OCP
+        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 PCI && PPC_OCP
+        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_OCP
+       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"
        depends on USB
diff -Nru a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
--- a/drivers/usb/host/ohci-dbg.c       2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/host/ohci-dbg.c       2004-10-12 10:40:05 -07:00
@@ -276,7 +276,7 @@
        ohci_dump_status (controller, NULL, NULL);
        if (controller->hcca)
                ohci_dbg (controller,
-                       "hcca frame #%04x\n", OHCI_FRAME_NO(controller->hcca));
+                       "hcca frame #%04x\n", ohci_frame_no(controller));
        ohci_dump_roothub (controller, 1, NULL, NULL);
 }
 
@@ -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,
@@ -643,7 +643,7 @@
        /* hcca */
        if (ohci->hcca)
                ohci_dbg_sw (ohci, &next, &size,
-                       "hcca frame 0x%04x\n", OHCI_FRAME_NO(ohci->hcca));
+                       "hcca frame 0x%04x\n", ohci_frame_no(ohci));
 
        /* other registers mostly affect frame timings */
        rdata = ohci_readl (ohci, &regs->fminterval);
diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c       2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/host/ohci-hcd.c       2004-10-12 10:40:05 -07:00
@@ -241,7 +241,7 @@
                if (retval < 0)
                        goto fail0;
                if (ed->type == PIPE_ISOCHRONOUS) {
-                       u16     frame = OHCI_FRAME_NO(ohci->hcca);
+                       u16     frame = ohci_frame_no(ohci);
 
                        /* delay a few frames before the first TD */
                        frame += max_t (u16, 8, ed->interval);
@@ -384,7 +384,7 @@
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
-       return OHCI_FRAME_NO(ohci->hcca);
+       return ohci_frame_no(ohci);
 }
 
 /*-------------------------------------------------------------------------*
@@ -647,7 +647,7 @@
         */
        spin_lock (&ohci->lock);
        if (ohci->ed_rm_list)
-               finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca),
+               finish_unlinks (ohci, ohci_frame_no(ohci),
                                ptregs);
        if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
                        && HCD_IS_RUNNING(ohci->hcd.state))
@@ -812,10 +812,15 @@
 #include "ohci-lh7a404.c"
 #endif
 
+#ifdef CONFIG_USB_OHCI_HCD_OCP
+#include "ohci-ocp.c"
+#endif
+
 #if !(defined(CONFIG_PCI) \
       || defined(CONFIG_SA1111) \
       || defined(CONFIG_ARCH_OMAP) \
       || defined (CONFIG_ARCH_LH7A404) \
+      || defined (CONFIG_USB_OHCI_HCD_OCP) \
        )
 #error "missing bus glue for ohci-hcd"
 #endif
diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
--- a/drivers/usb/host/ohci-hub.c       2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/host/ohci-hub.c       2004-10-12 10:40:05 -07:00
@@ -128,7 +128,7 @@
                mdelay (7);
        }
        dl_done_list (ohci, NULL);
-       finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca), NULL);
+       finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
        ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
                        &ohci->regs->intrstatus);
 
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c 2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/host/ohci-q.c 2004-10-12 10:40:05 -07:00
@@ -496,7 +496,7 @@
         * behave.  frame_no wraps every 2^16 msec, and changes right before
         * SF is triggered.
         */
-       ed->tick = OHCI_FRAME_NO(ohci->hcca) + 1;
+       ed->tick = ohci_frame_no(ohci) + 1;
 
 }
 
@@ -551,7 +551,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); 
@@ -723,7 +724,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.h b/drivers/usb/host/ohci.h
--- a/drivers/usb/host/ohci.h   2004-10-12 10:40:05 -07:00
+++ b/drivers/usb/host/ohci.h   2004-10-12 10:40:06 -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 IBM ocp 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 */
@@ -183,10 +187,9 @@
        /* 
         * OHCI defines u16 frame_no, followed by u16 zero pad.
         * Since some processors can't do 16 bit bus accesses,
-        * portable access must be a 32 bit byteswapped access.
+        * portable access must be a 32 bit access.
         */
        __hc32  frame_no;               /* current frame number */
-#define OHCI_FRAME_NO(hccap) ((u16)le32_to_cpup(&(hccap)->frame_no))
        __hc32  done_head;              /* info returned for an interrupt */
        u8      reserved_for_hc [116];
        u8      what [4];               /* spec only identifies 252 bytes :) */
@@ -512,6 +515,32 @@
 static inline u32 hc32_to_cpup (const struct ohci_hcd *ohci, const __hc32 *x)
 {
        return big_endian(ohci) ? be32_to_cpup(x) : le32_to_cpup(x);
+}
+
+#ifdef CONFIG_PPC_MPC52xx
+#define FRAME_NO_SHIFT 0
+#else
+#define FRAME_NO_SHIFT 16
+#endif
+
+static inline __u16 ohci_frame_no(const struct ohci_hcd *ohci)
+{
+       return (__u16)(big_endian(ohci) ?
+                       be32_to_cpup(&ohci->hcca->frame_no) >> FRAME_NO_SHIFT :
+                       le32_to_cpup(&ohci->hcca->frame_no));
+}
+
+static inline __u16 *ohci_hwPSWp(const struct ohci_hcd *ohci,
+                                const struct td *td, int index)
+{
+       return (__u16 *)(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-ocp.c b/drivers/usb/host/ohci-ocp.c
--- /dev/null   Wed Dec 31 16:00:00 196900
+++ b/drivers/usb/host/ohci-ocp.c       2004-08-02 13:33:46 -07:00
@@ -0,0 +1,311 @@
+/*
+ * 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 MontaVista Software Inc.
+ * 
+ * Bus Glue for PPC OCP driver
+ *
+ * Modified for ocp from ohci-sa1111.c
+ *  by Dale Farnsworth <[EMAIL PROTECTED]>
+ *
+ * This file is licenced under the GPL.
+ */
+
+#include <asm/ocp.h>
+
+/*
+ * Hardware-specific initialization
+ */
+static void ocp_start_hc(struct ocp_device *ocp)
+{
+       printk(KERN_DEBUG __FILE__ ": starting OCP OHCI USB Controller\n");
+}
+
+/*
+ * Hardware-specific cleanup
+ */
+static void ocp_stop_hc(struct ocp_device *ocp)
+{
+       printk(KERN_DEBUG __FILE__ ": stopping OCP OHCI USB Controller\n");
+}
+
+void usb_hcd_ocp_remove (struct usb_hcd *, struct ocp_device *);
+
+/* configure so an HC device and id are always provided */
+/* always called with process context; sleeping is OK */
+
+/**
+ * usb_hcd_ocp_probe - initialize OCP-based 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().
+ */
+int usb_hcd_ocp_probe (const struct hc_driver *driver,
+                         struct usb_hcd **hcd_out,
+                         struct ocp_device *ocp)
+{
+       int retval;
+       struct usb_hcd *hcd = 0;
+       struct ohci_hcd *ohci;
+       phys_addr_t addr = ocp->def->paddr;     /* really a vaddr */
+       static u64 ocp_dma_mask = 0xffffffffULL;
+
+       if (!request_mem_region (addr, sizeof (struct ohci_regs),
+                               driver->description)) {
+               dbg("request_mem_region failed");
+               return -EBUSY;
+       }
+
+       ocp_start_hc(ocp);
+
+       hcd = driver->hcd_alloc ();
+       if (hcd == NULL){
+               dbg ("hcd_alloc failed");
+               retval = -ENOMEM;
+               goto err1;
+       }
+
+       ohci = hcd_to_ohci (hcd);
+       ohci->flags |= OHCI_BIG_ENDIAN;
+
+       hcd->driver = (struct hc_driver *) driver;
+       hcd->description = driver->description;
+       hcd->irq = ocp->def->irq;
+       hcd->regs = (struct ohci_regs *) addr;
+       hcd->self.controller = &ocp->dev;
+
+       hcd->self.controller->dma_mask = &ocp_dma_mask;
+       hcd->self.controller->coherent_dma_mask = 0xffffffffULL;
+
+       retval = hcd_buffer_create (hcd);
+       if (retval != 0) {
+               dbg ("pool alloc fail");
+               goto err1;
+       }
+
+       retval = request_irq (hcd->irq, usb_hcd_irq, SA_INTERRUPT,
+                             hcd->description, hcd);
+       if (retval != 0) {
+               dbg("request_irq failed");
+               retval = -EBUSY;
+               goto err2;
+       }
+
+       dbg ("%s (OCP OHCI) at 0x%p, irq %d",
+             hcd->description, hcd->regs, hcd->irq);
+
+       usb_bus_init (&hcd->self);
+       hcd->self.op = &usb_hcd_operations;
+       hcd->self.hcpriv = (void *) hcd;
+       hcd->self.bus_name = "ocp";
+
+       INIT_LIST_HEAD (&hcd->dev_list);
+
+       usb_register_bus (&hcd->self);
+
+       if ((retval = driver->start (hcd)) < 0) 
+       {
+               usb_hcd_ocp_remove(hcd, ocp);
+               return retval;
+       }
+
+       *hcd_out = hcd;
+       return 0;
+
+ err2:
+       hcd_buffer_destroy (hcd);
+       if (hcd)
+               driver->hcd_free(hcd);
+ err1:
+       ocp_stop_hc(ocp);
+       release_mem_region(addr, sizeof (struct ohci_regs));
+       return retval;
+}
+
+
+/* may be called without controller electrically present */
+/* may be called with controller, bus, and devices active */
+
+/**
+ * usb_hcd_ocp_remove - shutdown processing for OCP-based HCDs
+ * @dev: USB Host Controller being removed
+ * Context: !in_interrupt()
+ *
+ * Reverses the effect of usb_hcd_ocp_probe(), first invoking
+ * the HCD's stop() method.  It is always called from a thread
+ * context, normally "rmmod", "apmd", or something similar.
+ *
+ */
+void usb_hcd_ocp_remove (struct usb_hcd *hcd, struct ocp_device *ocp)
+{
+       dbg ("remove: %s, state %x", hcd->self.bus_name, hcd->state);
+
+       if (in_interrupt ())
+               BUG ();
+
+       hcd->state = USB_STATE_QUIESCING;
+
+       dbg ("%s: roothub graceful disconnect", 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);
+
+       hcd->driver->hcd_free (hcd);
+
+       ocp_stop_hc(ocp);
+       release_mem_region(ocp->def->paddr, sizeof (struct ohci_regs));
+}
+
+static int __devinit
+ohci_ocp_start (struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+       int             ret;
+
+       ohci_dbg (ohci, "ohci_ocp_start, ohci:%p", ohci);
+
+       ohci->hcca = dma_alloc_coherent (hcd->self.controller,
+                       sizeof *ohci->hcca, &ohci->hcca_dma, 0);
+       if (!ohci->hcca)
+               return -ENOMEM;
+        
+       ohci_dbg (ohci, "ohci_ocp_start, ohci->hcca:%p", ohci->hcca);
+
+       memset (ohci->hcca, 0, sizeof (struct ohci_hcca));
+       if ((ret = ohci_mem_init (ohci)) < 0) {
+               ohci_stop (hcd);
+               return ret;
+       }
+       ohci->regs = hcd->regs;
+
+       if (hc_reset (ohci) < 0) {
+               ohci_stop (hcd);
+               return -ENODEV;
+       }
+
+       if (hc_start (ohci) < 0) {
+               err ("can't start %s", ohci->hcd.self.bus_name);
+               ohci_stop (hcd);
+               return -EBUSY;
+       }
+       create_debug_files (ohci);
+
+#ifdef DEBUG
+       ohci_dump (ohci, 1);
+#endif
+       return 0;
+}
+
+static const struct hc_driver ohci_ocp_hc_driver = {
+       .description =          "ocp_ohci",
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  ohci_irq,
+       .flags =                HCD_USB11,
+
+       /*
+        * basic lifecycle operations
+        */
+       .start =                ohci_ocp_start,
+#ifdef CONFIG_PM
+       /* suspend:             ohci_ocp_suspend,  -- tbd */
+       /* resume:              ohci_ocp_resume,   -- tbd */
+#endif
+       .stop =                 ohci_stop,
+
+       /*
+        * memory lifecycle (except per-request)
+        */
+       .hcd_alloc =            ohci_hcd_alloc,
+       .hcd_free =             ohci_hcd_free,
+
+       /*
+        * 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_ocp_drv_probe(struct ocp_device *ocp)
+{
+       struct usb_hcd *hcd = NULL;
+       int ret;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       ret = usb_hcd_ocp_probe(&ohci_ocp_hc_driver, &hcd, ocp);
+
+       if (ret == 0)
+               dev_set_drvdata(&ocp->dev, hcd);
+
+       return ret;
+}
+
+static void ohci_hcd_ocp_drv_remove(struct ocp_device *ocp)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(&ocp->dev);
+
+       usb_hcd_ocp_remove(hcd, ocp);
+
+       dev_set_drvdata(&ocp->dev, NULL);
+}
+
+static struct ocp_device_id ohci_hcd_ocp_ids[] __devinitdata = {
+       { .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_USB },
+       { .vendor = OCP_VENDOR_FREESCALE, .function = OCP_FUNC_USB },
+       { .vendor = OCP_VENDOR_INVALID /* Terminating entry */ }
+}
+
+MODULE_DEVICE_TABLE(ocp, ohci_hcd_ocp_ids);
+
+static struct ocp_driver ohci_hcd_ocp_driver = {
+       .name           = "ocp-ohci",
+       .id_table       = ohci_hcd_ocp_ids,
+       .probe          = ohci_hcd_ocp_drv_probe,
+       .remove         = ohci_hcd_ocp_drv_remove,
+};
+
+static int __init ohci_hcd_ocp_init (void)
+{
+       dbg (DRIVER_INFO " (OCP)");
+       dbg ("block sizes: ed %d td %d",
+               sizeof (struct ed), sizeof (struct td));
+
+       return ocp_register_driver(&ohci_hcd_ocp_driver);
+}
+
+static void __exit ohci_hcd_ocp_cleanup (void)
+{
+       ocp_unregister_driver(&ohci_hcd_ocp_driver);
+}
+
+module_init (ohci_hcd_ocp_init);
+module_exit (ohci_hcd_ocp_cleanup);


-------------------------------------------------------
This SF.net email is sponsored by: IT Product Guide on ITManagersJournal
Use IT products in your business? Tell us what you think of them. Give us
Your Opinions, Get Free ThinkGeek Gift Certificates! Click to find out more
http://productguide.itmanagersjournal.com/guidepromo.tmpl
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to