[PATCH] UHCI: use integer-sized frame numbers

This patch (as687) changes uhci-hcd to keep track of frame numbers as
full-sized integers rather than 11-bit values.  This makes them a lot
easier to handle and makes it possible to schedule beyond a 2-second
window, should anyone ever want to do so.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 395402e..5e75ad6d 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -13,7 +13,7 @@
  * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
  *               support from usb-ohci.c by Adam Richter, adam@yggdrasil.com).
  * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2005 Alan Stern, stern@rowland.harvard.edu
+ * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu
  *
  * Intel documents this fairly well, and as far as I know there
  * are no royalties or anything like that, but even so there are
@@ -31,7 +31,6 @@
 #include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/errno.h>
 #include <linux/unistd.h>
 #include <linux/interrupt.h>
@@ -146,7 +145,8 @@
 	outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD);
 
 	/* Set the current frame number */
-	outw(uhci->frame_number, uhci->io_addr + USBFRNUM);
+	outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
+			uhci->io_addr + USBFRNUM);
 
 	/* Mark controller as not halted before we enable interrupts */
 	uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
@@ -239,7 +239,6 @@
 		dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n");
 
 	uhci_get_current_frame_number(uhci);
-	smp_wmb();
 
 	uhci->rh_state = new_state;
 	uhci->is_stopped = UHCI_IS_STOPPED;
@@ -253,7 +252,6 @@
 {
 	uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
 	uhci->is_stopped = 0;
-	smp_wmb();
 
 	/* Mark it configured and running with a 64-byte max packet.
 	 * All interrupts are enabled, even though RESUME won't do anything.
@@ -360,12 +358,21 @@
 
 /*
  * Store the current frame number in uhci->frame_number if the controller
- * is runnning
+ * is runnning.  Expand from 11 bits (of which we use only 10) to a
+ * full-sized integer.
+ *
+ * Like many other parts of the driver, this code relies on being polled
+ * more than once per second as long as the controller is running.
  */
 static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
 {
-	if (!uhci->is_stopped)
-		uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
+	if (!uhci->is_stopped) {
+		unsigned delta;
+
+		delta = (inw(uhci->io_addr + USBFRNUM) - uhci->frame_number) &
+				(UHCI_NUMFRAMES - 1);
+		uhci->frame_number += delta;
+	}
 }
 
 /*
@@ -798,18 +805,15 @@
 static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-	unsigned long flags;
-	int is_stopped;
-	int frame_number;
+	unsigned frame_number;
+	unsigned delta;
 
 	/* Minimize latency by avoiding the spinlock */
-	local_irq_save(flags);
-	is_stopped = uhci->is_stopped;
-	smp_rmb();
-	frame_number = (is_stopped ? uhci->frame_number :
-			inw(uhci->io_addr + USBFRNUM));
-	local_irq_restore(flags);
-	return frame_number;
+	frame_number = uhci->frame_number;
+	barrier();
+	delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) &
+			(UHCI_NUMFRAMES - 1);
+	return frame_number + delta;
 }
 
 static const char hcd_name[] = "uhci_hcd";