summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/ohci-at91.c2
-rw-r--r--drivers/usb/host/ohci-au1xxx.c5
-rw-r--r--drivers/usb/host/ohci-dbg.c5
-rw-r--r--drivers/usb/host/ohci-ep93xx.c1
-rw-r--r--drivers/usb/host/ohci-hcd.c41
-rw-r--r--drivers/usb/host/ohci-hub.c67
-rw-r--r--drivers/usb/host/ohci-lh7a404.c5
-rw-r--r--drivers/usb/host/ohci-omap.c5
-rw-r--r--drivers/usb/host/ohci-pci.c5
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c1
-rw-r--r--drivers/usb/host/ohci-pxa27x.c1
-rw-r--r--drivers/usb/host/ohci-s3c2410.c1
-rw-r--r--drivers/usb/host/ohci-sa1111.c5
13 files changed, 99 insertions, 45 deletions
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 85cc059705a6..33b75087bc0c 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -239,7 +239,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
-
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index f7a975d5db09..44ed3a4c01ef 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -268,10 +268,6 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_au1xxx_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_au1xxx_suspend, -- tbd */
- /* resume: ohci_au1xxx_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
@@ -291,6 +287,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 7bfffcbbd226..da52609a9290 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
size -= temp;
next += temp;
+ temp = scnprintf (next, size, "hub poll timer %s\n",
+ ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+ size -= temp;
+ next += temp;
+
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 6531c4d26527..1a1d320b7995 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -134,6 +134,7 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 94d8cf4b36c1..0684f57c14a0 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -101,7 +101,7 @@
#include "../core/hcd.h"
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 August 04"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
@@ -110,9 +110,10 @@
#undef OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
-#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
- (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+ (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+ | OHCI_INTR_RD | OHCI_INTR_WDH)
#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -128,6 +129,8 @@
static const char hcd_name [] = "ohci_hcd";
+#define STATECHANGE_DELAY msecs_to_jiffies(300)
+
#include "ohci.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
@@ -446,7 +449,6 @@ static int ohci_init (struct ohci_hcd *ohci)
disable (ohci);
ohci->regs = hcd->regs;
- ohci->next_statechange = jiffies;
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
* was never needed for most non-PCI systems ... remove the code?
@@ -637,10 +639,14 @@ retry:
return -EOVERFLOW;
}
- /* start controller operations */
+ /* use rhsc irqs after khubd is fully initialized */
+ hcd->poll_rh = 1;
+ hcd->uses_new_polling = 1;
+
+ /* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
- ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
- ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
hcd->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +654,7 @@ retry:
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
- ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+ ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
@@ -672,6 +678,7 @@ retry:
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
spin_unlock_irq (&ohci->lock);
// POTPGT delay is bits 24-31, in 2 ms units.
@@ -709,7 +716,23 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
/* interrupt for some other device? */
} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
return IRQ_NOTMINE;
- }
+ }
+
+ /* NOTE: vendors didn't always make the same implementation
+ * choices for RHSC. Sometimes it triggers on an edge (like
+ * setting and maybe clearing a port status change bit); and
+ * it's level-triggered on other silicon, active until khubd
+ * clears all active port status change bits. Poll by timer
+ * til it's fully debounced and the difference won't matter.
+ */
+ if (ints & OHCI_INTR_RHSC) {
+ ohci_vdbg (ohci, "rhsc\n");
+ ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrdisable);
+ hcd->poll_rh = 1;
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
+ usb_hcd_poll_rh_status(hcd);
+ }
if (ints & OHCI_INTR_UE) {
disable (ohci);
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 5b0a23fd798b..f1b1ed086bde 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -36,6 +36,14 @@
/*-------------------------------------------------------------------------*/
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
+
#ifdef CONFIG_PM
#define OHCI_SCHED_ENABLES \
@@ -123,6 +131,9 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
/* no resumes until devices finish suspending */
ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ /* no timer polling */
+ hcd->poll_rh = 0;
+
done:
/* external suspend vs self autosuspend ... same effect */
if (status == 0)
@@ -256,8 +267,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
/* TRSMRCY */
msleep (10);
- /* keep it alive for ~5x suspend + resume costs */
- ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+ /* keep it alive for more than ~5x suspend + resume costs */
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
/* maybe turn schedules back on */
enables = 0;
@@ -302,9 +313,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
- int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+ int can_suspend;
unsigned long flags;
+ can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
spin_lock_irqsave (&ohci->lock, flags);
/* handle autosuspended root: finish resuming before
@@ -339,6 +351,10 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
for (i = 0; i < ohci->num_ports; i++) {
u32 status = roothub_portstatus (ohci, i);
+ /* can't autosuspend with active ports */
+ if ((status & RH_PS_PES) && !(status & RH_PS_PSS))
+ can_suspend = 0;
+
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
@@ -348,32 +364,41 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
buf [1] |= 1 << (i - 7);
continue;
}
+ }
- /* can suspend if no ports are enabled; or if all all
- * enabled ports are suspended AND remote wakeup is on.
- */
- if (!(status & RH_PS_CCS))
- continue;
- if ((status & RH_PS_PSS) && can_suspend)
- continue;
+ /* after root hub changes, stop polling after debouncing
+ * for a while and maybe kicking in autosuspend
+ */
+ if (changed) {
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
can_suspend = 0;
+ } else if (time_before (jiffies, ohci->next_statechange)) {
+ can_suspend = 0;
+ } else {
+#ifdef CONFIG_PM
+ can_suspend = can_suspend
+ && !ohci->ed_rm_list
+ && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
+ & ohci->hc_control)
+ == OHCI_USB_OPER;
+#endif
+ if (hcd->uses_new_polling) {
+ hcd->poll_rh = 0;
+ /* use INTR_RHSC iff INTR_RD won't apply */
+ if (!can_suspend)
+ ohci_writel (ohci, OHCI_INTR_RHSC,
+ &ohci->regs->intrenable);
+ }
}
+
done:
spin_unlock_irqrestore (&ohci->lock, flags);
-#ifdef CONFIG_PM
- /* save power by suspending idle root hubs;
+#ifdef CONFIG_PM
+ /* save power by autosuspending idle root hubs;
* INTR_RD wakes us when there's work
*/
- if (can_suspend
- && !changed
- && !ohci->ed_rm_list
- && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
- & ohci->hc_control)
- == OHCI_USB_OPER
- && time_after (jiffies, ohci->next_statechange)
- && usb_trylock_device (hcd->self.root_hub) == 0
- ) {
+ if (can_suspend && usb_trylock_device (hcd->self.root_hub) == 0) {
ohci_vdbg (ohci, "autosuspend\n");
(void) ohci_bus_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 5602da9bd52c..f2c9161d6d6a 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -173,10 +173,6 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_lh7a404_suspend, -- tbd */
- /* resume: ohci_lh7a404_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
/*
@@ -196,6 +192,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index c4c4babd4767..47f1c9bbef87 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -382,8 +382,10 @@ ohci_omap_start (struct usb_hcd *hcd)
int ret;
config = hcd->self.controller->platform_data;
- if (config->otg || config->rwc)
+ if (config->otg || config->rwc) {
+ ohci->hc_control = OHCI_CTRL_RWC;
writel(OHCI_CTRL_RWC, &ohci->regs->control);
+ }
if ((ret = ohci_run (ohci)) < 0) {
dev_err(hcd->self.controller, "can't start\n");
@@ -429,6 +431,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index 37e122812b67..ef874443aa9f 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -176,11 +176,13 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
+ .stop = ohci_stop,
+
#ifdef CONFIG_PM
+ /* these suspend/resume entries are for upstream PCI glue ONLY */
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
- .stop = ohci_stop,
/*
* managing i/o requests and associated device resources
@@ -199,6 +201,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
index 9fe56ff1615d..270aaaad8c6d 100644
--- a/drivers/usb/host/ohci-ppc-soc.c
+++ b/drivers/usb/host/ohci-ppc-soc.c
@@ -166,6 +166,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 6f559e102789..2752d36c2a78 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -288,6 +288,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index d2fc6969a9f7..c4c77d26ca1f 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -465,6 +465,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/
.hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index ce3de106cadc..71371de32ada 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -212,10 +212,6 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_sa1111_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_sa1111_suspend, -- tbd */
- /* resume: ohci_sa1111_resume, -- tbd */
-#endif
.stop = ohci_stop,
/*
@@ -235,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,