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-08-19 18:00:41 -07:00
+++ b/drivers/usb/Kconfig       2004-08-19 18:00:41 -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-08-19 18:00:41 -07:00
+++ b/drivers/usb/host/Kconfig  2004-08-19 18:00:41 -07:00
@@ -65,6 +65,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-08-19 18:00:41 -07:00
+++ b/drivers/usb/host/ohci-dbg.c       2004-08-19 18:00:41 -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 @@
                        ohci32_to_cpup (ohci, &td->hwCBP) & ~0x0fff,
                        ohci32_to_cpup (ohci, &td->hwBE));
                for (i = 0; i < MAXPSW; i++) {
-                       u16     psw = ohci16_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-08-19 18:00:41 -07:00
+++ b/drivers/usb/host/ohci-hcd.c       2004-08-19 18:00:41 -07:00
@@ -240,7 +240,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);
@@ -383,7 +383,7 @@
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
-       return OHCI_FRAME_NO(ohci->hcca);
+       return ohci_frame_no(ohci);
 }
 
 /*-------------------------------------------------------------------------*
@@ -646,7 +646,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))
@@ -811,10 +811,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-08-19 18:00:41 -07:00
+++ b/drivers/usb/host/ohci-hub.c       2004-08-19 18:00:41 -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-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-19 18:00:41 -07:00
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+#define DEBUG
+ 
+#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);
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c 2004-08-19 18:00:41 -07:00
+++ b/drivers/usb/host/ohci-q.c 2004-08-19 18:00:41 -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,7 @@
        td->hwINFO = cpu_to_ohci32 (ohci, info);
        if (is_iso) {
                td->hwCBP = cpu_to_ohci32 (ohci, data & 0xFFFFF000);
-               td->hwPSW [0] = cpu_to_ohci16 (ohci, (data & 0x0FFF) | 0xE000);
+               *ohci_hwPSWp(ohci, td, 0) = cpu_to_ohci16 (ohci, (data & 0x0FFF) | 
0xE000);
                td->ed->last_iso = info & 0xffff;
        } else {
                td->hwCBP = cpu_to_ohci32 (ohci, data); 
@@ -723,7 +723,7 @@
 
        /* ISO ... drivers see per-TD length/status */
        if (tdINFO & TD_ISO) {
-               u16     tdPSW = ohci16_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-08-19 18:00:41 -07:00
+++ b/drivers/usb/host/ohci.h   2004-08-19 18:00:41 -07:00
@@ -105,7 +105,11 @@
        __u32           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 */
        __u16           hwPSW [MAXPSW];
 
        /* rest are purely for the driver's use */
@@ -176,10 +180,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.
         */
        u32     frame_no;               /* current frame number */
-#define OHCI_FRAME_NO(hccap) ((u16)le32_to_cpup(&(hccap)->frame_no))
        __u32   done_head;              /* info returned for an interrupt */
        u8      reserved_for_hc [116];
        u8      what [4];               /* spec only identifies 252 bytes :) */
@@ -506,6 +509,32 @@
                                    const __u32 *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 ohci16_to_cpup(ohci, ohci_hwPSWp(ohci, td, index));
 }
 
 /*-------------------------------------------------------------------------*/



-------------------------------------------------------
SF.Net email is sponsored by Shop4tech.com-Lowest price on Blank Media
100pk Sonic DVD-R 4x for only $29 -100pk Sonic DVD+R for only $33
Save 50% off Retail on Ink & Toner - Free Shipping and Free Gift.
http://www.shop4tech.com/z/Inkjet_Cartridges/9_108_r285
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to