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;
- int status = 0;
- 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);
+ /* stop schedules, clean any completed work */
+ if (HCD_IS_RUNNING(hcd->state)) {
+ ehci_quiesce (ehci);
+ hcd->state = USB_STATE_QUIESCING;
+ }
+ ehci->command = readl (&ehci->regs->command);
+ if (ehci->reclaim)
+ ehci->reclaim_ready = 1;
+ ehci_work(ehci, NULL);
+
/* suspend any active/unsuspended ports, maybe allow wakeup */
while (port--) {
- u32 t1 = readl (&ehci->regs->port_status [port]);
- u32 t2 = t1;
+ u32 __iomem *reg = &ehci->regs->port_status [port];
+ u32 t1 = readl (reg);
+ u32 t2 = t1;
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
t2 |= PORT_SUSPEND;
- if (ehci->hcd.remote_wakeup)
+ if (hcd->remote_wakeup)
t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
else
t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2);
- writel (t2, &ehci->regs->port_status [port]);
+ writel (t2, reg);
}
}
- /* stop schedules, then turn off HC and clean any completed work */
- if (hcd->state == USB_STATE_RUNNING)
- ehci_ready (ehci);
- ehci->command = readl (&ehci->regs->command);
- writel (ehci->command & ~CMD_RUN, &ehci->regs->command);
- if (ehci->reclaim)
- ehci->reclaim_ready = 1;
- ehci_work(ehci, NULL);
- (void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000);
+ /* turn off now-idle HC */
+ ehci_halt (ehci);
+ hcd->state = HCD_STATE_SUSPENDED;
- root->dev.power.power_state = 3;
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock);
- return status;
+ return 0;
}
-/* caller owns root->serialize, and should reset/reinit on error */
+/* caller has locked the root hub, and should reset/reinit on error */
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) {
- 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 (pci) vAUX was lost? */
- }
+ } else
+ intr_enable = 0;
+ ehci_dbg(ehci, "resume root hub%s\n",
+ intr_enable ? " after power loss" : "");
/* restore CMD_RUN, framelist size, and irq threshold */
writel (ehci->command, &ehci->regs->command);
writel (temp, &ehci->regs->port_status [i]);
}
i = HCS_N_PORTS (ehci->hcs_params);
- msleep (20);
+ mdelay (20);
while (i--) {
temp = readl (&ehci->regs->port_status [i]);
if ((temp & PORT_SUSPEND) == 0)
temp |= CMD_ASE;
if (ehci->periodic_sched)
temp |= CMD_PSE;
- if (temp)
- writel (ehci->command | temp, &ehci->regs->command);
+ if (temp) {
+ ehci->command |= temp;
+ 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;
+ 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;
}
#else
-#define ehci_hub_suspend 0
-#define ehci_hub_resume 0
+#define ehci_hub_suspend NULL
+#define ehci_hub_resume NULL
#endif /* CONFIG_PM */
int ports, i, retval = 1;
unsigned long flags;
+ /* if !USB_SUSPEND, root hub timers won't get shut down ... */
+ if (!HCD_IS_RUNNING(hcd->state))
+ return 0;
+
/* init status to no-changes */
buf [0] = 0;
ports = HCS_N_PORTS (ehci->hcs_params);
status = STS_PCD;
}
}
+ /* FIXME autosuspend idle root hubs */
spin_unlock_irqrestore (&ehci->lock, flags);
return status ? retval : 0;
}
temp = 0x0008; /* per-port overcurrent reporting */
if (HCS_PPC (ehci->hcs_params))
temp |= 0x0001; /* per-port power control */
+#if 0
+// re-enable when we support USB_PORT_FEAT_INDICATOR below.
if (HCS_INDICATOR (ehci->hcs_params))
temp |= 0x0080; /* per-port indicators (LEDs) */
- desc->wHubCharacteristics = cpu_to_le16 (temp);
+#endif
+ desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
}
/*-------------------------------------------------------------------------*/
#endif
dbg_port (ehci, "GetStatus", wIndex + 1, temp);
// we "know" this alignment is good, caller used kmalloc()...
- *((u32 *) buf) = cpu_to_le32 (status);
+ *((__le32 *) buf) = cpu_to_le32 (status);
break;
case SetHubFeature:
switch (wValue) {
if ((temp & PORT_PE) == 0
|| (temp & PORT_RESET) != 0)
goto error;
- if (ehci->hcd.remote_wakeup)
+ if (hcd->remote_wakeup)
temp |= PORT_WAKE_BITS;
writel (temp | PORT_SUSPEND,
&ehci->regs->port_status [wIndex]);