This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / usb / host / ehci-hub.c
index 6affe7d..4ff7f86 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))
-               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;
@@ -67,45 +60,48 @@ 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, 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);
@@ -122,7 +118,7 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
                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)
@@ -139,19 +135,12 @@ static int ehci_hub_resume (struct usb_hcd *hcd)
                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;
 }
 
@@ -211,10 +200,6 @@ 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(ehci->hcd.state))
-               return 0;
-
        /* init status to no-changes */
        buf [0] = 0;
        ports = HCS_N_PORTS (ehci->hcs_params);
@@ -251,7 +236,6 @@ 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;
 }
@@ -281,12 +265,9 @@ 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) */
-#endif
-       desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
+       desc->wHubCharacteristics = cpu_to_le16 (temp);
 }
 
 /*-------------------------------------------------------------------------*/