[PATCH] USB UHCI: remove the FSBR kernel timer

This patch (as558) removes from the UHCI driver a kernel timer used for
checking Full Speed Bandwidth Reclamation (FSBR).  The checking can be
done during normal root-hub polling; it doesn't need a separate timer.

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 0d5d254..0c02489 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -97,14 +97,9 @@
 /* to make sure it doesn't hog all of the bandwidth */
 #define DEPTH_INTERVAL 5
 
-static inline void restart_timer(struct uhci_hcd *uhci)
-{
-	mod_timer(&uhci->stall_timer, jiffies + msecs_to_jiffies(100));
-}
-
-#include "uhci-hub.c"
 #include "uhci-debug.c"
 #include "uhci-q.c"
+#include "uhci-hub.c"
 
 /*
  * Make sure the controller is completely inactive, unable to
@@ -160,7 +155,6 @@
 {
 	reset_hc(uhci);
 	uhci->hc_inaccessible = 1;
-	del_timer(&uhci->stall_timer);
 }
 
 /*
@@ -287,8 +281,11 @@
 	/* Enable resume-detect interrupts if they work.
 	 * Then enter Global Suspend mode, still configured.
 	 */
-	int_enable = (resume_detect_interrupts_are_broken(uhci) ?
-			0 : USBINTR_RESUME);
+	uhci->working_RD = 1;
+	int_enable = USBINTR_RESUME;
+	if (resume_detect_interrupts_are_broken(uhci)) {
+		uhci->working_RD = int_enable = 0;
+	}
 	outw(int_enable, uhci->io_addr + USBINTR);
 	outw(USBCMD_EGSM | USBCMD_CF, uhci->io_addr + USBCMD);
 	mb();
@@ -315,7 +312,6 @@
 
 	uhci->rh_state = new_state;
 	uhci->is_stopped = UHCI_IS_STOPPED;
-	del_timer(&uhci->stall_timer);
 	uhci_to_hcd(uhci)->poll_rh = !int_enable;
 
 	uhci_scan_schedule(uhci, NULL);
@@ -335,7 +331,6 @@
 	mb();
 	uhci->rh_state = UHCI_RH_RUNNING;
 	uhci_to_hcd(uhci)->poll_rh = 1;
-	restart_timer(uhci);
 }
 
 static void wakeup_rh(struct uhci_hcd *uhci)
@@ -374,20 +369,6 @@
 	mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
 }
 
-static void stall_callback(unsigned long _uhci)
-{
-	struct uhci_hcd *uhci = (struct uhci_hcd *) _uhci;
-	unsigned long flags;
-
-	spin_lock_irqsave(&uhci->lock, flags);
-	uhci_scan_schedule(uhci, NULL);
-	check_fsbr(uhci);
-
-	if (!uhci->is_stopped)
-		restart_timer(uhci);
-	spin_unlock_irqrestore(&uhci->lock, flags);
-}
-
 static irqreturn_t uhci_irq(struct usb_hcd *hcd, struct pt_regs *regs)
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
@@ -418,8 +399,10 @@
 					"host controller halted, "
 					"very bad!\n");
 				hc_died(uhci);
-				spin_unlock_irqrestore(&uhci->lock, flags);
-				return IRQ_HANDLED;
+
+				/* Force a callback in case there are
+				 * pending unlinks */
+				mod_timer(&hcd->rh_timer, jiffies);
 			}
 			spin_unlock_irqrestore(&uhci->lock, flags);
 		}
@@ -427,10 +410,11 @@
 
 	if (status & USBSTS_RD)
 		usb_hcd_poll_rh_status(hcd);
