OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * ADM5120 HCD (Host Controller Driver) for USB |
||
3 | * |
||
4 | * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org> |
||
5 | * |
||
6 | * This file was derived from fragments of the OHCI driver. |
||
7 | * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> |
||
8 | * (C) Copyright 2000-2004 David Brownell <dbrownell@users.sourceforge.net> |
||
9 | * |
||
10 | * This program is free software; you can redistribute it and/or modify it |
||
11 | * under the terms of the GNU General Public License version 2 as published |
||
12 | * by the Free Software Foundation. |
||
13 | * |
||
14 | */ |
||
15 | |||
16 | #define OHCI_SCHED_ENABLES \ |
||
17 | (OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE) |
||
18 | |||
19 | #ifdef CONFIG_PM |
||
20 | static int admhc_restart(struct admhcd *ahcd); |
||
21 | |||
22 | static int admhc_rh_suspend(struct admhcd *ahcd, int autostop) |
||
23 | __releases(ahcd->lock) |
||
24 | __acquires(ahcd->lock) |
||
25 | { |
||
26 | int status = 0; |
||
27 | |||
28 | ahcd->hc_control = admhc_readl(ahcd, &ahcd->regs->control); |
||
29 | switch (ahcd->hc_control & OHCI_CTRL_HCFS) { |
||
30 | case OHCI_USB_RESUME: |
||
31 | admhc_dbg(ahcd, "resume/suspend?\n"); |
||
32 | ahcd->hc_control &= ~OHCI_CTRL_HCFS; |
||
33 | ahcd->hc_control |= OHCI_USB_RESET; |
||
34 | admhc_writel(ahcd, ahcd->hc_control, &ahcd->ahcd->regs->control); |
||
35 | (void) admhc_readl(ahcd, &ahcd->regs->control); |
||
36 | /* FALL THROUGH */ |
||
37 | case OHCI_USB_RESET: |
||
38 | status = -EBUSY; |
||
39 | admhc_dbg(ahcd, "needs reinit!\n"); |
||
40 | goto done; |
||
41 | case OHCI_USB_SUSPEND: |
||
42 | if (!ahcd->autostop) { |
||
43 | admhc_dbg(ahcd, "already suspended\n"); |
||
44 | goto done; |
||
45 | } |
||
46 | } |
||
47 | admhc_dbg(ahcd, "%s root hub\n", |
||
48 | autostop ? "auto-stop" : "suspend"); |
||
49 | |||
50 | /* First stop any processing */ |
||
51 | if (!autostop && (ahcd->hc_control & OHCI_SCHED_ENABLES)) { |
||
52 | ahcd->hc_control &= ~OHCI_SCHED_ENABLES; |
||
53 | admhc_writel(ahcd, ahcd->hc_control, &ahcd->ahcd->regs->control); |
||
54 | ahcd->hc_control = admhc_readl(ahcd, &ahcd->regs->control); |
||
55 | admhc_writel(ahcd, OHCI_INTR_SF, &ahcd->regs->intrstatus); |
||
56 | |||
57 | /* sched disables take effect on the next frame, |
||
58 | * then the last WDH could take 6+ msec |
||
59 | */ |
||
60 | admhc_dbg(ahcd, "stopping schedules ...\n"); |
||
61 | ahcd->autostop = 0; |
||
62 | spin_unlock_irq (&ahcd->lock); |
||
63 | msleep (8); |
||
64 | spin_lock_irq(&ahcd->lock); |
||
65 | } |
||
66 | dl_done_list (ahcd); |
||
67 | finish_unlinks (ahcd, admhc_frame_no(ahcd)); |
||
68 | |||
69 | /* maybe resume can wake root hub */ |
||
70 | if (device_may_wakeup(&admhcd_to_hcd(ahcd)->self.root_hub->dev) || |
||
71 | autostop) |
||
72 | ahcd->hc_control |= OHCI_CTRL_RWE; |
||
73 | else { |
||
74 | admhc_writel(ahcd, OHCI_INTR_RHSC, &ahcd->regs->intrdisable); |
||
75 | ahcd->hc_control &= ~OHCI_CTRL_RWE; |
||
76 | } |
||
77 | |||
78 | /* Suspend hub ... this is the "global (to this bus) suspend" mode, |
||
79 | * which doesn't imply ports will first be individually suspended. |
||
80 | */ |
||
81 | ahcd->hc_control &= ~OHCI_CTRL_HCFS; |
||
82 | ahcd->hc_control |= OHCI_USB_SUSPEND; |
||
83 | admhc_writel(ahcd, ahcd->hc_control, &ahcd->ahcd->regs->control); |
||
84 | (void) admhc_readl(ahcd, &ahcd->regs->control); |
||
85 | |||
86 | /* no resumes until devices finish suspending */ |
||
87 | if (!autostop) { |
||
88 | ahcd->next_statechange = jiffies + msecs_to_jiffies (5); |
||
89 | ahcd->autostop = 0; |
||
90 | } |
||
91 | |||
92 | done: |
||
93 | return status; |
||
94 | } |
||
95 | |||
96 | static inline struct ed *find_head(struct ed *ed) |
||
97 | { |
||
98 | /* for bulk and control lists */ |
||
99 | while (ed->ed_prev) |
||
100 | ed = ed->ed_prev; |
||
101 | return ed; |
||
102 | } |
||
103 | |||
104 | /* caller has locked the root hub */ |
||
105 | static int admhc_rh_resume(struct admhcd *ahcd) |
||
106 | __releases(ahcd->lock) |
||
107 | __acquires(ahcd->lock) |
||
108 | { |
||
109 | struct usb_hcd *hcd = admhcd_to_hcd (ahcd); |
||
110 | u32 temp, enables; |
||
111 | int status = -EINPROGRESS; |
||
112 | int autostopped = ahcd->autostop; |
||
113 | |||
114 | ahcd->autostop = 0; |
||
115 | ahcd->hc_control = admhc_readl(ahcd, &ahcd->regs->control); |
||
116 | |||
117 | if (ahcd->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) { |
||
118 | /* this can happen after resuming a swsusp snapshot */ |
||
119 | if (hcd->state == HC_STATE_RESUMING) { |
||
120 | admhc_dbg(ahcd, "BIOS/SMM active, control %03x\n", |
||
121 | ahcd->hc_control); |
||
122 | status = -EBUSY; |
||
123 | /* this happens when pmcore resumes HC then root */ |
||
124 | } else { |
||
125 | admhc_dbg(ahcd, "duplicate resume\n"); |
||
126 | status = 0; |
||
127 | } |
||
128 | } else switch (ahcd->hc_control & OHCI_CTRL_HCFS) { |
||
129 | case OHCI_USB_SUSPEND: |
||
130 | ahcd->hc_control &= ~(OHCI_CTRL_HCFS|OHCI_SCHED_ENABLES); |
||
131 | ahcd->hc_control |= OHCI_USB_RESUME; |
||
132 | admhc_writel(ahcd, ahcd->hc_control, &ahcd->ahcd->regs->control); |
||
133 | (void) admhc_readl(ahcd, &ahcd->regs->control); |
||
134 | admhc_dbg(ahcd, "%s root hub\n", |
||
135 | autostopped ? "auto-start" : "resume"); |
||
136 | break; |
||
137 | case OHCI_USB_RESUME: |
||
138 | /* HCFS changes sometime after INTR_RD */ |
||
139 | admhc_dbg(ahcd, "%swakeup root hub\n", |
||
140 | autostopped ? "auto-" : ""); |
||
141 | break; |
||
142 | case OHCI_USB_OPER: |
||
143 | /* this can happen after resuming a swsusp snapshot */ |
||
144 | admhc_dbg(ahcd, "snapshot resume? reinit\n"); |
||
145 | status = -EBUSY; |
||
146 | break; |
||
147 | default: /* RESET, we lost power */ |
||
148 | admhc_dbg(ahcd, "lost power\n"); |
||
149 | status = -EBUSY; |
||
150 | } |
||
151 | if (status == -EBUSY) { |
||
152 | if (!autostopped) { |
||
153 | spin_unlock_irq (&ahcd->lock); |
||
154 | (void) ahcd_init (ahcd); |
||
155 | status = admhc_restart (ahcd); |
||
156 | spin_lock_irq(&ahcd->lock); |
||
157 | } |
||
158 | return status; |
||
159 | } |
||
160 | if (status != -EINPROGRESS) |
||
161 | return status; |
||
162 | if (autostopped) |
||
163 | goto skip_resume; |
||
164 | spin_unlock_irq (&ahcd->lock); |
||
165 | |||
166 | /* Some controllers (lucent erratum) need extra-long delays */ |
||
167 | msleep (20 /* usb 11.5.1.10 */ + 12 /* 32 msec counter */ + 1); |
||
168 | |||
169 | temp = admhc_readl(ahcd, &ahcd->regs->control); |
||
170 | temp &= OHCI_CTRL_HCFS; |
||
171 | if (temp != OHCI_USB_RESUME) { |
||
172 | admhc_err (ahcd, "controller won't resume\n"); |
||
173 | spin_lock_irq(&ahcd->lock); |
||
174 | return -EBUSY; |
||
175 | } |
||
176 | |||
177 | /* disable old schedule state, reinit from scratch */ |
||
178 | admhc_writel(ahcd, 0, &ahcd->regs->ed_controlhead); |
||
179 | admhc_writel(ahcd, 0, &ahcd->regs->ed_controlcurrent); |
||
180 | admhc_writel(ahcd, 0, &ahcd->regs->ed_bulkhead); |
||
181 | admhc_writel(ahcd, 0, &ahcd->regs->ed_bulkcurrent); |
||
182 | admhc_writel(ahcd, 0, &ahcd->regs->ed_periodcurrent); |
||
183 | admhc_writel(ahcd, (u32) ahcd->hcca_dma, &ahcd->ahcd->regs->hcca); |
||
184 | |||
185 | /* Sometimes PCI D3 suspend trashes frame timings ... */ |
||
186 | periodic_reinit(ahcd); |
||
187 | |||
188 | /* the following code is executed with ahcd->lock held and |
||
189 | * irqs disabled if and only if autostopped is true |
||
190 | */ |
||
191 | |||
192 | skip_resume: |
||
193 | /* interrupts might have been disabled */ |
||
194 | admhc_writel(ahcd, OHCI_INTR_INIT, &ahcd->regs->int_enable); |
||
195 | if (ahcd->ed_rm_list) |
||
196 | admhc_writel(ahcd, OHCI_INTR_SF, &ahcd->regs->int_enable); |
||
197 | |||
198 | /* Then re-enable operations */ |
||
199 | admhc_writel(ahcd, OHCI_USB_OPER, &ahcd->regs->control); |
||
200 | (void) admhc_readl(ahcd, &ahcd->regs->control); |
||
201 | if (!autostopped) |
||
202 | msleep (3); |
||
203 | |||
204 | temp = ahcd->hc_control; |
||
205 | temp &= OHCI_CTRL_RWC; |
||
206 | temp |= OHCI_CONTROL_INIT | OHCI_USB_OPER; |
||
207 | ahcd->hc_control = temp; |
||
208 | admhc_writel(ahcd, temp, &ahcd->regs->control); |
||
209 | (void) admhc_readl(ahcd, &ahcd->regs->control); |
||
210 | |||
211 | /* TRSMRCY */ |
||
212 | if (!autostopped) { |
||
213 | msleep (10); |
||
214 | spin_lock_irq(&ahcd->lock); |
||
215 | } |
||
216 | /* now ahcd->lock is always held and irqs are always disabled */ |
||
217 | |||
218 | /* keep it alive for more than ~5x suspend + resume costs */ |
||
219 | ahcd->next_statechange = jiffies + STATECHANGE_DELAY; |
||
220 | |||
221 | /* maybe turn schedules back on */ |
||
222 | enables = 0; |
||
223 | temp = 0; |
||
224 | if (!ahcd->ed_rm_list) { |
||
225 | if (ahcd->ed_controltail) { |
||
226 | admhc_writel(ahcd, |
||
227 | find_head (ahcd->ed_controltail)->dma, |
||
228 | &ahcd->regs->ed_controlhead); |
||
229 | enables |= OHCI_CTRL_CLE; |
||
230 | temp |= OHCI_CLF; |
||
231 | } |
||
232 | if (ahcd->ed_bulktail) { |
||
233 | admhc_writel(ahcd, find_head (ahcd->ed_bulktail)->dma, |
||
234 | &ahcd->regs->ed_bulkhead); |
||
235 | enables |= OHCI_CTRL_BLE; |
||
236 | temp |= OHCI_BLF; |
||
237 | } |
||
238 | } |
||
239 | if (hcd->self.bandwidth_isoc_reqs || hcd->self.bandwidth_int_reqs) |
||
240 | enables |= OHCI_CTRL_PLE|OHCI_CTRL_IE; |
||
241 | if (enables) { |
||
242 | admhc_dbg(ahcd, "restarting schedules ... %08x\n", enables); |
||
243 | ahcd->hc_control |= enables; |
||
244 | admhc_writel(ahcd, ahcd->hc_control, &ahcd->ahcd->regs->control); |
||
245 | if (temp) |
||
246 | admhc_writel(ahcd, temp, &ahcd->regs->cmdstatus); |
||
247 | (void) admhc_readl(ahcd, &ahcd->regs->control); |
||
248 | } |
||
249 | |||
250 | return 0; |
||
251 | } |
||
252 | |||
253 | static int admhc_bus_suspend(struct usb_hcd *hcd) |
||
254 | { |
||
255 | struct admhcd *ahcd = hcd_to_admhcd(hcd); |
||
256 | int rc; |
||
257 | |||
258 | spin_lock_irq(&ahcd->lock); |
||
259 | |||
260 | if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) |
||
261 | rc = -ESHUTDOWN; |
||
262 | else |
||
263 | rc = admhc_rh_suspend(ahcd, 0); |
||
264 | spin_unlock_irq(&ahcd->lock); |
||
265 | return rc; |
||
266 | } |
||
267 | |||
268 | static int admhc_bus_resume(struct usb_hcd *hcd) |
||
269 | { |
||
270 | struct admhcd *ahcd = hcd_to_admhcd(hcd); |
||
271 | int rc; |
||
272 | |||
273 | if (time_before(jiffies, ahcd->next_statechange)) |
||
274 | msleep(5); |
||
275 | |||
276 | spin_lock_irq(&ahcd->lock); |
||
277 | |||
278 | if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) |
||
279 | rc = -ESHUTDOWN; |
||
280 | else |
||
281 | rc = admhc_rh_resume(ahcd); |
||
282 | spin_unlock_irq(&ahcd->lock); |
||
283 | |||
284 | /* poll until we know a device is connected or we autostop */ |
||
285 | if (rc == 0) |
||
286 | usb_hcd_poll_rh_status(hcd); |
||
287 | return rc; |
||
288 | } |
||
289 | |||
290 | /* Carry out polling-, autostop-, and autoresume-related state changes */ |
||
291 | static int admhc_root_hub_state_changes(struct admhcd *ahcd, int changed, |
||
292 | int any_connected) |
||
293 | { |
||
294 | int poll_rh = 1; |
||
295 | |||
296 | switch (ahcd->hc_control & OHCI_CTRL_HCFS) { |
||
297 | |||
298 | case OHCI_USB_OPER: |
||
299 | /* keep on polling until we know a device is connected |
||
300 | * and RHSC is enabled */ |
||
301 | if (!ahcd->autostop) { |
||
302 | if (any_connected || |
||
303 | !device_may_wakeup(&admhcd_to_hcd(ahcd) |
||
304 | ->self.root_hub->dev)) { |
||
305 | if (admhc_readl(ahcd, &ahcd->regs->int_enable) & |
||
306 | OHCI_INTR_RHSC) |
||
307 | poll_rh = 0; |
||
308 | } else { |
||
309 | ahcd->autostop = 1; |
||
310 | ahcd->next_statechange = jiffies + HZ; |
||
311 | } |
||
312 | |||
313 | /* if no devices have been attached for one second, autostop */ |
||
314 | } else { |
||
315 | if (changed || any_connected) { |
||
316 | ahcd->autostop = 0; |
||
317 | ahcd->next_statechange = jiffies + |
||
318 | STATECHANGE_DELAY; |
||
319 | } else if (time_after_eq(jiffies, |
||
320 | ahcd->next_statechange) |
||
321 | && !ahcd->ed_rm_list |
||
322 | && !(ahcd->hc_control & |
||
323 | OHCI_SCHED_ENABLES)) { |
||
324 | ahcd_rh_suspend(ahcd, 1); |
||
325 | } |
||
326 | } |
||
327 | break; |
||
328 | |||
329 | /* if there is a port change, autostart or ask to be resumed */ |
||
330 | case OHCI_USB_SUSPEND: |
||
331 | case OHCI_USB_RESUME: |
||
332 | if (changed) { |
||
333 | if (ahcd->autostop) |
||
334 | admhc_rh_resume(ahcd); |
||
335 | else |
||
336 | usb_hcd_resume_root_hub(admhcd_to_hcd(ahcd)); |
||
337 | } else { |
||
338 | /* everything is idle, no need for polling */ |
||
339 | poll_rh = 0; |
||
340 | } |
||
341 | break; |
||
342 | } |
||
343 | return poll_rh; |
||
344 | } |
||
345 | |||
346 | /*-------------------------------------------------------------------------*/ |
||
347 | |||
348 | /* must not be called from interrupt context */ |
||
349 | static int admhc_restart(struct admhcd *ahcd) |
||
350 | { |
||
351 | int temp; |
||
352 | int i; |
||
353 | struct urb_priv *priv; |
||
354 | |||
355 | /* mark any devices gone, so they do nothing till khubd disconnects. |
||
356 | * recycle any "live" eds/tds (and urbs) right away. |
||
357 | * later, khubd disconnect processing will recycle the other state, |
||
358 | * (either as disconnect/reconnect, or maybe someday as a reset). |
||
359 | */ |
||
360 | spin_lock_irq(&ahcd->lock); |
||
361 | admhc_disable(ahcd); |
||
362 | usb_root_hub_lost_power(admhcd_to_hcd(ahcd)->self.root_hub); |
||
363 | if (!list_empty(&ahcd->pending)) |
||
364 | admhc_dbg(ahcd, "abort schedule...\n"); |
||
365 | list_for_each_entry(priv, &ahcd->pending, pending) { |
||
366 | struct urb *urb = priv->td[0]->urb; |
||
367 | struct ed *ed = priv->ed; |
||
368 | |||
369 | switch (ed->state) { |
||
370 | case ED_OPER: |
||
371 | ed->state = ED_UNLINK; |
||
372 | ed->hwINFO |= cpu_to_hc32(ahcd, ED_DEQUEUE); |
||
373 | ed_deschedule (ahcd, ed); |
||
374 | |||
375 | ed->ed_next = ahcd->ed_rm_list; |
||
376 | ed->ed_prev = NULL; |
||
377 | ahcd->ed_rm_list = ed; |
||
378 | /* FALLTHROUGH */ |
||
379 | case ED_UNLINK: |
||
380 | break; |
||
381 | default: |
||
382 | admhc_dbg(ahcd, "bogus ed %p state %d\n", |
||
383 | ed, ed->state); |
||
384 | } |
||
385 | |||
386 | if (!urb->unlinked) |
||
387 | urb->unlinked = -ESHUTDOWN; |
||
388 | } |
||
389 | finish_unlinks(ahcd, 0); |
||
390 | spin_unlock_irq(&ahcd->lock); |
||
391 | |||
392 | /* paranoia, in case that didn't work: */ |
||
393 | |||
394 | /* empty the interrupt branches */ |
||
395 | for (i = 0; i < NUM_INTS; i++) ahcd->load[i] = 0; |
||
396 | for (i = 0; i < NUM_INTS; i++) ahcd->hcca->int_table[i] = 0; |
||
397 | |||
398 | /* no EDs to remove */ |
||
399 | ahcd->ed_rm_list = NULL; |
||
400 | |||
401 | /* empty control and bulk lists */ |
||
402 | ahcd->ed_controltail = NULL; |
||
403 | ahcd->ed_bulktail = NULL; |
||
404 | |||
405 | if ((temp = admhc_run(ahcd)) < 0) { |
||
406 | admhc_err(ahcd, "can't restart, %d\n", temp); |
||
407 | return temp; |
||
408 | } else { |
||
409 | /* here we "know" root ports should always stay powered, |
||
410 | * and that if we try to turn them back on the root hub |
||
411 | * will respond to CSC processing. |
||
412 | */ |
||
413 | i = ahcd->num_ports; |
||
414 | while (i--) |
||
415 | admhc_writel(ahcd, RH_PS_PSS, |
||
416 | &ahcd->regs->portstatus[i]); |
||
417 | admhc_dbg(ahcd, "restart complete\n"); |
||
418 | } |
||
419 | return 0; |
||
420 | } |
||
421 | |||
422 | #else /* CONFIG_PM */ |
||
423 | |||
424 | static inline int admhc_rh_resume(struct admhcd *ahcd) |
||
425 | { |
||
426 | return 0; |
||
427 | } |
||
428 | |||
429 | /* Carry out polling-related state changes. |
||
430 | * autostop isn't used when CONFIG_PM is turned off. |
||
431 | */ |
||
432 | static int admhc_root_hub_state_changes(struct admhcd *ahcd, int changed, |
||
433 | int any_connected) |
||
434 | { |
||
435 | /* If INSM is enabled, don't poll */ |
||
436 | if (admhc_readl(ahcd, &ahcd->regs->int_enable) & ADMHC_INTR_INSM) |
||
437 | return 0; |
||
438 | |||
439 | /* If no status changes are pending, enable status-change interrupts */ |
||
440 | if (!changed) { |
||
441 | admhc_intr_enable(ahcd, ADMHC_INTR_INSM); |
||
442 | return 0; |
||
443 | } |
||
444 | |||
445 | return 1; |
||
446 | } |
||
447 | |||
448 | #endif /* CONFIG_PM */ |
||
449 |