vserver 1.9.5.x5
[linux-2.6.git] / drivers / usb / host / ehci-hub.c
index 4ff7f86..8a1c130 100644 (file)
 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]);