diff options
author | Oliver Neukum <oliver@neukum.org> | 2009-11-27 09:17:59 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-11-30 19:43:16 -0500 |
commit | ee4ecb8ac63a5792bec448037d4b82ec4144f94b (patch) | |
tree | b272d4c5bcfe8ef8e7c94b6182fa2b3ed024b745 /drivers/usb | |
parent | 8d6499e5bde91ad05dea4f666bdfe79e65e7cf96 (diff) |
USB: work around for EHCI with quirky periodic schedules
a quirky chipset needs periodic schedules to run for a minimum
time before they can be disabled again. This enforces the requirement
with a time stamp and a calculated delay
Signed-off-by: Oliver Neukum <oliver@neukum.org>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pci.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/ehci-sched.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 2 |
4 files changed, 20 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9835e0713943..f5f5601701c9 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/errno.h> | 28 | #include <linux/errno.h> |
29 | #include <linux/init.h> | 29 | #include <linux/init.h> |
30 | #include <linux/timer.h> | 30 | #include <linux/timer.h> |
31 | #include <linux/ktime.h> | ||
31 | #include <linux/list.h> | 32 | #include <linux/list.h> |
32 | #include <linux/interrupt.h> | 33 | #include <linux/interrupt.h> |
33 | #include <linux/usb.h> | 34 | #include <linux/usb.h> |
@@ -676,6 +677,7 @@ static int ehci_run (struct usb_hcd *hcd) | |||
676 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ | 677 | ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ |
677 | msleep(5); | 678 | msleep(5); |
678 | up_write(&ehci_cf_port_reset_rwsem); | 679 | up_write(&ehci_cf_port_reset_rwsem); |
680 | ehci->last_periodic_enable = ktime_get_real(); | ||
679 | 681 | ||
680 | temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); | 682 | temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); |
681 | ehci_info (ehci, | 683 | ehci_info (ehci, |
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 378861b9d79a..ead5f4f2aa5a 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c | |||
@@ -111,6 +111,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd) | |||
111 | switch (pdev->vendor) { | 111 | switch (pdev->vendor) { |
112 | case PCI_VENDOR_ID_INTEL: | 112 | case PCI_VENDOR_ID_INTEL: |
113 | ehci->need_io_watchdog = 0; | 113 | ehci->need_io_watchdog = 0; |
114 | if (pdev->device == 0x27cc) { | ||
115 | ehci->broken_periodic = 1; | ||
116 | ehci_info(ehci, "using broken periodic workaround\n"); | ||
117 | } | ||
114 | break; | 118 | break; |
115 | case PCI_VENDOR_ID_TDI: | 119 | case PCI_VENDOR_ID_TDI: |
116 | if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { | 120 | if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { |
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index b25cdea93a1f..a5535b5e3fe2 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c | |||
@@ -475,6 +475,8 @@ static int enable_periodic (struct ehci_hcd *ehci) | |||
475 | /* make sure ehci_work scans these */ | 475 | /* make sure ehci_work scans these */ |
476 | ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) | 476 | ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) |
477 | % (ehci->periodic_size << 3); | 477 | % (ehci->periodic_size << 3); |
478 | if (unlikely(ehci->broken_periodic)) | ||
479 | ehci->last_periodic_enable = ktime_get_real(); | ||
478 | return 0; | 480 | return 0; |
479 | } | 481 | } |
480 | 482 | ||
@@ -486,6 +488,16 @@ static int disable_periodic (struct ehci_hcd *ehci) | |||
486 | if (--ehci->periodic_sched) | 488 | if (--ehci->periodic_sched) |
487 | return 0; | 489 | return 0; |
488 | 490 | ||
491 | if (unlikely(ehci->broken_periodic)) { | ||
492 | /* delay experimentally determined */ | ||
493 | ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000); | ||
494 | ktime_t now = ktime_get_real(); | ||
495 | s64 delay = ktime_us_delta(safe, now); | ||
496 | |||
497 | if (unlikely(delay > 0)) | ||
498 | udelay(delay); | ||
499 | } | ||
500 | |||
489 | /* did setting PSE not take effect yet? | 501 | /* did setting PSE not take effect yet? |
490 | * takes effect only at frame boundaries... | 502 | * takes effect only at frame boundaries... |
491 | */ | 503 | */ |
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 064e76821ff5..2d85e21ff282 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h | |||
@@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ | |||
118 | unsigned stamp; | 118 | unsigned stamp; |
119 | unsigned random_frame; | 119 | unsigned random_frame; |
120 | unsigned long next_statechange; | 120 | unsigned long next_statechange; |
121 | ktime_t last_periodic_enable; | ||
121 | u32 command; | 122 | u32 command; |
122 | 123 | ||
123 | /* SILICON QUIRKS */ | 124 | /* SILICON QUIRKS */ |
@@ -127,6 +128,7 @@ struct ehci_hcd { /* one per controller */ | |||
127 | unsigned big_endian_desc:1; | 128 | unsigned big_endian_desc:1; |
128 | unsigned has_amcc_usb23:1; | 129 | unsigned has_amcc_usb23:1; |
129 | unsigned need_io_watchdog:1; | 130 | unsigned need_io_watchdog:1; |
131 | unsigned broken_periodic:1; | ||
130 | 132 | ||
131 | /* required for usb32 quirk */ | 133 | /* required for usb32 quirk */ |
132 | #define OHCI_CTRL_HCFS (3 << 6) | 134 | #define OHCI_CTRL_HCFS (3 << 6) |