-
-	spin_lock_irqsave(&uhci->lock, flags);
-	uhci_scan_schedule(uhci, regs);
-	spin_unlock_irqrestore(&uhci->lock, flags);
+	else {
+		spin_lock_irqsave(&uhci->lock, flags);
+		uhci_scan_schedule(uhci, regs);
+		spin_unlock_irqrestore(&uhci->lock, flags);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -595,10 +579,6 @@
 
 	init_waitqueue_head(&uhci->waitqh);
 
-	init_timer(&uhci->stall_timer);
-	uhci->stall_timer.function = stall_callback;
-	uhci->stall_timer.data = (unsigned long) uhci;
-
 	uhci->fl = dma_alloc_coherent(uhci_dev(uhci), sizeof(*uhci->fl),
 			&dma_handle, 0);
 	if (!uhci->fl) {
@@ -745,11 +725,11 @@
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 
 	spin_lock_irq(&uhci->lock);
-	reset_hc(uhci);
+	if (!uhci->hc_inaccessible)
+		reset_hc(uhci);
 	uhci_scan_schedule(uhci, NULL);
 	spin_unlock_irq(&uhci->lock);
 
-	del_timer_sync(&uhci->stall_timer);
 	release_uhci(uhci);
 }
 
@@ -811,13 +791,12 @@
 	 */
 	pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, 0);
 	uhci->hc_inaccessible = 1;
+	hcd->poll_rh = 0;
 
 	/* FIXME: Enable non-PME# remote wakeup? */
 
 done:
 	spin_unlock_irq(&uhci->lock);
-	if (rc == 0)
-		del_timer_sync(&hcd->rh_timer);
 	return rc;
 }
 
@@ -850,8 +829,11 @@
 
 	spin_unlock_irq(&uhci->lock);
 
-	if (hcd->poll_rh)
+	if (!uhci->working_RD) {
+		/* Suspended root hub needs to be polled */
+		hcd->poll_rh = 1;
 		usb_hcd_poll_rh_status(hcd);
+	}
 	return 0;
 }
 #endif
diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
index bf9c5f9..282f40b 100644
--- a/drivers/usb/host/uhci-hcd.h
+++ b/drivers/usb/host/uhci-hcd.h
@@ -345,9 +345,6 @@
 
 /*
  * This describes the full uhci information.
- *
- * Note how the "proper" USB information is just
- * a subset of what the full implementation needs.
  */
 struct uhci_hcd {
 
@@ -360,8 +357,6 @@
 	struct dma_pool *qh_pool;
 	struct dma_pool *td_pool;
 
-	struct usb_bus *bus;
-
 	struct uhci_td *term_td;	/* Terminating TD, see UHCI bug */
 	struct uhci_qh *skelqh[UHCI_NUM_SKELQH];	/* Skeleton QH's */
 
@@ -380,6 +375,8 @@
 	unsigned int scan_in_progress:1;	/* Schedule scan is running */
 	unsigned int need_rescan:1;		/* Redo the schedule scan */
 	unsigned int hc_inaccessible:1;		/* HC is suspended or dead */
+	unsigned int working_RD:1;		/* Suspended root hub doesn't
+						   need to be polled */
 
 	/* Support for port suspend/resume/reset */
 	unsigned long port_c_suspend;		/* Bit-arrays of ports */
@@ -405,9 +402,7 @@
 	/* List of URB's awaiting completion callback */
 	struct list_head complete_list;		/* P: uhci->lock */
 
-	int rh_numports;
-
-	struct timer_list stall_timer;
+	int rh_numports;			/* Number of root-hub ports */
 
 	wait_queue_head_t waitqh;		/* endpoint_disable waiters */
 };
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index 4eace2b..a71e48a 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -145,15 +145,16 @@
 {
 	struct uhci_hcd *uhci = hcd_to_uhci(hcd);
 	unsigned long flags;
-	int status;
+	int status = 0;
 
 	spin_lock_irqsave(&uhci->lock, flags);
-	if (uhci->hc_inaccessible) {
-		status = 0;
-		goto done;
-	}
 
+	uhci_scan_schedule(uhci, NULL);
+	if (uhci->hc_inaccessible)
+		goto done;
+	check_fsbr(uhci);
 	uhci_check_ports(uhci);
+
 	status = get_hub_status_data(uhci, buf);
 
 	switch (uhci->rh_state) {
diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
index bbb36cd..ea0d168 100644
--- a/drivers/usb/host/uhci-q.c
+++ b/drivers/usb/host/uhci-q.c
@@ -33,7 +33,7 @@
 static inline void uhci_set_next_interrupt(struct uhci_hcd *uhci)
 {
 	if (uhci->is_stopped)
-		mod_timer(&uhci->stall_timer, jiffies);
+		mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies);
 	uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); 
 }