wip
[AROS.git] / arch / arm-native / soc / broadcom / 2708 / usb / usb2otg / usb2otg_hub.c
blob10cd80c6fc8b9b82538e5bd51d951b57a6b793de
1 /*
2 Copyright © 2013-2015, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #define DEBUG 1
7 #include <aros/debug.h>
9 #include <proto/exec.h>
10 #include <proto/kernel.h>
11 #include <proto/utility.h>
13 #include <devices/usb_hub.h>
15 #include "usb2otg_intern.h"
16 #include "usb2otg_hub.h"
18 WORD FNAME_ROOTHUB(cmdControlXFer)(struct IOUsbHWReq *ioreq,
19 struct USB2OTGUnit *otg_Unit,
20 LIBBASETYPEPTR USB2OTGBase)
22 UWORD rt = ioreq->iouh_SetupData.bmRequestType;
23 UWORD req = ioreq->iouh_SetupData.bRequest;
24 UWORD idx = AROS_WORD2LE(ioreq->iouh_SetupData.wIndex);
25 UWORD val = AROS_WORD2LE(ioreq->iouh_SetupData.wValue);
26 UWORD len = AROS_WORD2LE(ioreq->iouh_SetupData.wLength);
27 BOOL cmdgood;
28 ULONG cnt;
30 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER(%ld:%ld)\n", rt, req));
32 #if defined(OTG_FORCEHOSTMODE)
33 if (ioreq->iouh_Endpoint)
35 return (UHIOERR_STALL);
37 #endif
39 if (len != ioreq->iouh_Length)
41 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: IOReq Len mismatch! %ld != %ld\n", len, ioreq->iouh_Length));
42 return (UHIOERR_STALL);
45 switch (rt)
47 case (URTF_STANDARD|URTF_DEVICE):
48 switch (req)
50 case USR_SET_ADDRESS:
51 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Set Device Address to #%ld\n", val));
52 otg_Unit->hu_HubAddr = val;
53 ioreq->iouh_Actual = len;
55 unsigned int otg_RegVal = *((volatile unsigned int *)USB2OTG_DEVCFG);
56 otg_RegVal &= ~(0x7F << 4);
57 otg_RegVal |= ((val & 0x7F) << 4);
58 *((volatile unsigned int *)USB2OTG_DEVCFG) = otg_RegVal;
60 return (0);
62 case USR_SET_CONFIGURATION:
63 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Set Device Configuration to #%ld\n", val));
64 ioreq->iouh_Actual = len;
65 return (0);
67 break;
69 case (URTF_IN|URTF_STANDARD|URTF_DEVICE):
70 switch (req)
72 case USR_GET_STATUS:
73 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetStatus (%ld)\n", len));
74 if (len == 2);
76 UWORD *mptr = ioreq->iouh_Data;
77 *mptr++ = AROS_WORD2LE(U_GSF_SELF_POWERED);
78 return (0);
80 break;
82 case USR_GET_DESCRIPTOR:
83 switch (val >> 8)
85 case UDT_DEVICE:
86 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetDeviceDescriptor (%ld)\n", len));
87 ioreq->iouh_Actual = (len > sizeof(struct UsbStdDevDesc)) ? sizeof(struct UsbStdDevDesc) : len;
88 CopyMem((APTR)&OTGRootHubDevDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
90 return (0);
92 case UDT_CONFIGURATION:
93 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetConfigDescriptor (%ld)\n", len));
94 ioreq->iouh_Actual = (len > sizeof(struct OTGHubCfg)) ? sizeof(struct OTGHubCfg) : len;
95 CopyMem((APTR)&OTGRootHubCfg, ioreq->iouh_Data, ioreq->iouh_Actual);
97 return (0);
99 case UDT_STRING:
100 if (val & 0xFF) /* get lang array */
102 CONST_STRPTR source = NULL;
103 UWORD *mptr = ioreq->iouh_Data;
104 UWORD slen;
105 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetString %04lx (%ld)\n", val, len));
106 if ((val & 0xFF) > 4) /* index too high? */
108 return (UHIOERR_STALL);
111 source = OTGRootHubStrings[(val & 0xFF) - 1];
112 slen = strlen(source);
114 if (len > 1)
116 ioreq->iouh_Actual = 2;
117 *mptr++ = AROS_WORD2BE((slen << 9)|UDT_STRING);
118 /* "expand" string to utf16 */
119 while ((ioreq->iouh_Actual + 1) < len)
121 *mptr++ = AROS_WORD2LE(*source++);
122 ioreq->iouh_Actual += 2;
123 if (!(*source))
125 break;
129 } else {
130 UWORD *mptr = ioreq->iouh_Data;
131 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetLangArray %04lx (%ld)\n", val, len));
132 if (len > 1)
134 ioreq->iouh_Actual = 2;
135 mptr[0] = AROS_WORD2BE((4 << 8)|UDT_STRING);
136 if (len > 3)
138 ioreq->iouh_Actual += 2;
139 mptr[1] = AROS_WORD2LE(0x0409);
143 return (0);
145 default:
146 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported Descriptor %04lx\n", idx));
147 break;
149 break;
151 case USR_GET_CONFIGURATION:
152 if (len == 1)
154 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetConfiguration\n"));
155 ((UBYTE *) ioreq->iouh_Data)[0] = 1; // TODO: Expose 3 configurations? 1 = Host + Device, 2 = Host Only, 3 = Device Only?
156 ioreq->iouh_Actual = len;
157 return (0);
159 break;
161 break;
163 case (URTF_CLASS|URTF_OTHER):
165 switch (req)
167 case USR_SET_FEATURE:
169 if ((!idx) || (idx > 1))
171 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld out of range\n", idx));
172 return (UHIOERR_STALL);
175 cmdgood = FALSE;
177 #if (0)
178 ULONG oldval = *((volatile unsigned int *)USB2OTG_HOSTPORT) & ~(USB2OTG_HOSTPORT_PRTENCHNG|USB2OTG_HOSTPORT_PRTCONNSTS);
179 ULONG newval = oldval;
180 #else
181 unsigned int otg_RegVal, ns;
182 #endif
183 switch (val)
185 case UFS_PORT_ENABLE:
186 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Enabling Port #%ld\n", idx));
188 otg_RegVal = *((volatile unsigned int *)USB2OTG_HOSTPORT);
189 otg_RegVal |= USB2OTG_HOSTPORT_PRTENA;
190 *((volatile unsigned int *)USB2OTG_HOSTPORT) = otg_RegVal;
191 cmdgood = TRUE;
193 break;
195 case UFS_PORT_SUSPEND:
196 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Suspending Port #%ld\n", idx));
198 otg_RegVal = *((volatile unsigned int *)USB2OTG_HOSTPORT);
199 otg_RegVal |= USB2OTG_HOSTPORT_PRTSUSP;
200 *((volatile unsigned int *)USB2OTG_HOSTPORT) = otg_RegVal;
201 cmdgood = TRUE;
203 break;
205 case UFS_PORT_RESET:
207 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Resetting Port #%ld\n", idx));
209 for (ns = 0; ns < 100000; ns++) { asm volatile("mov r0, r0\n"); } // see USB 2.0 spec
211 otg_RegVal = *((volatile unsigned int *)USB2OTG_HOSTPORT);
212 otg_RegVal &= ~(USB2OTG_HOSTPORT_PRTCONNSTS|USB2OTG_HOSTPORT_PRTENA|USB2OTG_HOSTPORT_PRTENCHNG|USB2OTG_HOSTPORT_PRTOVRCURRCHNG);
213 otg_RegVal |= USB2OTG_HOSTPORT_PRTRST;
214 *((volatile unsigned int *)USB2OTG_HOSTPORT) = otg_RegVal;
216 for (ns = 0; ns < 50000; ns++) { asm volatile("mov r0, r0\n"); } // see USB 2.0 spec (tDRSTR)
218 otg_RegVal = *((volatile unsigned int *)USB2OTG_HOSTPORT);
219 otg_RegVal &= ~(USB2OTG_HOSTPORT_PRTCONNSTS|USB2OTG_HOSTPORT_PRTENA|USB2OTG_HOSTPORT_PRTENCHNG|USB2OTG_HOSTPORT_PRTOVRCURRCHNG| USB2OTG_HOSTPORT_PRTRST);
220 *((volatile unsigned int *)USB2OTG_HOSTPORT) = otg_RegVal;
222 for (ns = 0; ns < 20000; ns++) { asm volatile("mov r0, r0\n"); } // see USB 2.0 spec (tRSTRCY)
223 cmdgood = TRUE;
225 break;
227 case UFS_PORT_POWER:
228 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Powering Port #%ld\n", idx));
230 otg_RegVal = *((volatile unsigned int *)USB2OTG_HOSTPORT);
231 otg_RegVal |= USB2OTG_HOSTPORT_PRTPWR;
232 *((volatile unsigned int *)USB2OTG_HOSTPORT) = otg_RegVal;
233 cmdgood = TRUE;
235 break;
238 case UFS_PORT_LOW_SPEED:
239 case UFS_C_PORT_CONNECTION:
240 case UFS_C_PORT_OVER_CURRENT:
242 default:
243 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_SET_FEATURE - Unhandled feature %ld for Port #%ld\n", val, idx));
244 break;
246 if (cmdgood)
248 #if (0)
249 *((volatile unsigned int *)USB2OTG_HOSTPORT) = newval;
250 #endif
251 return (0);
254 break;
257 case USR_CLEAR_FEATURE:
259 if ((!idx) || (idx > 1))
261 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld out of range\n", idx));
262 return (UHIOERR_STALL);
265 cmdgood = FALSE;
267 ULONG oldval = *((volatile unsigned int *)USB2OTG_HOSTPORT) & ~(USB2OTG_HOSTPORT_PRTENCHNG|USB2OTG_HOSTPORT_PRTCONNSTS);
268 ULONG newval = oldval;
270 switch (val)
272 case UFS_PORT_ENABLE:
273 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Enable\n", idx));
275 newval &= ~USB2OTG_HOSTPORT_PRTENA;
276 cmdgood = TRUE;
278 break;
280 case UFS_PORT_SUSPEND:
281 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Suspend\n", idx));
283 newval &= ~USB2OTG_HOSTPORT_PRTSUSP;
284 cmdgood = TRUE;
286 break;
288 case UFS_PORT_POWER:
289 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Power\n", idx));
291 newval &= ~USB2OTG_HOSTPORT_PRTPWR;
292 cmdgood = TRUE;
294 break;
296 case UFS_C_PORT_CONNECTION:
297 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Connect Change\n", idx));
299 newval &= ~USB2OTG_HOSTPORT_PRTCONNDET;
300 otg_Unit->hu_HubPortChanged = TRUE;
301 cmdgood = TRUE;
303 break;
305 case UFS_C_PORT_ENABLE:
306 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Enable Change\n", idx));
308 newval &= ~USB2OTG_HOSTPORT_PRTENCHNG;
309 otg_Unit->hu_HubPortChanged = TRUE;
310 cmdgood = TRUE;
312 break;
314 case UFS_C_PORT_SUSPEND:
315 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Suspend Change\n", idx));
317 newval &= ~USB2OTG_HOSTPORT_PRTRES;
318 otg_Unit->hu_HubPortChanged = TRUE;
319 cmdgood = TRUE;
321 break;
323 case UFS_C_PORT_OVER_CURRENT:
324 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Over-Current Change\n", idx));
326 newval &= ~USB2OTG_HOSTPORT_PRTOVRCURRCHNG;
327 otg_Unit->hu_HubPortChanged = TRUE;
328 cmdgood = TRUE;
330 break;
332 case UFS_C_PORT_RESET:
333 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE Port #%ld Reset Change\n", idx));
335 newval &= ~USB2OTG_HOSTPORT_PRTRST;
336 otg_Unit->hu_HubPortChanged = TRUE;
337 cmdgood = TRUE;
339 break;
341 default:
342 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: USR_CLEAR_FEATURE - Unhandled feature %ld for Port #%ld\n", val, idx));
343 break;
345 if (cmdgood)
347 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld CLEAR_FEATURE %04lx->%04lx\n", idx, oldval, newval));
348 *((volatile unsigned int *)USB2OTG_HOSTPORT) = newval;
350 return (0);
352 break;
355 break;
358 case (URTF_IN|URTF_CLASS|URTF_OTHER):
360 switch (req)
362 case USR_GET_STATUS:
364 UWORD *mptr = ioreq->iouh_Data;
366 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Get Port #%ld Status..\n", idx));
368 if (len != sizeof(struct UsbPortStatus))
370 return (UHIOERR_STALL);
372 if ((!idx) || (idx > 1))
374 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld out of range\n", idx));
375 return (UHIOERR_STALL);
378 ULONG oldval = *((volatile unsigned int *)USB2OTG_HOSTPORT);
380 *mptr = 0;
381 if (oldval & USB2OTG_HOSTPORT_PRTPWR) *mptr |= AROS_WORD2LE(UPSF_PORT_POWER);
382 if (oldval & USB2OTG_HOSTPORT_PRTENA) *mptr |= AROS_WORD2LE(UPSF_PORT_ENABLE);
383 if (oldval & USB2OTG_HOSTPORT_PRTCONNSTS) *mptr |= AROS_WORD2LE(UPSF_PORT_CONNECTION);
384 if (oldval & USB2OTG_HOSTPORT_PRTSPD_LOW) *mptr |= AROS_WORD2LE(UPSF_PORT_LOW_SPEED);
385 if (oldval & USB2OTG_HOSTPORT_PRTRST) *mptr |= AROS_WORD2LE(UPSF_PORT_RESET);
386 if (oldval & USB2OTG_HOSTPORT_PRTSUSP) *mptr |= AROS_WORD2LE(UPSF_PORT_SUSPEND);
388 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld is %s\n", idx, (oldval & USB2OTG_HOSTPORT_PRTSPD_LOW) ? "LOWSPEED" : "FULLSPEED"));
389 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld Status %08lx\n", idx, *mptr));
391 mptr++;
392 *mptr = 0;
393 if (oldval & USB2OTG_HOSTPORT_PRTENCHNG) *mptr |= AROS_WORD2LE(UPSF_PORT_ENABLE);
394 if (oldval & USB2OTG_HOSTPORT_PRTCONNDET) *mptr |= AROS_WORD2LE(UPSF_PORT_CONNECTION);
395 if (oldval & USB2OTG_HOSTPORT_PRTRES) *mptr |= AROS_WORD2LE(UPSF_PORT_SUSPEND|UPSF_PORT_ENABLE);
397 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Port #%ld Change %08lx\n", idx, *mptr));
399 return (0);
402 break;
405 case (URTF_IN|URTF_CLASS|URTF_DEVICE):
407 switch (req)
409 case USR_GET_STATUS:
411 UWORD *mptr = ioreq->iouh_Data;
413 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetHubStatus (%ld)\n", len));
415 if (len < sizeof(struct UsbHubStatus))
417 return(UHIOERR_STALL);
420 *mptr++ = 0;
421 *mptr++ = 0;
422 ioreq->iouh_Actual = 4;
423 return (0);
426 case USR_GET_DESCRIPTOR:
428 switch (val >> 8)
430 case UDT_HUB:
432 ULONG hubdesclen = 9;
433 ULONG powergood = 1;
435 struct UsbHubDesc *uhd = (struct UsbHubDesc *) ioreq->iouh_Data;
436 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: GetHubDescriptor (%ld)\n", len));
438 ioreq->iouh_Actual = (len > hubdesclen) ? hubdesclen : len;
439 CopyMem((APTR)&OTGRootHubDesc, ioreq->iouh_Data, ioreq->iouh_Actual);
441 if (ioreq->iouh_Length)
443 uhd->bLength = hubdesclen;
446 if (ioreq->iouh_Length >= hubdesclen)
448 if (hubdesclen == 9)
450 uhd->DeviceRemovable = 0;
451 uhd->PortPwrCtrlMask = (1 << 3) - 2;
452 } else {
453 // each field is 16 bits wide
454 uhd->DeviceRemovable = 0;
455 uhd->PortPwrCtrlMask = 0;
456 ((UBYTE *)ioreq->iouh_Data)[9] = (1 << 3) - 2;
457 ((UBYTE *)ioreq->iouh_Data)[10] = ((1 << 3) - 2) >> 8;
460 return (0);
463 default:
464 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported Descriptor %04lx\n", idx));
465 break;
467 break;
473 D(bug("[USB2OTG:Hub] UHCMD_CONTROLXFER: Unsupported command %02lx %02lx %04lx %04lx %04lx!\n", rt, req, idx, val, len));
475 return (UHIOERR_STALL);
478 WORD FNAME_ROOTHUB(cmdIntXFer)(struct IOUsbHWReq *ioreq,
479 struct USB2OTGUnit *otg_Unit,
480 LIBBASETYPEPTR USB2OTGBase)
482 D(bug("[USB2OTG:Hub] UHCMD_INTXFER()\n"));
484 if ((ioreq->iouh_Endpoint != 1) || (!ioreq->iouh_Length))
486 return UHIOERR_STALL;
489 if (otg_Unit->hu_HubPortChanged)
491 D(bug("[USB2OTG:Hub] UHCMD_INTXFER: Registering Immediate Portchange\n"));
493 if (ioreq->iouh_Length == 1)
495 *((UBYTE *) ioreq->iouh_Data) = 1;
496 ioreq->iouh_Actual = 1;
498 else
500 ((UBYTE *) ioreq->iouh_Data)[0] = 1;
501 ((UBYTE *) ioreq->iouh_Data)[1] = 0;
502 ioreq->iouh_Actual = 2;
504 otg_Unit->hu_HubPortChanged = FALSE;
506 return 0;
509 D(bug("[USB2OTG:Hub] UHCMD_INTXFER: Queueing request\n"));
511 ioreq->iouh_Req.io_Flags &= ~IOF_QUICK;
512 Disable();
513 AddTail(&otg_Unit->hu_IOPendingQueue, (struct Node *)ioreq);
514 Enable();
516 return RC_DONTREPLY;
519 void FNAME_ROOTHUB(PendingIO)(struct USB2OTGUnit *otg_Unit)
521 struct IOUsbHWReq *ioreq;
523 D(bug("[USB2OTG:Hub] PendingIO(0x%p)\n", otg_Unit));
525 if (otg_Unit->hu_HubPortChanged && otg_Unit->hu_IOPendingQueue.lh_Head->ln_Succ)
527 D(bug("[USB2OTG:Hub] PendingIO: PortChange detected\n"));
528 Disable();
529 ioreq = (struct IOUsbHWReq *) otg_Unit->hu_IOPendingQueue.lh_Head;
530 while (((struct Node *) ioreq)->ln_Succ)
532 Remove(&ioreq->iouh_Req.io_Message.mn_Node);
533 if (ioreq->iouh_Length == 1)
535 *((UBYTE *) ioreq->iouh_Data) = 1;
536 ioreq->iouh_Actual = 1;
537 } else {
538 ((UBYTE *) ioreq->iouh_Data)[0] = 1;
539 ((UBYTE *) ioreq->iouh_Data)[1] = 0;
540 ioreq->iouh_Actual = 2;
542 ReplyMsg(&ioreq->iouh_Req.io_Message);
543 ioreq = (struct IOUsbHWReq *) otg_Unit->hu_IOPendingQueue.lh_Head;
545 otg_Unit->hu_HubPortChanged = FALSE;
546 Enable();