Commit 28b96d32 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

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

These EHCI updates go along with usbcore changes to avoid power_state.

 - Reinitialize one more register on resume-after-poweroff; and
   for controllers that need it, each port.

 - Avoid some pointless delays/faults

 - Defer re-enabling IRQS on resume path, to make it work with APM on a
   ThinkPad T40.  (From: Nickolai Zeldovich <nickolai@cs.stanford.edu>)
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent a0499caa
......@@ -172,13 +172,6 @@ static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec)
return -ETIMEDOUT;
}
/*
* hc states include: unknown, halted, ready, running
* transitional states are messy just now
* trying to avoid "running" unless urbs are active
* a "ready" hc can be finishing prefetched work
*/
/* force HC to halt state from unknown (EHCI spec section 2.3) */
static int ehci_halt (struct ehci_hcd *ehci)
{
......@@ -480,8 +473,8 @@ static int ehci_start (struct usb_hcd *hcd)
ehci->async->hw_qtd_next = EHCI_LIST_END;
ehci->async->qh_state = QH_STATE_LINKED;
ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
}
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
/*
* hcc_params controls whether ehci->regs->segment must (!!!)
......@@ -540,7 +533,6 @@ static int ehci_start (struct usb_hcd *hcd)
}
udev->speed = USB_SPEED_HIGH;
udev->state = first ? USB_STATE_ATTACHED : USB_STATE_CONFIGURED;
udev->dev.power.power_state = PM_SUSPEND_ON;
/*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
......@@ -663,20 +655,19 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
if (hcd->self.root_hub->dev.power.power_state)
return 0;
while (time_before (jiffies, ehci->next_statechange))
if (time_before (jiffies, ehci->next_statechange))
msleep (100);
#ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub, state);
#else
/* FIXME lock root hub */
usb_lock_device (hcd->self.root_hub);
(void) ehci_hub_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif
// save (PCI) FLADJ in case of Vaux power loss
// ... we'd only use it to handle clock skew
return 0;
}
......@@ -687,10 +678,11 @@ static int ehci_resume (struct usb_hcd *hcd)
unsigned port;
struct usb_device *root = hcd->self.root_hub;
int retval = -EINVAL;
int powerup = 0;
// maybe restore (PCI) FLADJ
while (time_before (jiffies, ehci->next_statechange))
if (time_before (jiffies, ehci->next_statechange))
msleep (100);
/* If any port is suspended, we know we can/must resume the HC. */
......@@ -704,6 +696,8 @@ static int ehci_resume (struct usb_hcd *hcd)
up (&hcd->self.root_hub->serialize);
break;
}
if ((status & PORT_POWER) == 0)
powerup = 1;
if (!root->children [port])
continue;
dbg_port (ehci, __FUNCTION__, port + 1, status);
......@@ -728,9 +722,20 @@ static int ehci_resume (struct usb_hcd *hcd)
/* restart; khubd will disconnect devices */
retval = ehci_start (hcd);
/* here we "know" root ports should always stay powered;
* but some controllers may lost all power.
*/
if (powerup) {
ehci_dbg (ehci, "...powerup ports...\n");
for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; )
(void) ehci_hub_control(hcd,
SetPortFeature, USB_PORT_FEAT_POWER,
port--, NULL, 0);
msleep(20);
}
}
if (retval == 0)
hcd->self.controller->power.power_state = 0;
return retval;
}
......
......@@ -33,24 +33,19 @@
static int ehci_hub_suspend (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
int port;
if (root->dev.power.power_state != 0)
return 0;
if (time_before (jiffies, ehci->next_statechange))
return -EAGAIN;
msleep(5);
port = HCS_N_PORTS (ehci->hcs_params);
spin_lock_irq (&ehci->lock);
/* for hcd->state HCD_STATE_SUSPENDED, also stop the non-USB side */
root->dev.power.power_state = 3;
root->state = USB_STATE_SUSPENDED;
/* stop schedules, clean any completed work */
if (HCD_IS_RUNNING(hcd->state))
if (HCD_IS_RUNNING(hcd->state)) {
ehci_quiesce (ehci);
ehci->hcd.state = USB_STATE_QUIESCING;
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
ehci->reclaim_ready = 1;
......@@ -78,6 +73,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
/* turn off now-idle HC */
ehci_halt (ehci);
ehci->hcd.state = HCD_STATE_SUSPENDED;
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock);
......@@ -89,27 +85,27 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
static int ehci_hub_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
u32 temp;
int i;
int intr_enable;
if (!root->dev.power.power_state)
return 0;
if (time_before (jiffies, ehci->next_statechange))
return -EAGAIN;
msleep(5);
spin_lock_irq (&ehci->lock);
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
temp = 1;
writel (INTR_MASK, &ehci->regs->intr_enable);
/* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
*/
intr_enable = 1;
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
/* FIXME will this work even if (pci) vAUX was lost? */
} else
temp = 0;
intr_enable = 0;
ehci_dbg(ehci, "resume root hub%s\n",
temp ? " after power loss" : "");
intr_enable ? " after power loss" : "");
/* restore CMD_RUN, framelist size, and irq threshold */
writel (ehci->command, &ehci->regs->command);
......@@ -148,9 +144,14 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
writel (ehci->command, &ehci->regs->command);
}
root->dev.power.power_state = 0;
ehci->next_statechange = jiffies + msecs_to_jiffies(5);
ehci->hcd.state = USB_STATE_RUNNING;
/* Now we can safely re-enable irqs */
if (intr_enable)
writel (INTR_MASK, &ehci->regs->intr_enable);
spin_unlock_irq (&ehci->lock);
return 0;
}
......@@ -210,6 +211,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
int ports, i, retval = 1;
unsigned long flags;
/* if !USB_SUSPEND, root hub timers won't get shut down ... */
if (!HCD_IS_RUNNING(ehci->hcd.state))
return 0;
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params);
......@@ -246,6 +251,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
status = STS_PCD;
}
}
/* FIXME autosuspend idle root hubs */
spin_unlock_irqrestore (&ehci->lock, flags);
return status ? retval : 0;
}
......
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