ChangeSet 1.2181.4.46, 2005/03/21 22:36:17-08:00, [EMAIL PROTECTED]
[PATCH] UHCI updates
This is the fourth of five updates to the uhci-hcd driver:
Reimplement the get_current_frame routines so that when the
controller isn't running they return a cached value. Also add
a flag to track whether the controller is running and allow
critical data structure updates to occur immediately if the
controller is stopped.
Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
uhci-hcd.c | 51 +++++++++++++++++++++++++++++++++++----------------
uhci-hcd.h | 4 +++-
uhci-q.c | 31 ++++++++++++++-----------------
3 files changed, 52 insertions(+), 34 deletions(-)
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c 2005-03-30 15:13:54 -08:00
+++ b/drivers/usb/host/uhci-hcd.c 2005-03-30 15:13:54 -08:00
@@ -89,7 +89,7 @@
static kmem_cache_t *uhci_up_cachep; /* urb_priv */
-static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci);
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci);
static void hc_state_transitions(struct uhci_hcd *uhci);
/* If a transfer is still active after this much time, turn off FSBR */
@@ -116,8 +116,11 @@
int called_uhci_finish_completion = 0;
spin_lock_irqsave(&uhci->lock, flags);
+ uhci_get_current_frame_number(uhci);
+
if (!list_empty(&uhci->urb_remove_list) &&
- uhci_get_current_frame_number(uhci) != uhci->urb_remove_age) {
+ uhci->frame_number + uhci->is_stopped !=
+ uhci->urb_remove_age) {
uhci_remove_pending_urbps(uhci);
uhci_finish_completion(uhci, NULL);
called_uhci_finish_completion = 1;
@@ -173,7 +176,6 @@
unsigned long io_addr = uhci->io_addr;
unsigned short status;
struct urb_priv *urbp, *tmp;
- unsigned int age;
/*
* Read the interrupt status, and write it back to clear the
@@ -203,13 +205,13 @@
uhci->resume_detect = 1;
spin_lock(&uhci->lock);
+ uhci_get_current_frame_number(uhci);
- age = uhci_get_current_frame_number(uhci);
- if (age != uhci->qh_remove_age)
+ if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age)
uhci_free_pending_qhs(uhci);
- if (age != uhci->td_remove_age)
+ if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age)
uhci_free_pending_tds(uhci);
- if (age != uhci->urb_remove_age)
+ if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age)
uhci_remove_pending_urbps(uhci);
if (list_empty(&uhci->urb_remove_list) &&
@@ -256,6 +258,7 @@
/* Another 10ms delay */
msleep(10);
uhci->resume_detect = 0;
+ uhci->is_stopped = UHCI_IS_STOPPED;
}
static void suspend_hc(struct uhci_hcd *uhci)
@@ -266,6 +269,10 @@
uhci->state = UHCI_SUSPENDED;
uhci->resume_detect = 0;
outw(USBCMD_EGSM, io_addr + USBCMD);
+
+ /* FIXME: Wait for the controller to actually stop */
+ uhci_get_current_frame_number(uhci);
+ uhci->is_stopped = UHCI_IS_STOPPED;
}
static void wakeup_hc(struct uhci_hcd *uhci)
@@ -280,6 +287,7 @@
outw(USBCMD_FGR | USBCMD_EGSM, io_addr + USBCMD);
uhci->state = UHCI_RESUMING_1;
uhci->state_end = jiffies + msecs_to_jiffies(20);
+ uhci->is_stopped = 0;
break;
case UHCI_RESUMING_1: /* End global resume */
@@ -386,11 +394,13 @@
}
/*
- * returns the current frame number for a USB bus/controller.
+ * Store the current frame number in uhci->frame_number if the controller
+ * is runnning
*/
-static unsigned int uhci_get_current_frame_number(struct uhci_hcd *uhci)
+static void uhci_get_current_frame_number(struct uhci_hcd *uhci)
{
- return inw(uhci->io_addr + USBFRNUM);
+ if (!uhci->is_stopped)
+ uhci->frame_number = inw(uhci->io_addr + USBFRNUM);
}
static int start_hc(struct uhci_hcd *uhci)
@@ -430,6 +440,7 @@
uhci->state = UHCI_RUNNING_GRACE;
uhci->state_end = jiffies + HZ;
outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD);
+ uhci->is_stopped = 0;
return 0;
}
@@ -767,11 +778,9 @@
spin_lock_irq(&uhci->lock);
/* Don't try to suspend broken motherboards, reset instead */
- if (suspend_allowed(uhci)) {
+ if (suspend_allowed(uhci))
suspend_hc(uhci);
- uhci->saved_framenumber =
- inw(uhci->io_addr + USBFRNUM) & 0x3ff;
- } else {
+ else {
spin_unlock_irq(&uhci->lock);
reset_hc(uhci);
spin_lock_irq(&uhci->lock);
@@ -800,7 +809,7 @@
*/
pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP,
0);
- outw(uhci->saved_framenumber, uhci->io_addr + USBFRNUM);
+ outw(uhci->frame_number, 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);
@@ -832,7 +841,17 @@
static int uhci_hcd_get_frame_number(struct usb_hcd *hcd)
{
- return uhci_get_current_frame_number(hcd_to_uhci(hcd));
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+ int frame_number;
+ unsigned long flags;
+
+ /* Minimize latency by avoiding the spinlock */
+ local_irq_save(flags);
+ rmb();
+ frame_number = (uhci->is_stopped ? uhci->frame_number :
+ inw(uhci->io_addr + USBFRNUM));
+ local_irq_restore(flags);
+ return frame_number;
}
static const char hcd_name[] = "uhci_hcd";
diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h
--- a/drivers/usb/host/uhci-hcd.h 2005-03-30 15:13:54 -08:00
+++ b/drivers/usb/host/uhci-hcd.h 2005-03-30 15:13:54 -08:00
@@ -366,7 +366,9 @@
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 */
+ unsigned int frame_number; /* As of last check */
+ unsigned int is_stopped;
+#define UHCI_IS_STOPPED 9999 /* Larger than a frame
# */
/* Support for port suspend/resume/reset */
unsigned long port_c_suspend; /* Bit-arrays of ports */
diff -Nru a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c
--- a/drivers/usb/host/uhci-q.c 2005-03-30 15:13:54 -08:00
+++ b/drivers/usb/host/uhci-q.c 2005-03-30 15:13:54 -08:00
@@ -264,7 +264,6 @@
{
struct uhci_qh *pqh;
__le32 newlink;
- unsigned int age;
if (!qh)
return;
@@ -310,10 +309,10 @@
list_del_init(&qh->urbp->queue_list);
qh->urbp = NULL;
- age = uhci_get_current_frame_number(uhci);
- if (age != uhci->qh_remove_age) {
+ uhci_get_current_frame_number(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->qh_remove_age) {
uhci_free_pending_qhs(uhci);
- uhci->qh_remove_age = age;
+ uhci->qh_remove_age = uhci->frame_number;
}
/* Check to see if the remove list is empty. Set the IOC bit */
@@ -492,7 +491,6 @@
{
struct uhci_td *td, *tmp;
struct urb_priv *urbp;
- unsigned int age;
urbp = (struct urb_priv *)urb->hcpriv;
if (!urbp)
@@ -502,10 +500,10 @@
dev_warn(uhci_dev(uhci), "urb %p still on uhci->urb_list "
"or uhci->remove_list!\n", urb);
- age = uhci_get_current_frame_number(uhci);
- if (age != uhci->td_remove_age) {
+ uhci_get_current_frame_number(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->td_remove_age) {
uhci_free_pending_tds(uhci);
- uhci->td_remove_age = age;
+ uhci->td_remove_age = uhci->frame_number;
}
/* Check to see if the remove list is empty. Set the IOC bit */
@@ -1063,11 +1061,11 @@
limits = isochronous_find_limits(uhci, urb, &start, &end);
if (urb->transfer_flags & URB_ISO_ASAP) {
- if (limits)
- urb->start_frame =
- (uhci_get_current_frame_number(uhci) +
- 10) & (UHCI_NUMFRAMES - 1);
- else
+ if (limits) {
+ uhci_get_current_frame_number(uhci);
+ urb->start_frame = (uhci->frame_number + 10)
+ & (UHCI_NUMFRAMES - 1);
+ } else
urb->start_frame = end;
} else {
urb->start_frame &= (UHCI_NUMFRAMES - 1);
@@ -1371,7 +1369,6 @@
struct uhci_hcd *uhci = hcd_to_uhci(hcd);
unsigned long flags;
struct urb_priv *urbp;
- unsigned int age;
spin_lock_irqsave(&uhci->lock, flags);
urbp = urb->hcpriv;
@@ -1381,10 +1378,10 @@
uhci_unlink_generic(uhci, urb);
- age = uhci_get_current_frame_number(uhci);
- if (age != uhci->urb_remove_age) {
+ uhci_get_current_frame_number(uhci);
+ if (uhci->frame_number + uhci->is_stopped != uhci->urb_remove_age) {
uhci_remove_pending_urbps(uhci);
- uhci->urb_remove_age = age;
+ uhci->urb_remove_age = uhci->frame_number;
}
/* If we're the first, set the next interrupt bit */
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html