According to the spec, the Run/stop bit in the Command register
should be cleared before setting the Enter Global Suspend Mode
bit.  It seems clear that this really means "before" and not "before
or at the same time as" (read the paragraph in question if you don't
believe me!).  Also, the windows VIA driver clears the RS bit and
delays for 5 milliseconds (!) before setting the EGSM bit.  Rather
than use a delay, the patch polls the HCHalted bit in the Status
register - the host controller sets it when it has stopped execution
of the schedule (first it needs to complete the current transaction).
At this point I have to admit that this patch makes no difference on
my machine - everything works fine with or without it.  So it is just
a matter of following the spec more closely.

--- linux/drivers/usb/uhci.c.2  2002-10-26 00:24:26.000000000 +0200
+++ linux/drivers/usb/uhci.c    2002-10-26 00:24:57.000000000 +0200
@@ -2502,9 +2502,18 @@
 static void suspend_hc(struct uhci *uhci)
 {
        unsigned int io_addr = uhci->io_addr;
+       unsigned int status;
 
        dbg("%x: suspend_hc", io_addr);
 
+       status = inw(io_addr + USBCMD);
+       outw(status & ~USBCMD_RS, io_addr + USBCMD);
+
+       /* Wait for the current transaction to finish */
+       status = inw(io_addr + USBSTS);
+       while (!(status & USBSTS_HCH))
+               status = inw(io_addr + USBSTS);
+
        outw(USBCMD_EGSM, io_addr + USBCMD);
 
        uhci->is_suspended = 1;
--- linux/drivers/usb/uhci.c.2	2002-10-26 00:24:26.000000000 +0200
+++ linux/drivers/usb/uhci.c	2002-10-26 00:24:57.000000000 +0200
@@ -2502,9 +2502,18 @@
 static void suspend_hc(struct uhci *uhci)
 {
 	unsigned int io_addr = uhci->io_addr;
+	unsigned int status;
 
 	dbg("%x: suspend_hc", io_addr);
 
+	status = inw(io_addr + USBCMD);
+	outw(status & ~USBCMD_RS, io_addr + USBCMD);
+
+	/* Wait for the current transaction to finish */
+	status = inw(io_addr + USBSTS);
+	while (!(status & USBSTS_HCH))
+		status = inw(io_addr + USBSTS);
+
 	outw(USBCMD_EGSM, io_addr + USBCMD);
 
 	uhci->is_suspended = 1;

Reply via email to