blob: 0a56dfa2745d645d9dd3a1041478d5cb03a50a24 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
David Brownelld49d4312005-05-07 13:21:50 -07002 * Copyright (C) 2001-2004 by David Brownell
David Brownell53bd6a62006-08-30 14:50:06 -07003 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * 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
Alan Stern0c0382e2005-10-13 17:08:02 -040033static int ehci_bus_suspend (struct usb_hcd *hcd)
Linus Torvalds1da177e2005-04-16 15:20:36 -070034{
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)
Greg Kroah-Hartman64f89792006-10-17 13:57:18 -070051 ehci->reclaim_ready = 1;
David Howells7d12e782006-10-05 14:55:46 +010052 ehci_work(ehci);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54 /* suspend any active/unsuspended ports, maybe allow wakeup */
55 while (port--) {
56 u32 __iomem *reg = &ehci->regs->port_status [port];
David Brownell10f65242005-08-31 10:55:38 -070057 u32 t1 = readl (reg) & ~PORT_RWC_BITS;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 u32 t2 = t1;
59
60 if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
61 t2 |= PORT_SUSPEND;
David Brownell2c1c3c42005-11-07 15:24:46 -080062 if (device_may_wakeup(&hcd->self.root_hub->dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 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 */
David Brownell4756ae52005-05-09 17:23:51 -070075 del_timer_sync (&ehci->watchdog);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 ehci_halt (ehci);
77 hcd->state = HC_STATE_SUSPENDED;
78
79 ehci->next_statechange = jiffies + msecs_to_jiffies(10);
80 spin_unlock_irq (&ehci->lock);
81 return 0;
82}
83
84
85/* caller has locked the root hub, and should reset/reinit on error */
Alan Stern0c0382e2005-10-13 17:08:02 -040086static int ehci_bus_resume (struct usb_hcd *hcd)
Linus Torvalds1da177e2005-04-16 15:20:36 -070087{
88 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
89 u32 temp;
90 int i;
91 int intr_enable;
92
93 if (time_before (jiffies, ehci->next_statechange))
94 msleep(5);
95 spin_lock_irq (&ehci->lock);
96
David Brownellf03c17f2005-11-23 15:45:28 -080097 /* Ideally and we've got a real resume here, and no port's power
98 * was lost. (For PCI, that means Vaux was maintained.) But we
99 * could instead be restoring a swsusp snapshot -- so that BIOS was
100 * the last user of the controller, not reset/pm hardware keeping
101 * state we gave to it.
102 */
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 /* re-init operational registers in case we lost power */
105 if (readl (&ehci->regs->intr_enable) == 0) {
David Brownell53bd6a62006-08-30 14:50:06 -0700106 /* at least some APM implementations will try to deliver
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 * IRQs right away, so delay them until we're ready.
David Brownell53bd6a62006-08-30 14:50:06 -0700108 */
109 intr_enable = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 writel (0, &ehci->regs->segment);
111 writel (ehci->periodic_dma, &ehci->regs->frame_list);
112 writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
113 } else
114 intr_enable = 0;
115 ehci_dbg(ehci, "resume root hub%s\n",
116 intr_enable ? " after power loss" : "");
117
118 /* restore CMD_RUN, framelist size, and irq threshold */
119 writel (ehci->command, &ehci->regs->command);
120
121 /* take ports out of suspend */
122 i = HCS_N_PORTS (ehci->hcs_params);
123 while (i--) {
124 temp = readl (&ehci->regs->port_status [i]);
David Brownell10f65242005-08-31 10:55:38 -0700125 temp &= ~(PORT_RWC_BITS
126 | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 if (temp & PORT_SUSPEND) {
128 ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
129 temp |= PORT_RESUME;
130 }
131 writel (temp, &ehci->regs->port_status [i]);
132 }
133 i = HCS_N_PORTS (ehci->hcs_params);
134 mdelay (20);
135 while (i--) {
136 temp = readl (&ehci->regs->port_status [i]);
137 if ((temp & PORT_SUSPEND) == 0)
138 continue;
David Brownell10f65242005-08-31 10:55:38 -0700139 temp &= ~(PORT_RWC_BITS | PORT_RESUME);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 writel (temp, &ehci->regs->port_status [i]);
141 ehci_vdbg (ehci, "resumed port %d\n", i + 1);
142 }
143 (void) readl (&ehci->regs->command);
144
145 /* maybe re-activate the schedule(s) */
146 temp = 0;
147 if (ehci->async->qh_next.qh)
148 temp |= CMD_ASE;
149 if (ehci->periodic_sched)
150 temp |= CMD_PSE;
151 if (temp) {
152 ehci->command |= temp;
153 writel (ehci->command, &ehci->regs->command);
154 }
155
156 ehci->next_statechange = jiffies + msecs_to_jiffies(5);
157 hcd->state = HC_STATE_RUNNING;
158
159 /* Now we can safely re-enable irqs */
160 if (intr_enable)
161 writel (INTR_MASK, &ehci->regs->intr_enable);
162
163 spin_unlock_irq (&ehci->lock);
164 return 0;
165}
166
167#else
168
Alan Stern0c0382e2005-10-13 17:08:02 -0400169#define ehci_bus_suspend NULL
170#define ehci_bus_resume NULL
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
172#endif /* CONFIG_PM */
173
174/*-------------------------------------------------------------------------*/
175
176static int check_reset_complete (
177 struct ehci_hcd *ehci,
178 int index,
179 int port_status
180) {
181 if (!(port_status & PORT_CONNECT)) {
182 ehci->reset_done [index] = 0;
183 return port_status;
184 }
185
186 /* if reset finished and it's still not enabled -- handoff */
187 if (!(port_status & PORT_PE)) {
188
189 /* with integrated TT, there's nobody to hand it to! */
190 if (ehci_is_TDI(ehci)) {
191 ehci_dbg (ehci,
192 "Failed to enable port %d on root hub TT\n",
193 index+1);
194 return port_status;
195 }
196
197 ehci_dbg (ehci, "port %d full speed --> companion\n",
198 index + 1);
199
200 // what happens if HCS_N_CC(params) == 0 ?
201 port_status |= PORT_OWNER;
David Brownell10f65242005-08-31 10:55:38 -0700202 port_status &= ~PORT_RWC_BITS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 writel (port_status, &ehci->regs->port_status [index]);
204
205 } else
206 ehci_dbg (ehci, "port %d high speed\n", index + 1);
207
208 return port_status;
209}
210
211/*-------------------------------------------------------------------------*/
212
213
214/* build "status change" packet (one or two bytes) from HC registers */
215
216static int
217ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
218{
219 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
220 u32 temp, status = 0;
David Brownell93f1a472006-11-16 23:34:58 -0800221 u32 mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 int ports, i, retval = 1;
223 unsigned long flags;
224
225 /* if !USB_SUSPEND, root hub timers won't get shut down ... */
226 if (!HC_IS_RUNNING(hcd->state))
227 return 0;
228
229 /* init status to no-changes */
230 buf [0] = 0;
231 ports = HCS_N_PORTS (ehci->hcs_params);
232 if (ports > 7) {
233 buf [1] = 0;
234 retval++;
235 }
David Brownell53bd6a62006-08-30 14:50:06 -0700236
David Brownell93f1a472006-11-16 23:34:58 -0800237 /* Some boards (mostly VIA?) report bogus overcurrent indications,
238 * causing massive log spam unless we completely ignore them. It
239 * may be relevant that VIA VT8235 controlers, where PORT_POWER is
240 * always set, seem to clear PORT_OCC and PORT_CSC when writing to
241 * PORT_POWER; that's surprising, but maybe within-spec.
242 */
243 if (!ignore_oc)
244 mask = PORT_CSC | PORT_PEC | PORT_OCC;
245 else
246 mask = PORT_CSC | PORT_PEC;
247 // PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND
248
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 /* no hub change reports (bit 0) for now (power, ...) */
250
251 /* port N changes (bit N)? */
252 spin_lock_irqsave (&ehci->lock, flags);
253 for (i = 0; i < ports; i++) {
254 temp = readl (&ehci->regs->port_status [i]);
255 if (temp & PORT_OWNER) {
256 /* don't report this in GetPortStatus */
257 if (temp & PORT_CSC) {
David Brownell10f65242005-08-31 10:55:38 -0700258 temp &= ~PORT_RWC_BITS;
259 temp |= PORT_CSC;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 writel (temp, &ehci->regs->port_status [i]);
261 }
262 continue;
263 }
264 if (!(temp & PORT_CONNECT))
265 ehci->reset_done [i] = 0;
David Brownell93f1a472006-11-16 23:34:58 -0800266 if ((temp & mask) != 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 || ((temp & PORT_RESUME) != 0
268 && time_after (jiffies,
269 ehci->reset_done [i]))) {
270 if (i < 7)
271 buf [0] |= 1 << (i + 1);
272 else
273 buf [1] |= 1 << (i - 7);
274 status = STS_PCD;
275 }
276 }
277 /* FIXME autosuspend idle root hubs */
278 spin_unlock_irqrestore (&ehci->lock, flags);
279 return status ? retval : 0;
280}
281
282/*-------------------------------------------------------------------------*/
283
284static void
285ehci_hub_descriptor (
286 struct ehci_hcd *ehci,
287 struct usb_hub_descriptor *desc
288) {
289 int ports = HCS_N_PORTS (ehci->hcs_params);
290 u16 temp;
291
292 desc->bDescriptorType = 0x29;
293 desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
294 desc->bHubContrCurrent = 0;
295
296 desc->bNbrPorts = ports;
297 temp = 1 + (ports / 8);
298 desc->bDescLength = 7 + 2 * temp;
299
300 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
301 memset (&desc->bitmap [0], 0, temp);
302 memset (&desc->bitmap [temp], 0xff, temp);
303
304 temp = 0x0008; /* per-port overcurrent reporting */
305 if (HCS_PPC (ehci->hcs_params))
306 temp |= 0x0001; /* per-port power control */
David Brownell56c1e262005-04-09 09:00:29 -0700307 else
308 temp |= 0x0002; /* no power switching */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309#if 0
310// re-enable when we support USB_PORT_FEAT_INDICATOR below.
311 if (HCS_INDICATOR (ehci->hcs_params))
312 temp |= 0x0080; /* per-port indicators (LEDs) */
313#endif
314 desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp);
315}
316
317/*-------------------------------------------------------------------------*/
318
David Brownell53bd6a62006-08-30 14:50:06 -0700319#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
321static int ehci_hub_control (
322 struct usb_hcd *hcd,
323 u16 typeReq,
324 u16 wValue,
325 u16 wIndex,
326 char *buf,
327 u16 wLength
328) {
329 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
330 int ports = HCS_N_PORTS (ehci->hcs_params);
331 u32 temp, status;
332 unsigned long flags;
333 int retval = 0;
David Brownellf0d7f272006-11-16 23:56:15 -0800334 unsigned selector;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336 /*
337 * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
338 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
339 * (track current state ourselves) ... blink for diagnostics,
340 * power, "this is the one", etc. EHCI spec supports this.
341 */
342
343 spin_lock_irqsave (&ehci->lock, flags);
344 switch (typeReq) {
345 case ClearHubFeature:
346 switch (wValue) {
347 case C_HUB_LOCAL_POWER:
348 case C_HUB_OVER_CURRENT:
349 /* no hub-wide feature/status flags */
350 break;
351 default:
352 goto error;
353 }
354 break;
355 case ClearPortFeature:
356 if (!wIndex || wIndex > ports)
357 goto error;
358 wIndex--;
359 temp = readl (&ehci->regs->port_status [wIndex]);
360 if (temp & PORT_OWNER)
361 break;
362
363 switch (wValue) {
364 case USB_PORT_FEAT_ENABLE:
365 writel (temp & ~PORT_PE,
366 &ehci->regs->port_status [wIndex]);
367 break;
368 case USB_PORT_FEAT_C_ENABLE:
David Brownell10f65242005-08-31 10:55:38 -0700369 writel((temp & ~PORT_RWC_BITS) | PORT_PEC,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 &ehci->regs->port_status [wIndex]);
371 break;
372 case USB_PORT_FEAT_SUSPEND:
373 if (temp & PORT_RESET)
374 goto error;
David Brownellf8aeb3b2006-01-20 13:55:14 -0800375 if (ehci->no_selective_suspend)
376 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 if (temp & PORT_SUSPEND) {
378 if ((temp & PORT_PE) == 0)
379 goto error;
380 /* resume signaling for 20 msec */
David Brownell10f65242005-08-31 10:55:38 -0700381 temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
382 writel (temp | PORT_RESUME,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 &ehci->regs->port_status [wIndex]);
384 ehci->reset_done [wIndex] = jiffies
385 + msecs_to_jiffies (20);
386 }
387 break;
388 case USB_PORT_FEAT_C_SUSPEND:
389 /* we auto-clear this feature */
390 break;
391 case USB_PORT_FEAT_POWER:
392 if (HCS_PPC (ehci->hcs_params))
David Brownell10f65242005-08-31 10:55:38 -0700393 writel (temp & ~(PORT_RWC_BITS | PORT_POWER),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 &ehci->regs->port_status [wIndex]);
395 break;
396 case USB_PORT_FEAT_C_CONNECTION:
David Brownell10f65242005-08-31 10:55:38 -0700397 writel((temp & ~PORT_RWC_BITS) | PORT_CSC,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 &ehci->regs->port_status [wIndex]);
399 break;
400 case USB_PORT_FEAT_C_OVER_CURRENT:
David Brownell10f65242005-08-31 10:55:38 -0700401 writel((temp & ~PORT_RWC_BITS) | PORT_OCC,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 &ehci->regs->port_status [wIndex]);
403 break;
404 case USB_PORT_FEAT_C_RESET:
405 /* GetPortStatus clears reset */
406 break;
407 default:
408 goto error;
409 }
410 readl (&ehci->regs->command); /* unblock posted write */
411 break;
412 case GetHubDescriptor:
413 ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
414 buf);
415 break;
416 case GetHubStatus:
417 /* no hub-wide feature/status flags */
418 memset (buf, 0, 4);
419 //cpu_to_le32s ((u32 *) buf);
420 break;
421 case GetPortStatus:
422 if (!wIndex || wIndex > ports)
423 goto error;
424 wIndex--;
425 status = 0;
426 temp = readl (&ehci->regs->port_status [wIndex]);
427
428 // wPortChange bits
429 if (temp & PORT_CSC)
430 status |= 1 << USB_PORT_FEAT_C_CONNECTION;
431 if (temp & PORT_PEC)
432 status |= 1 << USB_PORT_FEAT_C_ENABLE;
David Brownell93f1a472006-11-16 23:34:58 -0800433 if ((temp & PORT_OCC) && !ignore_oc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
435
436 /* whoever resumes must GetPortStatus to complete it!! */
437 if ((temp & PORT_RESUME)
438 && time_after (jiffies,
439 ehci->reset_done [wIndex])) {
440 status |= 1 << USB_PORT_FEAT_C_SUSPEND;
441 ehci->reset_done [wIndex] = 0;
442
443 /* stop resume signaling */
444 temp = readl (&ehci->regs->port_status [wIndex]);
David Brownell10f65242005-08-31 10:55:38 -0700445 writel (temp & ~(PORT_RWC_BITS | PORT_RESUME),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 &ehci->regs->port_status [wIndex]);
447 retval = handshake (
448 &ehci->regs->port_status [wIndex],
449 PORT_RESUME, 0, 2000 /* 2msec */);
450 if (retval != 0) {
451 ehci_err (ehci, "port %d resume error %d\n",
452 wIndex + 1, retval);
453 goto error;
454 }
455 temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
456 }
457
458 /* whoever resets must GetPortStatus to complete it!! */
459 if ((temp & PORT_RESET)
460 && time_after (jiffies,
461 ehci->reset_done [wIndex])) {
462 status |= 1 << USB_PORT_FEAT_C_RESET;
463 ehci->reset_done [wIndex] = 0;
464
465 /* force reset to complete */
David Brownell10f65242005-08-31 10:55:38 -0700466 writel (temp & ~(PORT_RWC_BITS | PORT_RESET),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 &ehci->regs->port_status [wIndex]);
David Brownellc22fa3a2005-06-13 07:15:28 -0700468 /* REVISIT: some hardware needs 550+ usec to clear
469 * this bit; seems too long to spin routinely...
470 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 retval = handshake (
472 &ehci->regs->port_status [wIndex],
David Brownellc22fa3a2005-06-13 07:15:28 -0700473 PORT_RESET, 0, 750);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 if (retval != 0) {
475 ehci_err (ehci, "port %d reset error %d\n",
476 wIndex + 1, retval);
477 goto error;
478 }
479
480 /* see what we found out */
481 temp = check_reset_complete (ehci, wIndex,
482 readl (&ehci->regs->port_status [wIndex]));
483 }
484
485 // don't show wPortStatus if it's owned by a companion hc
486 if (!(temp & PORT_OWNER)) {
487 if (temp & PORT_CONNECT) {
488 status |= 1 << USB_PORT_FEAT_CONNECTION;
489 // status may be from integrated TT
490 status |= ehci_port_speed(ehci, temp);
491 }
492 if (temp & PORT_PE)
493 status |= 1 << USB_PORT_FEAT_ENABLE;
494 if (temp & (PORT_SUSPEND|PORT_RESUME))
495 status |= 1 << USB_PORT_FEAT_SUSPEND;
496 if (temp & PORT_OC)
497 status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
498 if (temp & PORT_RESET)
499 status |= 1 << USB_PORT_FEAT_RESET;
500 if (temp & PORT_POWER)
501 status |= 1 << USB_PORT_FEAT_POWER;
502 }
503
504#ifndef EHCI_VERBOSE_DEBUG
505 if (status & ~0xffff) /* only if wPortChange is interesting */
506#endif
507 dbg_port (ehci, "GetStatus", wIndex + 1, temp);
508 // we "know" this alignment is good, caller used kmalloc()...
509 *((__le32 *) buf) = cpu_to_le32 (status);
510 break;
511 case SetHubFeature:
512 switch (wValue) {
513 case C_HUB_LOCAL_POWER:
514 case C_HUB_OVER_CURRENT:
515 /* no hub-wide feature/status flags */
516 break;
517 default:
518 goto error;
519 }
520 break;
521 case SetPortFeature:
David Brownellf0d7f272006-11-16 23:56:15 -0800522 selector = wIndex >> 8;
523 wIndex &= 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 if (!wIndex || wIndex > ports)
525 goto error;
526 wIndex--;
527 temp = readl (&ehci->regs->port_status [wIndex]);
528 if (temp & PORT_OWNER)
529 break;
530
David Brownell10f65242005-08-31 10:55:38 -0700531 temp &= ~PORT_RWC_BITS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 switch (wValue) {
533 case USB_PORT_FEAT_SUSPEND:
David Brownellf8aeb3b2006-01-20 13:55:14 -0800534 if (ehci->no_selective_suspend)
535 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 if ((temp & PORT_PE) == 0
537 || (temp & PORT_RESET) != 0)
538 goto error;
David Brownell2c1c3c42005-11-07 15:24:46 -0800539 if (device_may_wakeup(&hcd->self.root_hub->dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 temp |= PORT_WAKE_BITS;
541 writel (temp | PORT_SUSPEND,
542 &ehci->regs->port_status [wIndex]);
543 break;
544 case USB_PORT_FEAT_POWER:
545 if (HCS_PPC (ehci->hcs_params))
546 writel (temp | PORT_POWER,
547 &ehci->regs->port_status [wIndex]);
548 break;
549 case USB_PORT_FEAT_RESET:
550 if (temp & PORT_RESUME)
551 goto error;
552 /* line status bits may report this as low speed,
553 * which can be fine if this root hub has a
554 * transaction translator built in.
555 */
556 if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
557 && !ehci_is_TDI(ehci)
558 && PORT_USB11 (temp)) {
559 ehci_dbg (ehci,
560 "port %d low speed --> companion\n",
561 wIndex + 1);
562 temp |= PORT_OWNER;
563 } else {
564 ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
565 temp |= PORT_RESET;
566 temp &= ~PORT_PE;
567
568 /*
569 * caller must wait, then call GetPortStatus
570 * usb 2.0 spec says 50 ms resets on root
571 */
572 ehci->reset_done [wIndex] = jiffies
573 + msecs_to_jiffies (50);
574 }
575 writel (temp, &ehci->regs->port_status [wIndex]);
576 break;
David Brownellf0d7f272006-11-16 23:56:15 -0800577
578 /* For downstream facing ports (these): one hub port is put
579 * into test mode according to USB2 11.24.2.13, then the hub
580 * must be reset (which for root hub now means rmmod+modprobe,
581 * or else system reboot). See EHCI 2.3.9 and 4.14 for info
582 * about the EHCI-specific stuff.
583 */
584 case USB_PORT_FEAT_TEST:
585 if (!selector || selector > 5)
586 goto error;
587 ehci_quiesce(ehci);
588 ehci_halt(ehci);
589 temp |= selector << 16;
590 writel (temp, &ehci->regs->port_status [wIndex]);
591 break;
592
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 default:
594 goto error;
595 }
596 readl (&ehci->regs->command); /* unblock posted writes */
597 break;
598
599 default:
600error:
601 /* "stall" on error */
602 retval = -EPIPE;
603 }
604 spin_unlock_irqrestore (&ehci->lock, flags);
605 return retval;
606}