This patch updates EHCI suspend/resume so that its essential
components work on a few different implementations:

  - make root hub suspend/resume work
  - make remote wakeup work (given CONFIG_USB_SUSPEND patch)
  - separate root hub suspend/resume from PCI suspend/resume
  - say if controller supports remote wakeup (on this system)
  - sysfs register dump unavailable if controller is suspended

Plus a handful of minor cleanups.  Please merge, along with the
"hcd-0506.patch" I sent last week.

Tested by modifying sysfs power/state files, since ACPI doesn't
work on this system (so I can't test system suspend/resume):

 - For root hub(*) ... suspend/resume works, also remote wakeup
 - PCI controller ... suspend/resume works, remote wakeup
   signals PME# (according to "lspci -vv"), but that's ignored
   on my test sytem

Regardless of whether USB was active, "echo 1 > /proc/acpi/sleep"
produced a system that wouldn't resume, and the same result
came from "echo standby > /sys/power/state".  So that's about
as far as I can take this testing for now.

- Dave

(*) Doing this relies on the CONFIG_USB_SUSPEND patch.  Otherwise
    no USB devices respond to sysfs power/state updates.  The
    PCI suspend/resume is a superset of this.


--- bk2/xu26/drivers/usb/host/ehci-dbg.c        2004-04-06 19:41:24.000000000 -0700
+++ gadget-2.6/drivers/usb/host/ehci-dbg.c      2004-05-07 12:48:33.000000000 -0700
@@ -170,6 +170,20 @@
                itd->index[6], itd->index[7]);
 }
 
