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 _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel