MOXA linux-2.6.x / linux-2.6.9-uc0 from sdlinux-moxaart.tgz
[linux-2.6.9-moxart.git] / drivers / usb / host / ehci-hub.c
blob639b4406cab669e49fa4351b1df8890669008b17
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.
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.
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.
19 /* this file is part of ehci-hcd.c */
21 /*-------------------------------------------------------------------------*/
24 * EHCI Root Hub ... the nonsharable stuff
26 * Registers don't need cpu_to_le32, that happens transparently
29 /*-------------------------------------------------------------------------*/
31 #ifdef CONFIG_PM
33 static int ehci_hub_suspend (struct usb_hcd *hcd)
35 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
36 struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
37 int port;
38 int status = 0;
40 if (root->dev.power.power_state != 0)
41 return 0;
42 if (time_before (jiffies, ehci->next_statechange))
43 return -EAGAIN;
45 port = HCS_N_PORTS (ehci->hcs_params);
46 spin_lock_irq (&ehci->lock);
48 /* suspend any active/unsuspended ports, maybe allow wakeup */
49 while (port--) {
50 u32 t1 = readl (&ehci->regs->port_status [port]);
51 u32 t2 = t1;
53 if ((t1 & PORT_PE) && !(t1 & PORT_OWNER))
54 t2 |= PORT_SUSPEND;
55 if (ehci->hcd.remote_wakeup)
56 t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E;
57 else
58 t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
60 if (t1 != t2) {
61 ehci_vdbg (ehci, "port %d, %08x -> %08x\n",
62 port + 1, t1, t2);
63 writel (t2, &ehci->regs->port_status [port]);
67 /* stop schedules, then turn off HC and clean any completed work */
68 if (hcd->state == USB_STATE_RUNNING)
69 ehci_ready (ehci);
70 ehci->command = readl (&ehci->regs->command);
71 writel (ehci->command & ~CMD_RUN, &ehci->regs->command);
72 if (ehci->reclaim)
73 ehci->reclaim_ready = 1;
74 ehci_work(ehci, NULL);
75 (void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000);
77 root->dev.power.power_state = 3;
78 ehci->next_statechange = jiffies + msecs_to_jiffies(10);
79 spin_unlock_irq (&ehci->lock);
80 return status;
84 /* caller owns root->serialize, and should reset/reinit on error */
85 static int ehci_hub_resume (struct usb_hcd *hcd)
87 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
88 struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub;
89 u32 temp;
90 int i;
92 if (!root->dev.power.power_state)
93 return 0;
94 if (time_before (jiffies, ehci->next_statechange))
95 return -EAGAIN;
97 /* re-init operational registers in case we lost power */
98 if (readl (&ehci->regs->intr_enable) == 0) {
99 writel (INTR_MASK, &ehci->regs->intr_enable);
100 writel (0, &ehci->regs->segment);
101 writel (ehci->periodic_dma, &ehci->regs->frame_list);
102 writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
103 /* FIXME will this work even (pci) vAUX was lost? */
106 /* restore CMD_RUN, framelist size, and irq threshold */
107 writel (ehci->command, &ehci->regs->command);
109 /* take ports out of suspend */
110 i = HCS_N_PORTS (ehci->hcs_params);
111 while (i--) {
112 temp = readl (&ehci->regs->port_status [i]);
113 temp &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E);
114 if (temp & PORT_SUSPEND) {
115 ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
116 temp |= PORT_RESUME;
118 writel (temp, &ehci->regs->port_status [i]);
120 i = HCS_N_PORTS (ehci->hcs_params);
121 msleep (20);
122 while (i--) {
123 temp = readl (&ehci->regs->port_status [i]);
124 if ((temp & PORT_SUSPEND) == 0)
125 continue;
126 temp &= ~PORT_RESUME;
127 writel (temp, &ehci->regs->port_status [i]);
128 ehci_vdbg (ehci, "resumed port %d\n", i + 1);
130 (void) readl (&ehci->regs->command);
132 /* maybe re-activate the schedule(s) */
133 temp = 0;
134 if (ehci->async->qh_next.qh)
135 temp |= CMD_ASE;
136 if (ehci->periodic_sched)
137 temp |= CMD_PSE;
138 if (temp)
139 writel (ehci->command | temp, &ehci->regs->command);
141 root->dev.power.power_state = 0;
142 ehci->next_statechange = jiffies + msecs_to_jiffies(5);
143 ehci->hcd.state = USB_STATE_RUNNING;
144 return 0;
147 #else
149 #define ehci_hub_suspend NULL
150 #define ehci_hub_resume NULL
152 #endif /* CONFIG_PM */
154 /*-------------------------------------------------------------------------*/
156 static int check_reset_complete (
157 struct ehci_hcd *ehci,
158 int index,
159 int port_status
161 if (!(port_status & PORT_CONNECT)) {
162 ehci->reset_done [index] = 0;
163 return port_status;
166 /* if reset finished and it's still not enabled -- handoff */
167 if (!(port_status & PORT_PE)) {
169 /* with integrated TT, there's nobody to hand it to! */
170 if (ehci_is_ARC(ehci)) {
171 ehci_dbg (ehci,
172 "Failed to enable port %d on root hub TT\n",
173 index+1);
174 return port_status;
177 ehci_dbg (ehci, "port %d full speed --> companion\n",
178 index + 1);
180 // what happens if HCS_N_CC(params) == 0 ?
181 #ifndef CONFIG_ARCH_MOXACPU // add by Victor Yu. 01-03-2006
182 port_status |= PORT_OWNER;
183 #endif
184 writel (port_status, &ehci->regs->port_status [index]);
186 } else
187 ehci_dbg (ehci, "port %d high speed\n", index + 1);
189 return port_status;
192 /*-------------------------------------------------------------------------*/
195 /* build "status change" packet (one or two bytes) from HC registers */
197 static int
198 ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
200 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
201 u32 temp, status = 0;
202 int ports, i, retval = 1;
203 unsigned long flags;
205 /* init status to no-changes */
206 buf [0] = 0;
207 ports = HCS_N_PORTS (ehci->hcs_params);
208 if (ports > 7) {
209 buf [1] = 0;
210 retval++;
213 /* no hub change reports (bit 0) for now (power, ...) */
215 /* port N changes (bit N)? */
216 spin_lock_irqsave (&ehci->lock, flags);
217 for (i = 0; i < ports; i++) {
218 temp = readl (&ehci->regs->port_status [i]);
219 if (temp & PORT_OWNER) {
220 /* don't report this in GetPortStatus */
221 if (temp & PORT_CSC) {
222 temp &= ~PORT_CSC;
223 writel (temp, &ehci->regs->port_status [i]);
225 continue;
227 if (!(temp & PORT_CONNECT))
228 ehci->reset_done [i] = 0;
229 if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0
230 // PORT_STAT_C_SUSPEND?
231 || ((temp & PORT_RESUME) != 0
232 && time_after (jiffies,
233 ehci->reset_done [i]))) {
234 if (i < 7)
235 buf [0] |= 1 << (i + 1);
236 else
237 buf [1] |= 1 << (i - 7);
238 status = STS_PCD;
241 spin_unlock_irqrestore (&ehci->lock, flags);
242 return status ? retval : 0;
245 /*-------------------------------------------------------------------------*/
247 static void
248 ehci_hub_descriptor (
249 struct ehci_hcd *ehci,
250 struct usb_hub_descriptor *desc
252 int ports = HCS_N_PORTS (ehci->hcs_params);
253 u16 temp;
255 desc->bDescriptorType = 0x29;
256 desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */
257 desc->bHubContrCurrent = 0;
259 desc->bNbrPorts = ports;
260 temp = 1 + (ports / 8);
261 desc->bDescLength = 7 + 2 * temp;
263 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
264 memset (&desc->bitmap [0], 0, temp);
265 memset (&desc->bitmap [temp], 0xff, temp);
267 temp = 0x0008; /* per-port overcurrent reporting */
268 if (HCS_PPC (ehci->hcs_params))
269 temp |= 0x0001; /* per-port power control */
270 if (HCS_INDICATOR (ehci->hcs_params))
271 temp |= 0x0080; /* per-port indicators (LEDs) */
272 desc->wHubCharacteristics = cpu_to_le16 (temp);
275 /*-------------------------------------------------------------------------*/
277 #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
279 static int ehci_hub_control (
280 struct usb_hcd *hcd,
281 u16 typeReq,
282 u16 wValue,
283 u16 wIndex,
284 char *buf,
285 u16 wLength
287 struct ehci_hcd *ehci = hcd_to_ehci (hcd);
288 int ports = HCS_N_PORTS (ehci->hcs_params);
289 u32 temp, status;
290 unsigned long flags;
291 int retval = 0;
294 * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
295 * HCS_INDICATOR may say we can change LEDs to off/amber/green.
296 * (track current state ourselves) ... blink for diagnostics,
297 * power, "this is the one", etc. EHCI spec supports this.
300 spin_lock_irqsave (&ehci->lock, flags);
301 switch (typeReq) {
302 case ClearHubFeature:
303 switch (wValue) {
304 case C_HUB_LOCAL_POWER:
305 case C_HUB_OVER_CURRENT:
306 /* no hub-wide feature/status flags */
307 break;
308 default:
309 goto error;
311 break;
312 case ClearPortFeature:
313 if (!wIndex || wIndex > ports)
314 goto error;
315 #if 0
316 #ifdef CONFIG_ARCH_MOXACPU // add by Victor Yu. 01-10-2006
317 // because the CPU IP has bug to need to do this
318 if ( wValue == USB_PORT_FEAT_ENABLE ) {
319 temp = readl(&ehci->regs->command);
320 temp |= CMD_RUN;
321 writel(temp, &ehci->regs->command);
323 #endif
324 #endif
325 wIndex--;
326 temp = readl (&ehci->regs->port_status [wIndex]);
327 #ifndef CONFIG_ARCH_MOXACPU // add by Victor Yu. 12-30-2005
328 if (temp & PORT_OWNER)
329 break;
330 #endif
332 switch (wValue) {
333 case USB_PORT_FEAT_ENABLE:
334 writel (temp & ~PORT_PE,
335 &ehci->regs->port_status [wIndex]);
336 break;
337 case USB_PORT_FEAT_C_ENABLE:
338 writel (temp | PORT_PEC,
339 &ehci->regs->port_status [wIndex]);
340 break;
341 case USB_PORT_FEAT_SUSPEND:
342 if (temp & PORT_RESET)
343 goto error;
344 if (temp & PORT_SUSPEND) {
345 if ((temp & PORT_PE) == 0)
346 goto error;
347 /* resume signaling for 20 msec */
348 writel ((temp & ~PORT_WAKE_BITS) | PORT_RESUME,
349 &ehci->regs->port_status [wIndex]);
350 ehci->reset_done [wIndex] = jiffies
351 + msecs_to_jiffies (20);
353 break;
354 case USB_PORT_FEAT_C_SUSPEND:
355 /* we auto-clear this feature */
356 break;
357 case USB_PORT_FEAT_POWER:
358 if (HCS_PPC (ehci->hcs_params))
359 writel (temp & ~PORT_POWER,
360 &ehci->regs->port_status [wIndex]);
361 break;
362 case USB_PORT_FEAT_C_CONNECTION:
363 writel (temp | PORT_CSC,
364 &ehci->regs->port_status [wIndex]);
365 break;
366 case USB_PORT_FEAT_C_OVER_CURRENT:
367 writel (temp | PORT_OCC,
368 &ehci->regs->port_status [wIndex]);
369 break;
370 case USB_PORT_FEAT_C_RESET:
371 /* GetPortStatus clears reset */
372 break;
373 default:
374 goto error;
376 readl (&ehci->regs->command); /* unblock posted write */
377 break;
378 case GetHubDescriptor:
379 ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
380 buf);
381 break;
382 case GetHubStatus:
383 /* no hub-wide feature/status flags */
384 memset (buf, 0, 4);
385 //cpu_to_le32s ((u32 *) buf);
386 break;
387 case GetPortStatus:
388 if (!wIndex || wIndex > ports)
389 goto error;
390 wIndex--;
391 status = 0;
392 temp = readl (&ehci->regs->port_status [wIndex]);
394 // wPortChange bits
395 if (temp & PORT_CSC)
396 status |= 1 << USB_PORT_FEAT_C_CONNECTION;
397 if (temp & PORT_PEC)
398 status |= 1 << USB_PORT_FEAT_C_ENABLE;
399 if (temp & PORT_OCC)
400 status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
402 /* whoever resumes must GetPortStatus to complete it!! */
403 if ((temp & PORT_RESUME)
404 && time_after (jiffies,
405 ehci->reset_done [wIndex])) {
406 status |= 1 << USB_PORT_FEAT_C_SUSPEND;
407 ehci->reset_done [wIndex] = 0;
409 /* stop resume signaling */
410 temp = readl (&ehci->regs->port_status [wIndex]);
411 writel (temp & ~PORT_RESUME,
412 &ehci->regs->port_status [wIndex]);
413 retval = handshake (
414 &ehci->regs->port_status [wIndex],
415 PORT_RESUME, 0, 2000 /* 2msec */);
416 if (retval != 0) {
417 ehci_err (ehci, "port %d resume error %d\n",
418 wIndex + 1, retval);
419 goto error;
421 temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
424 /* whoever resets must GetPortStatus to complete it!! */
425 if ((temp & PORT_RESET)
426 && time_after (jiffies,
427 ehci->reset_done [wIndex])) {
428 status |= 1 << USB_PORT_FEAT_C_RESET;
429 ehci->reset_done [wIndex] = 0;
431 /* force reset to complete */
432 writel (temp & ~PORT_RESET,
433 &ehci->regs->port_status [wIndex]);
434 retval = handshake (
435 &ehci->regs->port_status [wIndex],
436 PORT_RESET, 0, 500);
437 if (retval != 0) {
438 ehci_err (ehci, "port %d reset error %d\n",
439 wIndex + 1, retval);
440 goto error;
443 /* see what we found out */
444 temp = check_reset_complete (ehci, wIndex,
445 readl (&ehci->regs->port_status [wIndex]));
446 #ifdef CONFIG_ARCH_MOXACPU // add by Victor Yu. 02-10-2006
447 // because the CPU IP has bug to need to do this
448 // Frarady suggests me to modify here
449 writel(readl(&ehci->regs->command)|CMD_RUN, &ehci->regs->command);
450 //ehci->hcd.state = USB_STATE_RUNNING;
451 #endif
454 // don't show wPortStatus if it's owned by a companion hc
455 if (!(temp & PORT_OWNER)) {
456 if (temp & PORT_CONNECT) {
457 status |= 1 << USB_PORT_FEAT_CONNECTION;
458 // status may be from integrated TT
459 status |= ehci_port_speed(ehci, temp);
461 if (temp & PORT_PE)
462 status |= 1 << USB_PORT_FEAT_ENABLE;
463 if (temp & (PORT_SUSPEND|PORT_RESUME))
464 status |= 1 << USB_PORT_FEAT_SUSPEND;
465 if (temp & PORT_OC)
466 status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
467 if (temp & PORT_RESET)
468 status |= 1 << USB_PORT_FEAT_RESET;
469 if (temp & PORT_POWER)
470 status |= 1 << USB_PORT_FEAT_POWER;
473 #ifndef EHCI_VERBOSE_DEBUG
474 if (status & ~0xffff) /* only if wPortChange is interesting */
475 #endif
476 dbg_port (ehci, "GetStatus", wIndex + 1, temp);
477 // we "know" this alignment is good, caller used kmalloc()...
478 *((__le32 *) buf) = cpu_to_le32 (status);
479 break;
480 case SetHubFeature:
481 switch (wValue) {
482 case C_HUB_LOCAL_POWER:
483 case C_HUB_OVER_CURRENT:
484 /* no hub-wide feature/status flags */
485 break;
486 default:
487 goto error;
489 break;
490 case SetPortFeature:
491 if (!wIndex || wIndex > ports)
492 goto error;
493 wIndex--;
494 temp = readl (&ehci->regs->port_status [wIndex]);
495 #ifndef CONFIG_ARCH_MOXACPU // add by Victor Yu. 12-30-2005
496 if (temp & PORT_OWNER)
497 break;
498 #endif
500 switch (wValue) {
501 case USB_PORT_FEAT_SUSPEND:
502 if ((temp & PORT_PE) == 0
503 || (temp & PORT_RESET) != 0)
504 goto error;
505 if (ehci->hcd.remote_wakeup)
506 temp |= PORT_WAKE_BITS;
507 writel (temp | PORT_SUSPEND,
508 &ehci->regs->port_status [wIndex]);
509 break;
510 case USB_PORT_FEAT_POWER:
511 if (HCS_PPC (ehci->hcs_params))
512 writel (temp | PORT_POWER,
513 &ehci->regs->port_status [wIndex]);
514 break;
515 case USB_PORT_FEAT_RESET:
516 if (temp & PORT_RESUME)
517 goto error;
518 /* line status bits may report this as low speed,
519 * which can be fine if this root hub has a
520 * transaction translator built in.
522 if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
523 && !ehci_is_ARC(ehci)
524 && PORT_USB11 (temp)) {
525 ehci_dbg (ehci,
526 "port %d low speed --> companion\n",
527 wIndex + 1);
528 #ifndef CONFIG_ARCH_MOXACPU // add by Victor Yu. 01-03-2006
529 temp |= PORT_OWNER;
530 #endif
531 //add by Victor Yu.08-21-2007,for reset anyway
532 goto reset_done;
533 } else {
534 reset_done://add by Victor Yu.08-21-2007,for reset anyway
535 ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
536 temp |= PORT_RESET;
537 temp &= ~PORT_PE;
540 * caller must wait, then call GetPortStatus
541 * usb 2.0 spec says 50 ms resets on root
543 ehci->reset_done [wIndex] = jiffies
544 + msecs_to_jiffies (50);
546 writel (temp, &ehci->regs->port_status [wIndex]);
547 break;
548 default:
549 goto error;
551 readl (&ehci->regs->command); /* unblock posted writes */
552 break;
554 default:
555 error:
556 /* "stall" on error */
557 retval = -EPIPE;
559 spin_unlock_irqrestore (&ehci->lock, flags);
560 return retval;