aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-07-11 11:22:05 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-16 19:53:16 -0400
commit3ca9aebac2ebb8f56d2d097636b8c568320a9f87 (patch)
treead4382a904320043a32e8b18263bc22215ce5cee /drivers
parentd58b4bcc6df8046cf9c3c59f9ff84d2cd86b93eb (diff)
USB: EHCI: use hrtimer for the periodic schedule
This patch (as1573) adds hrtimer support for managing ehci-hcd's periodic schedule. There are two issues to deal with. First, the schedule's state (on or off) must not be changed until the hardware status has caught up with the current command. This is handled by an hrtimer event that polls at 1-ms intervals to see when the Periodic Schedule Status (PSS) flag matches the Periodic Schedule Enable (PSE) value. Second, the schedule should not be turned off as soon as it becomes empty. Turning the schedule on and off takes time, so we want to wait until the schedule has been empty for a suitable period before turning it off. This is handled by an hrtimer event that gets set to expire 10 ms after the periodic schedule becomes empty. The existing code polls (for up to 1125 us and with interrupts disabled!) to check the status, and doesn't implement a delay before turning off the schedule. Furthermore, if the polling fails then the driver decides that the controller has died. This has caused problems for several people; some controllers can take 10 ms or more to turn off their periodic schedules. This patch fixes these issues. It also makes the "broken_periodic" workaround unnecessary; there is no longer any danger of turning off the periodic schedule after it has been on for less than 1 ms. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-hcd.c2
-rw-r--r--drivers/usb/host/ehci-pci.c4
-rw-r--r--drivers/usb/host/ehci-sched.c69
-rw-r--r--drivers/usb/host/ehci-timer.c80
-rw-r--r--drivers/usb/host/ehci.h7
5 files changed, 101 insertions, 61 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index f8fed163a23a..7e00ca095cea 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -546,7 +546,7 @@ static void ehci_work (struct ehci_hcd *ehci)
546 */ 546 */
547 if (ehci->rh_state == EHCI_RH_RUNNING && 547 if (ehci->rh_state == EHCI_RH_RUNNING &&
548 (ehci->async->qh_next.ptr != NULL || 548 (ehci->async->qh_next.ptr != NULL ||
549 ehci->periodic_sched != 0)) 549 ehci->periodic_count != 0))
550 timer_action (ehci, TIMER_IO_WATCHDOG); 550 timer_action (ehci, TIMER_IO_WATCHDOG);
551} 551}
552 552
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 21e5f963f331..2cb7d370c4ef 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -104,10 +104,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
104 break; 104 break;
105 case PCI_VENDOR_ID_INTEL: 105 case PCI_VENDOR_ID_INTEL:
106 ehci->fs_i_thresh = 1; 106 ehci->fs_i_thresh = 1;
107 if (pdev->device == 0x27cc) {
108 ehci->broken_periodic = 1;
109 ehci_info(ehci, "using broken periodic workaround\n");
110 }
111 if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) 107 if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB)
112 hcd->has_tt = 1; 108 hcd->has_tt = 1;
113 break; 109 break;
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 3429b8a33c58..f5c15880c65a 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -481,67 +481,26 @@ static int tt_no_collision (
481 481
482static int enable_periodic (struct ehci_hcd *ehci) 482static int enable_periodic (struct ehci_hcd *ehci)
483{ 483{
484 int status; 484 if (ehci->periodic_count++)
485
486 if (ehci->periodic_sched++)
487 return 0; 485 return 0;
488 486
489 /* did clearing PSE did take effect yet? 487 /* Stop waiting to turn off the periodic schedule */
490 * takes effect only at frame boundaries... 488 ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
491 */
492 status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
493 STS_PSS, 0, 9 * 125);
494 if (status) {
495 usb_hc_died(ehci_to_hcd(ehci));
496 return status;
497 }
498
499 ehci->command |= CMD_PSE;
500 ehci_writel(ehci, ehci->command, &ehci->regs->command);
501 /* posted write ... PSS happens later */
502 489
503 /* make sure ehci_work scans these */ 490 /* Don't start the schedule until PSS is 0 */
504 ehci->next_uframe = ehci_read_frame_index(ehci) 491 ehci_poll_PSS(ehci);
505 % (ehci->periodic_size << 3);
506 if (unlikely(ehci->broken_periodic))
507 ehci->last_periodic_enable = ktime_get_real();
508 return 0; 492 return 0;
509} 493}
510 494
511static int disable_periodic (struct ehci_hcd *ehci) 495static int disable_periodic (struct ehci_hcd *ehci)
512{ 496{
513 int status; 497 if (--ehci->periodic_count)
514
515 if (--ehci->periodic_sched)
516 return 0; 498 return 0;
517 499
518 if (unlikely(ehci->broken_periodic)) { 500 ehci->next_uframe = -1; /* the periodic schedule is empty */
519 /* delay experimentally determined */
520 ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000);
521 ktime_t now = ktime_get_real();
522 s64 delay = ktime_us_delta(safe, now);
523
524 if (unlikely(delay > 0))
525 udelay(delay);
526 }
527
528 /* did setting PSE not take effect yet?
529 * takes effect only at frame boundaries...
530 */
531 status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
532 STS_PSS, STS_PSS, 9 * 125);
533 if (status) {
534 usb_hc_died(ehci_to_hcd(ehci));
535 return status;
536 }
537
538 ehci->command &= ~CMD_PSE;
539 ehci_writel(ehci, ehci->command, &ehci->regs->command);
540 /* posted write ... */
541
542 free_cached_lists(ehci);
543 501
544 ehci->next_uframe = -1; 502 /* Don't turn off the schedule until PSS is 1 */
503 ehci_poll_PSS(ehci);
545 return 0; 504 return 0;
546} 505}
547 506
@@ -650,8 +609,7 @@ static int qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
650 qh->qh_state = QH_STATE_UNLINK; 609 qh->qh_state = QH_STATE_UNLINK;
651 qh->qh_next.ptr = NULL; 610 qh->qh_next.ptr = NULL;
652 611
653 /* maybe turn off periodic schedule */ 612 return 0;
654 return disable_periodic(ehci);
655} 613}
656 614
657static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) 615static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
@@ -706,6 +664,9 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
706 ehci_err(ehci, "can't reschedule qh %p, err %d\n", 664 ehci_err(ehci, "can't reschedule qh %p, err %d\n",
707 qh, rc); 665 qh, rc);
708 } 666 }
667
668 /* maybe turn off periodic schedule */
669 disable_periodic(ehci);
709} 670}
710 671
711/*-------------------------------------------------------------------------*/ 672/*-------------------------------------------------------------------------*/
@@ -2447,7 +2408,7 @@ restart:
2447 2408
2448 /* assume completion callbacks modify the queue */ 2409 /* assume completion callbacks modify the queue */
2449 if (unlikely (modified)) { 2410 if (unlikely (modified)) {
2450 if (likely(ehci->periodic_sched > 0)) 2411 if (likely(ehci->periodic_count > 0))
2451 goto restart; 2412 goto restart;
2452 /* short-circuit this scan */ 2413 /* short-circuit this scan */
2453 now_uframe = clock; 2414 now_uframe = clock;
@@ -2476,7 +2437,7 @@ restart:
2476 unsigned now; 2437 unsigned now;
2477 2438
2478 if (ehci->rh_state < EHCI_RH_RUNNING 2439 if (ehci->rh_state < EHCI_RH_RUNNING
2479 || ehci->periodic_sched == 0) 2440 || ehci->periodic_count == 0)
2480 break; 2441 break;
2481 ehci->next_uframe = now_uframe; 2442 ehci->next_uframe = now_uframe;
2482 now = ehci_read_frame_index(ehci) & (mod - 1); 2443 now = ehci_read_frame_index(ehci) & (mod - 1);
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index f6279e2883a8..ecd3296157c6 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -16,6 +16,28 @@
16 16
17/*-------------------------------------------------------------------------*/ 17/*-------------------------------------------------------------------------*/
18 18
19/* Set a bit in the USBCMD register */
20static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit)
21{
22 ehci->command |= bit;
23 ehci_writel(ehci, ehci->command, &ehci->regs->command);
24
25 /* unblock posted write */
26 ehci_readl(ehci, &ehci->regs->command);
27}
28
29/* Clear a bit in the USBCMD register */
30static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
31{
32 ehci->command &= ~bit;
33 ehci_writel(ehci, ehci->command, &ehci->regs->command);
34
35 /* unblock posted write */
36 ehci_readl(ehci, &ehci->regs->command);
37}
38
39/*-------------------------------------------------------------------------*/
40
19/* 41/*
20 * EHCI timer support... Now using hrtimers. 42 * EHCI timer support... Now using hrtimers.
21 * 43 *
@@ -45,6 +67,8 @@
45 * the event types indexed by enum ehci_hrtimer_event in ehci.h. 67 * the event types indexed by enum ehci_hrtimer_event in ehci.h.
46 */ 68 */
47static unsigned event_delays_ns[] = { 69static unsigned event_delays_ns[] = {
70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
71 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
48}; 72};
49 73
50/* Enable a pending hrtimer event */ 74/* Enable a pending hrtimer event */
@@ -67,12 +91,68 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
67} 91}
68 92
69 93
94/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
95static void ehci_poll_PSS(struct ehci_hcd *ehci)
96{
97 unsigned actual, want;
98
99 /* Don't do anything if the controller isn't running (e.g., died) */
100 if (ehci->rh_state != EHCI_RH_RUNNING)
101 return;
102
103 want = (ehci->command & CMD_PSE) ? STS_PSS : 0;
104 actual = ehci_readl(ehci, &ehci->regs->status) & STS_PSS;
105
106 if (want != actual) {
107
108 /* Poll again later, but give up after about 20 ms */
109 if (ehci->PSS_poll_count++ < 20) {
110 ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
111 return;
112 }
113 ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n");
114 }
115 ehci->PSS_poll_count = 0;
116
117 /* The status is up-to-date; restart or stop the schedule as needed */
118 if (want == 0) { /* Stopped */
119 free_cached_lists(ehci);
120 if (ehci->periodic_count > 0) {
121
122 /* make sure ehci_work scans these */
123 ehci->next_uframe = ehci_read_frame_index(ehci)
124 & ((ehci->periodic_size << 3) - 1);
125 ehci_set_command_bit(ehci, CMD_PSE);
126 }
127
128 } else { /* Running */
129 if (ehci->periodic_count == 0) {
130
131 /* Turn off the schedule after a while */
132 ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC,
133 true);
134 }
135 }
136}
137
138/* Turn off the periodic schedule after a brief delay */
139static void ehci_disable_PSE(struct ehci_hcd *ehci)
140{
141 ehci_clear_command_bit(ehci, CMD_PSE);
142
143 /* Poll to see when it actually stops */
144 ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
145}
146
147
70/* 148/*
71 * Handler functions for the hrtimer event types. 149 * Handler functions for the hrtimer event types.
72 * Keep this array in the same order as the event types indexed by 150 * Keep this array in the same order as the event types indexed by
73 * enum ehci_hrtimer_event in ehci.h. 151 * enum ehci_hrtimer_event in ehci.h.
74 */ 152 */
75static void (*event_handlers[])(struct ehci_hcd *) = { 153static void (*event_handlers[])(struct ehci_hcd *) = {
154 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
155 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
76}; 156};
77 157
78static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) 158static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 070be83028d5..da2e0ab23850 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -79,6 +79,8 @@ enum ehci_rh_state {
79 * ehci-timer.c) in parallel with this list. 79 * ehci-timer.c) in parallel with this list.
80 */ 80 */
81enum ehci_hrtimer_event { 81enum ehci_hrtimer_event {
82 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
83 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
82 EHCI_HRTIMER_NUM_EVENTS /* Must come last */ 84 EHCI_HRTIMER_NUM_EVENTS /* Must come last */
83}; 85};
84#define EHCI_HRTIMER_NO_EVENT 99 86#define EHCI_HRTIMER_NO_EVENT 99
@@ -90,6 +92,8 @@ struct ehci_hcd { /* one per controller */
90 ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS]; 92 ktime_t hr_timeouts[EHCI_HRTIMER_NUM_EVENTS];
91 struct hrtimer hrtimer; 93 struct hrtimer hrtimer;
92 94
95 int PSS_poll_count;
96
93 /* glue to PCI and HCD framework */ 97 /* glue to PCI and HCD framework */
94 struct ehci_caps __iomem *caps; 98 struct ehci_caps __iomem *caps;
95 struct ehci_regs __iomem *regs; 99 struct ehci_regs __iomem *regs;
@@ -116,7 +120,7 @@ struct ehci_hcd { /* one per controller */
116 120
117 union ehci_shadow *pshadow; /* mirror hw periodic table */ 121 union ehci_shadow *pshadow; /* mirror hw periodic table */
118 int next_uframe; /* scan periodic, start here */ 122 int next_uframe; /* scan periodic, start here */
119 unsigned periodic_sched; /* periodic activity count */ 123 unsigned periodic_count; /* periodic activity count */
120 unsigned uframe_periodic_max; /* max periodic time per uframe */ 124 unsigned uframe_periodic_max; /* max periodic time per uframe */
121 125
122 126
@@ -165,7 +169,6 @@ struct ehci_hcd { /* one per controller */
165 unsigned big_endian_capbase:1; 169 unsigned big_endian_capbase:1;
166 unsigned has_amcc_usb23:1; 170 unsigned has_amcc_usb23:1;
167 unsigned need_io_watchdog:1; 171 unsigned need_io_watchdog:1;
168 unsigned broken_periodic:1;
169 unsigned amd_pll_fix:1; 172 unsigned amd_pll_fix:1;
170 unsigned fs_i_thresh:1; /* Intel iso scheduling */ 173 unsigned fs_i_thresh:1; /* Intel iso scheduling */
171 unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/ 174 unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/