2 Copyright (C) 2006 by Michal Schulz
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the
17 Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include <exec/types.h>
26 #include <exec/ports.h>
29 #include <utility/tagitem.h>
30 #include <aros/debug.h>
31 #include <aros/symbolsets.h>
33 #include <devices/timer.h>
35 #include <hidd/hidd.h>
38 #include <proto/oop.h>
39 #include <proto/utility.h>
40 #include <proto/exec.h>
44 static const usb_hub_descriptor_t hub_descriptor
= {
45 bDescLength
: sizeof(usb_hub_descriptor_t
) - 31,
46 bDescriptorType
: UDESC_HUB
,
48 wHubCharacteristics
:0,
51 DeviceRemovable
: {0,},
54 AROS_INTP(OHCI_HubInterrupt
);
55 AROS_INTP(ohci_Handler
);
57 OOP_Object
*METHOD(OHCI
, Root
, New
)
60 D(bug("[OHCI] OHCI::New()\n"));
62 BASE(cl
->UserData
)->LibNode
.lib_OpenCnt
++;
64 o
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
) msg
);
67 ohci_data_t
*ohci
= OOP_INST_DATA(cl
, o
);
70 ohci
->pendingRHSC
= 1;
71 ohci
->tr
= ohci_CreateTimer();
74 NEWLIST(&ohci
->intList
);
76 NEWLIST(&ohci
->timerPort
.mp_MsgList
);
77 ohci
->timerPort
.mp_Flags
= PA_SOFTINT
;
78 ohci
->timerPort
.mp_Node
.ln_Type
= NT_MSGPORT
;
79 ohci
->timerPort
.mp_SigTask
= &ohci
->timerInt
;
80 ohci
->timerInt
.is_Code
= (VOID_FUNC
)OHCI_HubInterrupt
;
81 ohci
->timerInt
.is_Data
= ohci
;
83 ohci
->timerReq
= CreateIORequest(&ohci
->timerPort
, sizeof(struct timerequest
));
84 OpenDevice((STRPTR
)"timer.device", UNIT_VBLANK
, (struct IORequest
*)ohci
->timerReq
, 0);
86 ohci
->regs
= (ohci_registers_t
*)GetTagData(aHidd_OHCI_MemBase
, 0, msg
->attrList
);
87 ohci
->pciDriver
= (OOP_Object
*)GetTagData(aHidd_OHCI_PCIDriver
, 0, msg
->attrList
);
88 ohci
->pciDevice
= (OOP_Object
*)GetTagData(aHidd_OHCI_PCIDevice
, 0, msg
->attrList
);
89 ohci
->irqNum
= GetTagData(aHidd_OHCI_IRQ
, 0, msg
->attrList
);
91 ohci
->hcca
= HIDD_PCIDriver_AllocPCIMem(ohci
->pciDriver
, 4096);
93 CopyMem(&hub_descriptor
, &ohci
->hubDescr
, sizeof(usb_hub_descriptor_t
));
94 ohci
->hubDescr
.bNbrPorts
= GetTagData(aHidd_USBHub_NumPorts
, 0, msg
->attrList
);
95 ohci
->hubDescr
.wHubCharacteristics
= AROS_WORD2LE(UHD_PWR_NO_SWITCH
| UHD_OC_INDIVIDUAL
);
97 ohci
->irqHandler
.is_Node
.ln_Name
= "UHCI Intr";
98 ohci
->irqHandler
.is_Node
.ln_Pri
= 127;
99 ohci
->irqHandler
.is_Node
.ln_Type
= NT_INTERRUPT
;
100 ohci
->irqHandler
.is_Code
= (VOID_FUNC
)ohci_Handler
;
101 ohci
->irqHandler
.is_Data
= ohci
;
103 AddIntServer(INTB_KERNEL
+ ohci
->irqNum
, &ohci
->irqHandler
);
104 D(bug("[OHCI] IRQHandler = %08x int = %d\n", ohci
->irqHandler
, ohci
->irqNum
));
106 D(bug("[OHCI] New(): o=%p, ports=%d, regs=%p, drv=%p, dev=%p, hcca=%p\n", o
,
107 ohci
->hubDescr
.bNbrPorts
, ohci
->regs
, ohci
->pciDriver
, ohci
->pciDevice
,
113 AddTail(&ohci
->intList
, &ohci
->tmp
->is_Node
);
115 /* Allocate empty endpoint descriptors for chaining */
116 ohci
->ctrl_head
= ohci_AllocED(cl
, o
);
117 ohci
->ctrl_head
->edFlags
= AROS_LONG2OHCI(ED_K
);
118 ohci
->ctrl_head
->edNextED
= 0;
119 CacheClearE(ohci
->ctrl_head
, sizeof(ohci_ed_t
), CACRF_ClearD
);
121 ohci
->bulk_head
= ohci_AllocED(cl
, o
);
122 ohci
->bulk_head
->edFlags
= AROS_LONG2OHCI(ED_K
);
123 ohci
->bulk_head
->edNextED
= 0;
124 CacheClearE(ohci
->bulk_head
, sizeof(ohci_ed_t
), CACRF_ClearD
);
126 ohci
->isoc_head
= ohci_AllocED(cl
, o
);
127 ohci
->isoc_head
->edFlags
= AROS_LONG2OHCI(ED_K
);
128 ohci
->isoc_head
->edNextED
= 0;
129 CacheClearE(ohci
->isoc_head
, sizeof(ohci_ed_t
), CACRF_ClearD
);
132 * The endpoints for interrupts.
134 * There are 63 endpoint descriptors used for interrupt
135 * transfers. They form a tree, with several pooling rates
136 * ranging from 1ms to 32ms. The 1ms endpoint points to the
139 ohci
->int01
= ohci_AllocED(cl
, o
);
140 ohci
->int01
->edFlags
= AROS_LONG2OHCI(ED_K
);
141 ohci
->int01
->edNextED
= AROS_LONG2OHCI((uint32_t)ohci
->isoc_head
);
142 CacheClearE(ohci
->int01
, sizeof(ohci_ed_t
), CACRF_ClearD
);
144 for (i
=0; i
< 2; i
++)
146 ohci
->int02
[i
] = ohci_AllocED(cl
, o
);
147 ohci
->int02
[i
]->edFlags
= AROS_LONG2OHCI(ED_K
);
148 ohci
->int02
[i
]->edNextED
= AROS_LONG2OHCI((uint32_t)ohci
->int01
);
149 CacheClearE(ohci
->int02
[i
], sizeof(ohci_ed_t
), CACRF_ClearD
);
152 for (i
=0; i
< 4; i
++)
154 ohci
->int04
[i
] = ohci_AllocED(cl
, o
);
155 ohci
->int04
[i
]->edFlags
= AROS_LONG2OHCI(ED_K
);
156 ohci
->int04
[i
]->edNextED
= AROS_LONG2OHCI((uint32_t)ohci
->int02
[i
& 0x01]);
157 CacheClearE(ohci
->int04
[i
], sizeof(ohci_ed_t
), CACRF_ClearD
);
160 for (i
=0; i
< 8; i
++)
162 ohci
->int08
[i
] = ohci_AllocED(cl
, o
);
163 ohci
->int08
[i
]->edFlags
= AROS_LONG2OHCI(ED_K
);
164 ohci
->int08
[i
]->edNextED
= AROS_LONG2OHCI((uint32_t)ohci
->int04
[i
& 0x03]);
165 CacheClearE(ohci
->int08
[i
], sizeof(ohci_ed_t
), CACRF_ClearD
);
168 for (i
=0; i
< 16; i
++)
170 ohci
->int16
[i
] = ohci_AllocED(cl
, o
);
171 ohci
->int16
[i
]->edFlags
= AROS_LONG2OHCI(ED_K
);
172 ohci
->int16
[i
]->edNextED
= AROS_LONG2OHCI((uint32_t)ohci
->int08
[i
& 0x07]);
173 CacheClearE(ohci
->int16
[i
], sizeof(ohci_ed_t
), CACRF_ClearD
);
176 for (i
=0; i
< 32; i
++)
178 ohci
->int32
[i
] = ohci_AllocED(cl
, o
);
179 ohci
->int32
[i
]->edFlags
= AROS_LONG2OHCI(ED_K
);
180 ohci
->int32
[i
]->edNextED
= AROS_LONG2OHCI((uint32_t)ohci
->int16
[i
& 0x0f]);
181 CacheClearE(ohci
->int32
[i
], sizeof(ohci_ed_t
), CACRF_ClearD
);
183 /* Link this pointers with HCCA table */
184 ohci
->hcca
->hccaIntrTab
[i
] = AROS_LONG2OHCI((uint32_t)ohci
->int32
[i
]);
186 CacheClearE((APTR
)ohci
->hcca
, sizeof(ohci_hcca_t
), CACRF_ClearD
);
191 * Preserve some registers which were set by BIOS. I will have
192 * to get rid of it pretty soon
194 uint32_t ctl
= AROS_OHCI2LONG(mmio(ohci
->regs
->HcControl
));
195 uint32_t rwc
= ctl
| HC_CTRL_RWC
;
196 uint32_t fm
= AROS_OHCI2LONG(mmio(ohci
->regs
->HcFmInterval
));
197 uint32_t desca
= AROS_OHCI2LONG(mmio(ohci
->regs
->HcRhDescriptorA
));
198 uint32_t descb
= AROS_OHCI2LONG(mmio(ohci
->regs
->HcRhDescriptorB
));
200 D(bug("[OHCI] ctl=%08x fm=%08x desca=%08x descb=%08x\n", ctl
,fm
,desca
,descb
));
202 desca
&= ~HC_RHA_NPS
;
207 for (i
=0; i
< ohci
->hubDescr
.bNbrPorts
; i
++)
209 mmio(ohci
->regs
->HcRhPortStatus
[i
]) = AROS_LONG2OHCI(UPS_LOW_SPEED
);
210 ohci_Delay(ohci
->tr
, ohci
->hubDescr
.bPwrOn2PwrGood
* UHD_PWRON_FACTOR
+ USB_EXTRA_POWER_UP_TIME
);
213 mmio(ohci
->regs
->HcControl
) = AROS_LONG2OHCI(rwc
| HC_CTRL_HCFS_RESET
);
214 ohci_Delay(ohci
->tr
, USB_BUS_RESET_DELAY
);
216 mmio(ohci
->regs
->HcCommandStatus
) = AROS_LONG2OHCI(HC_CS_HCR
);
217 for (i
=0; i
< 10; i
++)
219 ohci_Delay(ohci
->tr
, 1);
220 if (!(mmio(ohci
->regs
->HcCommandStatus
) & AROS_LONG2OHCI(HC_CS_HCR
)))
225 D(bug("[OHCI] Reset not ready...\n"));
227 /* Initial setup of OHCI */
228 D(bug("[OHCI] Initial setup\n"));
230 mmio(ohci
->regs
->HcHCCA
) = AROS_LONG2OHCI((uint32_t)ohci
->hcca
);
231 mmio(ohci
->regs
->HcBulkHeadED
) = AROS_LONG2OHCI((uint32_t)ohci
->bulk_head
);
232 mmio(ohci
->regs
->HcControlHeadED
) = AROS_LONG2OHCI((uint32_t)ohci
->ctrl_head
);
233 mmio(ohci
->regs
->HcInterruptDisable
) = AROS_LONG2OHCI(0xc000007f);
235 ctl
= AROS_OHCI2LONG(mmio(ohci
->regs
->HcControl
));
236 ctl
&= ~(HC_CTRL_CBSR_MASK
| HC_CTRL_HCFS_MASK
| HC_CTRL_PLE
|
237 HC_CTRL_IE
| HC_CTRL_CLE
| HC_CTRL_BLE
| HC_CTRL_IR
);
238 ctl
|= HC_CTRL_PLE
| HC_CTRL_IE
| HC_CTRL_CLE
| HC_CTRL_BLE
|
239 rwc
| HC_CTRL_CBSR_1_4
| HC_CTRL_HCFS_SUSPENDED
;
242 mmio(ohci
->regs
->HcControl
) = AROS_LONG2OHCI(ctl
);
244 uint32_t ival
= HC_FM_GET_IVAL(fm
);
245 D(bug("[OHCI] ival=%08x\n", ival
));
246 fm
= (AROS_OHCI2LONG(mmio(ohci
->regs
->HcFmRemaining
)) & HC_FM_FIT
) ^ HC_FM_FIT
;
247 fm
|= HC_FM_FSMPS(ival
) | ival
;
248 D(bug("[OHCI] fm=%08x\n", fm
));
249 mmio(ohci
->regs
->HcFmInterval
) = AROS_LONG2OHCI(fm
);
250 uint32_t per
= HC_PERIODIC(ival
);
251 mmio(ohci
->regs
->HcPeriodicStart
) = AROS_LONG2OHCI(per
);
252 D(bug("[OHCI] periodic start=%08x\n", per
));
254 mmio(ohci
->regs
->HcRhDescriptorA
) = AROS_LONG2OHCI(desca
| HC_RHA_NOCP
);
255 mmio(ohci
->regs
->HcRhStatus
) = AROS_LONG2OHCI(HC_RHS_LPS
);
256 ohci_Delay(ohci
->tr
, 5);
257 mmio(ohci
->regs
->HcRhDescriptorA
) = AROS_LONG2OHCI(desca
);
258 mmio(ohci
->regs
->HcRhDescriptorB
) = AROS_LONG2OHCI(descb
);
260 if (HC_RHA_GET_POTPGT(desca
) != 0)
262 D(bug("[OHCI] delay=%d\n", HC_RHA_GET_POTPGT(desca
) * UHD_PWRON_FACTOR
));
263 ohci_Delay(ohci
->tr
, HC_RHA_GET_POTPGT(desca
) * UHD_PWRON_FACTOR
);
266 /* Enable interrupts */
267 mmio(ohci
->regs
->HcInterruptEnable
) = AROS_LONG2OHCI(HC_INTR_MIE
| HC_INTR_SO
| HC_INTR_WDH
|
268 HC_INTR_RD
| HC_INTR_UE
| HC_INTR_RHSC
);
270 D(bug("[OHCI] OHCI controller up and running.\n"));
276 OOP_MethodID mID
= OOP_GetMethodID((STRPTR
)IID_Root
, moRoot_Dispose
);
277 OOP_CoerceMethod(cl
, o
, (OOP_Msg
)&mID
);
281 D(bug("[OHCI] OHCI::New() = %p\n",o
));
284 BASE(cl
->UserData
)->LibNode
.lib_OpenCnt
--;
289 void METHOD(OHCI
, Root
, Dispose
)
291 ohci_data_t
*ohci
= OOP_INST_DATA(cl
, o
);
292 struct Library
*base
= &BASE(cl
->UserData
)->LibNode
;
294 ohci_DeleteTimer(ohci
->tr
);
296 D(bug("[OHCI] OHCI::Dispose\n"));
298 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
303 void METHOD(OHCI
, Root
, Get
)
307 if (IS_USBDEVICE_ATTR(msg
->attrID
, idx
))
311 case aoHidd_USBDevice_Address
:
314 case aoHidd_USBDevice_Hub
:
317 case aoHidd_USBDevice_Bus
:
318 *msg
->storage
= (intptr_t)o
;
321 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
325 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
328 /* Class initialization and destruction */
331 #define SD(x) (&LIBBASE->sd)
333 static int OHCI_InitClass(LIBBASETYPEPTR LIBBASE
)
336 D(bug("[OHCI] InitClass\n"));
338 HiddOHCIAttrBase
= OOP_ObtainAttrBase(IID_Drv_USB_OHCI
);
340 if (HiddOHCIAttrBase
)
342 struct TagItem tags
[] = {
343 { aHidd_OHCI_MemBase
, 0UL },
344 { aHidd_OHCI_PCIDevice
, 0UL },
345 { aHidd_OHCI_PCIDriver
, 0UL },
346 { aHidd_OHCI_IRQ
, 0UL },
347 { aHidd_USBHub_NumPorts
, 0UL },
348 { aHidd_USBHub_IsRoot
, 1UL },
349 { aHidd_USBDevice_Address
, 1UL },
353 for (i
=0; i
< LIBBASE
->sd
.numDevices
; i
++)
355 tags
[0].ti_Data
= LIBBASE
->sd
.ramBase
[i
];
356 tags
[1].ti_Data
= (intptr_t)LIBBASE
->sd
.pciDevice
[i
];
357 tags
[2].ti_Data
= (intptr_t)LIBBASE
->sd
.pciDriver
[i
];
358 tags
[3].ti_Data
= (intptr_t)LIBBASE
->sd
.irqNum
[i
];
359 tags
[4].ti_Data
= (intptr_t)LIBBASE
->sd
.numPorts
[i
];
361 D(bug("[OHCI] Initializing driver object: dev=%p, drv=%p, %d ports @ %p\n",
362 LIBBASE
->sd
.pciDevice
[i
], LIBBASE
->sd
.pciDriver
[i
], LIBBASE
->sd
.numPorts
[i
],
363 LIBBASE
->sd
.ramBase
[i
]));
365 LIBBASE
->sd
.ohciDevice
[i
] = OOP_NewObject(NULL
, CLID_Drv_USB_OHCI
, tags
);
366 HIDD_USB_AttachDriver(LIBBASE
->sd
.usb
, LIBBASE
->sd
.ohciDevice
[i
]);
373 static int OHCI_ExpungeClass(LIBBASETYPEPTR LIBBASE
)
375 D(bug("[OHCI] ExpungeClass\n"));
377 OOP_ReleaseAttrBase(IID_Drv_USB_OHCI
);
382 ADD2INITLIB(OHCI_InitClass
, 0)
383 ADD2EXPUNGELIB(OHCI_ExpungeClass
, 0)