Commit 5748930a authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: usb PM updates, OHCI (3/4)

These OHCI updates go along with usbcore changes to avoid power_state
(mostly for sysfs) and leave usbcore handling usb_device state (except
for quick-disconnect on STD resume).

 - detect and ignore a root hub status polling call made during
   suspend without USB_RESUME (it'd act like CardBus eject...)

 - detect and ignore a superfluous root hub resume call made in
   some cases with USB_RESUME

 - use irqsave spinlock calls in timer callback paths

 - don't expect usbcore to understand "disconnect the root hub"

 - avoid some pointless delays/faults

So it should fix a lot of the problems folk have been having with
OHCI in the latest 2.6.10-rc1-bk kernels.
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 8df93ce6
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#define DRIVER_VERSION "2004 Feb 02" #define DRIVER_VERSION "2004 Nov 08"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver" #define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
...@@ -657,8 +657,6 @@ static int ohci_run (struct ohci_hcd *ohci) ...@@ -657,8 +657,6 @@ static int ohci_run (struct ohci_hcd *ohci)
udev = hcd_to_bus (&ohci->hcd)->root_hub; udev = hcd_to_bus (&ohci->hcd)->root_hub;
if (udev) { if (udev) {
udev->dev.power.power_state = 0;
usb_set_device_state (udev, USB_STATE_CONFIGURED);
return 0; return 0;
} }
...@@ -799,6 +797,7 @@ static int ohci_restart (struct ohci_hcd *ohci) ...@@ -799,6 +797,7 @@ static int ohci_restart (struct ohci_hcd *ohci)
int temp; int temp;
int i; int i;
struct urb_priv *priv; struct urb_priv *priv;
struct usb_device *root = ohci->hcd.self.root_hub;
/* mark any devices gone, so they do nothing till khubd disconnects. /* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away. * recycle any "live" eds/tds (and urbs) right away.
...@@ -807,7 +806,11 @@ static int ohci_restart (struct ohci_hcd *ohci) ...@@ -807,7 +806,11 @@ static int ohci_restart (struct ohci_hcd *ohci)
*/ */
spin_lock_irq(&ohci->lock); spin_lock_irq(&ohci->lock);
disable (ohci); disable (ohci);
usb_set_device_state (ohci->hcd.self.root_hub, USB_STATE_NOTATTACHED); for (i = 0; i < root->maxchild; i++) {
if (root->children [i])
usb_set_device_state (root->children[i],
USB_STATE_NOTATTACHED);
}
if (!list_empty (&ohci->pending)) if (!list_empty (&ohci->pending))
ohci_dbg(ohci, "abort schedule...\n"); ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) { list_for_each_entry (priv, &ohci->pending, pending) {
...@@ -864,7 +867,6 @@ static int ohci_restart (struct ohci_hcd *ohci) ...@@ -864,7 +867,6 @@ static int ohci_restart (struct ohci_hcd *ohci)
ohci_writel (ohci, RH_PS_PSS, ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]); &ohci->regs->roothub.portstatus [temp]);
ohci_dbg (ohci, "restart complete\n"); ohci_dbg (ohci, "restart complete\n");
ohci->hcd.state = USB_STATE_RUNNING;
} }
return 0; return 0;
} }
......
...@@ -48,15 +48,10 @@ static int ohci_restart (struct ohci_hcd *ohci); ...@@ -48,15 +48,10 @@ static int ohci_restart (struct ohci_hcd *ohci);
static int ohci_hub_suspend (struct usb_hcd *hcd) static int ohci_hub_suspend (struct usb_hcd *hcd)
{ {
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub;
int status = 0; int status = 0;
unsigned long flags;
if (root->dev.power.power_state != 0) spin_lock_irqsave (&ohci->lock, flags);
return 0;
if (time_before (jiffies, ohci->next_statechange))
return -EAGAIN;
spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) { switch (ohci->hc_control & OHCI_CTRL_HCFS) {
...@@ -72,8 +67,8 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ...@@ -72,8 +67,8 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
ohci_dbg (ohci, "needs reinit!\n"); ohci_dbg (ohci, "needs reinit!\n");
goto done; goto done;
case OHCI_USB_SUSPEND: case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "already suspended?\n"); ohci_dbg (ohci, "already suspended\n");
goto succeed; goto done;
} }
ohci_dbg (ohci, "suspend root hub\n"); ohci_dbg (ohci, "suspend root hub\n");
...@@ -122,14 +117,10 @@ static int ohci_hub_suspend (struct usb_hcd *hcd) ...@@ -122,14 +117,10 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
/* no resumes until devices finish suspending */ /* no resumes until devices finish suspending */
ohci->next_statechange = jiffies + msecs_to_jiffies (5); ohci->next_statechange = jiffies + msecs_to_jiffies (5);
succeed:
/* it's not HCD_STATE_SUSPENDED unless access to this
* hub from the non-usb side (PCI, SOC, etc) stopped
*/
root->dev.power.power_state = 3;
usb_set_device_state (root, USB_STATE_SUSPENDED);
done: done:
spin_unlock_irq (&ohci->lock); if (status == 0)
ohci->hcd.state = HCD_STATE_SUSPENDED;
spin_unlock_irqrestore (&ohci->lock, flags);
return status; return status;
} }
...@@ -145,22 +136,26 @@ static inline struct ed *find_head (struct ed *ed) ...@@ -145,22 +136,26 @@ static inline struct ed *find_head (struct ed *ed)
static int ohci_hub_resume (struct usb_hcd *hcd) static int ohci_hub_resume (struct usb_hcd *hcd)
{ {
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub;
u32 temp, enables; u32 temp, enables;
int status = -EINPROGRESS; int status = -EINPROGRESS;
if (!root->dev.power.power_state)
return 0;
if (time_before (jiffies, ohci->next_statechange)) if (time_before (jiffies, ohci->next_statechange))
return -EAGAIN; msleep(5);
spin_lock_irq (&ohci->lock); spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control); ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
/* this can happen after suspend-to-disk */ /* this can happen after suspend-to-disk */
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n", if (hcd->state == USB_STATE_RESUMING) {
ohci->hc_control); ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
status = -EBUSY; ohci->hc_control);
status = -EBUSY;
/* this happens when pmcore resumes HC then root */
} else {
ohci_dbg (ohci, "duplicate resume\n");
status = 0;
}
} else switch (ohci->hc_control & OHCI_CTRL_HCFS) { } else switch (ohci->hc_control & OHCI_CTRL_HCFS) {
case OHCI_USB_SUSPEND: case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES); ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
...@@ -175,7 +170,6 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ...@@ -175,7 +170,6 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
break; break;
case OHCI_USB_OPER: case OHCI_USB_OPER:
ohci_dbg (ohci, "odd resume\n"); ohci_dbg (ohci, "odd resume\n");
root->dev.power.power_state = 0;
status = 0; status = 0;
break; break;
default: /* RESET, we lost power */ default: /* RESET, we lost power */
...@@ -245,8 +239,6 @@ static int ohci_hub_resume (struct usb_hcd *hcd) ...@@ -245,8 +239,6 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
/* TRSMRCY */ /* TRSMRCY */
msleep (10); msleep (10);
root->dev.power.power_state = 0;
usb_set_device_state (root, USB_STATE_CONFIGURED);
/* keep it alive for ~5x suspend + resume costs */ /* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + msecs_to_jiffies (250); ohci->next_statechange = jiffies + msecs_to_jiffies (250);
...@@ -315,10 +307,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -315,10 +307,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
int ports, i, changed = 0, length = 1; int ports, i, changed = 0, length = 1;
int can_suspend = 1; int can_suspend = 1;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HCD_IS_RUNNING(ohci->hcd.state))
return 0;
ports = roothub_a (ohci) & RH_A_NDP; ports = roothub_a (ohci) & RH_A_NDP;
if (ports > MAX_ROOT_PORTS) { if (ports > MAX_ROOT_PORTS) {
if (!HCD_IS_RUNNING(ohci->hcd.state))
return -ESHUTDOWN;
ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports, ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", ports,
ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP); ohci_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */ /* retry later; "should not happen" */
...@@ -362,6 +356,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -362,6 +356,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
#ifdef CONFIG_PM #ifdef CONFIG_PM
/* save power by suspending idle root hubs; /* save power by suspending idle root hubs;
* INTR_RD wakes us when there's work * INTR_RD wakes us when there's work
* NOTE: if we can do this, we don't need a root hub timer!
*/ */
if (can_suspend if (can_suspend
&& !changed && !changed
...@@ -369,6 +364,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -369,6 +364,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
&& ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES) && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
& ohci->hc_control) & ohci->hc_control)
== OHCI_USB_OPER == OHCI_USB_OPER
&& time_after (jiffies, ohci->next_statechange)
&& usb_trylock_device (hcd->self.root_hub) && usb_trylock_device (hcd->self.root_hub)
) { ) {
ohci_vdbg (ohci, "autosuspend\n"); ohci_vdbg (ohci, "autosuspend\n");
......
...@@ -106,7 +106,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state) ...@@ -106,7 +106,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_hcd *ohci = hcd_to_ohci (hcd);
/* suspend root hub, hoping it keeps power during suspend */ /* suspend root hub, hoping it keeps power during suspend */
while (time_before (jiffies, ohci->next_statechange)) if (time_before (jiffies, ohci->next_statechange))
msleep (100); msleep (100);
#ifdef CONFIG_USB_SUSPEND #ifdef CONFIG_USB_SUSPEND
...@@ -154,7 +154,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd) ...@@ -154,7 +154,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
#endif #endif
/* resume root hub */ /* resume root hub */
while (time_before (jiffies, ohci->next_statechange)) if (time_before (jiffies, ohci->next_statechange))
msleep (100); msleep (100);
#ifdef CONFIG_USB_SUSPEND #ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */ /* get extra cleanup even if remote wakeup isn't in use */
...@@ -166,7 +166,6 @@ static int ohci_pci_resume (struct usb_hcd *hcd) ...@@ -166,7 +166,6 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
#endif #endif
if (retval == 0) { if (retval == 0) {
hcd->self.controller->power.power_state = 0;
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac) if (_machine == _MACH_Pmac)
enable_irq (to_pci_dev(hcd->self.controller)->irq); enable_irq (to_pci_dev(hcd->self.controller)->irq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment