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