aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-07-11 11:22:21 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-16 19:54:25 -0400
commit314466101c6ae14f6f5db8a86eda1509ba2c02a8 (patch)
treed2605336919a20a1ee122c813ca7ddc479755d12 /drivers
parent9671cd7a91059bcd27665a884ee6568d31ef6857 (diff)
USB: EHCI: use hrtimer for async schedule
This patch (as1576) adds hrtimer support for managing ehci-hcd's async schedule. Just as with the earlier change to the periodic schedule management, two new hrtimer events take care of everything. One event polls at 1-ms intervals to see when the Asynchronous Schedule Status (ASS) flag matches the Asynchronous Schedule Enable (ASE) value; the schedule's state must not be changed until it does. The other event delays for 15 ms after the async schedule becomes empty before turning it off. The new events replace a busy-wait poll and a kernel timer usage. They also replace the rather illogical method currently used for indicating the async schedule should be turned off: attempting to unlink the dedicated QH at the head of the async list. 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.c13
-rw-r--r--drivers/usb/host/ehci-q.c68
-rw-r--r--drivers/usb/host/ehci-timer.c49
-rw-r--r--drivers/usb/host/ehci.h5
4 files changed, 86 insertions, 49 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index fd7ae16f77be..21d6fbc0a327 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -95,7 +95,6 @@ static const char hcd_name [] = "ehci_hcd";
95 95
96#define EHCI_IAA_MSECS 10 /* arbitrary */ 96#define EHCI_IAA_MSECS 10 /* arbitrary */
97#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ 97#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
98#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
99#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) 98#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1)
100 /* 5-ms async qh unlink delay */ 99 /* 5-ms async qh unlink delay */
101 100
@@ -137,7 +136,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
137 * SHRINK were pending, OFF would never be requested. 136 * SHRINK were pending, OFF would never be requested.
138 */ 137 */
139 if (timer_pending(&ehci->watchdog) 138 if (timer_pending(&ehci->watchdog)
140 && ((BIT(TIMER_ASYNC_SHRINK) | BIT(TIMER_ASYNC_OFF)) 139 && (BIT(TIMER_ASYNC_SHRINK)
141 & ehci->actions)) 140 & ehci->actions))
142 return; 141 return;
143 142
@@ -150,9 +149,6 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action)
150 return; 149 return;
151 t = EHCI_IO_JIFFIES; 150 t = EHCI_IO_JIFFIES;
152 break; 151 break;
153 case TIMER_ASYNC_OFF:
154 t = EHCI_ASYNC_JIFFIES;
155 break;
156 /* case TIMER_ASYNC_SHRINK: */ 152 /* case TIMER_ASYNC_SHRINK: */
157 default: 153 default:
158 t = EHCI_SHRINK_JIFFIES; 154 t = EHCI_SHRINK_JIFFIES;
@@ -376,10 +372,6 @@ static void ehci_watchdog(unsigned long param)
376 372
377 spin_lock_irqsave(&ehci->lock, flags); 373 spin_lock_irqsave(&ehci->lock, flags);
378 374
379 /* stop async processing after it's idled a bit */
380 if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
381 start_unlink_async (ehci, ehci->async);
382
383 /* ehci could run by timer, without IRQs ... */ 375 /* ehci could run by timer, without IRQs ... */
384 ehci_work (ehci); 376 ehci_work (ehci);
385 377
@@ -470,7 +462,8 @@ static void ehci_work (struct ehci_hcd *ehci)
470 if (ehci->scanning) 462 if (ehci->scanning)
471 return; 463 return;
472 ehci->scanning = 1; 464 ehci->scanning = 1;
473 scan_async (ehci); 465 if (ehci->async_count)
466 scan_async(ehci);
474 if (ehci->next_uframe != -1) 467 if (ehci->next_uframe != -1)
475 scan_periodic (ehci); 468 scan_periodic (ehci);
476 ehci->scanning = 0; 469 ehci->scanning = 0;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 285d5a0f3f70..d68764ef4476 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -964,6 +964,30 @@ done:
964 964
965/*-------------------------------------------------------------------------*/ 965/*-------------------------------------------------------------------------*/
966 966
967static void enable_async(struct ehci_hcd *ehci)
968{
969 if (ehci->async_count++)
970 return;
971
972 /* Stop waiting to turn off the async schedule */
973 ehci->enabled_hrtimer_events &= ~BIT(EHCI_HRTIMER_DISABLE_ASYNC);
974
975 /* Don't start the schedule until ASS is 0 */
976 ehci_poll_ASS(ehci);
977}
978
979static void disable_async(struct ehci_hcd *ehci)
980{
981 if (--ehci->async_count)
982 return;
983
984 /* The async schedule and async_unlink list are supposed to be empty */
985 WARN_ON(ehci->async->qh_next.qh || ehci->async_unlink);
986
987 /* Don't turn off the schedule until ASS is 1 */
988 ehci_poll_ASS(ehci);
989}
990
967/* move qh (and its qtds) onto async queue; maybe enable queue. */ 991/* move qh (and its qtds) onto async queue; maybe enable queue. */
968 992
969static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh) 993static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
@@ -977,24 +1001,11 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
977 1001
978 WARN_ON(qh->qh_state != QH_STATE_IDLE); 1002 WARN_ON(qh->qh_state != QH_STATE_IDLE);
979 1003
980 /* (re)start the async schedule? */
981 head = ehci->async;
982 timer_action_done (ehci, TIMER_ASYNC_OFF);
983 if (!head->qh_next.qh) {
984 if (!(ehci->command & CMD_ASE)) {
985 /* in case a clear of CMD_ASE didn't take yet */
986 (void)handshake(ehci, &ehci->regs->status,
987 STS_ASS, 0, 150);
988 ehci->command |= CMD_ASE;
989 ehci_writel(ehci, ehci->command, &ehci->regs->command);
990 /* posted write need not be known to HC yet ... */
991 }
992 }
993
994 /* clear halt and/or toggle; and maybe recover from silicon quirk */ 1004 /* clear halt and/or toggle; and maybe recover from silicon quirk */
995 qh_refresh(ehci, qh); 1005 qh_refresh(ehci, qh);
996 1006
997 /* splice right after start */ 1007 /* splice right after start */
1008 head = ehci->async;
998 qh->qh_next = head->qh_next; 1009 qh->qh_next = head->qh_next;
999 qh->hw->hw_next = head->hw->hw_next; 1010 qh->hw->hw_next = head->hw->hw_next;
1000 wmb (); 1011 wmb ();
@@ -1005,6 +1016,8 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1005 qh->xacterrs = 0; 1016 qh->xacterrs = 0;
1006 qh->qh_state = QH_STATE_LINKED; 1017 qh->qh_state = QH_STATE_LINKED;
1007 /* qtd completions reported later by interrupt */ 1018 /* qtd completions reported later by interrupt */
1019
1020 enable_async(ehci);
1008} 1021}
1009 1022
1010/*-------------------------------------------------------------------------*/ 1023/*-------------------------------------------------------------------------*/
@@ -1173,16 +1186,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
1173 1186
1174 qh_completions (ehci, qh); 1187 qh_completions (ehci, qh);
1175 1188
1176 if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING) { 1189 if (!list_empty(&qh->qtd_list) && ehci->rh_state == EHCI_RH_RUNNING)
1177 qh_link_async (ehci, qh); 1190 qh_link_async (ehci, qh);
1178 } else { 1191
1179 /* it's not free to turn the async schedule on/off; leave it 1192 disable_async(ehci);
1180 * active but idle for a while once it empties.
1181 */
1182 if (ehci->rh_state == EHCI_RH_RUNNING
1183 && ehci->async->qh_next.qh == NULL)
1184 timer_action (ehci, TIMER_ASYNC_OFF);
1185 }
1186 1193
1187 if (next) { 1194 if (next) {
1188 ehci->async_unlink = NULL; 1195 ehci->async_unlink = NULL;
@@ -1210,21 +1217,6 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
1210 BUG (); 1217 BUG ();
1211#endif 1218#endif
1212 1219
1213 /* stop async schedule right now? */
1214 if (unlikely (qh == ehci->async)) {
1215 /* can't get here without STS_ASS set */
1216 if (ehci->rh_state != EHCI_RH_HALTED
1217 && !ehci->async_unlink) {
1218 /* ... and CMD_IAAD clear */
1219 ehci->command &= ~CMD_ASE;
1220 ehci_writel(ehci, ehci->command, &ehci->regs->command);
1221 wmb ();
1222 // handshake later, if we need to
1223 timer_action_done (ehci, TIMER_ASYNC_OFF);
1224 }
1225 return;
1226 }
1227
1228 qh->qh_state = QH_STATE_UNLINK; 1220 qh->qh_state = QH_STATE_UNLINK;
1229 ehci->async_unlink = qh; 1221 ehci->async_unlink = qh;
1230 if (!qh->unlink_next) 1222 if (!qh->unlink_next)
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index ecd3296157c6..1e907dd3bb1b 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -67,8 +67,10 @@ static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
67 * 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.
68 */ 68 */
69static unsigned event_delays_ns[] = { 69static unsigned event_delays_ns[] = {
70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_ASS */
70 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */ 71 1 * NSEC_PER_MSEC, /* EHCI_HRTIMER_POLL_PSS */
71 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 72 10 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_PERIODIC */
73 15 * NSEC_PER_MSEC, /* EHCI_HRTIMER_DISABLE_ASYNC */
72}; 74};
73 75
74/* Enable a pending hrtimer event */ 76/* Enable a pending hrtimer event */
@@ -91,6 +93,51 @@ static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
91} 93}
92 94
93 95
96/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */
97static void ehci_poll_ASS(struct ehci_hcd *ehci)
98{
99 unsigned actual, want;
100
101 /* Don't enable anything if the controller isn't running (e.g., died) */
102 if (ehci->rh_state != EHCI_RH_RUNNING)
103 return;
104
105 want = (ehci->command & CMD_ASE) ? STS_ASS : 0;
106 actual = ehci_readl(ehci, &ehci->regs->status) & STS_ASS;
107
108 if (want != actual) {
109
110 /* Poll again later, but give up after about 20 ms */
111 if (ehci->ASS_poll_count++ < 20) {
112 ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
113 return;
114 }
115 ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n");
116 }
117 ehci->ASS_poll_count = 0;
118
119 /* The status is up-to-date; restart or stop the schedule as needed */
120 if (want == 0) { /* Stopped */
121 if (ehci->async_count > 0)
122 ehci_set_command_bit(ehci, CMD_ASE);
123
124 } else { /* Running */
125 if (ehci->async_count == 0) {
126
127 /* Turn off the schedule after a while */
128 ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_ASYNC,
129 true);
130 }
131 }
132}
133
134/* Turn off the async schedule after a brief delay */
135static void ehci_disable_ASE(struct ehci_hcd *ehci)
136{
137 ehci_clear_command_bit(ehci, CMD_ASE);
138}
139
140
94/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ 141/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */
95static void ehci_poll_PSS(struct ehci_hcd *ehci) 142static void ehci_poll_PSS(struct ehci_hcd *ehci)
96{ 143{
@@ -151,8 +198,10 @@ static void ehci_disable_PSE(struct ehci_hcd *ehci)
151 * enum ehci_hrtimer_event in ehci.h. 198 * enum ehci_hrtimer_event in ehci.h.
152 */ 199 */
153static void (*event_handlers[])(struct ehci_hcd *) = { 200static void (*event_handlers[])(struct ehci_hcd *) = {
201 ehci_poll_ASS, /* EHCI_HRTIMER_POLL_ASS */
154 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */ 202 ehci_poll_PSS, /* EHCI_HRTIMER_POLL_PSS */
155 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */ 203 ehci_disable_PSE, /* EHCI_HRTIMER_DISABLE_PERIODIC */
204 ehci_disable_ASE, /* EHCI_HRTIMER_DISABLE_ASYNC */
156}; 205};
157 206
158static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t) 207static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index da2e0ab23850..bf06bbb77ba4 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -79,8 +79,10 @@ 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_ASS, /* Poll for async schedule off */
82 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ 83 EHCI_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */
83 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ 84 EHCI_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */
85 EHCI_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */
84 EHCI_HRTIMER_NUM_EVENTS /* Must come last */ 86 EHCI_HRTIMER_NUM_EVENTS /* Must come last */
85}; 87};
86#define EHCI_HRTIMER_NO_EVENT 99 88#define EHCI_HRTIMER_NO_EVENT 99
@@ -93,6 +95,7 @@ struct ehci_hcd { /* one per controller */
93 struct hrtimer hrtimer; 95 struct hrtimer hrtimer;
94 96
95 int PSS_poll_count; 97 int PSS_poll_count;
98 int ASS_poll_count;
96 99
97 /* glue to PCI and HCD framework */ 100 /* glue to PCI and HCD framework */
98 struct ehci_caps __iomem *caps; 101 struct ehci_caps __iomem *caps;
@@ -110,6 +113,7 @@ struct ehci_hcd { /* one per controller */
110 struct ehci_qh *async_unlink_last; 113 struct ehci_qh *async_unlink_last;
111 struct ehci_qh *qh_scan_next; 114 struct ehci_qh *qh_scan_next;
112 unsigned scanning : 1; 115 unsigned scanning : 1;
116 unsigned async_count; /* async activity count */
113 117
114 /* periodic schedule support */ 118 /* periodic schedule support */
115#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ 119#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
@@ -229,7 +233,6 @@ static inline void iaa_watchdog_done(struct ehci_hcd *ehci)
229enum ehci_timer_action { 233enum ehci_timer_action {
230 TIMER_IO_WATCHDOG, 234 TIMER_IO_WATCHDOG,
231 TIMER_ASYNC_SHRINK, 235 TIMER_ASYNC_SHRINK,
232 TIMER_ASYNC_OFF,
233}; 236};
234 237
235static inline void 238static inline void