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) ...@@ -172,13 +172,6 @@ static int handshake (void __iomem *ptr, u32 mask, u32 done, int usec)
return -ETIMEDOUT; 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) */ /* force HC to halt state from unknown (EHCI spec section 2.3) */
static int ehci_halt (struct ehci_hcd *ehci) static int ehci_halt (struct ehci_hcd *ehci)
{ {
...@@ -480,8 +473,8 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -480,8 +473,8 @@ static int ehci_start (struct usb_hcd *hcd)
ehci->async->hw_qtd_next = EHCI_LIST_END; ehci->async->hw_qtd_next = EHCI_LIST_END;
ehci->async->qh_state = QH_STATE_LINKED; ehci->async->qh_state = QH_STATE_LINKED;
ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma); 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 (!!!) * hcc_params controls whether ehci->regs->segment must (!!!)
...@@ -540,7 +533,6 @@ static int ehci_start (struct usb_hcd *hcd) ...@@ -540,7 +533,6 @@ static int ehci_start (struct usb_hcd *hcd)
} }
udev->speed = USB_SPEED_HIGH; udev->speed = USB_SPEED_HIGH;
udev->state = first ? USB_STATE_ATTACHED : USB_STATE_CONFIGURED; 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 * 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) ...@@ -663,20 +655,19 @@ static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
if (hcd->self.root_hub->dev.power.power_state) if (time_before (jiffies, ehci->next_statechange))
return 0;
while (time_before (jiffies, ehci->next_statechange))
msleep (100); msleep (100);
#ifdef CONFIG_USB_SUSPEND #ifdef CONFIG_USB_SUSPEND
(void) usb_suspend_device (hcd->self.root_hub, state); (void) usb_suspend_device (hcd->self.root_hub, state);
#else #else
/* FIXME lock root hub */ usb_lock_device (hcd->self.root_hub);
(void) ehci_hub_suspend (hcd); (void) ehci_hub_suspend (hcd);
usb_unlock_device (hcd->self.root_hub);
#endif #endif
// save (PCI) FLADJ in case of Vaux power loss // save (PCI) FLADJ in case of Vaux power loss
// ... we'd only use it to handle clock skew
return 0; return 0;
} }
...@@ -687,10 +678,11 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -687,10 +678,11 @@ static int ehci_resume (struct usb_hcd *hcd)
unsigned port; unsigned port;
struct usb_device *root = hcd->self.root_hub; struct usb_device *root = hcd->self.root_hub;
int retval = -EINVAL; int retval = -EINVAL;
int powerup = 0;
// maybe restore (PCI) FLADJ // maybe restore (PCI) FLADJ
while (time_before (jiffies, ehci->next_statechange)) if (time_before (jiffies, ehci->next_statechange))
msleep (100); msleep (100);
/* If any port is suspended, we know we can/must resume the HC. */ /* 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) ...@@ -704,6 +696,8 @@ static int ehci_resume (struct usb_hcd *hcd)
up (&hcd->self.root_hub->serialize); up (&hcd->self.root_hub->serialize);
break; break;
} }
if ((status & PORT_POWER) == 0)
powerup = 1;
if (!root->children [port]) if (!root->children [port])
continue; continue;
dbg_port (ehci, __FUNCTION__, port + 1, status); dbg_port (ehci, __FUNCTION__, port + 1, status);
...@@ -728,9 +722,20 @@ static int ehci_resume (struct usb_hcd *hcd) ...@@ -728,9 +722,20 @@ static int ehci_resume (struct usb_hcd *hcd)
/* restart; khubd will disconnect devices */ /* restart; khubd will disconnect devices */
retval = ehci_start (hcd); 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; return retval;
} }
......
...@@ -33,24 +33,19 @@ ...@@ -33,24 +33,19 @@
static int ehci_hub_suspend (struct usb_hcd *hcd) static int ehci_hub_suspend (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
int port; int port;
if (root->dev.power.power_state != 0)
return 0;
if (time_before (jiffies, ehci->next_statechange)) if (time_before (jiffies, ehci->next_statechange))
return -EAGAIN; msleep(5);
port = HCS_N_PORTS (ehci->hcs_params); port = HCS_N_PORTS (ehci->hcs_params);
spin_lock_irq (&ehci->lock); 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 */ /* stop schedules, clean any completed work */
if (HCD_IS_RUNNING(hcd->state)) if (HCD_IS_RUNNING(hcd->state)) {
ehci_quiesce (ehci); ehci_quiesce (ehci);
ehci->hcd.state = USB_STATE_QUIESCING;
}
ehci->command = readl (&ehci->regs->command); ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim) if (ehci->reclaim)
ehci->reclaim_ready = 1; ehci->reclaim_ready = 1;
...@@ -78,6 +73,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) ...@@ -78,6 +73,7 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
/* turn off now-idle HC */ /* turn off now-idle HC */
ehci_halt (ehci); ehci_halt (ehci);
ehci->hcd.state = HCD_STATE_SUSPENDED;
ehci->next_statechange = jiffies + msecs_to_jiffies(10); ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock); spin_unlock_irq (&ehci->lock);
...@@ -89,27 +85,27 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) ...@@ -89,27 +85,27 @@ static int ehci_hub_suspend (struct usb_hcd *hcd)
static int ehci_hub_resume (struct usb_hcd *hcd) static int ehci_hub_resume (struct usb_hcd *hcd)
{ {
struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct ehci_hcd *ehci = hcd_to_ehci (hcd);
struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
u32 temp; u32 temp;
int i; int i;
int intr_enable;
if (!root->dev.power.power_state)
return 0;
if (time_before (jiffies, ehci->next_statechange)) 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 */ /* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) { if (readl (&ehci->regs->intr_enable) == 0) {
temp = 1; /* at least some APM implementations will try to deliver
writel (INTR_MASK, &ehci->regs->intr_enable); * IRQs right away, so delay them until we're ready.
*/
intr_enable = 1;
writel (0, &ehci->regs->segment); writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list); writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
/* FIXME will this work even if (pci) vAUX was lost? */
} else } else
temp = 0; intr_enable = 0;
ehci_dbg(ehci, "resume root hub%s\n", 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 */ /* restore CMD_RUN, framelist size, and irq threshold */
writel (ehci->command, &ehci->regs->command); writel (ehci->command, &ehci->regs->command);
...@@ -148,9 +144,14 @@ static int ehci_hub_resume (struct usb_hcd *hcd) ...@@ -148,9 +144,14 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
writel (ehci->command, &ehci->regs->command); writel (ehci->command, &ehci->regs->command);
} }
root->dev.power.power_state = 0;
ehci->next_statechange = jiffies + msecs_to_jiffies(5); ehci->next_statechange = jiffies + msecs_to_jiffies(5);
ehci->hcd.state = USB_STATE_RUNNING; 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; return 0;
} }
...@@ -210,6 +211,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -210,6 +211,10 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
int ports, i, retval = 1; int ports, i, retval = 1;
unsigned long flags; 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 */ /* init status to no-changes */
buf [0] = 0; buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params); ports = HCS_N_PORTS (ehci->hcs_params);
...@@ -246,6 +251,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -246,6 +251,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
status = STS_PCD; status = STS_PCD;
} }
} }
/* FIXME autosuspend idle root hubs */
spin_unlock_irqrestore (&ehci->lock, flags); spin_unlock_irqrestore (&ehci->lock, flags);
return status ? retval : 0; 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