1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** USBX Component */
16 /** */
17 /** EHCI Controller Driver */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22
23 /* Include necessary system files. */
24
25 #define UX_SOURCE_CODE
26
27 #include "ux_api.h"
28 #include "ux_hcd_ehci.h"
29 #include "ux_host_stack.h"
30
31
32 /* EHCI HCD extension for host mode select. */
33 #ifndef UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE
34
35 #if defined(K66) || defined(IMX6UL) || defined(XILINX_ZYNQ) || defined(MIMXRT)
36 #define UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci) do \
37 { \
38 _ux_hcd_ehci_register_write(hcd_ehci, (hcd_ehci -> ux_hcd_ehci_hcor + 0x1A), 0x03); \
39 } while(0)
40 #else
41 #define UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci)
42 #endif
43
44 #endif /* ifndef UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE */
45
46 #ifndef UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT
47
48 #if defined(IMX25) || defined(IMX6UL) || defined(K66) || defined(LPC3131) || \
49 defined(MCF5445X) || defined(MIMXRT)
50 #define UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT UX_TRUE
51 #else
52 #define UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT UX_FALSE
53 #endif
54
55 #endif /* ifndef UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT */
56
57 /**************************************************************************/
58 /* */
59 /* FUNCTION RELEASE */
60 /* */
61 /* _ux_hcd_ehci_initialize PORTABLE C */
62 /* 6.1.11 */
63 /* AUTHOR */
64 /* */
65 /* Chaoqiong Xiao, Microsoft Corporation */
66 /* */
67 /* DESCRIPTION */
68 /* */
69 /* This function initializes the EHCI controller. It sets the DMA */
70 /* areas, programs all the EHCI registers, sets up the ED and TD */
71 /* containers, sets the control, bulk and periodic lists. */
72 /* */
73 /* INPUT */
74 /* */
75 /* HCD Pointer to HCD */
76 /* */
77 /* OUTPUT */
78 /* */
79 /* Completion Status */
80 /* */
81 /* CALLS */
82 /* */
83 /* _ux_hcd_ehci_periodic_tree_create Create periodic tree */
84 /* _ux_hcd_ehci_power_root_hubs Power root HUBs */
85 /* _ux_hcd_ehci_register_read Read EHCI register */
86 /* _ux_hcd_ehci_register_write Write EHCI register */
87 /* _ux_utility_memory_allocate Allocate memory block */
88 /* _ux_utility_memory_delete Delete memory block */
89 /* _ux_utility_physical_address Get physical address */
90 /* _ux_host_semaphore_create Create semaphore */
91 /* _ux_host_semaphore_delete Delete semaphore */
92 /* _ux_host_mutex_create Create mutex */
93 /* _ux_host_mutex_delete Delete mutex */
94 /* _ux_utility_set_interrupt_handler Set interrupt handler */
95 /* _ux_utility_delay_ms Delay ms */
96 /* */
97 /* CALLED BY */
98 /* */
99 /* EHCI Controller Driver */
100 /* */
101 /* RELEASE HISTORY */
102 /* */
103 /* DATE NAME DESCRIPTION */
104 /* */
105 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
106 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
107 /* optimized based on compile */
108 /* definitions, */
109 /* resulting in version 6.1 */
110 /* 08-02-2021 Wen Wang Modified comment(s), */
111 /* fixed spelling error, */
112 /* resulting in version 6.1.8 */
113 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
114 /* refined macros names, */
115 /* resulting in version 6.1.10 */
116 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
117 /* fixed standalone compile, */
118 /* resulting in version 6.1.11 */
119 /* */
120 /**************************************************************************/
_ux_hcd_ehci_initialize(UX_HCD * hcd)121 UINT _ux_hcd_ehci_initialize(UX_HCD *hcd)
122 {
123 #if defined(UX_HOST_STANDALONE)
124 UX_PARAMETER_NOT_USED(hcd);
125 return(UX_FUNCTION_NOT_SUPPORTED);
126 #else
127
128 UX_HCD_EHCI *hcd_ehci;
129 UX_EHCI_ED *ed;
130 UX_EHCI_LINK_POINTER lp;
131 ULONG ehci_register;
132 ULONG port_index;
133 UINT status = UX_SUCCESS;
134
135
136 /* The controller initialized here is of EHCI type. */
137 hcd -> ux_hcd_controller_type = UX_EHCI_CONTROLLER;
138
139 #if UX_MAX_DEVICES > 1
140 /* Initialize the max bandwidth for periodic endpoints. On EHCI,
141 the spec says no more than 90% to be allocated for periodic. */
142 hcd -> ux_hcd_available_bandwidth = UX_EHCI_AVAILABLE_BANDWIDTH;
143 #endif
144
145 /* Allocate memory for this EHCI HCD instance. */
146 hcd_ehci = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HCD_EHCI));
147 if (hcd_ehci == UX_NULL)
148 return(UX_MEMORY_INSUFFICIENT);
149
150 /* Set the pointer to the EHCI HCD. */
151 hcd -> ux_hcd_controller_hardware = (VOID *) hcd_ehci;
152
153 /* Save the register memory address. */
154 hcd_ehci -> ux_hcd_ehci_base = (ULONG *) hcd -> ux_hcd_io;
155
156 /* Obtain the address of the HCOR registers. This is a byte offset from the
157 HCOR Cap registers. */
158 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCCR_CAP_LENGTH);
159 hcd_ehci -> ux_hcd_ehci_hcor = (ehci_register & 0xff) >> 2;
160
161 /* Set the generic HCD owner for the EHCI HCD. */
162 hcd_ehci -> ux_hcd_ehci_hcd_owner = hcd;
163
164 /* Initialize the function entry for this HCD. */
165 hcd -> ux_hcd_entry_function = _ux_hcd_ehci_entry;
166
167 /* Set the state of the controller to HALTED first. */
168 hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED;
169
170 /* Read the EHCI Controller Command register. */
171 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND);
172
173 /* Isolate the Frame list size. */
174 ehci_register = (ehci_register >> 2) & 3;
175
176 /* Frame list size selection. */
177 switch (ehci_register)
178 {
179
180 case 0 :
181
182 /* Frame list size is 1024 entries. */
183 hcd_ehci -> ux_hcd_ehci_frame_list_size = 1024;
184 break;
185
186 case 1 :
187
188 /* Frame list size is 512 entries. */
189 hcd_ehci -> ux_hcd_ehci_frame_list_size = 512;
190 break;
191 case 2 :
192
193 /* Frame list size is 256 entries. */
194 hcd_ehci -> ux_hcd_ehci_frame_list_size = 256;
195 break;
196
197 default :
198
199 /* Error, Wrong frame size. This should never happen. */
200 status = (UX_ERROR);
201 }
202
203 /* Allocate the EHCI controller frame list. The memory alignment is 4K.
204 The number of entries may be changeable in some controllers. We get the value in the command register. */
205 if (status == UX_SUCCESS)
206 {
207 hcd_ehci -> ux_hcd_ehci_frame_list = _ux_utility_memory_allocate(UX_ALIGN_4096, UX_CACHE_SAFE_MEMORY, (hcd_ehci -> ux_hcd_ehci_frame_list_size * 4));
208 if (hcd_ehci -> ux_hcd_ehci_frame_list == UX_NULL)
209 status = (UX_MEMORY_INSUFFICIENT);
210 }
211
212 /* Allocate the list of eds. All eds are allocated on 32 byte memory boundary. */
213 if (status == UX_SUCCESS)
214 {
215 hcd_ehci -> ux_hcd_ehci_ed_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_ED) * _ux_system_host -> ux_system_host_max_ed);
216 if (hcd_ehci -> ux_hcd_ehci_ed_list == UX_NULL)
217 status = (UX_MEMORY_INSUFFICIENT);
218 }
219
220 /* Allocate the list of tds. All tds are allocated on 32 byte memory boundary. */
221 if (status == UX_SUCCESS)
222 {
223 hcd_ehci -> ux_hcd_ehci_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_TD) * _ux_system_host -> ux_system_host_max_td);
224 if (hcd_ehci -> ux_hcd_ehci_td_list == UX_NULL)
225 status = (UX_MEMORY_INSUFFICIENT);
226 }
227
228 #if UX_MAX_ISO_TD == 0 || !defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
229 hcd_ehci -> ux_hcd_ehci_fsiso_td_list = UX_NULL;
230 #else
231
232 /* Allocate the list of isochronous Full Speed tds. All tds are allocated on 32 byte
233 memory boundary. */
234 if (status == UX_SUCCESS)
235 {
236 hcd_ehci -> ux_hcd_ehci_fsiso_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_FSISO_TD) * _ux_system_host -> ux_system_host_max_iso_td);
237 if (hcd_ehci -> ux_hcd_ehci_fsiso_td_list == UX_NULL)
238 status = (UX_MEMORY_INSUFFICIENT);
239 }
240 #endif
241
242 #if UX_MAX_ISO_TD == 0
243 hcd_ehci -> ux_hcd_ehci_hsiso_td_list = UX_NULL;
244 #else
245
246 /* Allocate the list of isochronous High Speed tds. All tds are allocated on 32 byte memory boundary. */
247 if (status == UX_SUCCESS)
248 {
249 hcd_ehci -> ux_hcd_ehci_hsiso_td_list = _ux_utility_memory_allocate(UX_ALIGN_64, UX_CACHE_SAFE_MEMORY, sizeof(UX_EHCI_HSISO_TD) * _ux_system_host -> ux_system_host_max_iso_td);
250 if (hcd_ehci -> ux_hcd_ehci_hsiso_td_list == UX_NULL)
251 status = (UX_MEMORY_INSUFFICIENT);
252 }
253 #endif
254
255 /* Initialize the periodic tree. */
256 if (status == UX_SUCCESS)
257 status = _ux_hcd_ehci_periodic_tree_create(hcd_ehci);
258
259 if (status == UX_SUCCESS)
260 {
261
262 #if UX_MAX_DEVICES > 1
263
264 /* Since this is a USB 2.0 controller, we can safely hardwire the version. */
265 hcd -> ux_hcd_version = 0x200;
266 #endif
267
268 /* The EHCI Controller should not be running. */
269 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND);
270 ehci_register &= ~EHCI_HC_IO_RS;
271 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register);
272 _ux_utility_delay_ms(2);
273
274 /* Perform a global reset to the controller. */
275 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND);
276 ehci_register |= EHCI_HC_IO_HCRESET;
277 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register);
278
279 /* Ensure the reset is complete. */
280 while (ehci_register & EHCI_HC_IO_HCRESET)
281 {
282
283 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND);
284 }
285
286 /* Enable host mode for hardware peripheral. */
287 UX_HCD_EHCI_EXT_USB_HOST_MODE_ENABLE(hcd_ehci);
288
289 /* Set the Frame List register of the controller. */
290 lp.void_ptr = _ux_utility_physical_address(hcd_ehci -> ux_hcd_ehci_frame_list);
291 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_FRAME_LIST_BASE_ADDRESS, lp.value);
292
293 /* We need one endpoint to be inserted into the Asynchronous list. */
294 ed = _ux_hcd_ehci_ed_obtain(hcd_ehci);
295 if (ed == UX_NULL)
296 status = (UX_NO_ED_AVAILABLE);
297 }
298
299 if (status == UX_SUCCESS)
300 {
301
302 /* Make this ED point to itself. */
303 lp.void_ptr = _ux_utility_physical_address(ed);
304
305 /* Store the physical address of this Ed into the asynch list. */
306 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_ASYNCH_LIST_ADDRESS, lp.value);
307
308 /* Store LP with QH Typ. */
309 lp.value |= UX_EHCI_QH_TYP_QH;
310 ed -> ux_ehci_ed_queue_head = lp.ed_ptr;
311
312 /* This ED will be the HEAD ED in the asynch list. */
313 ed -> ux_ehci_ed_cap0 = UX_EHCI_QH_HEAD;
314
315 /* We keep this ED as being the first, the last and the head ED. */
316 hcd_ehci -> ux_hcd_ehci_asynch_head_list = ed;
317 hcd_ehci -> ux_hcd_ehci_asynch_first_list = ed;
318 hcd_ehci -> ux_hcd_ehci_asynch_last_list = ed;
319
320 /* Set the EHCI Interrupt threshold default value (1 or 8 per ms)
321 and the size of the frame list. */
322 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, EHCI_HC_IO_ITC);
323
324 /* Get the number of ports on the controller. The number of ports
325 needs to be reflected both for the generic HCD container and the
326 local ehci container. */
327 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCCR_HCS_PARAMS);
328 hcd -> ux_hcd_nb_root_hubs = (UINT) (ehci_register & 0xf);
329 if (hcd -> ux_hcd_nb_root_hubs > UX_MAX_ROOTHUB_PORT)
330 hcd -> ux_hcd_nb_root_hubs = UX_MAX_ROOTHUB_PORT;
331 hcd_ehci -> ux_hcd_ehci_nb_root_hubs = hcd -> ux_hcd_nb_root_hubs;
332
333 /* The controller transceiver can now send the device connection/extraction
334 signals to the EHCI controller. */
335 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_CONFIG_FLAG, UX_EHCI_ROUTE_TO_LOCAL_HC);
336
337 /* Create mutex for periodic list modification. */
338 status = _ux_host_mutex_create(&hcd_ehci -> ux_hcd_ehci_periodic_mutex, "ehci_periodic_mutex");
339 if (status != UX_SUCCESS)
340 status = (UX_MUTEX_ERROR);
341 }
342
343 /* We must enable the HCD protection semaphore. */
344 if (status == UX_SUCCESS)
345 {
346 status = _ux_host_semaphore_create(&hcd_ehci -> ux_hcd_ehci_protect_semaphore, "ux_hcd_protect_semaphore", 1);
347 if (status != UX_SUCCESS)
348 status = (UX_SEMAPHORE_ERROR);
349 }
350
351 /* We must enable the HCD doorbell semaphore. */
352 if (status == UX_SUCCESS)
353 {
354 status = _ux_host_semaphore_create(&hcd_ehci -> ux_hcd_ehci_doorbell_semaphore, "ux_hcd_doorbell_semaphore", 0);
355 if (status != UX_SUCCESS)
356 status = (UX_SEMAPHORE_ERROR);
357 }
358
359 if (status == UX_SUCCESS)
360 {
361
362 /* The EHCI Controller can now be Started. */
363 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_USB_COMMAND);
364
365 /* Set the Frame list size and the RUN bit.. */
366 ehci_register |= UX_EHCI_FRAME_LIST_MASK
367 | EHCI_HC_IO_RS
368 | EHCI_HC_IO_ASE
369 | EHCI_HC_IO_PSE;
370 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_COMMAND, ehci_register);
371
372 /* Regular EHCI with embedded TT. */
373 hcd_ehci -> ux_hcd_ehci_embedded_tt = UX_HCD_EHCI_EXT_EMBEDDED_TT_SUPPORT;
374
375 /* All ports must now be powered to pick up device insertion. */
376 _ux_hcd_ehci_power_root_hubs(hcd_ehci);
377
378 /* Set the state of the controller to OPERATIONAL. */
379 hcd -> ux_hcd_status = UX_HCD_STATUS_OPERATIONAL;
380
381 /* Set the EHCI Interrupt Register. */
382 _ux_hcd_ehci_register_write(hcd_ehci, EHCI_HCOR_USB_INTERRUPT, EHCI_HC_INTERRUPT_ENABLE_NORMAL);
383
384 /* The controller interrupt must have a handler and be active now. */
385 _ux_utility_set_interrupt_handler(hcd -> ux_hcd_irq, _ux_hcd_ehci_interrupt_handler);
386
387
388 /* Force a enum process if CCS detected.
389 ** Because CSC may keep zero in this case.
390 */
391 for (port_index = 0, status = 0; port_index < hcd_ehci -> ux_hcd_ehci_nb_root_hubs; port_index ++)
392 {
393
394 /* Read register. */
395 ehci_register = _ux_hcd_ehci_register_read(hcd_ehci, EHCI_HCOR_PORT_SC + port_index);
396
397 /* Check CCS. */
398 if (ehci_register & EHCI_HC_PS_CCS)
399 {
400 hcd_ehci -> ux_hcd_ehci_hcd_owner -> ux_hcd_root_hub_signal[port_index]++;
401 status ++;
402 }
403 }
404
405 /* Wakeup enum thread. */
406 if (status != 0)
407 _ux_host_semaphore_put(&_ux_system_host -> ux_system_host_enum_semaphore);
408
409 /* Return successful status. */
410 return(UX_SUCCESS);
411 }
412
413 /* Error! Free resources! */
414 if (hcd_ehci -> ux_hcd_ehci_frame_list)
415 _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_frame_list);
416 if (hcd_ehci -> ux_hcd_ehci_ed_list)
417 _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_ed_list);
418 if (hcd_ehci -> ux_hcd_ehci_td_list)
419 _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_td_list);
420 #if UX_MAX_ISO_TD && defined(UX_HCD_EHCI_SPLIT_TRANSFER_ENABLE)
421 if (hcd_ehci -> ux_hcd_ehci_fsiso_td_list)
422 _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_fsiso_td_list);
423 #endif
424 #if UX_MAX_ISO_TD
425 if (hcd_ehci -> ux_hcd_ehci_hsiso_td_list)
426 _ux_utility_memory_free(hcd_ehci -> ux_hcd_ehci_hsiso_td_list);
427 #endif
428 if (hcd_ehci -> ux_hcd_ehci_periodic_mutex.tx_mutex_id != 0)
429 _ux_host_mutex_delete(&hcd_ehci -> ux_hcd_ehci_periodic_mutex);
430 if (hcd_ehci -> ux_hcd_ehci_protect_semaphore.tx_semaphore_id != 0)
431 _ux_host_semaphore_delete(&hcd_ehci -> ux_hcd_ehci_protect_semaphore);
432 if (hcd_ehci -> ux_hcd_ehci_doorbell_semaphore.tx_semaphore_id != 0)
433 _ux_host_semaphore_delete(&hcd_ehci -> ux_hcd_ehci_doorbell_semaphore);
434 _ux_utility_memory_free(hcd_ehci);
435
436 /* Return error status code. */
437 return(status);
438 #endif
439 }
440