diff options
Diffstat (limited to 'drivers')
-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 | 69 | ||||
-rw-r--r-- | drivers/usb/host/ehci-timer.c | 80 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 7 |
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 | ||
482 | static int enable_periodic (struct ehci_hcd *ehci) | 482 | static 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 | ||
511 | static int disable_periodic (struct ehci_hcd *ehci) | 495 | static 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 | ||
657 | static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh) | 615 | static 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 */ | ||
20 | static 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 */ | ||
30 | static 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 | */ |
47 | static unsigned event_delays_ns[] = { | 69 | static 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 */ | ||
95 | static 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 */ | ||
139 | static 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 | */ |
75 | static void (*event_handlers[])(struct ehci_hcd *) = { | 153 | static 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 | ||
78 | static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) | 158 | static 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 | */ |
81 | enum ehci_hrtimer_event { | 81 | enum 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*/ |