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 /** OHCI 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_ohci.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_hcd_ohci_initialize PORTABLE C */
37 /* 6.1.12 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function initializes the OHCI controller. It sets the dma */
45 /* areas, programs all the OHCI registers, setup the ED and TD */
46 /* containers, sets the control, and builds the periodic lists. */
47 /* */
48 /* INPUT */
49 /* */
50 /* HCD Pointer to HCD */
51 /* */
52 /* OUTPUT */
53 /* */
54 /* Completion Status */
55 /* */
56 /* CALLS */
57 /* */
58 /* _ux_hcd_ohci_periodic_tree_create Create OHCI periodic tree */
59 /* _ux_hcd_ohci_power_root_hubs Power root HUBs */
60 /* _ux_hcd_ohci_register_read Read OHCI register */
61 /* _ux_hcd_ohci_register_write Write OHCI register */
62 /* _ux_utility_memory_allocate Allocate memory block */
63 /* _ux_host_mutex_on Get mutex protection */
64 /* _ux_host_mutex_off Release mutex protection */
65 /* _ux_utility_physical_address Get physical address */
66 /* _ux_utility_set_interrupt_handler Setup interrupt handler */
67 /* */
68 /* CALLED BY */
69 /* */
70 /* Host Stack */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
77 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
78 /* optimized based on compile */
79 /* definitions, */
80 /* resulting in version 6.1 */
81 /* 01-31-2022 Xiuwen Cai Modified comment(s), */
82 /* fixed HcPeriodicStart value,*/
83 /* resulting in version 6.1.10 */
84 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
85 /* fixed standalone compile, */
86 /* resulting in version 6.1.11 */
87 /* 07-29-2022 Yajun Xia Modified comment(s), */
88 /* fixed OHCI PRSC issue, */
89 /* resulting in version 6.1.12 */
90 /* */
91 /**************************************************************************/
_ux_hcd_ohci_initialize(UX_HCD * hcd)92 UINT _ux_hcd_ohci_initialize(UX_HCD *hcd)
93 {
94
95 UX_HCD_OHCI *hcd_ohci;
96 ULONG ohci_register;
97 UINT index_loop;
98 UINT status;
99
100
101 /* The controller initialized here is of OHCI type. */
102 hcd -> ux_hcd_controller_type = UX_OHCI_CONTROLLER;
103
104 #if UX_MAX_DEVICES > 1
105 /* Initialize the max bandwidth for periodic endpoints. On OHCI, the spec says no
106 more than 90% to be allocated for periodic. */
107 hcd -> ux_hcd_available_bandwidth = UX_OHCI_AVAILABLE_BANDWIDTH;
108 #endif
109
110 /* Allocate memory for this OHCI HCD instance. */
111 hcd_ohci = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_HCD_OHCI));
112 if (hcd_ohci == UX_NULL)
113 return(UX_MEMORY_INSUFFICIENT);
114
115 /* Set the pointer to the OHCI HCD. */
116 hcd -> ux_hcd_controller_hardware = (VOID *) hcd_ohci;
117
118 /* Save the HCOR address. */
119 hcd_ohci -> ux_hcd_ohci_hcor = (ULONG *) hcd -> ux_hcd_io;
120
121 /* Set the generic HCD owner for the OHCI HCD. */
122 hcd_ohci -> ux_hcd_ohci_hcd_owner = hcd;
123
124 /* Initialize the function collector for this HCD. */
125 hcd -> ux_hcd_entry_function = _ux_hcd_ohci_entry;
126
127 /* Set the state of the controller to HALTED first. */
128 hcd -> ux_hcd_status = UX_HCD_STATUS_HALTED;
129
130 /* get an DMA safe address for the HCCA. This block of memory is to be aligned
131 on 256 bytes. */
132 hcd_ohci -> ux_hcd_ohci_hcca = _ux_utility_memory_allocate(UX_ALIGN_256, UX_CACHE_SAFE_MEMORY, sizeof(UX_HCD_OHCI_HCCA));
133 if (hcd_ohci -> ux_hcd_ohci_hcca == UX_NULL)
134 return(UX_MEMORY_INSUFFICIENT);
135
136 /* Allocate the list of eds. All eds are allocated on 16 byte memory boundary. */
137 hcd_ohci -> ux_hcd_ohci_ed_list = _ux_utility_memory_allocate(UX_ALIGN_16, UX_CACHE_SAFE_MEMORY, sizeof(UX_OHCI_ED) * _ux_system_host -> ux_system_host_max_ed);
138 if (hcd_ohci -> ux_hcd_ohci_ed_list == UX_NULL)
139 return(UX_MEMORY_INSUFFICIENT);
140
141 /* Allocate the list of tds. All tds are allocated on 32 byte memory boundary. */
142 hcd_ohci -> ux_hcd_ohci_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_OHCI_TD) * _ux_system_host -> ux_system_host_max_td);
143 if (hcd_ohci -> ux_hcd_ohci_td_list == UX_NULL)
144 return(UX_MEMORY_INSUFFICIENT);
145
146 /* Allocate the list of isochronous tds. All tds are allocated on 32 byte memory boundary. */
147 hcd_ohci -> ux_hcd_ohci_iso_td_list = _ux_utility_memory_allocate(UX_ALIGN_32, UX_CACHE_SAFE_MEMORY, sizeof(UX_OHCI_ISO_TD) * _ux_system_host -> ux_system_host_max_iso_td);
148 if (hcd_ohci -> ux_hcd_ohci_td_list == UX_NULL)
149 return(UX_MEMORY_INSUFFICIENT);
150
151 /* Initialize the periodic tree. */
152 status = _ux_hcd_ohci_periodic_tree_create(hcd_ohci);
153 if (status != UX_SUCCESS)
154 return(status);
155
156 #if UX_MAX_DEVICES > 1
157
158 /* Read the OHCI controller version, it is either USB 1.0 or 1.1. This is important for
159 filtering INT out endpoints on a 1.0 OHCI. */
160 hcd -> ux_hcd_version = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_REVISION);
161 #endif
162
163 /* Set the state of the OHCI controller to reset in the control register.
164 This is not compulsory but some controllers demand to start in this state. */
165 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, 0);
166
167 /* The following is time critical. If we get interrupted here, the controller will go in
168 suspend mode. Get the protection mutex. */
169 _ux_host_mutex_on(&_ux_system -> ux_system_mutex);
170
171 /* Send the reset command to the controller. The controller should ack
172 this command within 10us. We try this several time and check for timeout. */
173 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_COMMAND_STATUS, OHCI_HC_CS_HCR);
174
175 for (index_loop = 0; index_loop < UX_OHCI_RESET_RETRY; index_loop++)
176 {
177
178 ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_COMMAND_STATUS);
179 if ((ohci_register & OHCI_HC_CS_HCR) == 0)
180 break;
181 }
182
183 /* Check if the controller is reset properly. */
184 if ((ohci_register & OHCI_HC_CS_HCR) != 0)
185 {
186
187 /* Release the thread protection. */
188 _ux_host_mutex_off(&_ux_system -> ux_system_mutex);
189
190 /* Error trap. */
191 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_HCD, UX_CONTROLLER_INIT_FAILED);
192
193 /* If trace is enabled, insert this event into the trace buffer. */
194 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CONTROLLER_INIT_FAILED, 0, 0, 0, UX_TRACE_ERRORS, 0, 0)
195
196 return(UX_CONTROLLER_INIT_FAILED);
197 }
198
199 /* Set the HCCA pointer to the HCOR. */
200 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_HCCA, (ULONG) _ux_utility_physical_address(hcd_ohci -> ux_hcd_ohci_hcca));
201
202 /* For now and until we have control and bulk ED, reset the control and bulk head registers. */
203 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL_HEAD_ED, 0);
204 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL_CURRENT_ED, 0);
205 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_BULK_HEAD_ED, 0);
206 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_BULK_CURRENT_ED, 0);
207
208 /* Turn on the OHCI controller functional registers we will use after this operation,
209 the controller is operational. */
210 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_CONTROL, OHCI_HC_CONTROL_VALUE);
211 hcd -> ux_hcd_status = UX_HCD_STATUS_OPERATIONAL;
212
213 /* We can safely release the mutex protection. */
214 _ux_host_mutex_off(&_ux_system -> ux_system_mutex);
215
216 /* Set the controller interval. */
217 ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_FM_INTERVAL) & OHCI_HC_FM_INTERVAL_CLEAR;
218 ohci_register |= OHCI_HC_FM_INTERVAL_SET;
219 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_FM_INTERVAL, ohci_register);
220
221 /* Set HcPeriodicStart to a value that is 90% of the value in FrameInterval field of the HcFmInterval register. */
222 ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_FM_INTERVAL) & OHCI_HC_FM_INTERVAL_FI_MASK;
223 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_PERIODIC_START, ohci_register * 9 / 10);
224
225 /* Reset all the OHCI interrupts and re-enable only the ones we will use. */
226 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_DISABLE, OHCI_HC_INTERRUPT_DISABLE_ALL);
227 _ux_hcd_ohci_register_write(hcd_ohci, OHCI_HC_INTERRUPT_ENABLE, OHCI_HC_INTERRUPT_ENABLE_NORMAL);
228
229 /* Get the number of ports on the controller. The number of ports needs to be reflected both
230 for the generic HCD container and the local OHCI container. */
231 ohci_register = _ux_hcd_ohci_register_read(hcd_ohci, OHCI_HC_RH_DESCRIPTOR_A);
232 hcd -> ux_hcd_nb_root_hubs = (UINT) (ohci_register & 0xff);
233 if (hcd -> ux_hcd_nb_root_hubs > UX_MAX_ROOTHUB_PORT)
234 hcd -> ux_hcd_nb_root_hubs = UX_MAX_ROOTHUB_PORT;
235 hcd_ohci -> ux_hcd_ohci_nb_root_hubs = hcd -> ux_hcd_nb_root_hubs;
236
237 /* Create HCD event flags */
238 status = _ux_host_event_flags_create(&hcd_ohci -> ux_hcd_ohci_event_flags_group, "ux_hcd_ohci_event_flags_group");
239 if (status != UX_SUCCESS)
240 return(status);
241
242 /* All ports must now be powered to pick up device insertion. */
243 _ux_hcd_ohci_power_root_hubs(hcd_ohci);
244
245 /* Return successful completion. */
246 return(UX_SUCCESS);
247 }
248
249