1 /*
2 * Copyright (c) 2023 Nuvoton Technology Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_numaker_usbd
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/usb/usb_device.h>
11 #include <zephyr/dt-bindings/usb/usb.h>
12 #include <zephyr/sys/math_extras.h>
13 #include <zephyr/drivers/clock_control.h>
14 #include <zephyr/drivers/clock_control/clock_control_numaker.h>
15 #include <zephyr/drivers/reset.h>
16 #include <zephyr/drivers/pinctrl.h>
17
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(usb_dc_numaker, CONFIG_USB_DRIVER_LOG_LEVEL);
20
21 #include <soc.h>
22 #include <NuMicro.h>
23
24 /* USBD notes
25 *
26 * 1. Require 48MHz clock source
27 * (1) Not support HIRC48 as clock source. It involves trim with USB SOF packets
28 * and isn't suitable in HAL.
29 * (2) Instead of HICR48, core clock is required to be multiple of 48MHz e.g. 192MHz,
30 * to generate necessary 48MHz.
31 */
32
33 /* For bus reset, keep 'SE0' (USB spec: SE0 >= 2.5 ms) */
34 #define NUMAKER_USBD_BUS_RESET_DRV_SE0_US 3000
35
36 /* For bus resume, generate 'K' (USB spec: 'K' >= 1 ms) */
37 #define NUMAKER_USBD_BUS_RESUME_DRV_K_US 1500
38
39 /* Reserve DMA buffer for Setup/CTRL OUT/CTRL IN, required to be 8-byte aligned */
40 #define NUMAKER_USBD_DMABUF_SIZE_SETUP 8
41 #define NUMAKER_USBD_DMABUF_SIZE_CTRLOUT 64
42 #define NUMAKER_USBD_DMABUF_SIZE_CTRLIN 64
43
44 /* Maximum number of EP contexts across all instances
45 * This is to static-allocate EP contexts which can accommodate all instances.
46 * The number of effective EP contexts per instance is passed on through its
47 * num_bidir_endpoints, which must not be larger than this.
48 */
49 #define NUMAKER_USBD_EP_MAXNUM 25ul
50
51 /* Message type */
52 #define NUMAKER_USBD_MSG_TYPE_SW_RECONN 0 /* S/W reconnect */
53 #define NUMAKER_USBD_MSG_TYPE_CB_STATE 1 /* Callback for usb_dc_status_code */
54 #define NUMAKER_USBD_MSG_TYPE_CB_EP 2 /* Callback for usb_dc_ep_cb_status_code */
55
56 /* Message structure */
57 struct numaker_usbd_msg {
58 uint32_t type;
59 union {
60 struct {
61 enum usb_dc_status_code status_code;
62 } cb_device;
63 struct {
64 uint8_t ep;
65 enum usb_dc_ep_cb_status_code status_code;
66 } cb_ep;
67 };
68 };
69
70 /* Immutable device context */
71 struct numaker_usbd_config {
72 USBD_T *base;
73 const struct reset_dt_spec reset;
74 uint32_t clk_modidx;
75 uint32_t clk_src;
76 uint32_t clk_div;
77 const struct device *clkctrl_dev;
78 void (*irq_config_func)(const struct device *dev);
79 void (*irq_unconfig_func)(const struct device *dev);
80 const struct pinctrl_dev_config *pincfg;
81 uint32_t num_bidir_endpoints;
82 uint32_t dmabuf_size;
83 bool disallow_iso_inout_same;
84 };
85
86 /* EP context */
87 struct numaker_usbd_ep {
88 bool valid;
89
90 bool nak_clr; /* NAK cleared (ACK next transaction) */
91
92 const struct device *dev; /* Pointer to the containing device */
93
94 uint8_t ep_hw_idx; /* BSP USBD driver EP index EP0, EP1, EP2, etc */
95 uint32_t ep_hw_cfg; /* BSP USBD driver EP configuration */
96
97 /* EP DMA buffer */
98 bool dmabuf_valid;
99 uint32_t dmabuf_base;
100 uint32_t dmabuf_size;
101
102 /* On USBD, no H/W FIFO. Simulate based on above DMA buffer with
103 * one-shot implementation
104 */
105 uint32_t read_fifo_pos;
106 uint32_t read_fifo_used;
107 uint32_t write_fifo_pos;
108 uint32_t write_fifo_free;
109
110 /* NOTE: On USBD, Setup and CTRL OUT are not completely separated. CTRL OUT MXPLD
111 * can be overridden to 8 by next Setup. To overcome it, we make one copy of CTRL
112 * OUT MXPLD immediately on its interrupt.
113 */
114 uint32_t mxpld_ctrlout;
115
116 /* EP address */
117 bool addr_valid;
118 uint8_t addr;
119
120 /* EP MPS */
121 bool mps_valid;
122 uint16_t mps;
123
124 usb_dc_ep_callback cb; /* EP callback function */
125 };
126
127 /* EP context manager */
128 struct numaker_usbd_ep_mgmt {
129 /* EP context management
130 *
131 * Allocate-only, and de-allocate all on re-initialize in usb_dc_attach().
132 */
133 uint8_t ep_idx;
134
135 /* DMA buffer management
136 *
137 * Allocate-only, and de-allocate all on re-initialize in usb_dc_attach().
138 */
139 uint32_t dmabuf_pos;
140
141 /* Pass Setup packet from ISR to thread */
142 bool new_setup;
143 struct usb_setup_packet setup_packet;
144
145 struct numaker_usbd_ep ep_pool[NUMAKER_USBD_EP_MAXNUM];
146 };
147
148 /* Mutable device context */
149 struct numaker_usbd_data {
150 uint8_t addr; /* Host assigned USB device address */
151
152 struct k_mutex sync_mutex;
153
154 /* Enable interrupt top/bottom halves processing
155 *
156 * Registered callbacks may use mutex or other kernel functions which are not supported
157 * in interrupt context
158 */
159 struct k_msgq msgq;
160 struct numaker_usbd_msg msgq_buf[CONFIG_USB_DC_NUMAKER_MSG_QUEUE_SIZE];
161
162 K_KERNEL_STACK_MEMBER(msg_hdlr_thread_stack,
163 CONFIG_USB_DC_NUMAKER_MSG_HANDLER_THREAD_STACK_SIZE);
164 struct k_thread msg_hdlr_thread;
165
166 usb_dc_status_callback status_cb; /* Status callback function */
167
168 struct numaker_usbd_ep_mgmt ep_mgmt; /* EP management */
169 };
170
171 static inline const struct device *numaker_usbd_device_get(void);
172
numaker_usbd_lock(const struct device * dev)173 static inline void numaker_usbd_lock(const struct device *dev)
174 {
175 struct numaker_usbd_data *data = dev->data;
176
177 k_mutex_lock(&data->sync_mutex, K_FOREVER);
178 }
179
numaker_usbd_unlock(const struct device * dev)180 static inline void numaker_usbd_unlock(const struct device *dev)
181 {
182 struct numaker_usbd_data *data = dev->data;
183
184 k_mutex_unlock(&data->sync_mutex);
185 }
186
numaker_usbd_sw_connect(const struct device * dev)187 static inline void numaker_usbd_sw_connect(const struct device *dev)
188 {
189 const struct numaker_usbd_config *config = dev->config;
190 USBD_T *const base = config->base;
191
192 /* Clear all interrupts first for clean */
193 base->INTSTS = base->INTSTS;
194
195 /* Enable relevant interrupts */
196 base->INTEN = USBD_INT_BUS | USBD_INT_USB | USBD_INT_FLDET | USBD_INT_WAKEUP | USBD_INT_SOF;
197
198 /* Clear SE0 for connect */
199 base->SE0 &= ~USBD_DRVSE0;
200 }
201
numaker_usbd_sw_disconnect(const struct device * dev)202 static inline void numaker_usbd_sw_disconnect(const struct device *dev)
203 {
204 const struct numaker_usbd_config *config = dev->config;
205 USBD_T *const base = config->base;
206
207 /* Set SE0 for disconnect */
208 base->SE0 |= USBD_DRVSE0;
209 }
210
numaker_usbd_sw_reconnect(const struct device * dev)211 static inline void numaker_usbd_sw_reconnect(const struct device *dev)
212 {
213 /* Keep SE0 to trigger bus reset */
214 numaker_usbd_sw_disconnect(dev);
215 k_sleep(K_USEC(NUMAKER_USBD_BUS_RESET_DRV_SE0_US));
216 numaker_usbd_sw_connect(dev);
217 }
218
numaker_usbd_reset_addr(const struct device * dev)219 static inline void numaker_usbd_reset_addr(const struct device *dev)
220 {
221 const struct numaker_usbd_config *config = dev->config;
222 struct numaker_usbd_data *data = dev->data;
223 USBD_T *const base = config->base;
224
225 base->FADDR = 0;
226 data->addr = 0;
227 }
228
numaker_usbd_set_addr(const struct device * dev)229 static inline void numaker_usbd_set_addr(const struct device *dev)
230 {
231 const struct numaker_usbd_config *config = dev->config;
232 struct numaker_usbd_data *data = dev->data;
233 USBD_T *const base = config->base;
234
235 if (base->FADDR != data->addr) {
236 base->FADDR = data->addr;
237 }
238 }
239
240 /* USBD EP base by e.g. EP0, EP1, ... */
numaker_usbd_ep_base(const struct device * dev,uint32_t ep_hw_idx)241 static inline USBD_EP_T *numaker_usbd_ep_base(const struct device *dev, uint32_t ep_hw_idx)
242 {
243 const struct numaker_usbd_config *config = dev->config;
244 USBD_T *const base = config->base;
245
246 return base->EP + ep_hw_idx;
247 }
248
numaker_usbd_ep_fifo_max(struct numaker_usbd_ep * ep_cur)249 static inline uint32_t numaker_usbd_ep_fifo_max(struct numaker_usbd_ep *ep_cur)
250 {
251 /* NOTE: For one-shot implementation, effective size of EP FIFO is limited to EP MPS */
252 __ASSERT_NO_MSG(ep_cur->dmabuf_valid);
253 __ASSERT_NO_MSG(ep_cur->mps_valid);
254 __ASSERT_NO_MSG(ep_cur->mps <= ep_cur->dmabuf_size);
255 return ep_cur->mps;
256 }
257
numaker_usbd_ep_fifo_used(struct numaker_usbd_ep * ep_cur)258 static inline uint32_t numaker_usbd_ep_fifo_used(struct numaker_usbd_ep *ep_cur)
259 {
260 __ASSERT_NO_MSG(ep_cur->dmabuf_valid);
261
262 return USB_EP_DIR_IS_OUT(ep_cur->addr)
263 ? ep_cur->read_fifo_used
264 : numaker_usbd_ep_fifo_max(ep_cur) - ep_cur->write_fifo_free;
265 }
266
267 /* Reset EP FIFO
268 *
269 * NOTE: EP FIFO is based on EP DMA buffer, which may not be configured yet.
270 */
numaker_usbd_ep_fifo_reset(struct numaker_usbd_ep * ep_cur)271 static void numaker_usbd_ep_fifo_reset(struct numaker_usbd_ep *ep_cur)
272 {
273 if (ep_cur->dmabuf_valid && ep_cur->mps_valid) {
274 if (USB_EP_DIR_IS_OUT(ep_cur->addr)) {
275 /* Read FIFO */
276 ep_cur->read_fifo_pos = ep_cur->dmabuf_base;
277 ep_cur->read_fifo_used = 0;
278 } else {
279 /* Write FIFO */
280 ep_cur->write_fifo_pos = ep_cur->dmabuf_base;
281 ep_cur->write_fifo_free = numaker_usbd_ep_fifo_max(ep_cur);
282 }
283 }
284 }
285
numaker_usbd_ep_set_stall(struct numaker_usbd_ep * ep_cur)286 static inline void numaker_usbd_ep_set_stall(struct numaker_usbd_ep *ep_cur)
287 {
288 const struct device *dev = ep_cur->dev;
289 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
290
291 /* Set EP to stalled */
292 ep_base->CFGP |= USBD_CFGP_SSTALL_Msk;
293 }
294
295 /* Reset EP to unstalled and data toggle bit to 0 */
numaker_usbd_ep_clear_stall_n_data_toggle(struct numaker_usbd_ep * ep_cur)296 static inline void numaker_usbd_ep_clear_stall_n_data_toggle(struct numaker_usbd_ep *ep_cur)
297 {
298 const struct device *dev = ep_cur->dev;
299 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
300
301 /* Reset EP to unstalled */
302 ep_base->CFGP &= ~USBD_CFGP_SSTALL_Msk;
303
304 /* Reset EP data toggle bit to 0 */
305 ep_base->CFG &= ~USBD_CFG_DSQSYNC_Msk;
306 }
307
numaker_usbd_ep_is_stalled(struct numaker_usbd_ep * ep_cur)308 static inline bool numaker_usbd_ep_is_stalled(struct numaker_usbd_ep *ep_cur)
309 {
310 const struct device *dev = ep_cur->dev;
311 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
312
313 return ep_base->CFGP & USBD_CFGP_SSTALL_Msk;
314 }
315
numaker_usbd_send_msg(const struct device * dev,const struct numaker_usbd_msg * msg)316 static int numaker_usbd_send_msg(const struct device *dev, const struct numaker_usbd_msg *msg)
317 {
318 struct numaker_usbd_data *data = dev->data;
319 int rc;
320
321 rc = k_msgq_put(&data->msgq, msg, K_NO_WAIT);
322 if (rc < 0) {
323 /* Try to recover by S/W reconnect */
324 struct numaker_usbd_msg msg_reconn = {
325 .type = NUMAKER_USBD_MSG_TYPE_SW_RECONN,
326 };
327
328 LOG_ERR("Message queue overflow");
329
330 /* Discard all not yet received messages for error recovery below */
331 k_msgq_purge(&data->msgq);
332
333 rc = k_msgq_put(&data->msgq, &msg_reconn, K_NO_WAIT);
334 if (rc < 0) {
335 LOG_ERR("Message queue overflow again");
336 }
337 }
338
339 return rc;
340 }
341
numaker_usbd_hw_setup(const struct device * dev)342 static int numaker_usbd_hw_setup(const struct device *dev)
343 {
344 const struct numaker_usbd_config *config = dev->config;
345 USBD_T *const base = config->base;
346 int rc;
347 struct numaker_scc_subsys scc_subsys;
348
349 /* Reset controller ready? */
350 if (!device_is_ready(config->reset.dev)) {
351 LOG_ERR("Reset controller not ready");
352 return -ENODEV;
353 }
354
355 SYS_UnlockReg();
356
357 /* Configure USB PHY for USBD */
358 SYS->USBPHY = (SYS->USBPHY & ~SYS_USBPHY_USBROLE_Msk) |
359 (SYS_USBPHY_USBROLE_STD_USBD | SYS_USBPHY_USBEN_Msk | SYS_USBPHY_SBO_Msk);
360
361 /* Invoke Clock controller to enable module clock */
362 memset(&scc_subsys, 0x00, sizeof(scc_subsys));
363 scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC;
364 scc_subsys.pcc.clk_modidx = config->clk_modidx;
365 scc_subsys.pcc.clk_src = config->clk_src;
366 scc_subsys.pcc.clk_div = config->clk_div;
367
368 /* Equivalent to CLK_EnableModuleClock() */
369 rc = clock_control_on(config->clkctrl_dev, (clock_control_subsys_t)&scc_subsys);
370 if (rc < 0) {
371 goto cleanup;
372 }
373 /* Equivalent to CLK_SetModuleClock() */
374 rc = clock_control_configure(config->clkctrl_dev, (clock_control_subsys_t)&scc_subsys,
375 NULL);
376 if (rc < 0) {
377 goto cleanup;
378 }
379
380 /* Configure pinmux (NuMaker's SYS MFP) */
381 rc = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
382 if (rc < 0) {
383 goto cleanup;
384 }
385
386 /* Invoke Reset controller to reset module to default state */
387 /* Equivalent to SYS_ResetModule()
388 */
389 reset_line_toggle_dt(&config->reset);
390
391 /* Initialize USBD engine */
392 /* NOTE: BSP USBD driver: ATTR = 0x7D0 */
393 base->ATTR = USBD_ATTR_BYTEM_Msk | BIT(9) | USBD_ATTR_DPPUEN_Msk | USBD_ATTR_USBEN_Msk |
394 BIT(6) | USBD_ATTR_PHYEN_Msk;
395
396 /* Set SE0 for S/W disconnect */
397 numaker_usbd_sw_disconnect(dev);
398
399 /* NOTE: Ignore DT maximum-speed with USBD fixed to full-speed */
400
401 /* Initialize IRQ */
402 config->irq_config_func(dev);
403
404 cleanup:
405
406 SYS_LockReg();
407
408 return rc;
409 }
410
numaker_usbd_hw_shutdown(const struct device * dev)411 static void numaker_usbd_hw_shutdown(const struct device *dev)
412 {
413 const struct numaker_usbd_config *config = dev->config;
414 USBD_T *const base = config->base;
415 struct numaker_scc_subsys scc_subsys;
416
417 SYS_UnlockReg();
418
419 /* Uninitialize IRQ */
420 config->irq_unconfig_func(dev);
421
422 /* Set SE0 for S/W disconnect */
423 numaker_usbd_sw_disconnect(dev);
424
425 /* Disable USB PHY */
426 base->ATTR &= ~USBD_PHY_EN;
427
428 /* Invoke Clock controller to disable module clock */
429 memset(&scc_subsys, 0x00, sizeof(scc_subsys));
430 scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC;
431 scc_subsys.pcc.clk_modidx = config->clk_modidx;
432
433 /* Equivalent to CLK_DisableModuleClock() */
434 clock_control_off(config->clkctrl_dev, (clock_control_subsys_t)&scc_subsys);
435
436 /* Invoke Reset controller to reset module to default state */
437 /* Equivalent to SYS_ResetModule() */
438 reset_line_toggle_dt(&config->reset);
439
440 SYS_LockReg();
441 }
442
443 /* Interrupt top half processing for bus reset */
numaker_usbd_bus_reset_th(const struct device * dev)444 static void numaker_usbd_bus_reset_th(const struct device *dev)
445 {
446 const struct numaker_usbd_config *config = dev->config;
447 USBD_EP_T *ep_base;
448
449 for (uint32_t i = 0ul; i < config->num_bidir_endpoints; i++) {
450 ep_base = numaker_usbd_ep_base(dev, EP0 + i);
451
452 /* Cancel EP on-going transaction */
453 ep_base->CFGP |= USBD_CFGP_CLRRDY_Msk;
454
455 /* Reset EP to unstalled */
456 ep_base->CFGP &= ~USBD_CFGP_SSTALL_Msk;
457
458 /* Reset EP data toggle bit to 0 */
459 ep_base->CFG &= ~USBD_CFG_DSQSYNC_Msk;
460
461 /* Except EP0/EP1 kept resident for CTRL OUT/IN, disable all other EPs */
462 if (i >= 2) {
463 ep_base->CFG = 0;
464 }
465 }
466
467 numaker_usbd_reset_addr(dev);
468 }
469
numaker_usbd_remote_wakeup(const struct device * dev)470 static void numaker_usbd_remote_wakeup(const struct device *dev)
471 {
472 const struct numaker_usbd_config *config = dev->config;
473 USBD_T *const base = config->base;
474
475 /* Enable back USB/PHY first */
476 base->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
477
478 /* Then generate 'K' */
479 base->ATTR |= USBD_ATTR_RWAKEUP_Msk;
480 k_sleep(K_USEC(NUMAKER_USBD_BUS_RESUME_DRV_K_US));
481 base->ATTR ^= USBD_ATTR_RWAKEUP_Msk;
482 }
483
484 /* USBD SRAM base for DMA */
numaker_usbd_buf_base(const struct device * dev)485 static inline uint32_t numaker_usbd_buf_base(const struct device *dev)
486 {
487 const struct numaker_usbd_config *config = dev->config;
488 USBD_T *const base = config->base;
489
490 return ((uint32_t)base + 0x800ul);
491 }
492
493 /* Copy to user buffer from Setup FIFO */
numaker_usbd_setup_fifo_copy_to_user(const struct device * dev,uint8_t * usrbuf)494 static void numaker_usbd_setup_fifo_copy_to_user(const struct device *dev, uint8_t *usrbuf)
495 {
496 const struct numaker_usbd_config *config = dev->config;
497 USBD_T *const base = config->base;
498 uint32_t dmabuf_addr;
499
500 dmabuf_addr = numaker_usbd_buf_base(dev) + (base->STBUFSEG & USBD_STBUFSEG_STBUFSEG_Msk);
501
502 bytecpy(usrbuf, (uint8_t *)dmabuf_addr, 8ul);
503 }
504
505 /* Copy data to user buffer from EP FIFO
506 *
507 * size_p holds size to copy/copied on input/output
508 */
numaker_usbd_ep_fifo_copy_to_user(struct numaker_usbd_ep * ep_cur,uint8_t * usrbuf,uint32_t * size_p)509 static int numaker_usbd_ep_fifo_copy_to_user(struct numaker_usbd_ep *ep_cur, uint8_t *usrbuf,
510 uint32_t *size_p)
511 {
512 const struct device *dev = ep_cur->dev;
513 uint32_t dmabuf_addr;
514
515 __ASSERT_NO_MSG(size_p);
516 __ASSERT_NO_MSG(ep_cur->dmabuf_valid);
517
518 dmabuf_addr = numaker_usbd_buf_base(dev) + ep_cur->read_fifo_pos;
519
520 /* Clamp to read FIFO used count */
521 *size_p = MIN(*size_p, numaker_usbd_ep_fifo_used(ep_cur));
522
523 bytecpy(usrbuf, (uint8_t *)dmabuf_addr, *size_p);
524
525 /* Advance read FIFO */
526 ep_cur->read_fifo_pos += *size_p;
527 ep_cur->read_fifo_used -= *size_p;
528 if (ep_cur->read_fifo_used == 0) {
529 ep_cur->read_fifo_pos = ep_cur->dmabuf_base;
530 }
531
532 return 0;
533 }
534
535 /* Copy data from user buffer to EP FIFO
536 *
537 * size_p holds size to copy/copied on input/output
538 */
numaker_usbd_ep_fifo_copy_from_user(struct numaker_usbd_ep * ep_cur,const uint8_t * usrbuf,uint32_t * size_p)539 static int numaker_usbd_ep_fifo_copy_from_user(struct numaker_usbd_ep *ep_cur,
540 const uint8_t *usrbuf, uint32_t *size_p)
541 {
542 const struct device *dev = ep_cur->dev;
543 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
544 uint32_t dmabuf_addr;
545 uint32_t fifo_free;
546
547 __ASSERT_NO_MSG(size_p);
548 __ASSERT_NO_MSG(ep_cur->dmabuf_valid);
549 __ASSERT_NO_MSG(ep_cur->mps_valid);
550 __ASSERT_NO_MSG(ep_cur->mps <= ep_cur->dmabuf_size);
551
552 dmabuf_addr = numaker_usbd_buf_base(dev) + ep_base->BUFSEG;
553 fifo_free = numaker_usbd_ep_fifo_max(ep_cur) - numaker_usbd_ep_fifo_used(ep_cur);
554
555 *size_p = MIN(*size_p, fifo_free);
556
557 bytecpy((uint8_t *)dmabuf_addr, (uint8_t *)usrbuf, *size_p);
558
559 /* Advance write FIFO */
560 ep_cur->write_fifo_pos += *size_p;
561 ep_cur->write_fifo_free -= *size_p;
562 if (ep_cur->write_fifo_free == 0) {
563 ep_cur->write_fifo_pos = ep_cur->dmabuf_base;
564 }
565
566 return 0;
567 }
568
569 /* Update EP read/write FIFO on DATA OUT/IN completed */
numaker_usbd_ep_fifo_update(struct numaker_usbd_ep * ep_cur)570 static void numaker_usbd_ep_fifo_update(struct numaker_usbd_ep *ep_cur)
571 {
572 const struct device *dev = ep_cur->dev;
573 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
574
575 __ASSERT_NO_MSG(ep_cur->addr_valid);
576 __ASSERT_NO_MSG(ep_cur->dmabuf_valid);
577
578 if (USB_EP_DIR_IS_OUT(ep_cur->addr)) {
579 /* Read FIFO */
580 /* NOTE: For one-shot implementation, FIFO gets updated from empty. */
581 ep_cur->read_fifo_pos = ep_cur->dmabuf_base;
582 /* NOTE: See comment on mxpld_ctrlout for why make one copy of CTRL OUT's MXPLD */
583 if (USB_EP_GET_IDX(ep_cur->addr) == 0) {
584 ep_cur->read_fifo_used = ep_cur->mxpld_ctrlout;
585 } else {
586 ep_cur->read_fifo_used = ep_base->MXPLD;
587 }
588 } else {
589 /* Write FIFO */
590 /* NOTE: For one-shot implementation, FIFO gets to empty. */
591 ep_cur->write_fifo_pos = ep_cur->dmabuf_base;
592 ep_cur->write_fifo_free = numaker_usbd_ep_fifo_max(ep_cur);
593 }
594 }
595
numaker_usbd_ep_config_dmabuf(struct numaker_usbd_ep * ep_cur,uint32_t dmabuf_base,uint32_t dmabuf_size)596 static void numaker_usbd_ep_config_dmabuf(struct numaker_usbd_ep *ep_cur, uint32_t dmabuf_base,
597 uint32_t dmabuf_size)
598 {
599 const struct device *dev = ep_cur->dev;
600 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
601
602 ep_base->BUFSEG = dmabuf_base;
603
604 ep_cur->dmabuf_valid = true;
605 ep_cur->dmabuf_base = dmabuf_base;
606 ep_cur->dmabuf_size = dmabuf_size;
607 }
608
numaker_usbd_ep_abort(struct numaker_usbd_ep * ep_cur)609 static void numaker_usbd_ep_abort(struct numaker_usbd_ep *ep_cur)
610 {
611 const struct device *dev = ep_cur->dev;
612 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
613
614 /* Abort EP on-going transaction */
615 ep_base->CFGP |= USBD_CFGP_CLRRDY_Msk;
616
617 /* Need to clear NAK for next transaction */
618 ep_cur->nak_clr = false;
619 }
620
621 /* Configure EP major common parts */
numaker_usbd_ep_config_major(struct numaker_usbd_ep * ep_cur,const struct usb_dc_ep_cfg_data * const ep_cfg)622 static void numaker_usbd_ep_config_major(struct numaker_usbd_ep *ep_cur,
623 const struct usb_dc_ep_cfg_data *const ep_cfg)
624 {
625 const struct device *dev = ep_cur->dev;
626 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
627
628 ep_cur->mps_valid = true;
629 ep_cur->mps = ep_cfg->ep_mps;
630
631 /* Configure EP transfer type, DATA0/1 toggle, direction, number, etc. */
632 ep_cur->ep_hw_cfg = 0;
633
634 /* Clear STALL Response in Setup stage */
635 if (ep_cfg->ep_type == USB_DC_EP_CONTROL) {
636 ep_cur->ep_hw_cfg |= USBD_CFG_CSTALL;
637 }
638
639 /* Default to DATA0 */
640 ep_cur->ep_hw_cfg &= ~USBD_CFG_DSQSYNC_Msk;
641
642 /* Endpoint IN/OUT, though, default to disabled */
643 ep_cur->ep_hw_cfg |= USBD_CFG_EPMODE_DISABLE;
644
645 /* Isochronous or not */
646 if (ep_cfg->ep_type == USB_DC_EP_ISOCHRONOUS) {
647 ep_cur->ep_hw_cfg |= USBD_CFG_TYPE_ISO;
648 }
649
650 /* Endpoint index */
651 ep_cur->ep_hw_cfg |=
652 (USB_EP_GET_IDX(ep_cfg->ep_addr) << USBD_CFG_EPNUM_Pos) & USBD_CFG_EPNUM_Msk;
653
654 ep_base->CFG = ep_cur->ep_hw_cfg;
655 }
656
numaker_usbd_ep_enable(struct numaker_usbd_ep * ep_cur)657 static void numaker_usbd_ep_enable(struct numaker_usbd_ep *ep_cur)
658 {
659 const struct device *dev = ep_cur->dev;
660 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
661
662 /* For safe, EP (re-)enable from clean state */
663 numaker_usbd_ep_abort(ep_cur);
664 numaker_usbd_ep_clear_stall_n_data_toggle(ep_cur);
665 numaker_usbd_ep_fifo_reset(ep_cur);
666
667 /* Enable EP to IN/OUT */
668 ep_cur->ep_hw_cfg &= ~USBD_CFG_STATE_Msk;
669 if (USB_EP_DIR_IS_IN(ep_cur->addr)) {
670 ep_cur->ep_hw_cfg |= USBD_CFG_EPMODE_IN;
671 } else {
672 ep_cur->ep_hw_cfg |= USBD_CFG_EPMODE_OUT;
673 }
674
675 ep_base->CFG = ep_cur->ep_hw_cfg;
676
677 /* For USBD, no separate EP interrupt control */
678 }
679
numaker_usbd_ep_disable(struct numaker_usbd_ep * ep_cur)680 static void numaker_usbd_ep_disable(struct numaker_usbd_ep *ep_cur)
681 {
682 const struct device *dev = ep_cur->dev;
683 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
684
685 /* For USBD, no separate EP interrupt control */
686
687 /* Disable EP */
688 ep_cur->ep_hw_cfg = (ep_cur->ep_hw_cfg & ~USBD_CFG_STATE_Msk) | USBD_CFG_EPMODE_DISABLE;
689 ep_base->CFG = ep_cur->ep_hw_cfg;
690 }
691
692 /* Start EP data transaction */
numaker_usbd_ep_trigger(struct numaker_usbd_ep * ep_cur,uint32_t len)693 static void numaker_usbd_ep_trigger(struct numaker_usbd_ep *ep_cur, uint32_t len)
694 {
695 const struct device *dev = ep_cur->dev;
696 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_cur->ep_hw_idx);
697
698 ep_base->MXPLD = len;
699 }
700
numaker_usbd_ep_mgmt_alloc_ep(const struct device * dev)701 static struct numaker_usbd_ep *numaker_usbd_ep_mgmt_alloc_ep(const struct device *dev)
702 {
703 const struct numaker_usbd_config *config = dev->config;
704 struct numaker_usbd_data *data = dev->data;
705 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
706 struct numaker_usbd_ep *ep_cur = NULL;
707
708 if (ep_mgmt->ep_idx < config->num_bidir_endpoints) {
709 ep_cur = ep_mgmt->ep_pool + ep_mgmt->ep_idx;
710 ep_mgmt->ep_idx++;
711
712 __ASSERT_NO_MSG(!ep_cur->valid);
713
714 /* Indicate this EP context is allocated */
715 ep_cur->valid = true;
716 }
717
718 return ep_cur;
719 }
720
721 /* Allocate DMA buffer
722 *
723 * Return -ENOMEM on OOM error, or 0 on success with DMA buffer base/size (rounded up) allocated
724 */
numaker_usbd_ep_mgmt_alloc_dmabuf(const struct device * dev,uint32_t size,uint32_t * dmabuf_base_p,uint32_t * dmabuf_size_p)725 static int numaker_usbd_ep_mgmt_alloc_dmabuf(const struct device *dev, uint32_t size,
726 uint32_t *dmabuf_base_p, uint32_t *dmabuf_size_p)
727 {
728 const struct numaker_usbd_config *config = dev->config;
729 struct numaker_usbd_data *data = dev->data;
730 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
731
732 __ASSERT_NO_MSG(dmabuf_base_p);
733 __ASSERT_NO_MSG(dmabuf_size_p);
734
735 /* Required to be 8-byte aligned */
736 size = ROUND_UP(size, 8);
737
738 ep_mgmt->dmabuf_pos += size;
739 if (ep_mgmt->dmabuf_pos > config->dmabuf_size) {
740 ep_mgmt->dmabuf_pos -= size;
741 return -ENOMEM;
742 }
743
744 *dmabuf_base_p = ep_mgmt->dmabuf_pos - size;
745 *dmabuf_size_p = size;
746 return 0;
747 }
748
749 /* Initialize all endpoint-related */
numaker_usbd_ep_mgmt_init(const struct device * dev)750 static void numaker_usbd_ep_mgmt_init(const struct device *dev)
751 {
752 const struct numaker_usbd_config *config = dev->config;
753 struct numaker_usbd_data *data = dev->data;
754 USBD_T *const base = config->base;
755 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
756
757 struct numaker_usbd_ep *ep_cur;
758 struct numaker_usbd_ep *ep_end;
759
760 /* Initialize all fields to zero except persistent */
761 memset(ep_mgmt, 0x00, sizeof(*ep_mgmt));
762
763 ep_cur = ep_mgmt->ep_pool;
764 ep_end = ep_mgmt->ep_pool + config->num_bidir_endpoints;
765
766 /* Initialize all EP contexts */
767 for (; ep_cur != ep_end; ep_cur++) {
768 /* Pointer to the containing device */
769 ep_cur->dev = dev;
770
771 /* BSP USBD driver EP handle */
772 ep_cur->ep_hw_idx = EP0 + (ep_cur - ep_mgmt->ep_pool);
773 }
774
775 /* Reserve 1st/2nd EP contexts (BSP USBD driver EP0/EP1) for CTRL OUT/IN */
776 ep_mgmt->ep_idx = 2;
777
778 /* Reserve DMA buffer for Setup/CTRL OUT/CTRL IN, starting from 0 */
779 ep_mgmt->dmabuf_pos = 0;
780
781 /* Configure DMA buffer for Setup packet */
782 base->STBUFSEG = ep_mgmt->dmabuf_pos;
783 ep_mgmt->dmabuf_pos += NUMAKER_USBD_DMABUF_SIZE_SETUP;
784
785 /* Reserve 1st EP context (BSP USBD driver EP0) for CTRL OUT */
786 ep_cur = ep_mgmt->ep_pool + 0;
787 ep_cur->valid = true;
788 ep_cur->addr_valid = true;
789 ep_cur->addr = USB_EP_GET_ADDR(0, USB_EP_DIR_OUT);
790 numaker_usbd_ep_config_dmabuf(ep_cur, ep_mgmt->dmabuf_pos,
791 NUMAKER_USBD_DMABUF_SIZE_CTRLOUT);
792 ep_mgmt->dmabuf_pos += NUMAKER_USBD_DMABUF_SIZE_CTRLOUT;
793 ep_cur->mps_valid = true;
794 ep_cur->mps = NUMAKER_USBD_DMABUF_SIZE_CTRLOUT;
795
796 /* Reserve 2nd EP context (BSP USBD driver EP1) for CTRL IN */
797 ep_cur = ep_mgmt->ep_pool + 1;
798 ep_cur->valid = true;
799 ep_cur->addr_valid = true;
800 ep_cur->addr = USB_EP_GET_ADDR(0, USB_EP_DIR_IN);
801 numaker_usbd_ep_config_dmabuf(ep_cur, ep_mgmt->dmabuf_pos, NUMAKER_USBD_DMABUF_SIZE_CTRLIN);
802 ep_mgmt->dmabuf_pos += NUMAKER_USBD_DMABUF_SIZE_CTRLIN;
803 ep_cur->mps_valid = true;
804 ep_cur->mps = NUMAKER_USBD_DMABUF_SIZE_CTRLIN;
805 }
806
807 /* Find EP context by EP address */
numaker_usbd_ep_mgmt_find_ep(const struct device * dev,const uint8_t ep)808 static struct numaker_usbd_ep *numaker_usbd_ep_mgmt_find_ep(const struct device *dev,
809 const uint8_t ep)
810 {
811 const struct numaker_usbd_config *config = dev->config;
812 struct numaker_usbd_data *data = dev->data;
813 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
814 struct numaker_usbd_ep *ep_cur = ep_mgmt->ep_pool;
815 struct numaker_usbd_ep *ep_end = ep_mgmt->ep_pool + config->num_bidir_endpoints;
816
817 for (; ep_cur != ep_end; ep_cur++) {
818 if (!ep_cur->valid) {
819 continue;
820 }
821
822 if (!ep_cur->addr_valid) {
823 continue;
824 }
825
826 if (ep == ep_cur->addr) {
827 return ep_cur;
828 }
829 }
830
831 return NULL;
832 }
833
834 /* Bind EP context to EP address */
numaker_usbd_ep_mgmt_bind_ep(const struct device * dev,const uint8_t ep)835 static struct numaker_usbd_ep *numaker_usbd_ep_mgmt_bind_ep(const struct device *dev,
836 const uint8_t ep)
837 {
838 struct numaker_usbd_ep *ep_cur = numaker_usbd_ep_mgmt_find_ep(dev, ep);
839
840 if (!ep_cur) {
841 ep_cur = numaker_usbd_ep_mgmt_alloc_ep(dev);
842
843 if (!ep_cur) {
844 return NULL;
845 }
846
847 /* Bind EP context to EP address */
848 ep_cur->addr = ep;
849 ep_cur->addr_valid = true;
850 }
851
852 /* Assert EP context bound to EP address */
853 __ASSERT_NO_MSG(ep_cur->valid);
854 __ASSERT_NO_MSG(ep_cur->addr_valid);
855 __ASSERT_NO_MSG(ep_cur->addr == ep);
856
857 return ep_cur;
858 }
859
860 /* Interrupt bottom half processing for bus reset */
numaker_usbd_bus_reset_bh(const struct device * dev)861 static void numaker_usbd_bus_reset_bh(const struct device *dev)
862 {
863 const struct numaker_usbd_config *config = dev->config;
864 struct numaker_usbd_data *data = dev->data;
865 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
866
867 struct numaker_usbd_ep *ep_cur = ep_mgmt->ep_pool;
868 struct numaker_usbd_ep *ep_end = ep_mgmt->ep_pool + config->num_bidir_endpoints;
869
870 for (; ep_cur != ep_end; ep_cur++) {
871 /* Reset EP FIFO */
872 numaker_usbd_ep_fifo_reset(ep_cur);
873
874 /* Abort EP on-going transaction and signal H/W relinquishes DMA buffer ownership */
875 numaker_usbd_ep_abort(ep_cur);
876
877 /* Reset EP to unstalled and data toggle bit to 0 */
878 numaker_usbd_ep_clear_stall_n_data_toggle(ep_cur);
879 }
880
881 numaker_usbd_reset_addr(dev);
882 }
883
884 /* Interrupt bottom half processing for Setup/EP data transaction */
numaker_usbd_ep_bh(struct numaker_usbd_ep * ep_cur,enum usb_dc_ep_cb_status_code status_code)885 static void numaker_usbd_ep_bh(struct numaker_usbd_ep *ep_cur,
886 enum usb_dc_ep_cb_status_code status_code)
887 {
888 const struct device *dev = ep_cur->dev;
889 struct numaker_usbd_data *data = dev->data;
890 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
891
892 if (status_code == USB_DC_EP_SETUP) {
893 /* Zephyr USB device stack passes Setup packet via CTRL OUT EP. */
894 __ASSERT_NO_MSG(ep_cur->addr == USB_EP_GET_ADDR(0, USB_EP_DIR_OUT));
895
896 if (numaker_usbd_ep_fifo_used(ep_cur)) {
897 LOG_WRN("New Setup will override previous Control OUT data");
898 }
899
900 /* We should have reserved 1st/2nd EP contexts for CTRL OUT/IN */
901 __ASSERT_NO_MSG(ep_cur->addr == USB_EP_GET_ADDR(0, USB_EP_DIR_OUT));
902 __ASSERT_NO_MSG((ep_cur + 1)->addr == USB_EP_GET_ADDR(0, USB_EP_DIR_IN));
903
904 /* Reset CTRL OUT/IN FIFO due to new Setup packet */
905 numaker_usbd_ep_fifo_reset(ep_cur);
906 numaker_usbd_ep_fifo_reset(ep_cur + 1);
907
908 /* Relinquish CTRL OUT/IN DMA buffer ownership on behalf of H/W */
909 numaker_usbd_ep_abort(ep_cur);
910 numaker_usbd_ep_abort(ep_cur + 1);
911
912 /* Mark new Setup packet for read */
913 numaker_usbd_setup_fifo_copy_to_user(dev, (uint8_t *)&ep_mgmt->setup_packet);
914 ep_mgmt->new_setup = true;
915 } else if (status_code == USB_DC_EP_DATA_OUT) {
916 __ASSERT_NO_MSG(USB_EP_DIR_IS_OUT(ep_cur->addr));
917
918 /* Update EP read FIFO */
919 numaker_usbd_ep_fifo_update(ep_cur);
920
921 /* Need to clear NAK for next transaction */
922 ep_cur->nak_clr = false;
923 } else if (status_code == USB_DC_EP_DATA_IN) {
924 __ASSERT_NO_MSG(USB_EP_DIR_IS_IN(ep_cur->addr));
925
926 /* Update EP write FIFO */
927 numaker_usbd_ep_fifo_update(ep_cur);
928
929 /* Need to clear NAK for next transaction */
930 ep_cur->nak_clr = false;
931 }
932 }
933
934 /* Message handler for S/W reconnect */
numaker_usbd_msg_sw_reconn(const struct device * dev,struct numaker_usbd_msg * msg)935 static void numaker_usbd_msg_sw_reconn(const struct device *dev, struct numaker_usbd_msg *msg)
936 {
937 __ASSERT_NO_MSG(msg->type == NUMAKER_USBD_MSG_TYPE_SW_RECONN);
938
939 /* S/W reconnect for error recovery */
940 numaker_usbd_lock(dev);
941 numaker_usbd_sw_reconnect(dev);
942 numaker_usbd_unlock(dev);
943 }
944
945 /* Message handler for callback for usb_dc_status_code */
numaker_usbd_msg_cb_state(const struct device * dev,struct numaker_usbd_msg * msg)946 static void numaker_usbd_msg_cb_state(const struct device *dev, struct numaker_usbd_msg *msg)
947 {
948 struct numaker_usbd_data *data = dev->data;
949
950 __ASSERT_NO_MSG(msg->type == NUMAKER_USBD_MSG_TYPE_CB_STATE);
951
952 /* Interrupt bottom half processing for bus reset */
953 if (msg->cb_device.status_code == USB_DC_RESET) {
954 numaker_usbd_lock(dev);
955 numaker_usbd_bus_reset_bh(dev);
956 numaker_usbd_unlock(dev);
957 }
958
959 /* NOTE: Don't run callback with our mutex locked, or we may encounter
960 * deadlock because the Zephyr USB device stack can have its own
961 * synchronization.
962 */
963 if (data->status_cb) {
964 data->status_cb(msg->cb_device.status_code, NULL);
965 } else {
966 LOG_WRN("No status callback: status_code=%d", msg->cb_device.status_code);
967 }
968 }
969
970 /* Message handler for callback for usb_dc_ep_cb_status_code */
numaker_usbd_msg_cb_ep(const struct device * dev,struct numaker_usbd_msg * msg)971 static void numaker_usbd_msg_cb_ep(const struct device *dev, struct numaker_usbd_msg *msg)
972 {
973 uint8_t ep;
974 struct numaker_usbd_ep *ep_cur;
975
976 __ASSERT_NO_MSG(msg->type == NUMAKER_USBD_MSG_TYPE_CB_EP);
977
978 ep = msg->cb_ep.ep;
979
980 /* Bind EP context to EP address */
981 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
982
983 if (!ep_cur) {
984 LOG_ERR("Bind EP context: ep=0x%02x", ep);
985 return;
986 }
987
988 /* Interrupt bottom half processing for EP */
989 numaker_usbd_lock(dev);
990 numaker_usbd_ep_bh(ep_cur, msg->cb_ep.status_code);
991 numaker_usbd_unlock(dev);
992
993 /* NOTE: Same as above, don't run callback with our mutex locked */
994 if (ep_cur->cb) {
995 ep_cur->cb(ep, msg->cb_ep.status_code);
996 } else {
997 LOG_WRN("No EP callback: ep=0x%02x, status_code=%d", ep, msg->cb_ep.status_code);
998 }
999 }
1000
1001 /* Interrupt bottom half processing
1002 *
1003 * This thread is used to not run Zephyr USB device stack and callbacks in interrupt
1004 * context. This is because callbacks from this stack may use mutex or other kernel functions
1005 * which are not supported in interrupt context.
1006 */
numaker_usbd_msg_hdlr_thread_main(void * arg1,void * arg2,void * arg3)1007 static void numaker_usbd_msg_hdlr_thread_main(void *arg1, void *arg2, void *arg3)
1008 {
1009 const struct device *dev = (const struct device *)arg1;
1010 struct numaker_usbd_data *data = dev->data;
1011
1012 struct numaker_usbd_msg msg;
1013
1014 __ASSERT_NO_MSG(arg1);
1015 ARG_UNUSED(arg2);
1016 ARG_UNUSED(arg3);
1017
1018 while (true) {
1019 if (k_msgq_get(&data->msgq, &msg, K_FOREVER)) {
1020 continue;
1021 }
1022
1023 switch (msg.type) {
1024 case NUMAKER_USBD_MSG_TYPE_SW_RECONN:
1025 numaker_usbd_msg_sw_reconn(dev, &msg);
1026 break;
1027
1028 case NUMAKER_USBD_MSG_TYPE_CB_STATE:
1029 numaker_usbd_msg_cb_state(dev, &msg);
1030 break;
1031
1032 case NUMAKER_USBD_MSG_TYPE_CB_EP:
1033 numaker_usbd_msg_cb_ep(dev, &msg);
1034 break;
1035
1036 default:
1037 __ASSERT_NO_MSG(false);
1038 }
1039 }
1040 }
1041
numaker_udbd_isr(const struct device * dev)1042 static void numaker_udbd_isr(const struct device *dev)
1043 {
1044 const struct numaker_usbd_config *config = dev->config;
1045 struct numaker_usbd_data *data = dev->data;
1046 USBD_T *const base = config->base;
1047 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
1048
1049 struct numaker_usbd_msg msg = {0};
1050
1051 uint32_t volatile usbd_intsts = base->INTSTS;
1052 uint32_t volatile usbd_bus_state = base->ATTR;
1053
1054 /* USB plug-in/unplug */
1055 if (usbd_intsts & USBD_INTSTS_FLDET) {
1056 /* Floating detect */
1057 base->INTSTS = USBD_INTSTS_FLDET;
1058
1059 if (base->VBUSDET & USBD_VBUSDET_VBUSDET_Msk) {
1060 /* USB plug-in */
1061
1062 /* Enable back USB/PHY */
1063 base->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
1064
1065 /* Message for bottom-half processing */
1066 msg.type = NUMAKER_USBD_MSG_TYPE_CB_STATE;
1067 msg.cb_device.status_code = USB_DC_CONNECTED;
1068 numaker_usbd_send_msg(dev, &msg);
1069
1070 LOG_DBG("USB plug-in");
1071 } else {
1072 /* USB unplug */
1073
1074 /* Disable USB */
1075 base->ATTR &= ~USBD_USB_EN;
1076
1077 /* Message for bottom-half processing */
1078 msg.type = NUMAKER_USBD_MSG_TYPE_CB_STATE;
1079 msg.cb_device.status_code = USB_DC_DISCONNECTED;
1080 numaker_usbd_send_msg(dev, &msg);
1081
1082 LOG_DBG("USB unplug");
1083 }
1084 }
1085
1086 /* USB wake-up */
1087 if (usbd_intsts & USBD_INTSTS_WAKEUP) {
1088 /* Clear event flag */
1089 base->INTSTS = USBD_INTSTS_WAKEUP;
1090
1091 LOG_DBG("USB wake-up");
1092 }
1093
1094 /* USB reset/suspend/resume */
1095 if (usbd_intsts & USBD_INTSTS_BUS) {
1096 /* Clear event flag */
1097 base->INTSTS = USBD_INTSTS_BUS;
1098
1099 if (usbd_bus_state & USBD_STATE_USBRST) {
1100 /* Bus reset */
1101
1102 /* Enable back USB/PHY */
1103 base->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
1104
1105 /* Bus reset top half */
1106 numaker_usbd_bus_reset_th(dev);
1107
1108 /* Message for bottom-half processing */
1109 msg.type = NUMAKER_USBD_MSG_TYPE_CB_STATE;
1110 msg.cb_device.status_code = USB_DC_RESET;
1111 numaker_usbd_send_msg(dev, &msg);
1112
1113 LOG_DBG("USB reset");
1114 }
1115 if (usbd_bus_state & USBD_STATE_SUSPEND) {
1116 /* Enable USB but disable PHY */
1117 base->ATTR &= ~USBD_PHY_EN;
1118
1119 /* Message for bottom-half processing */
1120 msg.type = NUMAKER_USBD_MSG_TYPE_CB_STATE;
1121 msg.cb_device.status_code = USB_DC_SUSPEND;
1122 numaker_usbd_send_msg(dev, &msg);
1123
1124 LOG_DBG("USB suspend");
1125 }
1126 if (usbd_bus_state & USBD_STATE_RESUME) {
1127 /* Enable back USB/PHY */
1128 base->ATTR |= USBD_ATTR_USBEN_Msk | USBD_ATTR_PHYEN_Msk;
1129
1130 /* Message for bottom-half processing */
1131 msg.type = NUMAKER_USBD_MSG_TYPE_CB_STATE;
1132 msg.cb_device.status_code = USB_DC_RESUME;
1133 numaker_usbd_send_msg(dev, &msg);
1134
1135 LOG_DBG("USB resume");
1136 }
1137 }
1138
1139 /* USB SOF */
1140 if (usbd_intsts & USBD_INTSTS_SOFIF_Msk) {
1141 /* Clear event flag */
1142 base->INTSTS = USBD_INTSTS_SOFIF_Msk;
1143
1144 /* Message for bottom-half processing */
1145 msg.type = NUMAKER_USBD_MSG_TYPE_CB_STATE;
1146 msg.cb_device.status_code = USB_DC_SOF;
1147 numaker_usbd_send_msg(dev, &msg);
1148 }
1149
1150 /* USB Setup/EP */
1151 if (usbd_intsts & USBD_INTSTS_USB) {
1152 uint32_t epintsts;
1153
1154 /* Setup event */
1155 if (usbd_intsts & USBD_INTSTS_SETUP) {
1156 USBD_EP_T *ep0_base = numaker_usbd_ep_base(dev, EP0);
1157 USBD_EP_T *ep1_base = numaker_usbd_ep_base(dev, EP1);
1158
1159 /* Clear event flag */
1160 base->INTSTS = USBD_INTSTS_SETUP;
1161
1162 /* Clear the data IN/OUT ready flag of control endpoints */
1163 ep0_base->CFGP |= USBD_CFGP_CLRRDY_Msk;
1164 ep1_base->CFGP |= USBD_CFGP_CLRRDY_Msk;
1165
1166 /* By USB spec, following transactions, regardless of Data/Status stage,
1167 * will always be DATA1
1168 */
1169 ep0_base->CFG |= USBD_CFG_DSQSYNC_Msk;
1170 ep1_base->CFG |= USBD_CFG_DSQSYNC_Msk;
1171
1172 /* Message for bottom-half processing */
1173 /* NOTE: In Zephyr USB device stack, Setup packet is passed via
1174 * CTRL OUT EP
1175 */
1176 msg.type = NUMAKER_USBD_MSG_TYPE_CB_EP;
1177 msg.cb_ep.ep = USB_EP_GET_ADDR(0, USB_EP_DIR_OUT);
1178 msg.cb_ep.status_code = USB_DC_EP_SETUP;
1179 numaker_usbd_send_msg(dev, &msg);
1180 }
1181
1182 /* EP events */
1183 epintsts = base->EPINTSTS;
1184
1185 base->EPINTSTS = epintsts;
1186
1187 while (epintsts) {
1188 uint32_t ep_hw_idx = u32_count_trailing_zeros(epintsts);
1189 USBD_EP_T *ep_base = numaker_usbd_ep_base(dev, ep_hw_idx);
1190 uint8_t ep_dir;
1191 uint8_t ep_idx;
1192 uint8_t ep;
1193
1194 /* We don't enable INNAKEN interrupt, so as long as EP event occurs,
1195 * we can just regard one data transaction has completed (ACK for
1196 * CTRL/BULK/INT or no-ACK for Iso), that is, no need to check EPSTS0,
1197 * EPSTS1, etc.
1198 */
1199
1200 /* EP direction, number, and address */
1201 ep_dir = ((ep_base->CFG & USBD_CFG_STATE_Msk) == USBD_CFG_EPMODE_IN)
1202 ? USB_EP_DIR_IN
1203 : USB_EP_DIR_OUT;
1204 ep_idx = (ep_base->CFG & USBD_CFG_EPNUM_Msk) >> USBD_CFG_EPNUM_Pos;
1205 ep = USB_EP_GET_ADDR(ep_idx, ep_dir);
1206
1207 /* NOTE: See comment in usb_dc_set_address()'s implementation
1208 * for safe place to change USB device address
1209 */
1210 if (ep == USB_EP_GET_ADDR(0, USB_EP_DIR_IN)) {
1211 numaker_usbd_set_addr(dev);
1212 }
1213
1214 /* NOTE: See comment on mxpld_ctrlout for why make one copy of
1215 * CTRL OUT's MXPLD
1216 */
1217 if (ep == USB_EP_GET_ADDR(0, USB_EP_DIR_OUT)) {
1218 struct numaker_usbd_ep *ep_ctrlout = ep_mgmt->ep_pool + 0;
1219 USBD_EP_T *ep_ctrlout_base =
1220 numaker_usbd_ep_base(dev, ep_ctrlout->ep_hw_idx);
1221
1222 ep_ctrlout->mxpld_ctrlout = ep_ctrlout_base->MXPLD;
1223 }
1224
1225 /* Message for bottom-half processing */
1226 msg.type = NUMAKER_USBD_MSG_TYPE_CB_EP;
1227 msg.cb_ep.ep = ep;
1228 msg.cb_ep.status_code =
1229 USB_EP_DIR_IS_IN(ep) ? USB_DC_EP_DATA_IN : USB_DC_EP_DATA_OUT;
1230 numaker_usbd_send_msg(dev, &msg);
1231
1232 /* Have handled this EP and go next */
1233 epintsts &= ~BIT(ep_hw_idx);
1234 }
1235 }
1236 }
1237
1238 /* Zephyr USB device controller API implementation */
1239
usb_dc_attach(void)1240 int usb_dc_attach(void)
1241 {
1242 const struct device *dev = numaker_usbd_device_get();
1243 int rc;
1244
1245 numaker_usbd_lock(dev);
1246
1247 /* Initialize USB DC H/W */
1248 rc = numaker_usbd_hw_setup(dev);
1249 if (rc < 0) {
1250 LOG_ERR("Set up H/W");
1251 goto cleanup;
1252 }
1253
1254 /* USB device address defaults to 0 */
1255 numaker_usbd_reset_addr(dev);
1256
1257 /* Initialize all EPs */
1258 numaker_usbd_ep_mgmt_init(dev);
1259
1260 /* S/W connect */
1261 numaker_usbd_sw_connect(dev);
1262
1263 LOG_DBG("attached");
1264
1265 cleanup:
1266
1267 if (rc < 0) {
1268 usb_dc_detach();
1269 }
1270
1271 numaker_usbd_unlock(dev);
1272
1273 return rc;
1274 }
1275
usb_dc_detach(void)1276 int usb_dc_detach(void)
1277 {
1278 const struct device *dev = numaker_usbd_device_get();
1279 struct numaker_usbd_data *data = dev->data;
1280
1281 LOG_DBG("detached");
1282
1283 numaker_usbd_lock(dev);
1284
1285 /* S/W disconnect */
1286 numaker_usbd_sw_disconnect(dev);
1287
1288 /* Uninitialize USB DC H/W */
1289 numaker_usbd_hw_shutdown(numaker_usbd_device_get());
1290
1291 /* Purge message queue */
1292 k_msgq_purge(&data->msgq);
1293
1294 numaker_usbd_unlock(dev);
1295
1296 return 0;
1297 }
1298
usb_dc_reset(void)1299 int usb_dc_reset(void)
1300 {
1301 const struct device *dev = numaker_usbd_device_get();
1302
1303 LOG_DBG("usb_dc_reset");
1304
1305 numaker_usbd_lock(dev);
1306
1307 usb_dc_detach();
1308 usb_dc_attach();
1309
1310 numaker_usbd_unlock(dev);
1311
1312 return 0;
1313 }
1314
usb_dc_set_address(const uint8_t addr)1315 int usb_dc_set_address(const uint8_t addr)
1316 {
1317 const struct device *dev = numaker_usbd_device_get();
1318 struct numaker_usbd_data *data = dev->data;
1319
1320 LOG_DBG("USB device address=%u (0x%02x)", addr, addr);
1321
1322 numaker_usbd_lock(dev);
1323
1324 /* NOTE: Timing for configuring USB device address into H/W is critical. It must be done
1325 * in-between SET_ADDRESS control transfer and next transfer. For this, it is done in
1326 * IN ACK ISR of SET_ADDRESS control transfer.
1327 */
1328 data->addr = addr;
1329
1330 numaker_usbd_unlock(dev);
1331
1332 return 0;
1333 }
1334
usb_dc_set_status_callback(const usb_dc_status_callback cb)1335 void usb_dc_set_status_callback(const usb_dc_status_callback cb)
1336 {
1337 const struct device *dev = numaker_usbd_device_get();
1338 struct numaker_usbd_data *data = dev->data;
1339
1340 numaker_usbd_lock(dev);
1341
1342 data->status_cb = cb;
1343
1344 numaker_usbd_unlock(dev);
1345 }
1346
usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data * const ep_cfg)1347 int usb_dc_ep_check_cap(const struct usb_dc_ep_cfg_data *const ep_cfg)
1348 {
1349 const struct device *dev = numaker_usbd_device_get();
1350 const struct numaker_usbd_config *config = dev->config;
1351 int rc = 0;
1352 struct numaker_usbd_ep *ep_cur;
1353
1354 numaker_usbd_lock(dev);
1355
1356 /* For safe, require EP number for control transfer to be 0 */
1357 if ((ep_cfg->ep_type == USB_DC_EP_CONTROL) && USB_EP_GET_IDX(ep_cfg->ep_addr) != 0) {
1358 LOG_ERR("EP number for control transfer must be 0");
1359 rc = -ENOTSUP;
1360 goto cleanup;
1361 }
1362
1363 /* Some soc series don't allow ISO IN/OUT to be assigned the same EP number.
1364 * This is addressed by limiting all OUT/IN EP addresses in top/bottom halves,
1365 * except CTRL OUT/IN.
1366 */
1367 if (config->disallow_iso_inout_same && ep_cfg->ep_type != USB_DC_EP_CONTROL) {
1368 /* Limit all OUT EP addresses in top-half, except CTRL OUT */
1369 if (USB_EP_DIR_IS_OUT(ep_cfg->ep_addr) && USB_EP_GET_IDX(ep_cfg->ep_addr) >= 8) {
1370 LOG_DBG("Support only ISO OUT EP address 0x01~0x07: 0x%02x",
1371 ep_cfg->ep_addr);
1372 rc = -ENOTSUP;
1373 goto cleanup;
1374 }
1375
1376 /* Limit all IN EP addresses in bottom-half , except CTRL IN */
1377 if (USB_EP_DIR_IS_IN(ep_cfg->ep_addr) && USB_EP_GET_IDX(ep_cfg->ep_addr) < 8) {
1378 LOG_DBG("Support only ISO IN EP address 0x88~0x8F: 0x%02x",
1379 ep_cfg->ep_addr);
1380 rc = -ENOTSUP;
1381 goto cleanup;
1382 }
1383 }
1384
1385 /* To respect this capability check, pre-bind EP context to EP address,
1386 * and pre-determined its type
1387 */
1388 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep_cfg->ep_addr);
1389 if (!ep_cur) {
1390 LOG_ERR("Bind EP context: ep=0x%02x", ep_cfg->ep_addr);
1391 rc = -ENOMEM;
1392 goto cleanup;
1393 }
1394
1395 cleanup:
1396
1397 numaker_usbd_unlock(dev);
1398
1399 return rc;
1400 }
1401
usb_dc_ep_set_callback(const uint8_t ep,const usb_dc_ep_callback cb)1402 int usb_dc_ep_set_callback(const uint8_t ep, const usb_dc_ep_callback cb)
1403 {
1404 const struct device *dev = numaker_usbd_device_get();
1405 int rc = 0;
1406 struct numaker_usbd_ep *ep_cur;
1407
1408 numaker_usbd_lock(dev);
1409
1410 /* Bind EP context to EP address */
1411 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1412
1413 if (!ep_cur) {
1414 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1415 rc = -ENOMEM;
1416 goto cleanup;
1417 }
1418
1419 ep_cur->cb = cb;
1420
1421 cleanup:
1422
1423 numaker_usbd_unlock(dev);
1424
1425 return rc;
1426 }
1427
usb_dc_ep_configure(const struct usb_dc_ep_cfg_data * const ep_cfg)1428 int usb_dc_ep_configure(const struct usb_dc_ep_cfg_data *const ep_cfg)
1429 {
1430 const struct device *dev = numaker_usbd_device_get();
1431 int rc = 0;
1432 uint32_t dmabuf_base;
1433 uint32_t dmabuf_size;
1434 struct numaker_usbd_ep *ep_cur;
1435
1436 LOG_DBG("EP=0x%02x, MPS=%d, Type=%d", ep_cfg->ep_addr, ep_cfg->ep_mps, ep_cfg->ep_type);
1437
1438 numaker_usbd_lock(dev);
1439
1440 /* Bind EP context to EP address */
1441 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep_cfg->ep_addr);
1442
1443 if (!ep_cur) {
1444 LOG_ERR("Bind EP context: ep=0x%02x", ep_cfg->ep_addr);
1445 rc = -ENOMEM;
1446 goto cleanup;
1447 }
1448
1449 /* Configure EP DMA buffer */
1450 if (!ep_cur->dmabuf_valid || ep_cur->dmabuf_size < ep_cfg->ep_mps) {
1451 /* Allocate DMA buffer */
1452 rc = numaker_usbd_ep_mgmt_alloc_dmabuf(dev, ep_cfg->ep_mps, &dmabuf_base,
1453 &dmabuf_size);
1454 if (rc < 0) {
1455 LOG_ERR("Allocate DMA buffer failed");
1456 goto cleanup;
1457 }
1458
1459 /* Configure EP DMA buffer */
1460 numaker_usbd_ep_config_dmabuf(ep_cur, dmabuf_base, dmabuf_size);
1461 }
1462
1463 /* Configure EP majorly */
1464 numaker_usbd_ep_config_major(ep_cur, ep_cfg);
1465
1466 cleanup:
1467
1468 numaker_usbd_unlock(dev);
1469
1470 return rc;
1471 }
1472
usb_dc_ep_set_stall(const uint8_t ep)1473 int usb_dc_ep_set_stall(const uint8_t ep)
1474 {
1475 const struct device *dev = numaker_usbd_device_get();
1476 int rc = 0;
1477 struct numaker_usbd_ep *ep_cur;
1478
1479 LOG_DBG("Set stall: ep=0x%02x", ep);
1480
1481 numaker_usbd_lock(dev);
1482
1483 /* Bind EP context to EP address */
1484 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1485
1486 if (!ep_cur) {
1487 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1488 rc = -ENOMEM;
1489 goto cleanup;
1490 }
1491
1492 /* Set EP to stalled */
1493 numaker_usbd_ep_set_stall(ep_cur);
1494
1495 cleanup:
1496
1497 numaker_usbd_unlock(dev);
1498
1499 return rc;
1500 }
1501
usb_dc_ep_clear_stall(const uint8_t ep)1502 int usb_dc_ep_clear_stall(const uint8_t ep)
1503 {
1504 const struct device *dev = numaker_usbd_device_get();
1505 int rc = 0;
1506 struct numaker_usbd_ep *ep_cur;
1507
1508 LOG_DBG("Clear stall: ep=0x%02x", ep);
1509
1510 numaker_usbd_lock(dev);
1511
1512 /* Bind EP context to EP address */
1513 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1514
1515 if (!ep_cur) {
1516 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1517 rc = -ENOMEM;
1518 goto cleanup;
1519 }
1520
1521 /* Reset EP to unstalled and data toggle bit to 0 */
1522 numaker_usbd_ep_clear_stall_n_data_toggle(ep_cur);
1523
1524 cleanup:
1525
1526 numaker_usbd_unlock(dev);
1527
1528 return rc;
1529 }
1530
usb_dc_ep_is_stalled(const uint8_t ep,uint8_t * const stalled)1531 int usb_dc_ep_is_stalled(const uint8_t ep, uint8_t *const stalled)
1532 {
1533 const struct device *dev = numaker_usbd_device_get();
1534 int rc = 0;
1535 struct numaker_usbd_ep *ep_cur;
1536
1537 if (!stalled) {
1538 return -EINVAL;
1539 }
1540
1541 numaker_usbd_lock(dev);
1542
1543 /* Bind EP context to EP address */
1544 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1545
1546 if (!ep_cur) {
1547 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1548 rc = -ENOMEM;
1549 goto cleanup;
1550 }
1551
1552 *stalled = numaker_usbd_ep_is_stalled(ep_cur);
1553
1554 cleanup:
1555
1556 numaker_usbd_unlock(dev);
1557
1558 return rc;
1559 }
1560
usb_dc_ep_halt(const uint8_t ep)1561 int usb_dc_ep_halt(const uint8_t ep)
1562 {
1563 return usb_dc_ep_set_stall(ep);
1564 }
1565
usb_dc_ep_enable(const uint8_t ep)1566 int usb_dc_ep_enable(const uint8_t ep)
1567 {
1568 const struct device *dev = numaker_usbd_device_get();
1569 int rc = 0;
1570 struct numaker_usbd_ep *ep_cur;
1571
1572 LOG_DBG("Enable: ep=0x%02x", ep);
1573
1574 numaker_usbd_lock(dev);
1575
1576 /* Bind EP context to EP address */
1577 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1578
1579 if (!ep_cur) {
1580 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1581 rc = -ENOMEM;
1582 goto cleanup;
1583 }
1584
1585 numaker_usbd_ep_enable(ep_cur);
1586
1587 /* Trigger OUT transaction manually, or H/W will continue to reply NAK because
1588 * Zephyr USB device stack is unclear on kicking off by invoking usb_dc_ep_read()
1589 * or friends. We needn't do this for CTRL OUT because Setup sequence will involve
1590 * this.
1591 */
1592 if (USB_EP_DIR_IS_OUT(ep) && USB_EP_GET_IDX(ep) != 0) {
1593 rc = usb_dc_ep_read_continue(ep);
1594 if (rc < 0) {
1595 goto cleanup;
1596 }
1597 }
1598
1599 cleanup:
1600
1601 numaker_usbd_unlock(dev);
1602
1603 return rc;
1604 }
1605
usb_dc_ep_disable(const uint8_t ep)1606 int usb_dc_ep_disable(const uint8_t ep)
1607 {
1608 const struct device *dev = numaker_usbd_device_get();
1609 int rc = 0;
1610 struct numaker_usbd_ep *ep_cur;
1611
1612 LOG_DBG("Disable: ep=0x%02x", ep);
1613
1614 numaker_usbd_lock(dev);
1615
1616 /* Bind EP context to EP address */
1617 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1618
1619 if (!ep_cur) {
1620 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1621 rc = -ENOMEM;
1622 goto cleanup;
1623 }
1624
1625 numaker_usbd_ep_disable(ep_cur);
1626
1627 cleanup:
1628
1629 numaker_usbd_unlock(dev);
1630
1631 return rc;
1632 }
1633
usb_dc_ep_flush(const uint8_t ep)1634 int usb_dc_ep_flush(const uint8_t ep)
1635 {
1636 const struct device *dev = numaker_usbd_device_get();
1637 int rc = 0;
1638 struct numaker_usbd_ep *ep_cur;
1639
1640 LOG_DBG("ep=0x%02x", ep);
1641
1642 numaker_usbd_lock(dev);
1643
1644 /* Bind EP context to EP address */
1645 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1646
1647 if (!ep_cur) {
1648 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1649 rc = -ENOMEM;
1650 goto cleanup;
1651 }
1652
1653 numaker_usbd_ep_fifo_reset(ep_cur);
1654
1655 cleanup:
1656
1657 numaker_usbd_unlock(dev);
1658
1659 return rc;
1660 }
1661
usb_dc_ep_write(const uint8_t ep,const uint8_t * const data_buf,const uint32_t data_len,uint32_t * const ret_bytes)1662 int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data_buf, const uint32_t data_len,
1663 uint32_t *const ret_bytes)
1664 {
1665 const struct device *dev = numaker_usbd_device_get();
1666 int rc = 0;
1667 struct numaker_usbd_ep *ep_cur;
1668 uint32_t data_len_act;
1669
1670 numaker_usbd_lock(dev);
1671
1672 /* Bind EP context to EP address */
1673 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1674
1675 if (!ep_cur) {
1676 LOG_ERR("ep=0x%02x", ep);
1677 rc = -ENOMEM;
1678 goto cleanup;
1679 }
1680
1681 if (!USB_EP_DIR_IS_IN(ep)) {
1682 LOG_ERR("Invalid EP address 0x%02x for write", ep);
1683 rc = -EINVAL;
1684 goto cleanup;
1685 }
1686
1687 /* For USBD, avoid duplicate NAK clear */
1688 if (ep_cur->nak_clr) {
1689 LOG_WRN("ep 0x%02x busy", ep);
1690 rc = -EAGAIN;
1691 goto cleanup;
1692 }
1693
1694 /* For one-shot implementation, don't trigger next DATA IN with write FIFO not empty. */
1695 if (numaker_usbd_ep_fifo_used(ep_cur)) {
1696 LOG_WRN("ep 0x%02x: Write FIFO not empty for one-shot implementation", ep);
1697 rc = -EAGAIN;
1698 goto cleanup;
1699 }
1700
1701 /* NOTE: Null data or zero data length are valid, used for ZLP */
1702 if (data_buf && data_len) {
1703 data_len_act = data_len;
1704 rc = numaker_usbd_ep_fifo_copy_from_user(ep_cur, data_buf, &data_len_act);
1705 if (rc < 0) {
1706 LOG_ERR("Copy to FIFO from user buffer");
1707 goto cleanup;
1708 }
1709 } else {
1710 data_len_act = 0;
1711 }
1712
1713 /* Now H/W actually owns EP DMA buffer */
1714 numaker_usbd_ep_trigger(ep_cur, data_len_act);
1715
1716 /* NOTE: For one-shot implementation, at most MPS size can be written, though,
1717 * null 'ret_bytes' requires all data written.
1718 */
1719 if (ret_bytes) {
1720 *ret_bytes = data_len_act;
1721 } else if (data_len_act != data_len) {
1722 LOG_ERR("Expected write all %d bytes, but actual %d bytes written", data_len,
1723 data_len_act);
1724 rc = -EIO;
1725 goto cleanup;
1726 }
1727
1728 cleanup:
1729
1730 numaker_usbd_unlock(dev);
1731
1732 return rc;
1733 }
1734
usb_dc_ep_read(const uint8_t ep,uint8_t * const data,const uint32_t max_data_len,uint32_t * const read_bytes)1735 int usb_dc_ep_read(const uint8_t ep, uint8_t *const data, const uint32_t max_data_len,
1736 uint32_t *const read_bytes)
1737 {
1738 const struct device *dev = numaker_usbd_device_get();
1739 int rc = 0;
1740
1741 numaker_usbd_lock(dev);
1742
1743 rc = usb_dc_ep_read_wait(ep, data, max_data_len, read_bytes);
1744 if (rc < 0) {
1745 goto cleanup;
1746 }
1747
1748 rc = usb_dc_ep_read_continue(ep);
1749 if (rc < 0) {
1750 goto cleanup;
1751 }
1752
1753 cleanup:
1754
1755 numaker_usbd_unlock(dev);
1756
1757 return rc;
1758 }
1759
usb_dc_ep_read_wait(uint8_t ep,uint8_t * data_buf,uint32_t max_data_len,uint32_t * read_bytes)1760 int usb_dc_ep_read_wait(uint8_t ep, uint8_t *data_buf, uint32_t max_data_len, uint32_t *read_bytes)
1761 {
1762 const struct device *dev = numaker_usbd_device_get();
1763 struct numaker_usbd_data *data = dev->data;
1764 int rc = 0;
1765 struct numaker_usbd_ep_mgmt *ep_mgmt = &data->ep_mgmt;
1766 struct numaker_usbd_ep *ep_cur;
1767 uint32_t data_len_act = 0;
1768
1769 numaker_usbd_lock(dev);
1770
1771 /* Bind EP context to EP address */
1772 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1773
1774 if (!ep_cur) {
1775 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1776 rc = -ENOMEM;
1777 goto cleanup;
1778 }
1779
1780 if (!USB_EP_DIR_IS_OUT(ep)) {
1781 LOG_ERR("Invalid EP address 0x%02x for read", ep);
1782 rc = -EINVAL;
1783 goto cleanup;
1784 }
1785
1786 /* Special handling for USB_CONTROL_EP_OUT on Setup packet */
1787 if (ep == USB_CONTROL_EP_OUT && ep_mgmt->new_setup) {
1788 if (!data_buf || max_data_len != 8) {
1789 LOG_ERR("Invalid parameter for reading Setup packet");
1790 rc = -EINVAL;
1791 goto cleanup;
1792 }
1793
1794 memcpy(data_buf, &ep_mgmt->setup_packet, 8);
1795 ep_mgmt->new_setup = false;
1796
1797 if (read_bytes) {
1798 *read_bytes = 8;
1799 }
1800
1801 goto cleanup;
1802 }
1803
1804 /* For one-shot implementation, don't read FIFO with EP busy. */
1805 if (ep_cur->nak_clr) {
1806 LOG_WRN("ep 0x%02x busy", ep);
1807 rc = -EAGAIN;
1808 goto cleanup;
1809 }
1810
1811 /* NOTE: Null data and zero data length is valid, used for returning number of
1812 * available bytes for read
1813 */
1814 if (data_buf) {
1815 data_len_act = max_data_len;
1816 rc = numaker_usbd_ep_fifo_copy_to_user(ep_cur, data_buf, &data_len_act);
1817 if (rc < 0) {
1818 LOG_ERR("Copy from FIFO to user buffer");
1819 goto cleanup;
1820 }
1821
1822 if (read_bytes) {
1823 *read_bytes = data_len_act;
1824 }
1825 } else if (max_data_len) {
1826 LOG_ERR("Null data but non-zero data length");
1827 rc = -EINVAL;
1828 goto cleanup;
1829 } else {
1830 if (read_bytes) {
1831 *read_bytes = numaker_usbd_ep_fifo_used(ep_cur);
1832 }
1833 }
1834
1835 /* Suppress further USB_DC_EP_DATA_OUT events by replying NAK or disabling interrupt
1836 *
1837 * For USBD, further control is unnecessary because NAK is automatically replied until
1838 * next USBD_SET_PAYLOAD_LEN().
1839 */
1840
1841 cleanup:
1842
1843 numaker_usbd_unlock(dev);
1844
1845 return rc;
1846 }
1847
usb_dc_ep_read_continue(uint8_t ep)1848 int usb_dc_ep_read_continue(uint8_t ep)
1849 {
1850 const struct device *dev = numaker_usbd_device_get();
1851 int rc = 0;
1852 struct numaker_usbd_ep *ep_cur;
1853
1854 numaker_usbd_lock(dev);
1855
1856 /* Bind EP context to EP address */
1857 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1858
1859 if (!ep_cur) {
1860 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1861 rc = -ENOMEM;
1862 goto cleanup;
1863 }
1864
1865 if (!USB_EP_DIR_IS_OUT(ep)) {
1866 LOG_ERR("Invalid EP address 0x%02x for read", ep);
1867 rc = -EINVAL;
1868 goto cleanup;
1869 }
1870
1871 /* Avoid duplicate NAK clear */
1872 if (ep_cur->nak_clr) {
1873 rc = 0;
1874 goto cleanup;
1875 }
1876
1877 /* For one-shot implementation, don't trigger next DATA OUT, or overwrite. */
1878 if (numaker_usbd_ep_fifo_used(ep_cur)) {
1879 goto cleanup;
1880 }
1881
1882 __ASSERT_NO_MSG(ep_cur->mps_valid);
1883 numaker_usbd_ep_trigger(ep_cur, ep_cur->mps);
1884
1885 cleanup:
1886
1887 numaker_usbd_unlock(dev);
1888
1889 return rc;
1890 }
1891
usb_dc_ep_mps(const uint8_t ep)1892 int usb_dc_ep_mps(const uint8_t ep)
1893 {
1894 const struct device *dev = numaker_usbd_device_get();
1895 int rc = 0;
1896 struct numaker_usbd_ep *ep_cur;
1897 uint16_t ep_mps = 0;
1898
1899 numaker_usbd_lock(dev);
1900
1901 /* Bind EP context to EP address */
1902 ep_cur = numaker_usbd_ep_mgmt_bind_ep(dev, ep);
1903
1904 if (!ep_cur) {
1905 LOG_ERR("Bind EP context: ep=0x%02x", ep);
1906 rc = -ENOMEM;
1907 goto cleanup;
1908 }
1909
1910 __ASSERT_NO_MSG(ep_cur->mps_valid);
1911 ep_mps = ep_cur->mps;
1912
1913 cleanup:
1914
1915 numaker_usbd_unlock(dev);
1916
1917 return rc == 0 ? ep_mps : rc;
1918 }
1919
usb_dc_wakeup_request(void)1920 int usb_dc_wakeup_request(void)
1921 {
1922 const struct device *dev = numaker_usbd_device_get();
1923 int rc = 0;
1924
1925 LOG_DBG("Remote wakeup");
1926
1927 numaker_usbd_lock(dev);
1928
1929 numaker_usbd_remote_wakeup(dev);
1930
1931 numaker_usbd_unlock(dev);
1932
1933 return rc;
1934 }
1935
numaker_udbd_init(const struct device * dev)1936 static int numaker_udbd_init(const struct device *dev)
1937 {
1938 struct numaker_usbd_data *data = dev->data;
1939 int rc = 0;
1940
1941 /* Initialize all fields to zero */
1942 memset(data, 0x00, sizeof(*data));
1943
1944 k_mutex_init(&data->sync_mutex);
1945
1946 /* Set up interrupt top/bottom halves processing */
1947
1948 k_msgq_init(&data->msgq, (char *)data->msgq_buf, sizeof(struct numaker_usbd_msg),
1949 CONFIG_USB_DC_NUMAKER_MSG_QUEUE_SIZE);
1950
1951 k_thread_create(&data->msg_hdlr_thread, data->msg_hdlr_thread_stack,
1952 CONFIG_USB_DC_NUMAKER_MSG_HANDLER_THREAD_STACK_SIZE,
1953 numaker_usbd_msg_hdlr_thread_main, (void *)dev, NULL, NULL, K_PRIO_COOP(2),
1954 0, K_NO_WAIT);
1955
1956 k_thread_name_set(&data->msg_hdlr_thread, "numaker_usbd");
1957
1958 return rc;
1959 }
1960
1961 #define USB_DC_NUMAKER_INIT(inst) \
1962 PINCTRL_DT_INST_DEFINE(inst); \
1963 \
1964 static void numaker_usbd_irq_config_func_##inst(const struct device *dev) \
1965 { \
1966 IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), numaker_udbd_isr, \
1967 DEVICE_DT_INST_GET(inst), 0); \
1968 \
1969 irq_enable(DT_INST_IRQN(inst)); \
1970 } \
1971 \
1972 static void numaker_uusbd_irq_unconfig_func_##inst(const struct device *dev) \
1973 { \
1974 irq_disable(DT_INST_IRQN(inst)); \
1975 } \
1976 \
1977 static const struct numaker_usbd_config numaker_usbd_config_##inst = { \
1978 .base = (USBD_T *)DT_INST_REG_ADDR(inst), \
1979 .reset = RESET_DT_SPEC_INST_GET(inst), \
1980 .clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index), \
1981 .clk_src = DT_INST_CLOCKS_CELL(inst, clock_source), \
1982 .clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider), \
1983 .clkctrl_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(inst))), \
1984 .irq_config_func = numaker_usbd_irq_config_func_##inst, \
1985 .irq_unconfig_func = numaker_uusbd_irq_unconfig_func_##inst, \
1986 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
1987 .num_bidir_endpoints = DT_INST_PROP(inst, num_bidir_endpoints), \
1988 .dmabuf_size = DT_INST_PROP(inst, dma_buffer_size), \
1989 .disallow_iso_inout_same = DT_INST_PROP(inst, disallow_iso_in_out_same_number), \
1990 }; \
1991 \
1992 static struct numaker_usbd_data numaker_usbd_data_##inst; \
1993 \
1994 BUILD_ASSERT(DT_INST_PROP(inst, num_bidir_endpoints) <= NUMAKER_USBD_EP_MAXNUM, \
1995 "num_bidir_endpoints exceeds support limit by USBD driver"); \
1996 \
1997 DEVICE_DT_INST_DEFINE(inst, numaker_udbd_init, NULL, &numaker_usbd_data_##inst, \
1998 &numaker_usbd_config_##inst, POST_KERNEL, \
1999 CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL);
2000
2001 USB_DC_NUMAKER_INIT(0);
2002
2003 /* Get USB DC device context instance 0 */
numaker_usbd_device_get(void)2004 static inline const struct device *numaker_usbd_device_get(void)
2005 {
2006 return DEVICE_DT_INST_GET(0);
2007 }
2008