X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fehci-hub.c;h=8a1c130f944826bb5bf2a4f7c8c884b11903dfe2;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=4ff7f868b1570c93e38cb45699147f245eb2f927;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4ff7f868b..8a1c130f9 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -33,26 +33,33 @@ 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); @@ -60,48 +67,45 @@ static int ehci_hub_suspend (struct usb_hcd *hcd) 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); @@ -118,7 +122,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd) 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) @@ -135,12 +139,19 @@ static int ehci_hub_resume (struct usb_hcd *hcd) 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; } @@ -200,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(hcd->state)) + return 0; + /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); @@ -236,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; } @@ -265,9 +281,12 @@ ehci_hub_descriptor ( 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); } /*-------------------------------------------------------------------------*/ @@ -480,7 +499,7 @@ static int ehci_hub_control ( 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]);