ChangeSet 1.1587.3.46, 2004/05/11 15:33:38-07:00, david-b@pacbell.net

[PATCH] USB: EHCI power management updates

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.


 drivers/usb/host/ehci-dbg.c |   30 ++++++-
 drivers/usb/host/ehci-hcd.c |  136 ++++++++++++++++----------------
 drivers/usb/host/ehci-hub.c |  182 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/usb/host/ehci.h     |   19 +++-
 4 files changed, 287 insertions(+), 80 deletions(-)


diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
--- a/drivers/usb/host/ehci-dbg.c	Fri May 14 15:29:01 2004
+++ b/drivers/usb/host/ehci-dbg.c	Fri May 14 15:29:01 2004
@@ -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;
diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
--- a/drivers/usb/host/ehci-hcd.c	Fri May 14 15:29:01 2004
+++ b/drivers/usb/host/ehci-hcd.c	Fri May 14 15:29:01 2004
@@ -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 (dsaxena@plexity.net)
  * 2003-12-29 Rewritten high speed iso transfer support (by Michal Sojka,
  *	<sojkam@centrum.cz>, 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);
diff -Nru a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
--- a/drivers/usb/host/ehci-hub.c	Fri May 14 15:29:01 2004
+++ b/drivers/usb/host/ehci-hub.c	Fri May 14 15:29:01 2004
@@ -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;
diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
--- a/drivers/usb/host/ehci.h	Fri May 14 15:29:01 2004
+++ b/drivers/usb/host/ehci.h	Fri May 14 15:29:01 2004
@@ -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 */
@@ -592,6 +595,14 @@
 #endif
 
 /*-------------------------------------------------------------------------*/
+
+#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
