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