ChangeSet 1.1608.84.7, 2004/03/08 15:07:52-08:00, stern@rowland.harvard.edu

[PATCH] USB UHCI: restore more state following PM resume

Some systems don't save the internal state of the UHCI registers across a
PM suspend/resume cycle very well.  This patch saves & restores the
Frame Number and the Framelist Base Address registers (in addition to the
Interrupt Enable register, which was added separately in a recent patch.)


 drivers/usb/host/uhci-hcd.c |   14 ++++++++++----
 drivers/usb/host/uhci-hcd.h |    1 +
 2 files changed, 11 insertions(+), 4 deletions(-)


diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	Tue Mar 16 15:03:34 2004
+++ b/drivers/usb/host/uhci-hcd.c	Tue Mar 16 15:03:34 2004
@@ -2444,9 +2444,11 @@
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
 	/* Don't try to suspend broken motherboards, reset instead */
-	if (suspend_allowed(uhci))
+	if (suspend_allowed(uhci)) {
 		suspend_hc(uhci);
-	else
+		uhci->saved_framenumber =
+				inw(uhci->io_addr + USBFRNUM) & 0x3ff;
+	} else
 		reset_hc(uhci);
 	return 0;
 }
@@ -2460,9 +2462,13 @@
 	if (uhci->state == UHCI_SUSPENDED) {
 
 		/*
-		 * Some systems clear the Interrupt Enable register during
-		 * PM suspend/resume, so reinitialize it.
+		 * Some systems don't maintain the UHCI register values
+		 * during a PM suspend/resume cycle, so reinitialize
+		 * the Frame Number, the Framelist Base Address, and the
+		 * Interrupt Enable registers.
 		 */
+		outw(uhci->saved_framenumber, uhci->io_addr + USBFRNUM);
+		outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD);
 		outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC |
 				USBINTR_SP, uhci->io_addr + USBINTR);
 		uhci->resume_detect = 1;
diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
--- a/drivers/usb/host/uhci-hcd.h	Tue Mar 16 15:03:34 2004
+++ b/drivers/usb/host/uhci-hcd.h	Tue Mar 16 15:03:34 2004
@@ -350,6 +350,7 @@
 	enum uhci_state state;			/* FIXME: needs a spinlock */
 	unsigned long state_end;		/* Time of next transition */
 	int resume_detect;			/* Need a Global Resume */
+	unsigned int saved_framenumber;		/* Save during PM suspend */
 
 	/* Main list of URB's currently controlled by this HC */
 	spinlock_t urb_list_lock;
