ChangeSet 1.1500.2.171, 2004/02/06 14:19:24-08:00, [EMAIL PROTECTED]

[PATCH] USB: USB misc OHCI updates

Here are three minor OHCI changes:

* Turn off periodic dma transfers until they're needed.  Extra DMAs
  consume power, and can trigger problems on marginal systems.

* New module param "power_switching" (default false).  Many boards
  will have no problems with this mode.  It makes OHCI act more like
  most external hubs and like EHCI.

* Minor SMP cleanup affecting display-only usbfs statistics.

On one system, turning off the periodic DMAs made two of the four
active OHCI controllers work in cases that previously triggered
"Unrecoverable Error" IRQs.


 drivers/usb/host/ohci-hcd.c |   35 ++++++++++++++++++++++++++---------
 drivers/usb/host/ohci-hub.c |    2 ++
 drivers/usb/host/ohci-pci.c |    3 +++
 drivers/usb/host/ohci-q.c   |   35 +++++++++++++++++++++++++----------
 4 files changed, 56 insertions(+), 19 deletions(-)


diff -Nru a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c       Mon Feb  9 14:37:09 2004
+++ b/drivers/usb/host/ohci-hcd.c       Mon Feb  9 14:37:09 2004
@@ -81,6 +81,7 @@
 #endif
 
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
@@ -103,7 +104,7 @@
 #include <asm/byteorder.h>
 
 
-#define DRIVER_VERSION "2003 Oct 13"
+#define DRIVER_VERSION "2004 Feb 02"
 #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
 #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
 
@@ -112,8 +113,7 @@
 // #define OHCI_VERBOSE_DEBUG  /* not always helpful */
 
 /* For initializing controller (mask in an HCFS mode too) */
-#define        OHCI_CONTROL_INIT \
-        (OHCI_CTRL_CBSR & 0x3) | OHCI_CTRL_IE | OHCI_CTRL_PLE
+#define        OHCI_CONTROL_INIT       OHCI_CTRL_CBSR
 
 #define OHCI_UNLINK_TIMEOUT     (HZ / 10)
 
@@ -133,6 +133,12 @@
 #include "ohci-mem.c"
 #include "ohci-q.c"
 
+
+/* Some boards don't support per-port power switching */
+static int power_switching = 0;
+module_param (power_switching, bool, 0);
+MODULE_PARM_DESC (power_switching, "true (not default) to switch port power");
+
 /*-------------------------------------------------------------------------*/
 
 /*
@@ -288,11 +294,8 @@
                 * with HC dead, we won't respect hc queue pointers
                 * any more ... just clean up every urb's memory.
                 */
-               if (urb->hcpriv) {
-                       spin_unlock (&ohci->lock);
+               if (urb->hcpriv)
                        finish_urb (ohci, urb, NULL);
-                       spin_lock (&ohci->lock);
-               }
        }
        spin_unlock_irqrestore (&ohci->lock, flags);
        return 0;
@@ -413,6 +416,14 @@
        ohci->hc_control = readl (&ohci->regs->control);
        ohci->hc_control &= OHCI_CTRL_RWC;      /* hcfs 0 = RESET */
        writel (ohci->hc_control, &ohci->regs->control);
+       if (power_switching) {
+               unsigned ports = roothub_a (ohci) & RH_A_NDP; 
+
+               /* power down each port */
+               for (temp = 0; temp < ports; temp++)
+                       writel (RH_PS_LSDA,
+                               &ohci->regs->roothub.portstatus [temp]);
+       }
        // flush those pci writes
        (void) readl (&ohci->regs->control);
        wait_ms (50);
@@ -502,15 +513,21 @@
                /* NSC 87560 and maybe others */
                tmp |= RH_A_NOCP;
                tmp &= ~(RH_A_POTPGT | RH_A_NPS);
+       } else if (power_switching) {
+               /* act like most external hubs:  use per-port power
+                * switching and overcurrent reporting.
+                */
+               tmp &= ~(RH_A_NPS | RH_A_NOCP);
+               tmp |= RH_A_PSM | RH_A_OCPM;
        } else {
                /* hub power always on; required for AMD-756 and some
-                * Mac platforms, use this mode everywhere by default
+                * Mac platforms.  ganged overcurrent reporting, if any.
                 */
                tmp |= RH_A_NPS;
        }
        writel (tmp, &ohci->regs->roothub.a);
        writel (RH_HS_LPSC, &ohci->regs->roothub.status);
-       writel (0, &ohci->regs->roothub.b);
+       writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b);
        // flush those pci writes
        (void) readl (&ohci->regs->control);
 
diff -Nru a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
--- a/drivers/usb/host/ohci-hub.c       Mon Feb  9 14:37:09 2004
+++ b/drivers/usb/host/ohci-hub.c       Mon Feb  9 14:37:09 2004
@@ -128,6 +128,8 @@
        desc->bDescLength = 7 + 2 * temp;
 
        temp = 0;
