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))
- msleep(5);
+ return -EAGAIN;
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);
- 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 __iomem *reg = &ehci->regs->port_status [port];
- u32 t1 = readl (reg);
- u32 t2 = t1;
+ u32 t1 = readl (&ehci->regs->port_status [port]);
+ u32 t2 = t1;
if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
t2 |= PORT_SUSPEND;
if (t1 != t2) {
ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
port + 1, t1, t2);
- writel (t2, reg);
+ writel (t2, &ehci->regs->port_status [port]);
}
}
- /* turn off now-idle HC */
- ehci_halt (ehci);
- ehci->hcd.state = HCD_STATE_SUSPENDED;
+ /* 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);
+ root->dev.power.power_state = 3;
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
spin_unlock_irq (&ehci->lock);
- return 0;
+ return status;
}
-/* caller has locked the root hub, and should reset/reinit on error */
+/* caller owns root->serialize, 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))
- msleep(5);
- spin_lock_irq (&ehci->lock);
+ return -EAGAIN;
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
- /* at least some APM implementations will try to deliver
- * IRQs right away, so delay them until we're ready.
- */
- intr_enable = 1;
+ writel (INTR_MASK, &ehci->regs->intr_enable);
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
- } else
- intr_enable = 0;
- ehci_dbg(ehci, "resume root hub%s\n",
- intr_enable ? " after power loss" : "");
+ /* FIXME will this work even (pci) vAUX was lost? */
+ }
/* 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);
- mdelay (20);
+ msleep (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) {
- ehci->command |= temp;
- writel (ehci->command, &ehci->regs->command);
- }
+ if (temp)
+ writel (ehci->command | temp, &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;
}
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);
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) */
-#endif
- desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
+ desc->wHubCharacteristics = cpu_to_le16 (temp);
}
/*-------------------------------------------------------------------------*/