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;
