Split off OHCI support into a separate device. Numerous bug fixes, most
[AROS.git] / rom / usb / pciusbhc / ohci / roothub.c
blob49bdee0d8eb309ab127f9d2ab77756c1e4a7284a
1 /*
2 Copyright © 2002-2009, Chris Hodges. All rights reserved.
3 Copyright © 2009-2012, The AROS Development Team. All rights reserved.
4 $Id$
5 */
7 #include <devices/usb_hub.h>
8 #include <proto/utility.h>
9 #include <proto/exec.h>
10 #include <proto/timer.h>
12 #include <strings.h>
14 #include "debug.h"
15 #include "chip.h"
16 #include "pci.h"
18 #include "cmd_protos.h"
19 #include "chip_protos.h"
21 /* we cannot use AROS_WORD2LE in struct initializer */
22 #if AROS_BIG_ENDIAN
23 #define WORD2LE(w) (UWORD)(((w) >> 8) & 0x00FF) | (((w) << 8) & 0xFF00)
24 #else
25 #define WORD2LE(w) (w)
26 #endif
28 /* Root hub data */
29 static const struct UsbStdDevDesc RHDevDesc =
31 sizeof(struct UsbStdDevDesc), UDT_DEVICE, WORD2LE(0x0110),
32 HUB_CLASSCODE, 0, 0, 8, WORD2LE(0x0000), WORD2LE(0x0000),
33 WORD2LE(0x0100), 1, 2, 0, 1
36 static const struct UsbStdCfgDesc RHCfgDesc =
38 9, UDT_CONFIGURATION, WORD2LE(9 + 9 + 7), 1, 1, 3,
39 USCAF_ONE | USCAF_SELF_POWERED, 0
41 static const struct UsbStdIfDesc RHIfDesc =
42 {9, UDT_INTERFACE, 0, 0, 1, HUB_CLASSCODE, 0, 0, 4};
43 static const struct UsbStdEPDesc RHEPDesc =
44 {7, UDT_ENDPOINT, URTF_IN | 1, USEAF_INTERRUPT, WORD2LE(8), 255};
45 static const struct UsbHubDesc RHHubDesc =
48 UDT_HUB,
50 WORD2LE(UHCF_INDIVID_POWER | UHCF_INDIVID_OVP),
57 static const CONST_STRPTR RHStrings[] =
59 "Chris Hodges", "PCI Root Hub Unit x", "Standard Config",
60 "Hub interface"
63 /* /// "cmdControlXFerRootHub()" */
64 WORD cmdControlXFerRootHub(struct IOUsbHWReq *ioreq,
65 struct PCIUnit *unit, struct PCIDevice *base)
67 struct PCIController *hc;
68 struct PCIController *chc;
69 UWORD rt = ioreq->iouh_SetupData.bmRequestType;
70 UWORD req = ioreq->iouh_SetupData.bRequest;
71 UWORD idx = AROS_WORD2LE(ioreq->iouh_SetupData.wIndex);
72 UWORD val = AROS_WORD2LE(ioreq->iouh_SetupData.wValue);
73 UWORD len = AROS_WORD2LE(ioreq->iouh_SetupData.wLength);
74 UWORD hciport, i;
75 ULONG numports = unit->hu_RootHubPorts, reg_val, flag;
76 BOOL cmdgood;
78 if (ioreq->iouh_Endpoint)
80 return UHIOERR_STALL;
83 if (len != ioreq->iouh_Length)
85 KPRINTF(20, ("RH: Len (%ld != %ld) mismatch!\n",
86 len != ioreq->iouh_Length));
87 return UHIOERR_STALL;
89 switch (rt)
91 case (URTF_STANDARD | URTF_DEVICE):
92 switch (req)
94 case USR_SET_ADDRESS:
95 KPRINTF(1, ("RH: SetAddress = %ld\n", val));
96 unit->hu_RootHubAddr = val;
97 ioreq->iouh_Actual = len;
98 return 0;
100 case USR_SET_CONFIGURATION:
101 KPRINTF(1, ("RH: SetConfiguration=%ld\n", val));
102 ioreq->iouh_Actual = len;
103 return 0;
105 break;
107 case (URTF_IN | URTF_STANDARD | URTF_DEVICE):
108 switch (req)
110 case USR_GET_STATUS:
112 UWORD *mptr = ioreq->iouh_Data;
113 if (len != sizeof(struct UsbPortStatus))
115 return UHIOERR_STALL;
117 if ((!idx) && (idx > numports))
119 KPRINTF(20, ("Port %ld out of range\n", idx));
120 return UHIOERR_STALL;
122 hc = unit->hu_PortMap11[idx - 1];
123 hciport = unit->hu_PortNum11[idx - 1];
125 UWORD portreg = OHCI_PORTSTATUS + (hciport << 2);
126 ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
128 *mptr = AROS_WORD2LE(TranslatePortFlags(oldval,
129 OHPF_PORTPOWER | OHPF_OVERCURRENT
130 | OHPF_PORTCONNECTED | OHPF_PORTENABLE
131 | OHPF_LOWSPEED | OHPF_PORTRESET
132 | OHPF_PORTSUSPEND));
134 KPRINTF(5, ("OHCI Port %ld (glob. %ld) is %s\n",
135 hciport, idx,
136 oldval & OHPF_LOWSPEED ? "LOWSPEED" :
137 "FULLSPEED"));
138 KPRINTF(5, ("OHCI Port %ld Status %08lx (%08lx)\n", idx,
139 *mptr, oldval));
141 mptr++;
142 hc->hc_PortChangeMap[hciport] |=
143 TranslatePortFlags(oldval,
144 OHPF_OVERCURRENTCHG | OHPF_RESETCHANGE |
145 OHPF_ENABLECHANGE | OHPF_CONNECTCHANGE |
146 OHPF_RESUMEDTX);
147 *mptr = AROS_WORD2LE(hc->hc_PortChangeMap[hciport]);
148 KPRINTF(5, ("OHCI Port %ld Change %08lx\n", idx,
149 *mptr));
150 return 0;
153 return 0;
156 case USR_GET_DESCRIPTOR:
157 switch (val >> 8)
159 case UDT_DEVICE:
160 KPRINTF(1, ("RH: GetDeviceDescriptor (%ld)\n", len));
161 ioreq->iouh_Actual =
162 (len >
163 sizeof(struct UsbStdDevDesc)) ? sizeof(struct
164 UsbStdDevDesc) : len;
165 CopyMem((APTR) & RHDevDesc, ioreq->iouh_Data,
166 ioreq->iouh_Actual);
167 if (ioreq->iouh_Length >= sizeof(struct UsbStdDevDesc))
169 if (unit->hu_RootHub20Ports)
171 struct UsbStdDevDesc *usdd =
172 (struct UsbStdDevDesc *)ioreq->iouh_Data;
174 // signal a highspeed root hub
175 usdd->bcdUSB = AROS_WORD2LE(0x0200);
177 usdd->bDeviceProtocol = 1; // single TT
180 return 0;
182 case UDT_CONFIGURATION:
184 UBYTE tmpbuf[9 + 9 + 7];
185 KPRINTF(1, ("RH: GetConfigDescriptor (%ld)\n", len));
186 CopyMem((APTR) & RHCfgDesc, tmpbuf, 9);
187 CopyMem((APTR) & RHIfDesc, &tmpbuf[9], 9);
188 CopyMem((APTR) & RHEPDesc, &tmpbuf[9 + 9], 7);
189 if (unit->hu_RootHub20Ports)
191 struct UsbStdEPDesc *usepd =
192 (struct UsbStdEPDesc *)&tmpbuf[9 + 9];
193 usepd->bInterval = 12; // 2048 µFrames
195 ioreq->iouh_Actual =
196 (len > 9 + 9 + 7) ? 9 + 9 + 7 : len;
197 CopyMem(tmpbuf, ioreq->iouh_Data, ioreq->iouh_Actual);
198 return 0;
201 case UDT_STRING:
202 if (val & 0xff) /* get lang array */
204 CONST_STRPTR source = NULL;
205 UWORD *mptr = ioreq->iouh_Data;
206 UWORD slen = 1;
207 KPRINTF(1, ("RH: GetString %04lx (%ld)\n", val, len));
208 if ((val & 0xff) > 4) /* index too high? */
210 return UHIOERR_STALL;
212 source = RHStrings[(val & 0xff) - 1];
213 if (len > 1)
215 ioreq->iouh_Actual = 2;
216 while (*source++)
218 slen++;
220 source = RHStrings[(val & 0xff) - 1];
221 *mptr++ = AROS_WORD2BE((slen << 9) | UDT_STRING);
222 while (ioreq->iouh_Actual + 1 < len)
224 // special hack for unit number in root hub string
225 if (((val & 0xff) == 2) && (source[1] == 0))
227 *mptr++ =
228 AROS_WORD2LE('0' + unit->hu_UnitNo);
230 else
232 *mptr++ = AROS_WORD2LE(*source);
234 source++;
235 ioreq->iouh_Actual += 2;
236 if (!(*source))
238 break;
243 else
245 UWORD *mptr = ioreq->iouh_Data;
246 KPRINTF(1, ("RH: GetLangArray %04lx (%ld)\n", val,
247 len));
248 if (len > 1)
250 ioreq->iouh_Actual = 2;
251 mptr[0] = AROS_WORD2BE((4 << 8) | UDT_STRING);
252 if (len > 3)
254 ioreq->iouh_Actual += 2;
255 mptr[1] = AROS_WORD2LE(0x0409);
259 return 0;
261 default:
262 KPRINTF(1, ("RH: Unsupported Descriptor %04lx\n", idx));
264 break;
266 case USR_GET_CONFIGURATION:
267 if (len == 1)
269 KPRINTF(1, ("RH: GetConfiguration\n"));
270 ((UBYTE *) ioreq->iouh_Data)[0] = 1;
271 ioreq->iouh_Actual = len;
272 return 0;
274 break;
276 break;
278 case (URTF_CLASS | URTF_OTHER):
279 switch (req)
281 case USR_SET_FEATURE:
282 if ((!idx) && (idx > numports))
284 KPRINTF(20, ("Port %ld out of range\n", idx));
285 return UHIOERR_STALL;
287 chc = unit->hu_PortMap11[idx - 1];
288 hc = chc;
289 hciport = unit->hu_PortNum11[idx - 1];
290 KPRINTF(10,
291 ("Set Feature %ld maps from global Port %ld "
292 "to local Port %ld\n",
293 val, idx, hciport));
294 cmdgood = TRUE;
295 UWORD portreg = OHCI_PORTSTATUS + (hciport << 2);
296 if (val == UFS_PORT_RESET)
298 KPRINTF(10, ("Resetting Port (%s)\n",
299 READREG32_LE(hc->hc_RegBase,
300 portreg) & OHPF_PORTRESET ? "already" : "ok"));
301 // make sure we have at least 50ms of reset time here,
302 // as required for a root hub port
303 for (i = 0; i < 5; i++)
305 WRITEREG32_LE(hc->hc_RegBase, portreg, OHPF_PORTRESET);
306 DelayMS(10, unit);
308 DelayMS(5, unit);
310 ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
311 KPRINTF(10, ("OHCI Reset release (%s %s)\n",
312 oldval & OHPF_PORTRESET ? "didn't turn off" :
313 "okay",
314 oldval & OHPF_PORTENABLE ? "enabled" :
315 "not enabled"));
316 if (oldval & OHPF_PORTRESET)
318 DelayMS(40, unit);
319 oldval = READREG32_LE(hc->hc_RegBase, portreg);
320 KPRINTF(10, ("OHCI Reset 2nd release (%s %s)\n",
321 oldval & OHPF_PORTRESET ? "didn't turn off" :
322 "okay",
323 oldval & OHPF_PORTENABLE ? "enabled" :
324 "still not enabled"));
326 // make enumeration possible
327 unit->hu_DevControllers[0] = hc;
328 return 0;
330 else
332 switch (val)
334 /* case UFS_PORT_CONNECTION: not possible */
335 case UFS_PORT_ENABLE:
336 KPRINTF(10, ("Enabling Port (%s)\n",
337 oldval & OHPF_PORTENABLE ? "already" : "ok"));
338 reg_val = OHPF_PORTENABLE;
339 break;
341 case UFS_PORT_SUSPEND:
342 KPRINTF(10, ("Suspending Port (%s)\n",
343 oldval & OHPF_PORTSUSPEND ? "already" : "ok"));
344 reg_val = OHPF_PORTSUSPEND;
345 break;
347 /* case UFS_PORT_OVER_CURRENT: not possible */
348 case UFS_PORT_POWER:
349 KPRINTF(10, ("Powering Port (%s)\n",
350 oldval & OHPF_PORTPOWER ? "already" : "ok"));
351 reg_val = OHPF_PORTPOWER;
352 break;
354 /* case UFS_PORT_LOW_SPEED: not possible */
355 /* case UFS_C_PORT_CONNECTION:
356 case UFS_C_PORT_ENABLE:
357 case UFS_C_PORT_SUSPEND:
358 case UFS_C_PORT_OVER_CURRENT:
359 case UFS_C_PORT_RESET: */
360 default:
361 cmdgood = FALSE;
363 if (cmdgood)
365 WRITEREG32_LE(hc->hc_RegBase, portreg, reg_val);
366 return 0;
368 break;
371 break;
373 case USR_CLEAR_FEATURE:
374 if ((!idx) && (idx > numports))
376 KPRINTF(20, ("Port %ld out of range\n", idx));
377 return UHIOERR_STALL;
379 if (unit->hu_EhciOwned[idx - 1])
381 hc = unit->hu_PortMap20[idx - 1];
382 hciport = idx - 1;
384 else
386 hc = unit->hu_PortMap11[idx - 1];
387 hciport = unit->hu_PortNum11[idx - 1];
389 KPRINTF(10,
390 ("Clear Feature %ld maps from global Port %ld "
391 "to local Port %ld\n",
392 val, idx, hciport));
393 cmdgood = TRUE;
394 flag = 0;
396 UWORD portreg = OHCI_PORTSTATUS + (hciport << 2);
397 ULONG __unused oldval =
398 READREG32_LE(hc->hc_RegBase, portreg);
400 switch (val)
402 case UFS_PORT_ENABLE:
403 KPRINTF(10, ("Disabling Port (%s)\n",
404 oldval & OHPF_PORTENABLE ? "ok" : "already"));
405 reg_val = OHPF_PORTDISABLE;
406 break;
408 case UFS_PORT_SUSPEND:
409 KPRINTF(10, ("Resuming Port (%s)\n",
410 oldval & OHPF_PORTSUSPEND ? "ok" : "already"));
411 //flag = UPSF_PORT_SUSPEND; // manually fake suspend change
412 reg_val = OHPF_RESUME;
413 break;
415 case UFS_PORT_POWER:
416 KPRINTF(10, ("Unpowering Port (%s)\n",
417 oldval & OHPF_PORTPOWER ? "ok" : "already"));
418 reg_val = OHPF_PORTUNPOWER;
419 break;
421 case UFS_C_PORT_CONNECTION:
422 reg_val = OHPF_CONNECTCHANGE;
423 flag = UPSF_PORT_CONNECTION;
424 break;
426 case UFS_C_PORT_ENABLE:
427 reg_val = OHPF_ENABLECHANGE;
428 flag = UPSF_PORT_ENABLE;
429 break;
431 case UFS_C_PORT_SUSPEND:
432 reg_val = OHPF_RESUMEDTX;
433 flag = UPSF_PORT_SUSPEND;
434 break;
436 case UFS_C_PORT_OVER_CURRENT:
437 reg_val = OHPF_OVERCURRENTCHG;
438 flag = UPSF_PORT_OVER_CURRENT;
439 break;
441 case UFS_C_PORT_RESET:
442 reg_val = OHPF_RESETCHANGE;
443 flag = UPSF_PORT_RESET;
444 break;
445 default:
446 cmdgood = FALSE;
448 if (cmdgood)
450 WRITEREG32_LE(hc->hc_RegBase, portreg, reg_val);
451 hc->hc_PortChangeMap[hciport] &= ~flag;
452 return 0;
454 break;
457 break;
459 break;
461 case (URTF_IN | URTF_CLASS | URTF_OTHER):
462 switch (req)
464 case USR_GET_STATUS:
466 UWORD *mptr = ioreq->iouh_Data;
467 if (len != sizeof(struct UsbPortStatus))
469 return UHIOERR_STALL;
471 if ((!idx) && (idx > numports))
473 KPRINTF(20, ("Port %ld out of range\n", idx));
474 return UHIOERR_STALL;
476 if (unit->hu_EhciOwned[idx - 1])
478 hc = unit->hu_PortMap20[idx - 1];
479 hciport = idx - 1;
481 else
483 hc = unit->hu_PortMap11[idx - 1];
484 hciport = unit->hu_PortNum11[idx - 1];
487 UWORD portreg = OHCI_PORTSTATUS + (hciport << 2);
488 ULONG oldval = READREG32_LE(hc->hc_RegBase, portreg);
490 *mptr = 0;
491 *mptr = AROS_WORD2LE(TranslatePortFlags(oldval,
492 OHPF_PORTPOWER | OHPF_OVERCURRENT
493 | OHPF_PORTCONNECTED | OHPF_PORTENABLE
494 | OHPF_LOWSPEED | OHPF_PORTRESET
495 | OHPF_PORTSUSPEND));
497 KPRINTF(5, ("OHCI Port %ld (glob. %ld) is %s\n",
498 hciport, idx,
499 oldval & OHPF_LOWSPEED ? "LOWSPEED" :
500 "FULLSPEED"));
501 KPRINTF(5, ("OHCI Port %ld Status %08lx (%08lx)\n", idx,
502 *mptr, oldval));
504 mptr++;
505 hc->hc_PortChangeMap[hciport] |=
506 TranslatePortFlags(oldval,
507 OHPF_OVERCURRENTCHG | OHPF_RESETCHANGE
508 | OHPF_ENABLECHANGE | OHPF_CONNECTCHANGE
509 | OHPF_RESUMEDTX);
510 *mptr = AROS_WORD2LE(hc->hc_PortChangeMap[hciport]);
511 KPRINTF(5, ("OHCI Port %ld Change %08lx\n", idx,
512 *mptr));
513 return 0;
516 return 0;
520 break;
522 case (URTF_IN | URTF_CLASS | URTF_DEVICE):
523 switch (req)
525 case USR_GET_STATUS:
527 UWORD *mptr = ioreq->iouh_Data;
528 if (len < sizeof(struct UsbHubStatus))
530 return UHIOERR_STALL;
532 *mptr++ = 0;
533 *mptr++ = 0;
534 ioreq->iouh_Actual = 4;
535 return 0;
538 case USR_GET_DESCRIPTOR:
539 switch (val >> 8)
541 case UDT_HUB:
543 ULONG hubdesclen = 9;
544 ULONG powergood = 1;
546 struct UsbHubDesc *uhd =
547 (struct UsbHubDesc *)ioreq->iouh_Data;
548 KPRINTF(1, ("RH: GetHubDescriptor (%ld)\n", len));
550 if (unit->hu_RootHubPorts > 7)
552 hubdesclen += 2; // needs two bytes for port masks
555 ioreq->iouh_Actual =
556 (len > hubdesclen) ? hubdesclen : len;
557 CopyMem((APTR) & RHHubDesc, ioreq->iouh_Data,
558 ioreq->iouh_Actual);
560 if (ioreq->iouh_Length)
562 uhd->bLength = hubdesclen;
565 if (ioreq->iouh_Length >= 6)
567 hc = (struct PCIController *)unit->
568 hu_Controllers.lh_Head;
569 while (hc->hc_Node.ln_Succ)
572 ULONG localpwgood =
573 (READREG32_LE(hc->hc_RegBase,
574 OHCI_HUBDESCA) & OHAM_POWERGOOD) >>
575 OHAS_POWERGOOD;
576 if (localpwgood > powergood)
578 powergood = localpwgood;
579 KPRINTF(10,
580 ("Increasing power good time to %ld\n",
581 powergood));
584 hc = (struct PCIController *)hc->
585 hc_Node.ln_Succ;
588 uhd->bPwrOn2PwrGood = powergood;
590 if (ioreq->iouh_Length >= hubdesclen)
592 uhd->bNbrPorts = unit->hu_RootHubPorts;
593 if (hubdesclen == 9)
595 uhd->DeviceRemovable = 0;
596 uhd->PortPwrCtrlMask =
597 (1 << (unit->hu_RootHubPorts + 2)) - 2;
599 else
601 // each field is now 16 bits wide
602 uhd->DeviceRemovable = 0;
603 uhd->PortPwrCtrlMask = 0;
604 ((UBYTE *) ioreq->iouh_Data)[9] =
605 (1 << (unit->hu_RootHubPorts + 2)) - 2;
606 ((UBYTE *) ioreq->iouh_Data)[10] =
607 ((1 << (unit->hu_RootHubPorts + 2)) -
608 2) >> 8;
611 return 0;
614 default:
615 KPRINTF(20, ("RH: Unsupported Descriptor %04lx\n", idx));
617 break;
621 KPRINTF(20, ("RH: Unsupported command %02x %02x %04x %04x %04x!\n",
622 (UWORD) rt, (UWORD) req, idx, val, len));
623 return UHIOERR_STALL;
625 /* \\\ */
627 /* /// "cmdIntXFerRootHub()" */
628 WORD cmdIntXFerRootHub(struct IOUsbHWReq * ioreq,
629 struct PCIUnit * unit, struct PCIDevice * base)
631 if ((ioreq->iouh_Endpoint != 1) || (!ioreq->iouh_Length))
633 return UHIOERR_STALL;
636 if (unit->hu_RootPortChanges)
638 KPRINTF(1, ("Immediate Portchange map %04lx\n",
639 unit->hu_RootPortChanges));
640 if ((unit->hu_RootHubPorts < 8) || (ioreq->iouh_Length == 1))
642 *((UBYTE *) ioreq->iouh_Data) = unit->hu_RootPortChanges;
643 ioreq->iouh_Actual = 1;
645 else
647 ((UBYTE *) ioreq->iouh_Data)[0] = unit->hu_RootPortChanges;
648 ((UBYTE *) ioreq->iouh_Data)[1] = unit->hu_RootPortChanges >> 8;
649 ioreq->iouh_Actual = 2;
651 unit->hu_RootPortChanges = 0;
652 return 0;
654 ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
655 Disable();
656 AddTail(&unit->hu_RHIOQueue, (struct Node *)ioreq);
657 Enable();
658 return RC_DONTREPLY;
660 /* \\\ */
662 /* /// "CheckRootHubChanges()" */
663 void CheckRootHubChanges(struct PCIUnit *unit)
665 struct IOUsbHWReq *ioreq;
667 if (unit->hu_RootPortChanges && unit->hu_RHIOQueue.lh_Head->ln_Succ)
669 KPRINTF(1, ("Portchange map %04lx\n", unit->hu_RootPortChanges));
670 Disable();
671 ioreq = (struct IOUsbHWReq *)unit->hu_RHIOQueue.lh_Head;
672 while (((struct Node *)ioreq)->ln_Succ)
674 Remove(&ioreq->iouh_Req.io_Message.mn_Node);
675 if ((unit->hu_RootHubPorts < 8) || (ioreq->iouh_Length == 1))
677 *((UBYTE *) ioreq->iouh_Data) = unit->hu_RootPortChanges;
678 ioreq->iouh_Actual = 1;
680 else
682 ((UBYTE *) ioreq->iouh_Data)[0] = unit->hu_RootPortChanges;
683 ((UBYTE *) ioreq->iouh_Data)[1] =
684 unit->hu_RootPortChanges >> 8;
685 ioreq->iouh_Actual = 2;
687 ReplyMsg(&ioreq->iouh_Req.io_Message);
688 ioreq = (struct IOUsbHWReq *)unit->hu_RHIOQueue.lh_Head;
690 unit->hu_RootPortChanges = 0;
691 Enable();
694 /* \\\ */