+static void __attribute__((__unused__))
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd) 
+{
+       ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
+               label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
+       ehci_dbg (ehci,
+               "  addr %08x sched %04x result %08x buf %08x %08x\n", 
+               le32_to_cpu(sitd->hw_fullspeed_ep),
+               le32_to_cpu(sitd->hw_uframe),
+               le32_to_cpu(sitd->hw_results),
+               le32_to_cpu(sitd->hw_buf [0]),
+               le32_to_cpu(sitd->hw_buf [1]));
+}
+
 static int __attribute__((__unused__))
 dbg_status_buf (char *buf, unsigned len, char *label, u32 status)
 {
@@ -625,11 +639,20 @@
 
        spin_lock_irqsave (&ehci->lock, flags);
 
+       if (bus->controller->power.power_state) {
+               size = scnprintf (next, size,
+                       "bus %s, device %s (driver " DRIVER_VERSION ")\n"
+                       "SUSPENDED (no register access)\n",
+                       hcd->self.controller->bus->name,
+                       hcd->self.controller->bus_id);
+               goto done;
+       }
+
        /* Capability Registers */
        i = HC_VERSION(readl (&ehci->caps->hc_capbase));
        temp = scnprintf (next, size,
-               "bus %s device %s\n"
-               "EHCI %x.%02x, hcd state %d (driver " DRIVER_VERSION ")\n",
+               "bus %s, device %s (driver " DRIVER_VERSION ")\n"
+               "EHCI %x.%02x, hcd state %d\n",
                hcd->self.controller->bus->name,
                hcd->self.controller->bus_id,
                i >> 8, i & 0x0ff, ehci->hcd.state);
@@ -672,7 +695,7 @@
        next += temp;
 
        for (i = 0; i < HCS_N_PORTS (ehci->hcs_params); i++) {
-               temp = dbg_port_buf (scratch, sizeof scratch, label, i,
+               temp = dbg_port_buf (scratch, sizeof scratch, label, i + 1,
                                readl (&ehci->regs->port_status [i]));
                temp = scnprintf (next, size, fmt, temp, scratch);
                size -= temp;
@@ -701,6 +724,7 @@
        next += temp;
 #endif
 
+done:
        spin_unlock_irqrestore (&ehci->lock, flags);
 
        return PAGE_SIZE - size;
--- bk2/xu26/drivers/usb/host/ehci.h    2004-04-27 19:18:14.000000000 -0700
+++ gadget-2.6/drivers/usb/host/ehci.h  2004-05-07 12:49:08.000000000 -0700
@@ -84,6 +84,8 @@
        struct notifier_block   reboot_notifier;
        unsigned long           actions;
        unsigned                stamp;
+       unsigned long           next_statechange;
+       u32                     command;
 
        unsigned                is_arc_rh_tt:1; /* ARC roothub with TT */
 
@@ -99,8 +101,6 @@
 /* unwrap an HCD pointer to get an EHCI_HCD pointer */ 
 #define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd)
 
-/* NOTE:  urb->transfer_flags expected to not use this bit !!! */
-#define EHCI_STATE_UNLINK      0x8000          /* urb being unlinked */
 
 enum ehci_timer_action {
        TIMER_IO_WATCHDOG,
@@ -221,7 +221,7 @@
        u32             segment;        /* address bits 63:32 if needed */
        /* PERIODICLISTBASE: offset 0x14 */
        u32             frame_list;     /* points to periodic list */
-       /* ASYNCICLISTADDR: offset 0x18 */
+       /* ASYNCLISTADDR: offset 0x18 */
        u32             async_next;     /* address of next async queue head */
 
        u32             reserved [9];
@@ -237,7 +237,10 @@
 #define PORT_WKDISC_E  (1<<21)         /* wake on disconnect (enable) */
 #define PORT_WKCONN_E  (1<<20)         /* wake on connect (enable) */
 /* 19:16 for port testing */
-/* 15:14 for using port indicator leds (if HCS_INDICATOR allows) */
+#define PORT_LED_OFF   (0<<14)
+#define PORT_LED_AMBER (1<<14)
+#define PORT_LED_GREEN (2<<14)
+#define PORT_LED_MASK  (3<<14)
 #define PORT_OWNER     (1<<13)         /* true: companion hc owns this port */
 #define PORT_POWER     (1<<12)         /* true: has power (see PPC) */
 #define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */
@@ -593,6 +595,14 @@
 
 /*-------------------------------------------------------------------------*/
 
+#define        MSEC_TO_JIFFIES(msec) ((HZ * (msec) + 999) / 1000)
+
+static inline void msec_delay(int msec)
+{
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(MSEC_TO_JIFFIES(msec));
+}
+
 #ifndef DEBUG
 #define STUB_DEBUG_FILES
 #endif /* DEBUG */
--- bk2/xu26/drivers/usb/host/ehci-hcd.c        2004-04-27 19:18:14.000000000 -0700
+++ gadget-2.6/drivers/usb/host/ehci-hcd.c      2004-05-10 12:38:11.904672256 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2002 by David Brownell
+ * Copyright (c) 2000-2004 by David Brownell
  * 
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -69,6 +69,7 @@
  *
  * HISTORY:
  *
+ * 2004-05-10 Root hub and PCI suspend/resume support; remote wakeup. (db)
  * 2004-02-24 Replace pci_* with generic dma_* API calls ([EMAIL PROTECTED])
  * 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka,
  *     <[EMAIL PROTECTED]>, updates by DB).
@@ -96,7 +97,7 @@
  * 2001-June   Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "2003-Dec-29"
+#define DRIVER_VERSION "2004-May-10"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
@@ -128,7 +129,7 @@
 module_param (log2_irq_thresh, int, S_IRUGO);
 MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
 
-#define        INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
+#define        INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
 
 /*-------------------------------------------------------------------------*/
 
@@ -201,6 +202,7 @@
        dbg_cmd (ehci, "reset", command);
        writel (command, &ehci->regs->command);
        ehci->hcd.state = USB_STATE_HALT;
+       ehci->next_statechange = jiffies;
        return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
 }
 
@@ -241,6 +243,8 @@
 
 /*-------------------------------------------------------------------------*/
 
+static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
+
 #include "ehci-hub.c"
 #include "ehci-mem.c"
 #include "ehci-q.c"
@@ -248,8 +252,6 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
-
 static void ehci_watchdog (unsigned long param)
 {
        struct ehci_hcd         *ehci = (struct ehci_hcd *) param;
@@ -428,12 +430,17 @@
 #ifdef CONFIG_PCI
        if (hcd->self.controller->bus == &pci_bus_type) {
                struct pci_dev          *pdev;
+               u16                     port_wake;
 
                pdev = to_pci_dev(hcd->self.controller);
 
                /* Serial Bus Release Number is at PCI 0x60 offset */
                pci_read_config_byte(pdev, 0x60, &sbrn);
 
+               /* port wake capability, reported by boot firmware */
+               pci_read_config_word(pdev, 0x62, &port_wake);
+               hcd->can_wakeup = (port_wake & 1) != 0;
+
                /* help hc dma work well with cachelines */
                pci_set_mwi (pdev);
 
@@ -615,41 +622,26 @@
 
 /* suspend/resume, section 4.3 */
 
+/* These routines rely on PCI to handle powerdown and wakeup, and
+ * transceivers that don't need any software attention to set up
+ * the right sort of wakeup.  
+ */
+
 static int ehci_suspend (struct usb_hcd *hcd, u32 state)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
-       int                     ports;
-       int                     i;
-
-       ehci_dbg (ehci, "suspend to %d\n", state);
 
-       ports = HCS_N_PORTS (ehci->hcs_params);
+       while (time_before (jiffies, ehci->next_statechange))
+               msec_delay (100);
 
-       // FIXME:  This assumes what's probably a D3 level suspend...
-
-       // FIXME:  usb wakeup events on this bus should resume the machine.
-       // pci config register PORTWAKECAP controls which ports can do it;
-       // bios may have initted the register...
-
-       /* suspend each port, then stop the hc */
-       for (i = 0; i < ports; i++) {
-               int     temp = readl (&ehci->regs->port_status [i]);
-
-               if ((temp & PORT_PE) == 0
-                               || (temp & PORT_OWNER) != 0)
-                       continue;
-               ehci_dbg (ehci, "suspend port %d", i);
-               temp |= PORT_SUSPEND;
-               writel (temp, &ehci->regs->port_status [i]);
-       }
-
-       if (hcd->state == USB_STATE_RUNNING)
-               ehci_ready (ehci);
-       writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
-
-// save pci FLADJ value
+#ifdef CONFIG_USB_SUSPEND
+       (void) usb_suspend_device (hcd->self.root_hub);
+#else
+       /* FIXME lock root hub */
+       (void) ehci_hub_suspend (hcd);
+#endif
 
-       /* who tells PCI to reduce power consumption? */
+       // save (PCI) FLADJ in case of Vaux power loss
 
        return 0;
 }
@@ -657,40 +649,22 @@
 static int ehci_resume (struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
-       int                     ports;
-       int                     i;
+       int                     retval;
 
-       ehci_dbg (ehci, "resume\n");
+       // maybe restore (PCI) FLADJ
 
-       ports = HCS_N_PORTS (ehci->hcs_params);
+       while (time_before (jiffies, ehci->next_statechange))
+               msec_delay (100);
 
-       // FIXME:  if controller didn't retain state,
-       // return and let generic code clean it up
-       // test configured_flag ?
-
-       /* resume HC and each port */
-// restore pci FLADJ value
-       // khubd and drivers will set HC running, if needed;
-       hcd->state = USB_STATE_RUNNING;
-       // FIXME Philips/Intel/... etc don't really have a "READY"
-       // state ... turn on CMD_RUN too
-       for (i = 0; i < ports; i++) {
-               int     temp = readl (&ehci->regs->port_status [i]);
-
-               if ((temp & PORT_PE) == 0
-                               || (temp & PORT_SUSPEND) != 0)
-                       continue;
-               ehci_dbg (ehci, "resume port %d", i);
-               temp |= PORT_RESUME;
-               writel (temp, &ehci->regs->port_status [i]);
-               readl (&ehci->regs->command);   /* unblock posted writes */
-
-               wait_ms (20);
-               temp &= ~PORT_RESUME;
-               writel (temp, &ehci->regs->port_status [i]);
-       }
-       readl (&ehci->regs->command);   /* unblock posted writes */
-       return 0;
+#ifdef CONFIG_USB_SUSPEND
+       retval = usb_resume_device (hcd->self.root_hub);
+#else
+       /* FIXME lock root hub */
+       retval = ehci_hub_resume (hcd);
+#endif
+       if (retval == 0)
+               hcd->self.controller->power.power_state = 0;
+       return retval;
 }
 
 #endif
@@ -752,7 +726,7 @@
        bh = 0;
 
 #ifdef EHCI_VERBOSE_DEBUG
-       /* unrequested/ignored: Port Change Detect, Frame List Rollover */
+       /* unrequested/ignored: Frame List Rollover */
        dbg_status (ehci, "irq", status);
 #endif
 
@@ -774,6 +748,34 @@
                bh = 1;
        }
 
+       /* remote wakeup [4.3.1] */
+       if ((status & STS_PCD) && ehci->hcd.remote_wakeup) {
+               unsigned        i = HCS_N_PORTS (ehci->hcs_params);
+
+               /* resume root hub? */
+               status = readl (&ehci->regs->command);
+               if (!(status & CMD_RUN))
+                       writel (status | CMD_RUN, &ehci->regs->command);
+
+               while (i--) {
+                       status = readl (&ehci->regs->port_status [i]);
+                       if (status & PORT_OWNER)
+                               continue;
+                       if (!(status & PORT_RESUME)
+                                       || ehci->reset_done [i] != 0)
+                               continue;
+
+                       /* start 20 msec resume signaling from this port,
+                        * and make khubd collect PORT_STAT_C_SUSPEND to
+                        * stop that signaling.
+                        */
+                       ehci->reset_done [i] = jiffies + MSEC_TO_JIFFIES (20);
+                       mod_timer (&ehci->hcd.rh_timer,
+                                       ehci->reset_done [i] + 1);
+                       ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
+               }
+       }
+
        /* PCI errors [4.15.2.4] */
        if (unlikely ((status & STS_FATAL) != 0)) {
                ehci_err (ehci, "fatal error\n");
@@ -814,7 +816,6 @@
        struct ehci_hcd         *ehci = hcd_to_ehci (hcd);
        struct list_head        qtd_list;
 
-       urb->transfer_flags &= ~EHCI_STATE_UNLINK;
        INIT_LIST_HEAD (&qtd_list);
 
        switch (usb_pipetype (urb->pipe)) {
@@ -914,7 +915,6 @@
 
                // wait till next completion, do it then.
                // completion irqs can wait up to 1024 msec,
-               urb->transfer_flags |= EHCI_STATE_UNLINK;
                break;
        }
        spin_unlock_irqrestore (&ehci->lock, flags);
--- bk2/xu26/drivers/usb/host/ehci-hub.c        2004-04-23 19:01:38.000000000 -0700
+++ gadget-2.6/drivers/usb/host/ehci-hub.c      2004-05-10 12:40:19.763234800 -0700
@@ -28,6 +28,131 @@
 
 /*-------------------------------------------------------------------------*/
 
+#ifdef CONFIG_PM
+
+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;
+
+       port = HCS_N_PORTS (ehci->hcs_params);
+       spin_lock_irq (&ehci->lock);
+
+       /* suspend any active/unsuspended ports, maybe allow wakeup */
+       while (port--) {
+               u32     t1 = readl (&ehci->regs->port_status [port]);
+               u32     t2 = t1;
+
+               if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
+                       t2 |= PORT_SUSPEND;
+               if (ehci->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]);
+               }
+       }
+
+       /* 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, 0);
+       (void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000);
+
+       root->dev.power.power_state = 3;
+       ehci->next_statechange = jiffies + MSEC_TO_JIFFIES(10);
+       spin_unlock_irq (&ehci->lock);
+       return status;
+}
+
+
+/* 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;
+
+       if (!root->dev.power.power_state)
+               return 0;
+       if (time_before (jiffies, ehci->next_statechange))
+               return -EAGAIN;
+
+       /* re-init operational registers in case we lost power */
+       if (readl (&ehci->regs->intr_enable) == 0) {
+               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);
+               /* FIXME will this work even (pci) vAUX was lost? */
+       }
+
+       /* restore CMD_RUN, framelist size, and irq threshold */
+       writel (ehci->command, &ehci->regs->command);
+
+       /* take ports out of suspend */
+       i = HCS_N_PORTS (ehci->hcs_params);
+       while (i--) {
+               temp = readl (&ehci->regs->port_status [i]);
+               temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
+               if (temp & PORT_SUSPEND) {
+                       ehci->reset_done [i] = jiffies + MSEC_TO_JIFFIES (20);
+                       temp |= PORT_RESUME;
+               }
+               writel (temp, &ehci->regs->port_status [i]);
+       }
+       i = HCS_N_PORTS (ehci->hcs_params);
+       wait_ms (20);
+       while (i--) {
+               temp = readl (&ehci->regs->port_status [i]);
+               if ((temp & PORT_SUSPEND) == 0)
+                       continue;
+               temp &= ~PORT_RESUME;
+               writel (temp, &ehci->regs->port_status [i]);
+               ehci_vdbg (ehci, "resumed port %d\n", i + 1);
+       }
+       (void) readl (&ehci->regs->command);
+
+       /* maybe re-activate the schedule(s) */
+       temp = 0;
+       if (ehci->async->qh_next.qh)
+               temp |= CMD_ASE;
+       if (ehci->periodic_sched)
+               temp |= CMD_PSE;
+       if (temp)
+               writel (ehci->command | temp, &ehci->regs->command);
+
+       root->dev.power.power_state = 0;
+       ehci->next_statechange = jiffies + MSEC_TO_JIFFIES(5);
+       ehci->hcd.state = USB_STATE_RUNNING;
+       return 0;
+}
+
+#else
+
+#define ehci_hub_suspend       0
+#define ehci_hub_resume                0
+
+#endif /* CONFIG_PM */
+
+/*-------------------------------------------------------------------------*/
+
 static int check_reset_complete (
        struct ehci_hcd *ehci,
        int             index,
@@ -99,7 +224,11 @@
                }
                if (!(temp & PORT_CONNECT))
                        ehci->reset_done [i] = 0;
