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