1 /*
2 * Copyright (c) 2021 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 /*---------------------------------------------------------------------
9 * Include
10 *---------------------------------------------------------------------
11 */
12 #include "hpm_usb_drv.h"
13 #include "hpm_misc.h"
14 #include "hpm_soc_feature.h"
15 #include "hpm_common.h"
16 /*---------------------------------------------------------------------
17 * Macro Enum Declaration
18 *---------------------------------------------------------------------
19 */
20
21 /* ENDPTCTRL */
22 enum {
23 ENDPTCTRL_STALL = HPM_BITSMASK(1, 0),
24 ENDPTCTRL_TYPE = HPM_BITSMASK(3, 2),
25 ENDPTCTRL_TOGGLE_INHIBIT = HPM_BITSMASK(1, 5),
26 ENDPTCTRL_TOGGLE_RESET = HPM_BITSMASK(1, 6),
27 ENDPTCTRL_ENABLE = HPM_BITSMASK(1, 7),
28 };
29
30 /*---------------------------------------------------------------------
31 * Internal API
32 *---------------------------------------------------------------------
33 */
34
35 /* De-initialize USB phy */
usb_phy_deinit(USB_Type * ptr)36 static void usb_phy_deinit(USB_Type *ptr)
37 {
38 ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK; /* set otg_utmi_suspend_m for naneng usbphy */
39
40 ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK; /* clear otg_utmi_reset_sw for naneng usbphy */
41
42 ptr->PHY_CTRL1 &= ~USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK; /* clear cfg_rst_n */
43
44 ptr->PHY_CTRL1 &= ~USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK; /* clear otg_suspendm */
45 }
46
47 /*---------------------------------------------------------------------
48 * Driver API
49 *---------------------------------------------------------------------
50 */
51 /* Initialize USB phy */
usb_phy_init(USB_Type * ptr)52 void usb_phy_init(USB_Type *ptr)
53 {
54 uint32_t status;
55
56 usb_phy_enable_dp_dm_pulldown(ptr);
57 ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK; /* set otg_utmi_reset_sw for naneng usbphy */
58 ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK; /* clr otg_utmi_suspend_m for naneng usbphy */
59 ptr->PHY_CTRL1 &= ~USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK; /* clr cfg_rst_n */
60
61 do {
62 status = USB_OTG_CTRL0_OTG_UTMI_RESET_SW_GET(ptr->OTG_CTRL0); /* wait for reset status */
63 } while (status == 0);
64
65 ptr->OTG_CTRL0 |= USB_OTG_CTRL0_OTG_UTMI_SUSPENDM_SW_MASK; /* set otg_utmi_suspend_m for naneng usbphy */
66
67 for (volatile uint32_t i = 0; i < USB_PHY_INIT_DELAY_COUNT; i++) {
68 (void)ptr->PHY_CTRL1; /* used for delay */
69 }
70
71 ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_WKDPDMCHG_EN_MASK; /* Disable dp/dm wakeup */
72
73 ptr->OTG_CTRL0 &= ~USB_OTG_CTRL0_OTG_UTMI_RESET_SW_MASK; /* clear otg_utmi_reset_sw for naneng usbphy */
74
75 /* otg utmi clock detection */
76 ptr->PHY_STATUS |= USB_PHY_STATUS_UTMI_CLK_VALID_MASK; /* write 1 to clear valid status */
77 do {
78 status = USB_PHY_STATUS_UTMI_CLK_VALID_GET(ptr->PHY_STATUS); /* get utmi clock status */
79 } while (status == 0);
80
81 ptr->PHY_CTRL1 |= USB_PHY_CTRL1_UTMI_CFG_RST_N_MASK; /* set cfg_rst_n */
82
83 ptr->PHY_CTRL1 |= USB_PHY_CTRL1_UTMI_OTG_SUSPENDM_MASK; /* set otg_suspendm */
84 }
85
usb_dcd_bus_reset(USB_Type * ptr,uint16_t ep0_max_packet_size)86 void usb_dcd_bus_reset(USB_Type *ptr, uint16_t ep0_max_packet_size)
87 {
88 (void) ep0_max_packet_size;
89 /* The reset value for all endpoint types is the control endpoint. If one endpoint
90 * direction is enabled and the paired endpoint of opposite direction is disabled, then the
91 * endpoint type of the unused direction must be changed from the control type to any other
92 * type (e.g. bulk). Leaving an un-configured endpoint control will cause undefined behavior
93 * for the data PID tracking on the active endpoint.
94 */
95
96 for (uint32_t i = 1; i < USB_SOC_DCD_MAX_ENDPOINT_COUNT; i++) {
97 ptr->ENDPTCTRL[i] = USB_ENDPTCTRL_TXT_SET(usb_xfer_bulk) | USB_ENDPTCTRL_RXT_SET(usb_xfer_bulk);
98 }
99
100 /* Clear All Registers */
101 ptr->ENDPTNAK = ptr->ENDPTNAK;
102 ptr->ENDPTNAKEN = 0;
103 ptr->USBSTS = ptr->USBSTS;
104 ptr->ENDPTSETUPSTAT = ptr->ENDPTSETUPSTAT;
105 ptr->ENDPTCOMPLETE = ptr->ENDPTCOMPLETE;
106
107 while (ptr->ENDPTPRIME) {
108 }
109 ptr->ENDPTFLUSH = 0xFFFFFFFF;
110 while (ptr->ENDPTFLUSH) {
111 }
112 }
113
usb_dcd_init(USB_Type * ptr)114 void usb_dcd_init(USB_Type *ptr)
115 {
116 /* Initialize USB phy */
117 usb_phy_init(ptr);
118
119 /* Reset controller */
120 ptr->USBCMD |= USB_USBCMD_RST_MASK;
121 while (USB_USBCMD_RST_GET(ptr->USBCMD)) {
122 }
123
124 /* Set mode to device, must be set immediately after reset */
125 ptr->USBMODE &= ~USB_USBMODE_CM_MASK;
126 ptr->USBMODE |= USB_USBMODE_CM_SET(2);
127
128 /* Disable setup lockout, please refer to "Control Endpoint Operation" section in RM. */
129 ptr->USBMODE &= ~USB_USBMODE_SLOM_MASK;
130
131 /* Set the endian */
132 ptr->USBMODE &= ~USB_USBMODE_ES_MASK;
133
134 /* TODO Force fullspeed on non-highspeed port */
135 /* ptr->PORTSC1 |= USB_PORTSC1_PFSC_MASK; */
136
137 /* Set parallel interface signal */
138 ptr->PORTSC1 &= ~USB_PORTSC1_STS_MASK;
139
140 /* Set parallel transceiver width */
141 ptr->PORTSC1 &= ~USB_PORTSC1_PTW_MASK;
142
143 #ifdef CONFIG_USB_DEVICE_FS
144 /* Set usb forced to full speed mode */
145 ptr->PORTSC1 |= USB_PORTSC1_PFSC_MASK;
146 #endif
147
148 /* Not use interrupt threshold. */
149 ptr->USBCMD &= ~USB_USBCMD_ITC_MASK;
150
151 /* Enable VBUS discharge */
152 ptr->OTGSC |= USB_OTGSC_VD_MASK;
153 }
154
usb_dcd_deinit(USB_Type * ptr)155 void usb_dcd_deinit(USB_Type *ptr)
156 {
157 /* Stop */
158 ptr->USBCMD &= ~USB_USBCMD_RS_MASK;
159
160 /* Reset controller */
161 ptr->USBCMD |= USB_USBCMD_RST_MASK;
162 while (USB_USBCMD_RST_GET(ptr->USBCMD)) {
163 }
164
165 /* De-initialize USB phy */
166 usb_phy_deinit(ptr);
167
168 /* Reset endpoint list address register */
169 ptr->ENDPTLISTADDR = 0;
170
171 /* Reset status register */
172 ptr->USBSTS = ptr->USBSTS;
173
174 /* Reset interrupt enable register */
175 ptr->USBINTR = 0;
176 }
177
178 /* Connect by enabling internal pull-up resistor on D+/D- */
usb_dcd_connect(USB_Type * ptr)179 void usb_dcd_connect(USB_Type *ptr)
180 {
181 ptr->USBCMD |= USB_USBCMD_RS_MASK;
182 }
183
184 /* Disconnect by disabling internal pull-up resistor on D+/D- */
usb_dcd_disconnect(USB_Type * ptr)185 void usb_dcd_disconnect(USB_Type *ptr)
186 {
187 /* Stop */
188 ptr->USBCMD &= ~USB_USBCMD_RS_MASK;
189
190 /* Pullup DP to make the phy switch into full speed mode */
191 ptr->USBCMD |= USB_USBCMD_RS_MASK;
192
193 /* Clear the sof flag */
194 ptr->USBSTS |= USB_USBSTS_SRI_MASK;
195
196 /* Wait a SOF (It will not be a dead loop even usb cable is not connected.) */
197 while (USB_USBSTS_SRI_GET(ptr->USBSTS) == 0) {
198 }
199
200 /* Disconnect */
201 ptr->USBCMD &= ~USB_USBCMD_RS_MASK;
202 }
203
204 /*---------------------------------------------------------------------
205 * Endpoint API
206 *---------------------------------------------------------------------
207 */
usb_dcd_edpt_open(USB_Type * ptr,usb_endpoint_config_t * config)208 void usb_dcd_edpt_open(USB_Type *ptr, usb_endpoint_config_t *config)
209 {
210 uint8_t const epnum = config->ep_addr & 0x0f;
211 uint8_t const dir = (config->ep_addr & 0x80) >> 7;
212
213 /* Enable EP Control */
214 uint32_t temp = ptr->ENDPTCTRL[epnum];
215 temp &= ~((0x03 << 2) << (dir ? 16 : 0));
216 temp |= ((config->xfer << 2) | ENDPTCTRL_ENABLE | ENDPTCTRL_TOGGLE_RESET) << (dir ? 16 : 0);
217 ptr->ENDPTCTRL[epnum] = temp;
218 }
219
usb_dcd_edpt_get_type(USB_Type * ptr,uint8_t ep_addr)220 uint8_t usb_dcd_edpt_get_type(USB_Type *ptr, uint8_t ep_addr)
221 {
222 uint8_t const epnum = ep_addr & 0x0f;
223 uint8_t const dir = (ep_addr & 0x80) >> 7;
224 uint32_t temp = ptr->ENDPTCTRL[epnum];
225
226 return dir ? USB_ENDPTCTRL_TXT_GET(temp) : USB_ENDPTCTRL_RXT_GET(temp);
227 }
228
usb_dcd_edpt_xfer(USB_Type * ptr,uint8_t ep_idx)229 void usb_dcd_edpt_xfer(USB_Type *ptr, uint8_t ep_idx)
230 {
231 uint32_t offset = ep_idx / 2 + ((ep_idx % 2) ? 16 : 0);
232
233 /* Start transfer */
234 ptr->ENDPTPRIME = 1 << offset;
235 }
236
usb_dcd_edpt_stall(USB_Type * ptr,uint8_t ep_addr)237 void usb_dcd_edpt_stall(USB_Type *ptr, uint8_t ep_addr)
238 {
239 uint8_t const epnum = ep_addr & 0x0f;
240 uint8_t const dir = (ep_addr & 0x80) >> 7;
241
242 ptr->ENDPTCTRL[epnum] |= ENDPTCTRL_STALL << (dir ? 16 : 0);
243 }
244
usb_dcd_edpt_clear_stall(USB_Type * ptr,uint8_t ep_addr)245 void usb_dcd_edpt_clear_stall(USB_Type *ptr, uint8_t ep_addr)
246 {
247 uint8_t const epnum = ep_addr & 0x0f;
248 uint8_t const dir = (ep_addr & 0x80) >> 7;
249
250 /* data toggle also need to be reset */
251 ptr->ENDPTCTRL[epnum] |= ENDPTCTRL_TOGGLE_RESET << (dir ? 16 : 0);
252 ptr->ENDPTCTRL[epnum] &= ~(ENDPTCTRL_STALL << (dir ? 16 : 0));
253 }
254
usb_dcd_edpt_check_stall(USB_Type * ptr,uint8_t ep_addr)255 bool usb_dcd_edpt_check_stall(USB_Type *ptr, uint8_t ep_addr)
256 {
257 uint8_t const epnum = ep_addr & 0x0f;
258 uint8_t const dir = (ep_addr & 0x80) >> 7;
259
260 return (ptr->ENDPTCTRL[epnum] & (ENDPTCTRL_STALL << (dir ? 16 : 0))) ? true : false;
261 }
262
usb_dcd_edpt_close(USB_Type * ptr,uint8_t ep_addr)263 void usb_dcd_edpt_close(USB_Type *ptr, uint8_t ep_addr)
264 {
265 uint8_t const epnum = ep_addr & 0x0f;
266 uint8_t const dir = (ep_addr & 0x80) >> 7;
267
268 uint32_t primebit = HPM_BITSMASK(1, epnum) << (dir ? 16 : 0);
269
270 /* Flush the endpoint to stop a transfer. */
271 do {
272 /* Set the corresponding bit(s) in the ENDPTFLUSH register */
273 ptr->ENDPTFLUSH |= primebit;
274
275 /* Wait until all bits in the ENDPTFLUSH register are cleared. */
276 while (0U != (ptr->ENDPTFLUSH & primebit)) {
277 }
278 /*
279 * Read the ENDPTSTAT register to ensure that for all endpoints
280 * commanded to be flushed, that the corresponding bits
281 * are now cleared.
282 */
283 } while (0U != (ptr->ENDPTSTAT & primebit));
284
285 /* Disable the endpoint */
286 ptr->ENDPTCTRL[epnum] &= ~((ENDPTCTRL_TYPE | ENDPTCTRL_ENABLE | ENDPTCTRL_STALL) << (dir ? 16 : 0));
287 ptr->ENDPTCTRL[epnum] |= (usb_xfer_bulk << 2) << (dir ? 16 : 0);
288 }
289
usb_dcd_remote_wakeup(USB_Type * ptr)290 void usb_dcd_remote_wakeup(USB_Type *ptr)
291 {
292 (void) ptr;
293 }
294
usb_hcd_init(USB_Type * ptr,uint32_t int_mask,uint16_t framelist_size)295 bool usb_hcd_init(USB_Type *ptr, uint32_t int_mask, uint16_t framelist_size)
296 {
297 uint8_t framelist_size_bf = 0;
298
299 if (framelist_size > USB_SOC_HCD_FRAMELIST_MAX_ELEMENTS || framelist_size == 0) {
300 return false;
301 }
302
303 framelist_size_bf = 10 - get_first_set_bit_from_lsb(framelist_size);
304
305 if (framelist_size != (1 << get_first_set_bit_from_lsb(framelist_size))) {
306 return false;
307 }
308
309 usb_phy_init(ptr);
310
311 /* Reset controller */
312 ptr->USBCMD |= USB_USBCMD_RST_MASK;
313 while (USB_USBCMD_RST_GET(ptr->USBCMD)) {
314 }
315
316 /* Set mode to host, must be set immediately after reset */
317 ptr->USBMODE &= ~USB_USBMODE_CM_MASK;
318 ptr->USBMODE |= USB_USBMODE_CM_SET(usb_ctrl_mode_host);
319
320 /* Set the endian */
321 ptr->USBMODE &= ~USB_USBMODE_ES_MASK;
322
323 /* Set parallel interface signal */
324 ptr->PORTSC1 &= ~USB_PORTSC1_STS_MASK;
325
326 /* Set parallel transceiver width */
327 ptr->PORTSC1 &= ~USB_PORTSC1_PTW_MASK;
328
329 /* Not use interrupt threshold. */
330 ptr->USBCMD &= ~USB_USBCMD_ITC_MASK;
331
332 /* USB INT Register */
333 ptr->USBSTS = ptr->USBSTS;
334 ptr->USBINTR |= int_mask;
335
336 /* USB CMD Register */
337 ptr->USBCMD = USB_USBCMD_ASE_MASK | USB_USBCMD_PSE_MASK
338 | USB_USBCMD_FS_2_SET(framelist_size_bf >> 2)
339 | USB_USBCMD_FS_1_SET(framelist_size_bf);
340
341 return true;
342 }
343
usb_hcd_port_reset(USB_Type * ptr)344 void usb_hcd_port_reset(USB_Type *ptr)
345 {
346 if (usb_phy_get_line_state(ptr) == usb_line_state2) {
347 ptr->PORTSC1 |= USB_PORTSC1_STS_MASK;
348 } else {
349 ptr->PORTSC1 &= ~USB_PORTSC1_STS_MASK;
350 }
351
352 ptr->PORTSC1 &= ~USB_PORTSC1_PE_MASK;
353 ptr->PORTSC1 |= USB_PORTSC1_PR_MASK;
354
355 /* wait until port reset sequence is completed */
356 while (USB_PORTSC1_PR_GET(ptr->PORTSC1)) {
357 }
358 }
359