Flynn:
As promised, here is a patch for 2.6.17-rc1. It should restore the old
behavior and power-consumption for your UHCI controllers. Please try it
out and let me know how well it works for you.
Alan Stern
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-q.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-q.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-q.c
@@ -13,7 +13,7 @@
* (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface
* support from usb-ohci.c by Adam Richter, [EMAIL PROTECTED]).
* (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c)
- * (C) Copyright 2004-2005 Alan Stern, [EMAIL PROTECTED]
+ * (C) Copyright 2004-2006 Alan Stern, [EMAIL PROTECTED]
*/
static void uhci_free_pending_tds(struct uhci_hcd *uhci);
@@ -161,6 +161,7 @@ static struct uhci_qh *uhci_alloc_qh(str
if (!qh)
return NULL;
+ memset(qh, 0, sizeof(*qh));
qh->dma_handle = dma_handle;
qh->element = UHCI_PTR_TERM;
@@ -182,7 +183,6 @@ static struct uhci_qh *uhci_alloc_qh(str
} else { /* Skeleton QH */
qh->state = QH_STATE_ACTIVE;
- qh->udev = NULL;
}
return qh;
}
@@ -207,7 +207,6 @@ static void uhci_free_qh(struct uhci_hcd
static void uhci_save_toggle(struct uhci_qh *qh, struct urb *urb)
{
struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
- struct uhci_td *td;
/* If the QH element pointer is UHCI_PTR_TERM then then currently
* executing URB has already been unlinked, so this one isn't it. */
@@ -221,16 +220,8 @@ static void uhci_save_toggle(struct uhci
usb_pipetype(urb->pipe) == PIPE_INTERRUPT))
return;
- /* Find the first active TD; that's the device's toggle state */
- list_for_each_entry(td, &urbp->td_list, list) {
- if (td_status(td) & TD_CTRL_ACTIVE) {
- qh->needs_fixup = 1;
- qh->initial_toggle = uhci_toggle(td_token(td));
- return;
- }
- }
-
- WARN_ON(1);
+ qh->needs_fixup = 1;
+ qh->initial_toggle = uhci_toggle(td_token(qh->next_td));
}
/*
@@ -302,9 +293,13 @@ static void uhci_activate_qh(struct uhci
struct uhci_td *td = list_entry(urbp->td_list.next,
struct uhci_td, list);
+ qh->next_td = td;
qh->element = cpu_to_le32(td->dma_handle);
}
+ qh->advance_jiffies = jiffies;
+ qh->wait_expired = 0;
+
if (qh->state == QH_STATE_ACTIVE)
return;
qh->state = QH_STATE_ACTIVE;
@@ -439,28 +434,48 @@ static void uhci_free_urb_priv(struct uh
kmem_cache_free(uhci_up_cachep, urbp);
}
-static void uhci_inc_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+
+/*
+ * Full-Speed Bandwidth Reclamation (FSBR).
+ * We turn on FSBR whenever a queue that wants it is advancing,
+ * and leave it on for a short time thereafter.
+ * Invariant: If uhci->fsbr_wanted is true then FSBR is on.
+ */
+static inline void uhci_fsbr_on(struct uhci_hcd *uhci)
{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ uhci->fsbr_is_on = 1;
+ uhci->skel_term_qh->link = cpu_to_le32(
+ uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
+}
- if ((!(urb->transfer_flags & URB_NO_FSBR)) && !urbp->fsbr) {
+static inline void uhci_fsbr_off(struct uhci_hcd *uhci)
+{
+ uhci->fsbr_is_on = 0;
+ uhci->skel_term_qh->link = UHCI_PTR_TERM;
+}
+
+static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+{
+ struct urb_priv *urbp = (struct urb_priv *) urb->hcpriv;
+
+ if (!(urb->transfer_flags & URB_NO_FSBR))
urbp->fsbr = 1;
- if (!uhci->fsbr++ && !uhci->fsbrtimeout)
- uhci->skel_term_qh->link =
cpu_to_le32(uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH;
- }
}
-static void uhci_dec_fsbr(struct uhci_hcd *uhci, struct urb *urb)
+static void uhci_qh_wants_fsbr(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
- struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+ struct urb_priv *urbp =
+ list_entry(qh->queue.next, struct urb_priv, node);
- if ((!(urb->transfer_flags & URB_NO_FSBR)) && urbp->fsbr) {
- urbp->fsbr = 0;
- if (!--uhci->fsbr)
- uhci->fsbrtimeout = jiffies + FSBR_DELAY;
+ if (urbp->fsbr) {
+ uhci->fsbr_wanted = 1;
+ uhci->fsbr_jiffies = jiffies;
+ if (!uhci->fsbr_is_on)
+ uhci_fsbr_on(uhci);
}
}
+
/*
* Map status to standard result codes
*
@@ -606,7 +621,7 @@ static int uhci_submit_control(struct uh
qh->skel = uhci->skel_ls_control_qh;
else {
qh->skel = uhci->skel_fs_control_qh;
- uhci_inc_fsbr(uhci, urb);
+ uhci_add_fsbr(uhci, urb);
}
return 0;
@@ -633,6 +648,7 @@ static int usb_control_retrigger_status(
urbp->short_transfer = 1;
td = list_entry(urbp->td_list.prev, struct uhci_td, list);
+ urbp->qh->next_td = td;
urbp->qh->element = cpu_to_le32(td->dma_handle);
return -EINPROGRESS;
@@ -661,8 +677,10 @@ static int uhci_result_control(struct uh
/* The first TD is the SETUP stage, check the status, but skip */
/* the count */
status = uhci_status_bits(td_status(td));
- if (status & TD_CTRL_ACTIVE)
+ if (status & TD_CTRL_ACTIVE) {
+ urbp->qh->next_td = td;
return -EINPROGRESS;
+ }
if (status)
goto td_error;
@@ -677,8 +695,10 @@ static int uhci_result_control(struct uh
ctrlstat = td_status(td);
status = uhci_status_bits(ctrlstat);
- if (status & TD_CTRL_ACTIVE)
+ if (status & TD_CTRL_ACTIVE) {
+ urbp->qh->next_td = td;
return -EINPROGRESS;
+ }
urb->actual_length += uhci_actual_length(ctrlstat);
@@ -714,8 +734,10 @@ status_stage:
#endif
status = uhci_status_bits(status);
- if (status & TD_CTRL_ACTIVE)
+ if (status & TD_CTRL_ACTIVE) {
+ urbp->qh->next_td = td;
return -EINPROGRESS;
+ }
if (status)
goto td_error;
@@ -878,8 +900,10 @@ static int uhci_result_common(struct uhc
unsigned int ctrlstat = td_status(td);
status = uhci_status_bits(ctrlstat);
- if (status & TD_CTRL_ACTIVE)
+ if (status & TD_CTRL_ACTIVE) {
+ urbp->qh->next_td = td;
return -EINPROGRESS;
+ }
urb->actual_length += uhci_actual_length(ctrlstat);
@@ -954,7 +978,7 @@ static inline int uhci_submit_bulk(struc
qh->skel = uhci->skel_bulk_qh;
ret = uhci_submit_common(uhci, urb, qh);
if (ret == 0)
- uhci_inc_fsbr(uhci, urb);
+ uhci_add_fsbr(uhci, urb);
return ret;
}
@@ -1147,8 +1171,12 @@ static int uhci_urb_enqueue(struct usb_h
/* If the new URB is the first and only one on this QH then either
* the QH is new and idle or else it's unlinked and waiting to
* become idle, so we can activate it right away. */
- if (qh->queue.next == &urbp->node)
+ if (qh->queue.next == &urbp->node) {
+ qh->next_td = list_entry(urbp->td_list.next,
+ struct uhci_td, list);
+ uhci_qh_wants_fsbr(uhci, qh);
uhci_activate_qh(uhci, qh);
+ }
goto done;
err_submit_failed:
@@ -1221,7 +1249,6 @@ __acquires(uhci->lock)
qh->needs_fixup = 0;
}
- uhci_dec_fsbr(uhci, urb); /* Safe since it checks */
uhci_free_urb_priv(uhci, urbp);
switch (usb_pipetype(urb->pipe)) {
@@ -1348,6 +1375,43 @@ static void uhci_free_pending_tds(struct
}
/*
+ * Check for queues that have made some forward progress.
+ */
+static int uhci_qh_has_advanced(struct uhci_hcd *uhci, struct uhci_qh *qh)
+{
+ unsigned status;
+
+restart:
+ status = td_status(qh->next_td);
+ if (!(status & TD_CTRL_ACTIVE)) {
+
+ /* We're okay, the queue has advanced */
+ qh->wait_expired = 0;
+ qh->advance_jiffies = jiffies;
+ return 1;
+ }
+
+ /* The queue hasn't moved; check for timeout */
+ if (!qh->wait_expired && time_after(jiffies,
+ qh->advance_jiffies + QH_WAIT_TIMEOUT)) {
+ __le32 old_status, new_status, hw_status;
+
+ /* Let us know when the queue starts moving again.
+ * Do this carefully because the hardware might
+ * be updating td->status right now. */
+ old_status = cpu_to_le32(status);
+ new_status = cpu_to_le32(status | TD_CTRL_IOC);
+ hw_status = cmpxchg(&qh->next_td->status,
+ old_status, new_status);
+ if (hw_status != old_status)
+ goto restart;
+
+ qh->wait_expired = 1;
+ }
+ return 0;
+}
+
+/*
* Process events in the schedule, but only in one thread at a time
*/
static void uhci_scan_schedule(struct uhci_hcd *uhci, struct pt_regs *regs)
@@ -1361,7 +1425,8 @@ static void uhci_scan_schedule(struct uh
return;
}
uhci->scan_in_progress = 1;
- rescan:
+ uhci->fsbr_wanted = 0;
+rescan:
uhci->need_rescan = 0;
uhci_clear_next_interrupt(uhci);
@@ -1377,7 +1442,14 @@ static void uhci_scan_schedule(struct uh
while ((qh = uhci->next_qh) != uhci->skelqh[i]) {
uhci->next_qh = list_entry(qh->node.next,
struct uhci_qh, node);
- uhci_scan_qh(uhci, qh, regs);
+
+ if (qh->state != QH_STATE_ACTIVE ||
+ i == 1 || /* skel_iso_qh */
+ uhci_qh_has_advanced(uhci, qh)) {
+ uhci_scan_qh(uhci, qh, regs);
+ if (qh->state == QH_STATE_ACTIVE)
+ uhci_qh_wants_fsbr(uhci, qh);
+ }
}
}
@@ -1389,21 +1461,15 @@ static void uhci_scan_schedule(struct uh
if (uhci->is_stopped)
uhci_free_pending_tds(uhci);
+ if (uhci->fsbr_wanted)
+ uhci->fsbr_jiffies = jiffies;
+ else if (uhci->fsbr_is_on && time_after(jiffies, uhci->fsbr_jiffies +
+ FSBR_OFF_DELAY))
+ uhci_fsbr_off(uhci);
+
if (list_empty(&uhci->td_remove_list) &&
list_empty(&uhci->skel_unlink_qh->node))
uhci_clear_next_interrupt(uhci);
else
uhci_set_next_interrupt(uhci);
}
-
-static void check_fsbr(struct uhci_hcd *uhci)
-{
- /* For now, don't scan URBs for FSBR timeouts.
- * Add it back in later... */
-
- /* Really disable FSBR */
- if (!uhci->fsbr && uhci->fsbrtimeout && time_after_eq(jiffies,
uhci->fsbrtimeout)) {
- uhci->fsbrtimeout = 0;
- uhci->skel_term_qh->link = UHCI_PTR_TERM;
- }
-}
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-hub.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-hub.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-hub.c
@@ -173,7 +173,6 @@ static int uhci_hub_status_data(struct u
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);
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-hcd.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.c
@@ -87,15 +87,6 @@ static void suspend_rh(struct uhci_hcd *
static void wakeup_rh(struct uhci_hcd *uhci);
static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
-/* If a transfer is still active after this much time, turn off FSBR */
-#define IDLE_TIMEOUT msecs_to_jiffies(50)
-#define FSBR_DELAY msecs_to_jiffies(50)
-
-/* When we timeout an idle transfer for FSBR, we'll switch it over to */
-/* depth first traversal. We'll do it in groups of this number of TDs */
-/* to make sure it doesn't hog all of the bandwidth */
-#define DEPTH_INTERVAL 5
-
#include "uhci-debug.c"
#include "uhci-q.c"
#include "uhci-hub.c"
@@ -258,6 +249,8 @@ __acquires(uhci->lock)
uhci_to_hcd(uhci)->poll_rh = !int_enable;
uhci_scan_schedule(uhci, NULL);
+ uhci->fsbr_wanted = 0;
+ uhci_fsbr_off(uhci);
}
static void start_rh(struct uhci_hcd *uhci)
@@ -490,9 +483,6 @@ static int uhci_start(struct usb_hcd *hc
hcd->uses_new_polling = 1;
- uhci->fsbr = 0;
- uhci->fsbrtimeout = 0;
-
spin_lock_init(&uhci->lock);
INIT_LIST_HEAD(&uhci->td_remove_list);
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-debug.c
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-debug.c
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-debug.c
@@ -152,8 +152,10 @@ static int uhci_show_qh(struct uhci_qh *
if (len < 80 * 6)
return 0;
- out += sprintf(out, "%*s[%p] link (%08x) element (%08x)\n", space, "",
- qh, le32_to_cpu(qh->link), le32_to_cpu(element));
+ out += sprintf(out, "%*s[%p] link (%08x) element (%08x)"
+ " next_td [%p]\n", space, "",
+ qh, le32_to_cpu(qh->link), le32_to_cpu(element),
+ qh->next_td);
if (element & UHCI_PTR_QH)
out += sprintf(out, "%*s Element points to QH (bug?)\n",
space, "");
@@ -275,7 +277,7 @@ static int uhci_show_status(struct uhci_
unsigned short portsc1, portsc2;
/* Try to make sure there's enough memory */
- if (len < 80 * 6)
+ if (len < 80 * 8)
return 0;
usbcmd = inw(io_addr + 0);
@@ -314,6 +316,7 @@ static int uhci_show_status(struct uhci_
out += sprintf(out, " sof = %02x\n", sof);
out += uhci_show_sc(1, portsc1, out, len - (out - buf));
out += uhci_show_sc(2, portsc2, out, len - (out - buf));
+ out += sprintf(out, "FSBR: %d\n", uhci->fsbr_is_on);
return out - buf;
}
Index: linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.h
===================================================================
--- linux-2.6.17-rc1.orig/drivers/usb/host/uhci-hcd.h
+++ linux-2.6.17-rc1/drivers/usb/host/uhci-hcd.h
@@ -84,6 +84,13 @@
#define CAN_SCHEDULE_FRAMES 1000 /* how far in the future frames
* can be scheduled */
+/* When no queues need Full-Speed Bandwidth Reclamation,
+ * delay this long before turning FSBR off */
+#define FSBR_OFF_DELAY msecs_to_jiffies(400)
+
+/* If a queue hasn't advanced after this much time, assume it is stuck */
+#define QH_WAIT_TIMEOUT msecs_to_jiffies(300)
+
/*
* Queue Headers
@@ -129,13 +136,16 @@ struct uhci_qh {
struct list_head queue; /* Queue of urbps for this QH */
struct uhci_qh *skel; /* Skeleton for this QH */
struct uhci_td *dummy_td; /* Dummy TD to end the queue */
+ struct uhci_td *next_td; /* Next TD to execute */
+ unsigned long advance_jiffies; /* Time of last queue advance */
unsigned int unlink_frame; /* When the QH was unlinked */
int state; /* QH_STATE_xxx; see above */
unsigned int initial_toggle:1; /* Endpoint's current toggle value */
unsigned int needs_fixup:1; /* Must fix the TD toggle values */
unsigned int is_stopped:1; /* Queue was stopped by an error */
+ unsigned int wait_expired:1; /* QH_WAIT_TIMEOUT has expired */
} __attribute__((aligned(16)));
/*
@@ -396,8 +406,7 @@ struct uhci_hcd {
__le32 *frame;
void **frame_cpu; /* CPU's frame list */
- int fsbr; /* Full-speed bandwidth reclamation */
- unsigned long fsbrtimeout; /* FSBR delay */
+ unsigned long fsbr_jiffies; /* Time when FSBR was last wanted */
enum uhci_rh_state rh_state;
unsigned long auto_stop_time; /* When to AUTO_STOP */
@@ -412,6 +421,8 @@ struct uhci_hcd {
unsigned int working_RD:1; /* Suspended root hub doesn't
need to be polled */
unsigned int is_initialized:1; /* Data structure is usable */
+ unsigned int fsbr_wanted:1; /* Some URB wants FSBR */
+ unsigned int fsbr_is_on:1; /* FSBR is turned on */
/* Support for port suspend/resume/reset */
unsigned long port_c_suspend; /* Bit-arrays of ports */
@@ -455,8 +466,8 @@ struct urb_priv {
struct uhci_qh *qh; /* QH for this URB */
struct list_head td_list;
- unsigned fsbr : 1; /* URB turned on FSBR */
- unsigned short_transfer : 1; /* URB got a short transfer, no
+ unsigned fsbr:1; /* URB wants FSBR */
+ unsigned short_transfer:1; /* URB got a short transfer, no
* need to rescan */
};
-------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel