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 @@
#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_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
......@@ -657,8 +657,6 @@ static int ohci_run (struct ohci_hcd *ohci)
udev = hcd_to_bus (&ohci->hcd)->root_hub;
if (udev) {
udev->dev.power.power_state = 0;
usb_set_device_state (udev, USB_STATE_CONFIGURED);
return 0;
}
......@@ -799,6 +797,7 @@ static int ohci_restart (struct ohci_hcd *ohci)
int temp;
int i;
struct urb_priv *priv;
struct usb_device *root = ohci->hcd.self.root_hub;
/* mark any devices gone, so they do nothing till khubd disconnects.
* recycle any "live" eds/tds (and urbs) right away.
......@@ -807,7 +806,11 @@ static int ohci_restart (struct ohci_hcd *ohci)
*/
spin_lock_irq(&ohci->lock);
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))
ohci_dbg(ohci, "abort schedule...\n");
list_for_each_entry (priv, &ohci->pending, pending) {
......@@ -864,7 +867,6 @@ static int ohci_restart (struct ohci_hcd *ohci)
ohci_writel (ohci, RH_PS_PSS,
&ohci->regs->roothub.portstatus [temp]);
ohci_dbg (ohci, "restart complete\n");
ohci->hcd.state = USB_STATE_RUNNING;
}
return 0;
}
......
......@@ -48,15 +48,10 @@ static int ohci_restart (struct ohci_hcd *ohci);
static int ohci_hub_suspend (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub;
int status = 0;
unsigned long flags;
if (root->dev.power.power_state != 0)
return 0;
if (time_before (jiffies, ohci->next_statechange))
return -EAGAIN;
spin_lock_irq (&ohci->lock);
spin_lock_irqsave (&ohci->lock, flags);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
......@@ -72,8 +67,8 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
ohci_dbg (ohci, "already suspended?\n");
goto succeed;
ohci_dbg (ohci, "already suspended\n");
goto done;
}
ohci_dbg (ohci, "suspend root hub\n");
......@@ -122,14 +117,10 @@ static int ohci_hub_suspend (struct usb_hcd *hcd)
/* no resumes until devices finish suspending */
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:
spin_unlock_irq (&ohci->lock);
if (status == 0)
ohci->hcd.state = HCD_STATE_SUSPENDED;
spin_unlock_irqrestore (&ohci->lock, flags);
return status;
}
......@@ -145,22 +136,26 @@ static inline struct ed *find_head (struct ed *ed)
static int ohci_hub_resume (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
struct usb_device *root = hcd_to_bus (&ohci->hcd)->root_hub;
u32 temp, enables;
int status = -EINPROGRESS;
if (!root->dev.power.power_state)
return 0;
if (time_before (jiffies, ohci->next_statechange))
return -EAGAIN;
msleep(5);
spin_lock_irq (&ohci->lock);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
/* this can happen after suspend-to-disk */
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
ohci->hc_control);
status = -EBUSY;
if (hcd->state == USB_STATE_RESUMING) {
ohci_dbg (ohci, "BIOS/SMM active, control %03x\n",
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) {
case OHCI_USB_SUSPEND:
ohci->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES);
......@@ -175,7 +170,6 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
break;
case OHCI_USB_OPER:
ohci_dbg (ohci, "odd resume\n");
root->dev.power.power_state = 0;
status = 0;
break;
default: /* RESET, we lost power */
......@@ -245,8 +239,6 @@ static int ohci_hub_resume (struct usb_hcd *hcd)
/* TRSMRCY */
msleep (10);
root->dev.power.power_state = 0;
usb_set_device_state (root, USB_STATE_CONFIGURED);
/* keep it alive for ~5x suspend + resume costs */
ohci->next_statechange = jiffies + msecs_to_jiffies (250);
......@@ -315,10 +307,12 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
int ports, i, changed = 0, length = 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;
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_readl (ohci, &ohci->regs->roothub.a) & RH_A_NDP);
/* retry later; "should not happen" */
......@@ -362,6 +356,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
#ifdef CONFIG_PM
/* save power by suspending idle root hubs;
* 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
&& !changed
......@@ -369,6 +364,7 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
&& ((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)
) {
ohci_vdbg (ohci, "autosuspend\n");
......
......@@ -106,7 +106,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, u32 state)
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
/* 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);
#ifdef CONFIG_USB_SUSPEND
......@@ -154,7 +154,7 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
#endif
/* resume root hub */
while (time_before (jiffies, ohci->next_statechange))
if (time_before (jiffies, ohci->next_statechange))
msleep (100);
#ifdef CONFIG_USB_SUSPEND
/* get extra cleanup even if remote wakeup isn't in use */
......@@ -166,7 +166,6 @@ static int ohci_pci_resume (struct usb_hcd *hcd)
#endif
if (retval == 0) {
hcd->self.controller->power.power_state = 0;
#ifdef CONFIG_PMAC_PBOOK
if (_machine == _MACH_Pmac)
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