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