-               if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
+               if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0
+                               // PORT_STAT_C_SUSPEND?
+                               || ((temp & PORT_RESUME) != 0
+                                       && time_after (jiffies,
+                                               ehci->reset_done [i]))) {
                        if (i < 7)
                            buf [0] |= 1 << (i + 1);
                        else
@@ -143,6 +272,8 @@
 
 /*-------------------------------------------------------------------------*/
 
+#define        PORT_WAKE_BITS  (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+
 static int ehci_hub_control (
        struct usb_hcd  *hcd,
        u16             typeReq,
@@ -194,8 +325,20 @@
                                &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_SUSPEND:
+                       if (temp & PORT_RESET)
+                               goto error;
+                       if (temp & PORT_SUSPEND) {
+                               if ((temp & PORT_PE) == 0)
+                                       goto error;
+                               /* resume signaling for 20 msec */
+                               writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
+                                       &ehci->regs->port_status [wIndex]);
+                               ehci->reset_done [wIndex] = jiffies
+                                               + MSEC_TO_JIFFIES (20);
+                       }
+                       break;
                case USB_PORT_FEAT_C_SUSPEND:
-                       /* ? */
+                       /* we auto-clear this feature */
                        break;
                case USB_PORT_FEAT_POWER:
                        if (HCS_PPC (ehci->hcs_params))
@@ -239,15 +382,37 @@
                        status |= 1 << USB_PORT_FEAT_C_CONNECTION;
                if (temp & PORT_PEC)
                        status |= 1 << USB_PORT_FEAT_C_ENABLE;
-               // USB_PORT_FEAT_C_SUSPEND
                if (temp & PORT_OCC)
                        status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
 
+               /* whoever resumes must GetPortStatus to complete it!! */
+               if ((temp & PORT_RESUME)
+                               && time_after (jiffies,
+                                       ehci->reset_done [wIndex])) {
+                       status |= 1 << USB_PORT_FEAT_C_SUSPEND;
+                       ehci->reset_done [wIndex] = 0;
+
+                       /* stop resume signaling */
+                       temp = readl (&ehci->regs->port_status [wIndex]);
+                       writel (temp & ~PORT_RESUME,
+                               &ehci->regs->port_status [wIndex]);
+                       retval = handshake (
+                                       &ehci->regs->port_status [wIndex],
+                                       PORT_RESUME, 0, 2000 /* 2msec */);
+                       if (retval != 0) {
+                               ehci_err (ehci, "port %d resume error %d\n",
+                                       wIndex + 1, retval);
+                               goto error;
+                       }
+                       temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
+               }
+
                /* whoever resets must GetPortStatus to complete it!! */
                if ((temp & PORT_RESET)
                                && time_after (jiffies,
                                        ehci->reset_done [wIndex])) {
                        status |= 1 << USB_PORT_FEAT_C_RESET;
+                       ehci->reset_done [wIndex] = 0;
 
                        /* force reset to complete */
                        writel (temp & ~PORT_RESET,
@@ -275,7 +440,7 @@
                        }
                        if (temp & PORT_PE)
                                status |= 1 << USB_PORT_FEAT_ENABLE;
-                       if (temp & PORT_SUSPEND)
+                       if (temp & (PORT_SUSPEND|PORT_RESUME))
                                status |= 1 << USB_PORT_FEAT_SUSPEND;
                        if (temp & PORT_OC)
                                status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
@@ -312,6 +477,11 @@
 
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
+                       if ((temp & PORT_PE) == 0
+                                       || (temp & PORT_RESET) != 0)
+                               goto error;
+                       if (ehci->hcd.remote_wakeup)
+                               temp |= PORT_WAKE_BITS;
                        writel (temp | PORT_SUSPEND,
                                &ehci->regs->port_status [wIndex]);
                        break;
@@ -321,6 +491,8 @@
                                        &ehci->regs->port_status [wIndex]);
                        break;
                case USB_PORT_FEAT_RESET:
+                       if (temp & PORT_RESUME)
+                               goto error;
                        /* line status bits may report this as low speed,
                         * which can be fine if this root hub has a
                         * transaction translator built in.
@@ -342,7 +514,7 @@
                                 * usb 2.0 spec says 50 ms resets on root
                                 */
                                ehci->reset_done [wIndex] = jiffies
-                                       + ((50 /* msec */ * HZ) / 1000);
+                                               + MSEC_TO_JIFFIES (50);
                        }
                        writel (temp, &ehci->regs->port_status [wIndex]);
                        break;

Reply via email to