+       if (rh & RH_A_NPS)              /* no power switching? */
+           temp |= 0x0002;
        if (rh & RH_A_PSM)              /* per-port power switching? */
            temp |= 0x0001;
        if (rh & RH_A_NOCP)             /* no overcurrent reporting? */
diff -Nru a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
--- a/drivers/usb/host/ohci-pci.c       Mon Feb  9 14:37:09 2004
+++ b/drivers/usb/host/ohci-pci.c       Mon Feb  9 14:37:09 2004
@@ -266,6 +266,9 @@
                        if (ohci->ed_bulktail)
                                ohci->hc_control |= OHCI_CTRL_BLE;
                }
+               if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs
+                               || hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs)
+                       ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
                hcd->state = USB_STATE_RUNNING;
                writel (ohci->hc_control, &ohci->regs->control);
 
diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
--- a/drivers/usb/host/ohci-q.c Mon Feb  9 14:37:09 2004
+++ b/drivers/usb/host/ohci-q.c Mon Feb  9 14:37:09 2004
@@ -30,7 +30,7 @@
 /*
  * URB goes back to driver, and isn't reissued.
  * It's completely gone from HC data structures.
- * PRECONDITION:  no locks held, irqs blocked  (Giveback can call into HCD.)
+ * PRECONDITION:  ohci lock held, irqs blocked.
  */
 static void
 finish_urb (struct ohci_hcd *ohci, struct urb *urb, struct pt_regs *regs)
@@ -55,7 +55,6 @@
        }
        spin_unlock (&urb->lock);
 
-       // what lock protects these?
        switch (usb_pipetype (urb->pipe)) {
        case PIPE_ISOCHRONOUS:
                hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs--;
@@ -68,7 +67,18 @@
 #ifdef OHCI_VERBOSE_DEBUG
        urb_print (urb, "RET", usb_pipeout (urb->pipe));
 #endif
+
+       /* urb->complete() can reenter this HCD */
+       spin_unlock (&ohci->lock);
        usb_hcd_giveback_urb (&ohci->hcd, urb, regs);
+       spin_lock (&ohci->lock);
+
+       /* stop periodic dma if it's not needed */
+       if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs == 0
+                       && hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs == 0) {
+               ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE);
+               writel (ohci->hc_control, &ohci->regs->control);
+       }
 }
 
 
@@ -549,6 +559,7 @@
        int             cnt = 0;
        u32             info = 0;
        int             is_out = usb_pipeout (urb->pipe);
+       int             periodic = 0;
 
        /* OHCI handles the bulk/interrupt data toggles itself.  We just
         * use the device toggle bits for resetting, and rely on the fact
@@ -578,7 +589,8 @@
         */
        case PIPE_INTERRUPT:
                /* ... and periodic urbs have extra accounting */
-               hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs++;
+               periodic = hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs++ == 0
+                       && hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs == 0;
                /* FALLTHROUGH */
        case PIPE_BULK:
                info = is_out
@@ -646,9 +658,17 @@
                                data + urb->iso_frame_desc [cnt].offset,
                                urb->iso_frame_desc [cnt].length, urb, cnt);
                }
-               hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs++;
+               periodic = hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs++ == 0
+                       && hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs == 0;
                break;
        }
+
+       /* start periodic dma if needed */
+       if (periodic) {
+               ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE;
+               writel (ohci->hc_control, &ohci->regs->control);
+       }
+
        // ASSERT (urb_priv->length == cnt);
 }
 
@@ -949,9 +969,7 @@
                        /* if URB is done, clean up */
                        if (urb_priv->td_cnt == urb_priv->length) {
                                modified = completed = 1;
-                               spin_unlock (&ohci->lock);
                                finish_urb (ohci, urb, regs);
-                               spin_lock (&ohci->lock);
                        }
                }
                if (completed && !list_empty (&ed->td_list))
@@ -1030,11 +1048,8 @@
                urb_priv->td_cnt++;
 
                /* If all this urb's TDs are done, call complete() */
-               if (urb_priv->td_cnt == urb_priv->length) {
-                       spin_unlock (&ohci->lock);
+               if (urb_priv->td_cnt == urb_priv->length)
                        finish_urb (ohci, urb, regs);
-                       spin_lock (&ohci->lock);
-               }
 
                /* clean schedule:  unlink EDs that are no longer busy */
                if (list_empty (&ed->td_list))



-------------------------------------------------------
The SF.Net email is sponsored by EclipseCon 2004
Premiere Conference on Open Tools Development and Integration
See the breadth of Eclipse activity. February 3-5 in Anaheim, CA.
http://www.eclipsecon.org/osdn
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to