diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/host/ehci-hub.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/host/ehci-hub.c')
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 553 |
1 files changed, 553 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c new file mode 100644 index 000000000000..2373537fabed --- /dev/null +++ b/drivers/usb/host/ehci-hub.c | |||
@@ -0,0 +1,553 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2001-2002 by David Brownell | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the | ||
6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
7 | * option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, but | ||
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | * for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software Foundation, | ||
16 | * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
17 | */ | ||
18 | |||
19 | /* this file is part of ehci-hcd.c */ | ||
20 | |||
21 | /*-------------------------------------------------------------------------*/ | ||
22 | |||
23 | /* | ||
24 | * EHCI Root Hub ... the nonsharable stuff | ||
25 | * | ||
26 | * Registers don't need cpu_to_le32, that happens transparently | ||
27 | */ | ||
28 | |||
29 | /*-------------------------------------------------------------------------*/ | ||
30 | |||
31 | #ifdef CONFIG_PM | ||
32 | |||
33 | static int ehci_hub_suspend (struct usb_hcd *hcd) | ||
34 | { | ||
35 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | ||
36 | int port; | ||
37 | |||
38 | if (time_before (jiffies, ehci->next_statechange)) | ||
39 | msleep(5); | ||
40 | |||
41 | port = HCS_N_PORTS (ehci->hcs_params); | ||
42 | spin_lock_irq (&ehci->lock); | ||
43 | |||
44 | /* stop schedules, clean any completed work */ | ||
45 | if (HC_IS_RUNNING(hcd->state)) { | ||
46 | ehci_quiesce (ehci); | ||
47 | hcd->state = HC_STATE_QUIESCING; | ||
48 | } | ||
49 | ehci->command = readl (&ehci->regs->command); | ||
50 | if (ehci->reclaim) | ||
51 | ehci->reclaim_ready = 1; | ||
52 | ehci_work(ehci, NULL); | ||
53 | |||
54 | /* suspend any active/unsuspended ports, maybe allow wakeup */ | ||
55 | while (port--) { | ||
56 | u32 __iomem *reg = &ehci->regs->port_status [port]; | ||
57 | u32 t1 = readl (reg); | ||
58 | u32 t2 = t1; | ||
59 | |||
60 | if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) | ||
61 | t2 |= PORT_SUSPEND; | ||
62 | if (hcd->remote_wakeup) | ||
63 | t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; | ||
64 | else | ||
65 | t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); | ||
66 | |||
67 | if (t1 != t2) { | ||
68 | ehci_vdbg (ehci, "port %d, %08x -> %08x\n", | ||
69 | port + 1, t1, t2); | ||
70 | writel (t2, reg); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | /* turn off now-idle HC */ | ||
75 | ehci_halt (ehci); | ||
76 | hcd->state = HC_STATE_SUSPENDED; | ||
77 | |||
78 | ehci->next_statechange = jiffies + msecs_to_jiffies(10); | ||
79 | spin_unlock_irq (&ehci->lock); | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | |||
84 | /* caller has locked the root hub, and should reset/reinit on error */ | ||
85 | static int ehci_hub_resume (struct usb_hcd *hcd) | ||
86 | { | ||
87 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | ||
88 | u32 temp; | ||
89 | int i; | ||
90 | int intr_enable; | ||
91 | |||
92 | if (time_before (jiffies, ehci->next_statechange)) | ||
93 | msleep(5); | ||
94 | spin_lock_irq (&ehci->lock); | ||
95 | |||
96 | /* re-init operational registers in case we lost power */ | ||
97 | if (readl (&ehci->regs->intr_enable) == 0) { | ||
98 | /* at least some APM implementations will try to deliver | ||
99 | * IRQs right away, so delay them until we're ready. | ||
100 | */ | ||
101 | intr_enable = 1; | ||
102 | writel (0, &ehci->regs->segment); | ||
103 | writel (ehci->periodic_dma, &ehci->regs->frame_list); | ||
104 | writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); | ||
105 | } else | ||
106 | intr_enable = 0; | ||
107 | ehci_dbg(ehci, "resume root hub%s\n", | ||
108 | intr_enable ? " after power loss" : ""); | ||
109 | |||
110 | /* restore CMD_RUN, framelist size, and irq threshold */ | ||
111 | writel (ehci->command, &ehci->regs->command); | ||
112 | |||
113 | /* take ports out of suspend */ | ||
114 | i = HCS_N_PORTS (ehci->hcs_params); | ||
115 | while (i--) { | ||
116 | temp = readl (&ehci->regs->port_status [i]); | ||
117 | temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); | ||
118 | if (temp & PORT_SUSPEND) { | ||
119 | ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); | ||
120 | temp |= PORT_RESUME; | ||
121 | } | ||
122 | writel (temp, &ehci->regs->port_status [i]); | ||
123 | } | ||
124 | i = HCS_N_PORTS (ehci->hcs_params); | ||
125 | mdelay (20); | ||
126 | while (i--) { | ||
127 | temp = readl (&ehci->regs->port_status [i]); | ||
128 | if ((temp & PORT_SUSPEND) == 0) | ||
129 | continue; | ||
130 | temp &= ~PORT_RESUME; | ||
131 | writel (temp, &ehci->regs->port_status [i]); | ||
132 | ehci_vdbg (ehci, "resumed port %d\n", i + 1); | ||
133 | } | ||
134 | (void) readl (&ehci->regs->command); | ||
135 | |||
136 | /* maybe re-activate the schedule(s) */ | ||
137 | temp = 0; | ||
138 | if (ehci->async->qh_next.qh) | ||
139 | temp |= CMD_ASE; | ||
140 | if (ehci->periodic_sched) | ||
141 | temp |= CMD_PSE; | ||
142 | if (temp) { | ||
143 | ehci->command |= temp; | ||
144 | writel (ehci->command, &ehci->regs->command); | ||
145 | } | ||
146 | |||
147 | ehci->next_statechange = jiffies + msecs_to_jiffies(5); | ||
148 | hcd->state = HC_STATE_RUNNING; | ||
149 | |||
150 | /* Now we can safely re-enable irqs */ | ||
151 | if (intr_enable) | ||
152 | writel (INTR_MASK, &ehci->regs->intr_enable); | ||
153 | |||
154 | spin_unlock_irq (&ehci->lock); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | #else | ||
159 | |||
160 | #define ehci_hub_suspend NULL | ||
161 | #define ehci_hub_resume NULL | ||
162 | |||
163 | #endif /* CONFIG_PM */ | ||
164 | |||
165 | /*-------------------------------------------------------------------------*/ | ||
166 | |||
167 | static int check_reset_complete ( | ||
168 | struct ehci_hcd *ehci, | ||
169 | int index, | ||
170 | int port_status | ||
171 | ) { | ||
172 | if (!(port_status & PORT_CONNECT)) { | ||
173 | ehci->reset_done [index] = 0; | ||
174 | return port_status; | ||
175 | } | ||
176 | |||
177 | /* if reset finished and it's still not enabled -- handoff */ | ||
178 | if (!(port_status & PORT_PE)) { | ||
179 | |||
180 | /* with integrated TT, there's nobody to hand it to! */ | ||
181 | if (ehci_is_TDI(ehci)) { | ||
182 | ehci_dbg (ehci, | ||
183 | "Failed to enable port %d on root hub TT\n", | ||
184 | index+1); | ||
185 | return port_status; | ||
186 | } | ||
187 | |||
188 | ehci_dbg (ehci, "port %d full speed --> companion\n", | ||
189 | index + 1); | ||
190 | |||
191 | // what happens if HCS_N_CC(params) == 0 ? | ||
192 | port_status |= PORT_OWNER; | ||
193 | writel (port_status, &ehci->regs->port_status [index]); | ||
194 | |||
195 | } else | ||
196 | ehci_dbg (ehci, "port %d high speed\n", index + 1); | ||
197 | |||
198 | return port_status; | ||
199 | } | ||
200 | |||
201 | /*-------------------------------------------------------------------------*/ | ||
202 | |||
203 | |||
204 | /* build "status change" packet (one or two bytes) from HC registers */ | ||
205 | |||
206 | static int | ||
207 | ehci_hub_status_data (struct usb_hcd *hcd, char *buf) | ||
208 | { | ||
209 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | ||
210 | u32 temp, status = 0; | ||
211 | int ports, i, retval = 1; | ||
212 | unsigned long flags; | ||
213 | |||
214 | /* if !USB_SUSPEND, root hub timers won't get shut down ... */ | ||
215 | if (!HC_IS_RUNNING(hcd->state)) | ||
216 | return 0; | ||
217 | |||
218 | /* init status to no-changes */ | ||
219 | buf [0] = 0; | ||
220 | ports = HCS_N_PORTS (ehci->hcs_params); | ||
221 | if (ports > 7) { | ||
222 | buf [1] = 0; | ||
223 | retval++; | ||
224 | } | ||
225 | |||
226 | /* no hub change reports (bit 0) for now (power, ...) */ | ||
227 | |||
228 | /* port N changes (bit N)? */ | ||
229 | spin_lock_irqsave (&ehci->lock, flags); | ||
230 | for (i = 0; i < ports; i++) { | ||
231 | temp = readl (&ehci->regs->port_status [i]); | ||
232 | if (temp & PORT_OWNER) { | ||
233 | /* don't report this in GetPortStatus */ | ||
234 | if (temp & PORT_CSC) { | ||
235 | temp &= ~PORT_CSC; | ||
236 | writel (temp, &ehci->regs->port_status [i]); | ||
237 | } | ||
238 | continue; | ||
239 | } | ||
240 | if (!(temp & PORT_CONNECT)) | ||
241 | ehci->reset_done [i] = 0; | ||
242 | if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0 | ||
243 | // PORT_STAT_C_SUSPEND? | ||
244 | || ((temp & PORT_RESUME) != 0 | ||
245 | && time_after (jiffies, | ||
246 | ehci->reset_done [i]))) { | ||
247 | if (i < 7) | ||
248 | buf [0] |= 1 << (i + 1); | ||
249 | else | ||
250 | buf [1] |= 1 << (i - 7); | ||
251 | status = STS_PCD; | ||
252 | } | ||
253 | } | ||
254 | /* FIXME autosuspend idle root hubs */ | ||
255 | spin_unlock_irqrestore (&ehci->lock, flags); | ||
256 | return status ? retval : 0; | ||
257 | } | ||
258 | |||
259 | /*-------------------------------------------------------------------------*/ | ||
260 | |||
261 | static void | ||
262 | ehci_hub_descriptor ( | ||
263 | struct ehci_hcd *ehci, | ||
264 | struct usb_hub_descriptor *desc | ||
265 | ) { | ||
266 | int ports = HCS_N_PORTS (ehci->hcs_params); | ||
267 | u16 temp; | ||
268 | |||
269 | desc->bDescriptorType = 0x29; | ||
270 | desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */ | ||
271 | desc->bHubContrCurrent = 0; | ||
272 | |||
273 | desc->bNbrPorts = ports; | ||
274 | temp = 1 + (ports / 8); | ||
275 | desc->bDescLength = 7 + 2 * temp; | ||
276 | |||
277 | /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ | ||
278 | memset (&desc->bitmap [0], 0, temp); | ||
279 | memset (&desc->bitmap [temp], 0xff, temp); | ||
280 | |||
281 | temp = 0x0008; /* per-port overcurrent reporting */ | ||
282 | if (HCS_PPC (ehci->hcs_params)) | ||
283 | temp |= 0x0001; /* per-port power control */ | ||
284 | #if 0 | ||
285 | // re-enable when we support USB_PORT_FEAT_INDICATOR below. | ||
286 | if (HCS_INDICATOR (ehci->hcs_params)) | ||
287 | temp |= 0x0080; /* per-port indicators (LEDs) */ | ||
288 | #endif | ||
289 | desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp); | ||
290 | } | ||
291 | |||
292 | /*-------------------------------------------------------------------------*/ | ||
293 | |||
294 | #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) | ||
295 | |||
296 | static int ehci_hub_control ( | ||
297 | struct usb_hcd *hcd, | ||
298 | u16 typeReq, | ||
299 | u16 wValue, | ||
300 | u16 wIndex, | ||
301 | char *buf, | ||
302 | u16 wLength | ||
303 | ) { | ||
304 | struct ehci_hcd *ehci = hcd_to_ehci (hcd); | ||
305 | int ports = HCS_N_PORTS (ehci->hcs_params); | ||
306 | u32 temp, status; | ||
307 | unsigned long flags; | ||
308 | int retval = 0; | ||
309 | |||
310 | /* | ||
311 | * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. | ||
312 | * HCS_INDICATOR may say we can change LEDs to off/amber/green. | ||
313 | * (track current state ourselves) ... blink for diagnostics, | ||
314 | * power, "this is the one", etc. EHCI spec supports this. | ||
315 | */ | ||
316 | |||
317 | spin_lock_irqsave (&ehci->lock, flags); | ||
318 | switch (typeReq) { | ||
319 | case ClearHubFeature: | ||
320 | switch (wValue) { | ||
321 | case C_HUB_LOCAL_POWER: | ||
322 | case C_HUB_OVER_CURRENT: | ||
323 | /* no hub-wide feature/status flags */ | ||
324 | break; | ||
325 | default: | ||
326 | goto error; | ||
327 | } | ||
328 | break; | ||
329 | case ClearPortFeature: | ||
330 | if (!wIndex || wIndex > ports) | ||
331 | goto error; | ||
332 | wIndex--; | ||
333 | temp = readl (&ehci->regs->port_status [wIndex]); | ||
334 | if (temp & PORT_OWNER) | ||
335 | break; | ||
336 | |||
337 | switch (wValue) { | ||
338 | case USB_PORT_FEAT_ENABLE: | ||
339 | writel (temp & ~PORT_PE, | ||
340 | &ehci->regs->port_status [wIndex]); | ||
341 | break; | ||
342 | case USB_PORT_FEAT_C_ENABLE: | ||
343 | writel (temp | PORT_PEC, | ||
344 | &ehci->regs->port_status [wIndex]); | ||
345 | break; | ||
346 | case USB_PORT_FEAT_SUSPEND: | ||
347 | if (temp & PORT_RESET) | ||
348 | goto error; | ||
349 | if (temp & PORT_SUSPEND) { | ||
350 | if ((temp & PORT_PE) == 0) | ||
351 | goto error; | ||
352 | /* resume signaling for 20 msec */ | ||
353 | writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME, | ||
354 | &ehci->regs->port_status [wIndex]); | ||
355 | ehci->reset_done [wIndex] = jiffies | ||
356 | + msecs_to_jiffies (20); | ||
357 | } | ||
358 | break; | ||
359 | case USB_PORT_FEAT_C_SUSPEND: | ||
360 | /* we auto-clear this feature */ | ||
361 | break; | ||
362 | case USB_PORT_FEAT_POWER: | ||
363 | if (HCS_PPC (ehci->hcs_params)) | ||
364 | writel (temp & ~PORT_POWER, | ||
365 | &ehci->regs->port_status [wIndex]); | ||
366 | break; | ||
367 | case USB_PORT_FEAT_C_CONNECTION: | ||
368 | writel (temp | PORT_CSC, | ||
369 | &ehci->regs->port_status [wIndex]); | ||
370 | break; | ||
371 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
372 | writel (temp | PORT_OCC, | ||
373 | &ehci->regs->port_status [wIndex]); | ||
374 | break; | ||
375 | case USB_PORT_FEAT_C_RESET: | ||
376 | /* GetPortStatus clears reset */ | ||
377 | break; | ||
378 | default: | ||
379 | goto error; | ||
380 | } | ||
381 | readl (&ehci->regs->command); /* unblock posted write */ | ||
382 | break; | ||
383 | case GetHubDescriptor: | ||
384 | ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) | ||
385 | buf); | ||
386 | break; | ||
387 | case GetHubStatus: | ||
388 | /* no hub-wide feature/status flags */ | ||
389 | memset (buf, 0, 4); | ||
390 | //cpu_to_le32s ((u32 *) buf); | ||
391 | break; | ||
392 | case GetPortStatus: | ||
393 | if (!wIndex || wIndex > ports) | ||
394 | goto error; | ||
395 | wIndex--; | ||
396 | status = 0; | ||
397 | temp = readl (&ehci->regs->port_status [wIndex]); | ||
398 | |||
399 | // wPortChange bits | ||
400 | if (temp & PORT_CSC) | ||
401 | status |= 1 << USB_PORT_FEAT_C_CONNECTION; | ||
402 | if (temp & PORT_PEC) | ||
403 | status |= 1 << USB_PORT_FEAT_C_ENABLE; | ||
404 | if (temp & PORT_OCC) | ||
405 | status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; | ||
406 | |||
407 | /* whoever resumes must GetPortStatus to complete it!! */ | ||
408 | if ((temp & PORT_RESUME) | ||
409 | && time_after (jiffies, | ||
410 | ehci->reset_done [wIndex])) { | ||
411 | status |= 1 << USB_PORT_FEAT_C_SUSPEND; | ||
412 | ehci->reset_done [wIndex] = 0; | ||
413 | |||
414 | /* stop resume signaling */ | ||
415 | temp = readl (&ehci->regs->port_status [wIndex]); | ||
416 | writel (temp & ~PORT_RESUME, | ||
417 | &ehci->regs->port_status [wIndex]); | ||
418 | retval = handshake ( | ||
419 | &ehci->regs->port_status [wIndex], | ||
420 | PORT_RESUME, 0, 2000 /* 2msec */); | ||
421 | if (retval != 0) { | ||
422 | ehci_err (ehci, "port %d resume error %d\n", | ||
423 | wIndex + 1, retval); | ||
424 | goto error; | ||
425 | } | ||
426 | temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); | ||
427 | } | ||
428 | |||
429 | /* whoever resets must GetPortStatus to complete it!! */ | ||
430 | if ((temp & PORT_RESET) | ||
431 | && time_after (jiffies, | ||
432 | ehci->reset_done [wIndex])) { | ||
433 | status |= 1 << USB_PORT_FEAT_C_RESET; | ||
434 | ehci->reset_done [wIndex] = 0; | ||
435 | |||
436 | /* force reset to complete */ | ||
437 | writel (temp & ~PORT_RESET, | ||
438 | &ehci->regs->port_status [wIndex]); | ||
439 | retval = handshake ( | ||
440 | &ehci->regs->port_status [wIndex], | ||
441 | PORT_RESET, 0, 500); | ||
442 | if (retval != 0) { | ||
443 | ehci_err (ehci, "port %d reset error %d\n", | ||
444 | wIndex + 1, retval); | ||
445 | goto error; | ||
446 | } | ||
447 | |||
448 | /* see what we found out */ | ||
449 | temp = check_reset_complete (ehci, wIndex, | ||
450 | readl (&ehci->regs->port_status [wIndex])); | ||
451 | } | ||
452 | |||
453 | // don't show wPortStatus if it's owned by a companion hc | ||
454 | if (!(temp & PORT_OWNER)) { | ||
455 | if (temp & PORT_CONNECT) { | ||
456 | status |= 1 << USB_PORT_FEAT_CONNECTION; | ||
457 | // status may be from integrated TT | ||
458 | status |= ehci_port_speed(ehci, temp); | ||
459 | } | ||
460 | if (temp & PORT_PE) | ||
461 | status |= 1 << USB_PORT_FEAT_ENABLE; | ||
462 | if (temp & (PORT_SUSPEND|PORT_RESUME)) | ||
463 | status |= 1 << USB_PORT_FEAT_SUSPEND; | ||
464 | if (temp & PORT_OC) | ||
465 | status |= 1 << USB_PORT_FEAT_OVER_CURRENT; | ||
466 | if (temp & PORT_RESET) | ||
467 | status |= 1 << USB_PORT_FEAT_RESET; | ||
468 | if (temp & PORT_POWER) | ||
469 | status |= 1 << USB_PORT_FEAT_POWER; | ||
470 | } | ||
471 | |||
472 | #ifndef EHCI_VERBOSE_DEBUG | ||
473 | if (status & ~0xffff) /* only if wPortChange is interesting */ | ||
474 | #endif | ||
475 | dbg_port (ehci, "GetStatus", wIndex + 1, temp); | ||
476 | // we "know" this alignment is good, caller used kmalloc()... | ||
477 | *((__le32 *) buf) = cpu_to_le32 (status); | ||
478 | break; | ||
479 | case SetHubFeature: | ||
480 | switch (wValue) { | ||
481 | case C_HUB_LOCAL_POWER: | ||
482 | case C_HUB_OVER_CURRENT: | ||
483 | /* no hub-wide feature/status flags */ | ||
484 | break; | ||
485 | default: | ||
486 | goto error; | ||
487 | } | ||
488 | break; | ||
489 | case SetPortFeature: | ||
490 | if (!wIndex || wIndex > ports) | ||
491 | goto error; | ||
492 | wIndex--; | ||
493 | temp = readl (&ehci->regs->port_status [wIndex]); | ||
494 | if (temp & PORT_OWNER) | ||
495 | break; | ||
496 | |||
497 | switch (wValue) { | ||
498 | case USB_PORT_FEAT_SUSPEND: | ||
499 | if ((temp & PORT_PE) == 0 | ||
500 | || (temp & PORT_RESET) != 0) | ||
501 | goto error; | ||
502 | if (hcd->remote_wakeup) | ||
503 | temp |= PORT_WAKE_BITS; | ||
504 | writel (temp | PORT_SUSPEND, | ||
505 | &ehci->regs->port_status [wIndex]); | ||
506 | break; | ||
507 | case USB_PORT_FEAT_POWER: | ||
508 | if (HCS_PPC (ehci->hcs_params)) | ||
509 | writel (temp | PORT_POWER, | ||
510 | &ehci->regs->port_status [wIndex]); | ||
511 | break; | ||
512 | case USB_PORT_FEAT_RESET: | ||
513 | if (temp & PORT_RESUME) | ||
514 | goto error; | ||
515 | /* line status bits may report this as low speed, | ||
516 | * which can be fine if this root hub has a | ||
517 | * transaction translator built in. | ||
518 | */ | ||
519 | if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT | ||
520 | && !ehci_is_TDI(ehci) | ||
521 | && PORT_USB11 (temp)) { | ||
522 | ehci_dbg (ehci, | ||
523 | "port %d low speed --> companion\n", | ||
524 | wIndex + 1); | ||
525 | temp |= PORT_OWNER; | ||
526 | } else { | ||
527 | ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); | ||
528 | temp |= PORT_RESET; | ||
529 | temp &= ~PORT_PE; | ||
530 | |||
531 | /* | ||
532 | * caller must wait, then call GetPortStatus | ||
533 | * usb 2.0 spec says 50 ms resets on root | ||
534 | */ | ||
535 | ehci->reset_done [wIndex] = jiffies | ||
536 | + msecs_to_jiffies (50); | ||
537 | } | ||
538 | writel (temp, &ehci->regs->port_status [wIndex]); | ||
539 | break; | ||
540 | default: | ||
541 | goto error; | ||
542 | } | ||
543 | readl (&ehci->regs->command); /* unblock posted writes */ | ||
544 | break; | ||
545 | |||
546 | default: | ||
547 | error: | ||
548 | /* "stall" on error */ | ||
549 | retval = -EPIPE; | ||
550 | } | ||
551 | spin_unlock_irqrestore (&ehci->lock, flags); | ||
552 | return retval; | ||
553 | } | ||