diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 85cc059705a645e947eae76f13be929048f8eef4..33b75087bc0c169270379f93be9caf509df4dd12 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 f7a975d5db097757965b758baf14612c86b72f62..44ed3a4c01efe4f9fb8a9e7239240e30258daa67 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 7bfffcbbd226ec5a6089e775a2116825027b5988..da52609a929028487df3a0249b2d88cb023d2f66 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 6531c4d26527208eb6dac426a93d59ef36939546..1a1d320b7995d1451701daf0ca570c8a94de05da 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 94d8cf4b36c19cf1472ae3c64d5874d143fd60fa..0684f57c14a0ebe3b58ff61dd60feb175df6c095 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 @@ static int ohci_run (struct ohci_hcd *ohci) 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 @@ static int ohci_run (struct ohci_hcd *ohci) /* 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 @@ static int ohci_run (struct ohci_hcd *ohci) // 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, ®s->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, ®s->intrdisable); + hcd->poll_rh = 1; + ohci->next_statechange = jiffies + STATECHANGE_DELAY; + ohci_writel (ohci, OHCI_INTR_RHSC, ®s->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 5b0a23fd798bb46294885432878e4c6429a660ed..f1b1ed086bde25d1859d3cb79e3ed7ff905d7a92 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 5602da9bd52c9446e6706544f7acb11a57672aea..f2c9161d6d6aa7d4ef0bd9309f72944765bd0d5b 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 c4c4babd4767f52bd12ed0a67586021a471f1b43..47f1c9bbef878852e785284826bfacc71f715ccf 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 37e122812b6797ee7a934a0fecd3cd0eaab1406b..ef874443aa9f3d4523f63bd48c2c498cc2bfc5bc 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 9fe56ff1615dab3740cf6d75b4149c1b3556c131..270aaaad8c6dd74c14ce6ca41e055872a559005c 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 6f559e102789462e9efbff65ee30bcbf45e92bf4..2752d36c2a78217e3a09ea6c83ec60fbcb315fc2 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 d2fc6969a9f77ae6784fe2838aee33e9fe5f9bbe..c4c77d26ca1ff5200543675d42b2628e575f6fab 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 ce3de106cadc46872f8d57491b634c7a2aa2ac67..71371de32ada4c21bed7e61d7a30914ea35db9b4 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,