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