1 /*
2 * Copyright (c) 2024, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "usb_hc_ohci.h"
7
8 /* Frame Interval / Periodic Start.
9 *
10 * At 12Mbps, there are 12000 bit time in each 1Msec frame.
11 */
12
13 #define OHCI_FMINTERVAL_FI (12000 - 1)
14 #define OHCI_FMINTERVAL_FSMPS ((6 * (OHCI_FMINTERVAL_FI - 210)) / 7)
15 #define DEFAULT_FMINTERVAL ((OHCI_FMINTERVAL_FSMPS << OHCI_FMINT_FSMPS_SHIFT) | OHCI_FMINTERVAL_FI)
16 #define DEFAULT_PERSTART ((OHCI_FMINTERVAL_FI * 9) / 10)
17
18 struct ohci_hcd g_ohci_hcd[CONFIG_USBHOST_MAX_BUS];
19
20 USB_NOCACHE_RAM_SECTION struct ohci_ed_hw g_ohci_ed_pool[CONFIG_USBHOST_MAX_BUS][CONFIG_USB_OHCI_ED_NUM];
21 USB_NOCACHE_RAM_SECTION struct ohci_hcca ohci_hcca[CONFIG_USBHOST_MAX_BUS];
22
ohci_init(struct usbh_bus * bus)23 int ohci_init(struct usbh_bus *bus)
24 {
25 volatile uint32_t timeout = 0;
26 uint32_t regval;
27 struct ohci_ed_hw *ed;
28
29 memset(&g_ohci_hcd[bus->hcd.hcd_id], 0, sizeof(struct ohci_hcd));
30 memset(g_ohci_ed_pool[bus->hcd.hcd_id], 0, sizeof(struct ohci_ed_hw) * CONFIG_USB_OHCI_ED_NUM);
31
32 for (uint32_t i = 0; i < 32; i++) {
33 ohci_hcca[bus->hcd.hcd_id].inttbl[i] = 0;
34 }
35
36 for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) {
37 ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index];
38 if ((uint32_t)&ed->hw % 32) {
39 USB_LOG_ERR("struct ohci_ed_hw is not align 32\r\n");
40 return -USB_ERR_INVAL;
41 }
42 for (uint8_t i = 0; i < CONFIG_USB_OHCI_TD_NUM; i++) {
43 if ((uint32_t)&ed->td_pool[i] % 32) {
44 USB_LOG_ERR("struct ohci_td_hw is not align 32\r\n");
45 return -USB_ERR_INVAL;
46 }
47 }
48 }
49
50 for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) {
51 ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index];
52 ed->waitsem = usb_osal_sem_create(0);
53 }
54
55 USB_LOG_INFO("OHCI hcrevision:0x%02x\r\n", (unsigned int)OHCI_HCOR->hcrevision);
56
57 OHCI_HCOR->hcintdis = OHCI_INT_MIE;
58 OHCI_HCOR->hccontrol = 0;
59
60 OHCI_HCOR->hccmdsts = OHCI_CMDST_HCR;
61 while (OHCI_HCOR->hccmdsts & OHCI_CMDST_HCR) {
62 usb_osal_msleep(1);
63 timeout++;
64 if (timeout > 100) {
65 return -USB_ERR_TIMEOUT;
66 }
67 }
68
69 OHCI_HCOR->hcfminterval = DEFAULT_FMINTERVAL;
70 OHCI_HCOR->hcperiodicstart = DEFAULT_PERSTART;
71 OHCI_HCOR->hclsthreshold = 0x628;
72
73 OHCI_HCOR->hccontrolheaded = 0;
74 OHCI_HCOR->hcbulkheaded = 0;
75 OHCI_HCOR->hchcca = (uintptr_t)&ohci_hcca[bus->hcd.hcd_id];
76
77 /* Clear pending interrupts */
78 regval = OHCI_HCOR->hcintsts;
79 OHCI_HCOR->hcintsts = regval;
80
81 /* Put HC in operational state */
82 regval = OHCI_HCOR->hccontrol;
83 regval &= ~OHCI_CTRL_CBSR;
84 regval &= ~OHCI_CTRL_HCFS_MASK;
85 regval |= OHCI_CTRL_HCFS_OPER;
86 regval |= OHCI_CTRL_CBSR;
87 regval |= OHCI_CTRL_CLE;
88 OHCI_HCOR->hccontrol = regval;
89
90 g_ohci_hcd[bus->hcd.hcd_id].n_ports = OHCI_HCOR->hcrhdescriptora & OHCI_RHDESCA_NDP_MASK;
91 USB_LOG_INFO("OHCI n_ports:%d\r\n", g_ohci_hcd[bus->hcd.hcd_id].n_ports);
92
93 OHCI_HCOR->hcrhdescriptora &= ~OHCI_RHDESCA_PSM;
94 OHCI_HCOR->hcrhdescriptora &= ~OHCI_RHDESCA_NPS;
95
96 /* Set global power in HcRhStatus */
97 OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_SGP;
98 usb_osal_msleep(20);
99
100 /* Enable OHCI interrupts */
101 OHCI_HCOR->hcinten = OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE;
102
103 return 0;
104 }
105
ohci_deinit(struct usbh_bus * bus)106 int ohci_deinit(struct usbh_bus *bus)
107 {
108 uint32_t regval;
109 struct ohci_ed_hw *ed;
110
111 /* Disable OHCI interrupts */
112 OHCI_HCOR->hcintdis = OHCI_INT_WDH | OHCI_INT_RHSC | OHCI_INT_MIE;
113
114 /* Clear pending interrupts */
115 regval = OHCI_HCOR->hcintsts;
116 OHCI_HCOR->hcintsts = regval;
117
118 OHCI_HCOR->hcrhsts &= ~OHCI_RHSTATUS_SGP;
119
120 regval = OHCI_HCOR->hccontrol;
121 regval &= ~OHCI_CTRL_HCFS_MASK;
122 regval |= OHCI_CTRL_HCFS_SUSPEND;
123 OHCI_HCOR->hccontrol = regval;
124
125 for (uint8_t index = 0; index < CONFIG_USB_OHCI_ED_NUM; index++) {
126 ed = &g_ohci_ed_pool[bus->hcd.hcd_id][index];
127 usb_osal_sem_delete(ed->waitsem);
128 }
129
130 return 0;
131 }
132
ohci_get_frame_number(struct usbh_bus * bus)133 uint16_t ohci_get_frame_number(struct usbh_bus *bus)
134 {
135 return OHCI_HCOR->hcfmnumber;
136 }
137
ohci_roothub_control(struct usbh_bus * bus,struct usb_setup_packet * setup,uint8_t * buf)138 int ohci_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf)
139 {
140 uint8_t nports;
141 uint8_t port;
142 uint32_t temp;
143
144 nports = g_ohci_hcd[bus->hcd.hcd_id].n_ports;
145
146 port = setup->wIndex;
147 if (setup->bmRequestType & USB_REQUEST_RECIPIENT_DEVICE) {
148 switch (setup->bRequest) {
149 case HUB_REQUEST_CLEAR_FEATURE:
150 switch (setup->wValue) {
151 case HUB_FEATURE_HUB_C_LOCALPOWER:
152 break;
153 case HUB_FEATURE_HUB_C_OVERCURRENT:
154 break;
155 default:
156 return -USB_ERR_NOTSUPP;
157 }
158 break;
159 case HUB_REQUEST_SET_FEATURE:
160 switch (setup->wValue) {
161 case HUB_FEATURE_HUB_C_LOCALPOWER:
162 break;
163 case HUB_FEATURE_HUB_C_OVERCURRENT:
164 break;
165 default:
166 return -USB_ERR_NOTSUPP;
167 }
168 break;
169 case HUB_REQUEST_GET_DESCRIPTOR:
170 break;
171 case HUB_REQUEST_GET_STATUS:
172 memset(buf, 0, 4);
173 break;
174 default:
175 break;
176 }
177 } else if (setup->bmRequestType & USB_REQUEST_RECIPIENT_OTHER) {
178 switch (setup->bRequest) {
179 case HUB_REQUEST_CLEAR_FEATURE:
180 if (!port || port > nports) {
181 return -USB_ERR_INVAL;
182 }
183
184 switch (setup->wValue) {
185 case HUB_PORT_FEATURE_ENABLE:
186 temp = OHCI_RHPORTST_CCS;
187 break;
188 case HUB_PORT_FEATURE_SUSPEND:
189 temp = OHCI_HCOR->hccontrol;
190 temp &= ~OHCI_CTRL_HCFS_MASK;
191 temp |= OHCI_CTRL_HCFS_RESUME;
192 OHCI_HCOR->hccontrol = temp;
193
194 usb_osal_msleep(20);
195
196 temp = OHCI_HCOR->hccontrol;
197 temp &= ~OHCI_CTRL_HCFS_MASK;
198 temp |= OHCI_CTRL_HCFS_OPER;
199 OHCI_HCOR->hccontrol = temp;
200
201 temp = OHCI_RHPORTST_POCI;
202 break;
203 case HUB_PORT_FEATURE_C_SUSPEND:
204 temp = OHCI_RHPORTST_PSSC;
205 break;
206 case HUB_PORT_FEATURE_POWER:
207 OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_CGP;
208 temp = OHCI_RHPORTST_LSDA;
209 break;
210 case HUB_PORT_FEATURE_C_CONNECTION:
211 temp = OHCI_RHPORTST_CSC;
212 break;
213 case HUB_PORT_FEATURE_C_ENABLE:
214 temp = OHCI_RHPORTST_PESC;
215 break;
216 case HUB_PORT_FEATURE_C_OVER_CURREN:
217 temp = OHCI_RHPORTST_OCIC;
218 break;
219 case HUB_PORT_FEATURE_C_RESET:
220 temp = OHCI_RHPORTST_PRSC;
221 break;
222 default:
223 return -USB_ERR_NOTSUPP;
224 }
225 OHCI_HCOR->hcrhportsts[port - 1] = temp;
226 break;
227 case HUB_REQUEST_SET_FEATURE:
228 if (!port || port > nports) {
229 return -USB_ERR_INVAL;
230 }
231
232 switch (setup->wValue) {
233 case HUB_PORT_FEATURE_SUSPEND:
234 temp = OHCI_HCOR->hccontrol;
235 temp &= ~OHCI_CTRL_HCFS_MASK;
236 temp |= OHCI_CTRL_HCFS_SUSPEND;
237 OHCI_HCOR->hccontrol = temp;
238
239 break;
240 case HUB_PORT_FEATURE_POWER:
241 OHCI_HCOR->hcrhsts = OHCI_RHSTATUS_SGP;
242 break;
243 case HUB_PORT_FEATURE_RESET:
244 OHCI_HCOR->hcrhportsts[port - 1] = OHCI_RHPORTST_PRS;
245
246 while (OHCI_HCOR->hcrhportsts[port - 1] & OHCI_RHPORTST_PRS) {
247 }
248 break;
249
250 default:
251 return -USB_ERR_NOTSUPP;
252 }
253 break;
254 case HUB_REQUEST_GET_STATUS:
255 if (!port || port > nports) {
256 return -USB_ERR_INVAL;
257 }
258 temp = OHCI_HCOR->hcrhportsts[port - 1];
259 memcpy(buf, &temp, 4);
260 break;
261 default:
262 break;
263 }
264 }
265 return 0;
266 }
267
ohci_submit_urb(struct usbh_urb * urb)268 int ohci_submit_urb(struct usbh_urb *urb)
269 {
270 return -USB_ERR_NOTSUPP;
271 }
272
ohci_kill_urb(struct usbh_urb * urb)273 int ohci_kill_urb(struct usbh_urb *urb)
274 {
275 return -USB_ERR_NOTSUPP;
276 }
277
OHCI_IRQHandler(uint8_t busid)278 void OHCI_IRQHandler(uint8_t busid)
279 {
280 uint32_t usbsts;
281 struct usbh_bus *bus;
282
283 bus = &g_usbhost_bus[busid];
284
285 usbsts = OHCI_HCOR->hcintsts & OHCI_HCOR->hcinten;
286
287 if (usbsts & OHCI_INT_RHSC) {
288 OHCI_HCOR->hcintsts = OHCI_INT_RHSC;
289 for (int port = 0; port < CONFIG_USBHOST_MAX_RHPORTS; port++) {
290 uint32_t portsc = OHCI_HCOR->hcrhportsts[port];
291
292 if (portsc & OHCI_RHPORTST_CSC) {
293 if (OHCI_HCOR->hcrhsts & OHCI_RHSTATUS_DRWE) {
294 /* If DRWE is set, Connect Status Change indicates a remote wake-up event */
295 } else {
296 if (portsc & OHCI_RHPORTST_CCS) {
297 } else {
298 }
299 bus->hcd.roothub.int_buffer[0] |= (1 << (port + 1));
300 usbh_hub_thread_wakeup(&bus->hcd.roothub);
301 }
302 }
303 }
304 }
305 if (usbsts & OHCI_INT_WDH) {
306 OHCI_HCOR->hcintsts = OHCI_INT_WDH;
307 }
308 }
309
310 #ifndef CONFIG_USB_EHCI_WITH_OHCI
usb_hc_low_level_init(struct usbh_bus * bus)311 __WEAK void usb_hc_low_level_init(struct usbh_bus *bus)
312 {
313 (void)bus;
314 }
315
usb_hc_low_level_deinit(struct usbh_bus * bus)316 __WEAK void usb_hc_low_level_deinit(struct usbh_bus *bus)
317 {
318 (void)bus;
319 }
320
usb_hc_init(struct usbh_bus * bus)321 int usb_hc_init(struct usbh_bus *bus)
322 {
323 usb_hc_low_level_init(bus);
324 return ohci_init(bus);
325 }
326
usb_hc_deinit(struct usbh_bus * bus)327 int usb_hc_deinit(struct usbh_bus *bus)
328 {
329 ohci_deinit(bus);
330 usb_hc_low_level_deinit(bus);
331 return 0;
332 }
333
usbh_roothub_control(struct usbh_bus * bus,struct usb_setup_packet * setup,uint8_t * buf)334 int usbh_roothub_control(struct usbh_bus *bus, struct usb_setup_packet *setup, uint8_t *buf)
335 {
336 return ohci_roothub_control(bus, setup, buf);
337 }
338
usbh_submit_urb(struct usbh_urb * urb)339 int usbh_submit_urb(struct usbh_urb *urb)
340 {
341 return ohci_submit_urb(urb);
342 }
343
usbh_kill_urb(struct usbh_urb * urb)344 int usbh_kill_urb(struct usbh_urb *urb)
345 {
346 return ohci_kill_urb(urb);
347 }
348
USBH_IRQHandler(uint8_t busid)349 void USBH_IRQHandler(uint8_t busid)
350 {
351 OHCI_IRQHandler(busid);
352 }
353 #endif