Skip to content

Commit c2f2d8c

Browse files
tmon-nordicfabiobaltieri
authored andcommitted
drivers: usb: udc_dwc2: Replace queue with events
When the queue is full, all messages posted inside interrupt handlers are simply dropped. This problem can be remedied by having the message queue large enough, but determining the maximum number of messages that can ever be posted in the system is really complex task. Hopefully in DWC2 driver there is finite number of events that have to be processed inside thread handler. Therefore it is unnecessary to determine the maximum queue size for the events if the events are posted to k_event object instead of send to k_msgq object. Use combination of three k_event structures to handle all possible event sources. This not only guarantees by design that no event will be lost, but also slightly reduces the memory usage. Signed-off-by: Tomasz Moń <[email protected]>
1 parent 2c78a4d commit c2f2d8c

File tree

2 files changed

+128
-96
lines changed

2 files changed

+128
-96
lines changed

drivers/usb/udc/Kconfig.dwc2

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ config UDC_DWC2
77
depends on DT_HAS_SNPS_DWC2_ENABLED
88
select NRFS if NRFS_HAS_VBUS_DETECTOR_SERVICE
99
select NRFS_VBUS_DETECTOR_SERVICE_ENABLED if NRFS_HAS_VBUS_DETECTOR_SERVICE
10+
select EVENTS
1011
help
1112
DWC2 USB device controller driver.
1213

@@ -37,11 +38,3 @@ config UDC_DWC2_THREAD_PRIORITY
3738
default 8
3839
help
3940
DWC2 driver thread priority.
40-
41-
config UDC_DWC2_MAX_QMESSAGES
42-
int "UDC DWC2 maximum number of ISR event messages"
43-
depends on UDC_DWC2
44-
range 4 64
45-
default 8
46-
help
47-
DWC2 maximum number of ISR event messages.

drivers/usb/udc/udc_dwc2.c

Lines changed: 127 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,14 @@ enum dwc2_drv_event_type {
2929
DWC2_DRV_EVT_XFER,
3030
/* Setup packet received */
3131
DWC2_DRV_EVT_SETUP,
32-
/* OUT transaction for specific endpoint is finished */
33-
DWC2_DRV_EVT_DOUT,
34-
/* IN transaction for specific endpoint is finished */
35-
DWC2_DRV_EVT_DIN,
36-
/* Core should exit hibernation */
37-
DWC2_DRV_EVT_HIBERNATION_EXIT,
32+
/* Transaction on endpoint is finished */
33+
DWC2_DRV_EVT_EP_FINISHED,
34+
/* Core should exit hibernation due to bus reset */
35+
DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET,
36+
/* Core should exit hibernation due to host resume */
37+
DWC2_DRV_EVT_HIBERNATION_EXIT_HOST_RESUME,
3838
};
3939

40-
enum dwc2_hibernation_exit_reason {
41-
DWC2_HIBERNATION_EXIT_BUS_RESET,
42-
DWC2_HIBERNATION_EXIT_HOST_WAKEUP,
43-
};
44-
45-
struct dwc2_drv_event {
46-
const struct device *dev;
47-
enum dwc2_drv_event_type type;
48-
union {
49-
uint8_t ep;
50-
enum dwc2_hibernation_exit_reason exit_reason;
51-
};
52-
};
53-
54-
K_MSGQ_DEFINE(drv_msgq, sizeof(struct dwc2_drv_event),
55-
CONFIG_UDC_DWC2_MAX_QMESSAGES, sizeof(void *));
56-
57-
5840
/* Minimum RX FIFO size in 32-bit words considering the largest used OUT packet
5941
* of 512 bytes. The value must be adjusted according to the number of OUT
6042
* endpoints.
@@ -111,6 +93,12 @@ struct dwc2_reg_backup {
11193
/* Driver private data per instance */
11294
struct udc_dwc2_data {
11395
struct k_thread thread_data;
96+
/* Main events the driver thread waits for */
97+
struct k_event drv_evt;
98+
/* Transfer triggers (OUT on bits 0-15, IN on bits 16-31) */
99+
struct k_event xfer_new;
100+
/* Finished transactions (OUT on bits 0-15, IN on bits 16-31) */
101+
struct k_event xfer_finished;
114102
struct dwc2_reg_backup backup;
115103
uint32_t ghwcfg1;
116104
uint32_t txf_set;
@@ -1502,12 +1490,9 @@ static int udc_dwc2_ep_set_halt(const struct device *dev,
15021490
static int udc_dwc2_ep_clear_halt(const struct device *dev,
15031491
struct udc_ep_config *const cfg)
15041492
{
1493+
struct udc_dwc2_data *const priv = udc_get_private(dev);
15051494
mem_addr_t dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
15061495
uint32_t dxepctl;
1507-
struct dwc2_drv_event evt = {
1508-
.ep = cfg->addr,
1509-
.type = DWC2_DRV_EVT_XFER,
1510-
};
15111496

15121497
dxepctl = sys_read32(dxepctl_reg);
15131498
dxepctl &= ~USB_DWC2_DEPCTL_STALL;
@@ -1519,7 +1504,16 @@ static int udc_dwc2_ep_clear_halt(const struct device *dev,
15191504

15201505
/* Resume queued transfers if any */
15211506
if (udc_buf_peek(dev, cfg->addr)) {
1522-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
1507+
uint32_t ep_bit;
1508+
1509+
if (USB_EP_DIR_IS_IN(cfg->addr)) {
1510+
ep_bit = BIT(16 + USB_EP_GET_IDX(cfg->addr));
1511+
} else {
1512+
ep_bit = BIT(USB_EP_GET_IDX(cfg->addr));
1513+
}
1514+
1515+
k_event_post(&priv->xfer_new, ep_bit);
1516+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_XFER));
15231517
}
15241518

15251519
return 0;
@@ -1529,16 +1523,22 @@ static int udc_dwc2_ep_enqueue(const struct device *dev,
15291523
struct udc_ep_config *const cfg,
15301524
struct net_buf *const buf)
15311525
{
1532-
struct dwc2_drv_event evt = {
1533-
.ep = cfg->addr,
1534-
.type = DWC2_DRV_EVT_XFER,
1535-
};
1526+
struct udc_dwc2_data *const priv = udc_get_private(dev);
15361527

15371528
LOG_DBG("%p enqueue %x %p", dev, cfg->addr, buf);
15381529
udc_buf_put(cfg, buf);
15391530

15401531
if (!cfg->stat.halted) {
1541-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
1532+
uint32_t ep_bit;
1533+
1534+
if (USB_EP_DIR_IS_IN(cfg->addr)) {
1535+
ep_bit = BIT(16 + USB_EP_GET_IDX(cfg->addr));
1536+
} else {
1537+
ep_bit = BIT(USB_EP_GET_IDX(cfg->addr));
1538+
}
1539+
1540+
k_event_post(&priv->xfer_new, ep_bit);
1541+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_XFER));
15421542
}
15431543

15441544
return 0;
@@ -2016,6 +2016,7 @@ static int udc_dwc2_shutdown(const struct device *dev)
20162016
static int dwc2_driver_preinit(const struct device *dev)
20172017
{
20182018
const struct udc_dwc2_config *config = dev->config;
2019+
struct udc_dwc2_data *const priv = udc_get_private(dev);
20192020
struct udc_data *data = dev->data;
20202021
uint16_t mps = 1023;
20212022
uint32_t numdeveps;
@@ -2024,6 +2025,10 @@ static int dwc2_driver_preinit(const struct device *dev)
20242025

20252026
k_mutex_init(&data->mutex);
20262027

2028+
k_event_init(&priv->drv_evt);
2029+
k_event_init(&priv->xfer_new);
2030+
k_event_init(&priv->xfer_finished);
2031+
20272032
data->caps.addr_before_status = true;
20282033
data->caps.mps0 = UDC_MPS0_64;
20292034

@@ -2264,7 +2269,6 @@ static inline void dwc2_handle_in_xfercompl(const struct device *dev,
22642269
{
22652270
struct udc_dwc2_data *const priv = udc_get_private(dev);
22662271
struct udc_ep_config *ep_cfg;
2267-
struct dwc2_drv_event evt;
22682272
struct net_buf *buf;
22692273

22702274
ep_cfg = udc_get_ep_cfg(dev, ep_idx | USB_EP_DIR_IN);
@@ -2279,10 +2283,8 @@ static inline void dwc2_handle_in_xfercompl(const struct device *dev,
22792283
return;
22802284
}
22812285

2282-
evt.dev = dev;
2283-
evt.ep = ep_cfg->addr;
2284-
evt.type = DWC2_DRV_EVT_DIN;
2285-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
2286+
k_event_post(&priv->xfer_finished, BIT(16 + ep_idx));
2287+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
22862288
}
22872289

22882290
static inline void dwc2_handle_iepint(const struct device *dev)
@@ -2326,7 +2328,6 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev,
23262328
struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx);
23272329
struct udc_dwc2_data *const priv = udc_get_private(dev);
23282330
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
2329-
struct dwc2_drv_event evt;
23302331
uint32_t bcnt;
23312332
struct net_buf *buf;
23322333
uint32_t doeptsiz;
@@ -2341,9 +2342,6 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev,
23412342
return;
23422343
}
23432344

2344-
evt.type = DWC2_DRV_EVT_DOUT;
2345-
evt.ep = ep_cfg->addr;
2346-
23472345
/* The original transfer size value is necessary here because controller
23482346
* decreases the value for every byte stored.
23492347
*/
@@ -2391,7 +2389,8 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev,
23912389
net_buf_tailroom(buf)) {
23922390
dwc2_prep_rx(dev, buf, ep_cfg);
23932391
} else {
2394-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
2392+
k_event_post(&priv->xfer_finished, BIT(ep_idx));
2393+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
23952394
}
23962395
}
23972396

@@ -2443,12 +2442,7 @@ static inline void dwc2_handle_oepint(const struct device *dev)
24432442
}
24442443

24452444
if (status & USB_DWC2_DOEPINT_SETUP) {
2446-
struct dwc2_drv_event evt = {
2447-
.type = DWC2_DRV_EVT_SETUP,
2448-
.ep = USB_CONTROL_EP_OUT,
2449-
};
2450-
2451-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
2445+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_SETUP));
24522446
}
24532447

24542448
if (status & USB_DWC2_DOEPINT_STSPHSERCVD) {
@@ -2583,30 +2577,24 @@ static void udc_dwc2_isr_handler(const struct device *dev)
25832577
if (priv->hibernated) {
25842578
uint32_t gpwrdn = sys_read32((mem_addr_t)&base->gpwrdn);
25852579
bool reset, resume = false;
2586-
enum dwc2_hibernation_exit_reason exit_reason;
25872580

25882581
/* Clear interrupts */
25892582
sys_write32(gpwrdn, (mem_addr_t)&base->gpwrdn);
25902583

25912584
if (gpwrdn & USB_DWC2_GPWRDN_LNSTSCHNG) {
25922585
resume = usb_dwc2_get_gpwrdn_linestate(gpwrdn) ==
25932586
USB_DWC2_GPWRDN_LINESTATE_DM1DP0;
2594-
exit_reason = DWC2_HIBERNATION_EXIT_HOST_WAKEUP;
25952587
}
25962588

25972589
reset = gpwrdn & USB_DWC2_GPWRDN_RESETDETECTED;
2598-
if (reset) {
2599-
exit_reason = DWC2_HIBERNATION_EXIT_BUS_RESET;
2600-
}
26012590

2602-
if (reset || resume) {
2603-
struct dwc2_drv_event evt = {
2604-
.dev = dev,
2605-
.type = DWC2_DRV_EVT_HIBERNATION_EXIT,
2606-
.exit_reason = exit_reason,
2607-
};
2591+
if (resume) {
2592+
k_event_post(&priv->drv_evt,
2593+
BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_HOST_RESUME));
2594+
}
26082595

2609-
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
2596+
if (reset) {
2597+
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET));
26102598
}
26112599

26122600
(void)dwc2_quirk_irq_clear(dev);
@@ -2700,60 +2688,111 @@ static void udc_dwc2_isr_handler(const struct device *dev)
27002688
(void)dwc2_quirk_irq_clear(dev);
27012689
}
27022690

2691+
static uint8_t pull_next_ep_from_bitmap(uint32_t *bitmap)
2692+
{
2693+
unsigned int bit;
2694+
2695+
__ASSERT_NO_MSG(bitmap && *bitmap);
2696+
2697+
bit = find_lsb_set(*bitmap) - 1;
2698+
*bitmap &= ~BIT(bit);
2699+
2700+
if (bit >= 16) {
2701+
return USB_EP_DIR_IN | (bit - 16);
2702+
} else {
2703+
return USB_EP_DIR_OUT | bit;
2704+
}
2705+
}
2706+
27032707
static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)
27042708
{
27052709
const struct device *dev = (const struct device *)arg;
27062710
struct udc_dwc2_data *const priv = udc_get_private(dev);
27072711
const struct udc_dwc2_config *const config = dev->config;
27082712
struct udc_ep_config *ep_cfg;
2709-
struct dwc2_drv_event evt;
2713+
const uint32_t hibernation_exit_events = (BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET) |
2714+
BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_HOST_RESUME));
2715+
uint32_t prev;
2716+
uint32_t evt;
2717+
uint32_t eps;
2718+
uint8_t ep;
27102719

27112720
/* This is the bottom-half of the ISR handler and the place where
27122721
* a new transfer can be fed.
27132722
*/
2714-
k_msgq_get(&drv_msgq, &evt, K_FOREVER);
2715-
ep_cfg = udc_get_ep_cfg(dev, evt.ep);
2723+
evt = k_event_wait(&priv->drv_evt, UINT32_MAX, false, K_FOREVER);
2724+
2725+
if (evt & BIT(DWC2_DRV_EVT_XFER)) {
2726+
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_XFER));
2727+
2728+
LOG_DBG("New transfer(s) in the queue");
2729+
eps = k_event_test(&priv->xfer_new, UINT32_MAX);
2730+
k_event_clear(&priv->xfer_new, eps);
2731+
2732+
while (eps) {
2733+
ep = pull_next_ep_from_bitmap(&eps);
2734+
ep_cfg = udc_get_ep_cfg(dev, ep);
2735+
2736+
if (!udc_ep_is_busy(dev, ep_cfg->addr)) {
2737+
dwc2_handle_xfer_next(dev, ep_cfg);
2738+
} else {
2739+
LOG_DBG("ep 0x%02x busy", ep_cfg->addr);
2740+
}
2741+
}
2742+
}
2743+
2744+
if (evt & BIT(DWC2_DRV_EVT_EP_FINISHED)) {
2745+
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
2746+
2747+
eps = k_event_test(&priv->xfer_finished, UINT32_MAX);
2748+
k_event_clear(&priv->xfer_finished, eps);
2749+
2750+
while (eps) {
2751+
ep = pull_next_ep_from_bitmap(&eps);
2752+
ep_cfg = udc_get_ep_cfg(dev, ep);
2753+
2754+
if (USB_EP_DIR_IS_IN(ep)) {
2755+
LOG_DBG("DIN event ep 0x%02x", ep);
2756+
dwc2_handle_evt_din(dev, ep_cfg);
2757+
} else {
2758+
LOG_DBG("DOUT event ep 0x%02x", ep_cfg->addr);
2759+
dwc2_handle_evt_dout(dev, ep_cfg);
2760+
}
2761+
2762+
if (!udc_ep_is_busy(dev, ep_cfg->addr)) {
2763+
dwc2_handle_xfer_next(dev, ep_cfg);
2764+
} else {
2765+
LOG_DBG("ep 0x%02x busy", ep_cfg->addr);
2766+
}
2767+
}
2768+
}
2769+
2770+
if (evt & BIT(DWC2_DRV_EVT_SETUP)) {
2771+
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_SETUP));
27162772

2717-
switch (evt.type) {
2718-
case DWC2_DRV_EVT_XFER:
2719-
LOG_DBG("New transfer in the queue");
2720-
break;
2721-
case DWC2_DRV_EVT_SETUP:
27222773
LOG_DBG("SETUP event");
27232774
dwc2_handle_evt_setup(dev);
2724-
break;
2725-
case DWC2_DRV_EVT_DOUT:
2726-
LOG_DBG("DOUT event ep 0x%02x", ep_cfg->addr);
2727-
dwc2_handle_evt_dout(dev, ep_cfg);
2728-
break;
2729-
case DWC2_DRV_EVT_DIN:
2730-
LOG_DBG("DIN event");
2731-
dwc2_handle_evt_din(dev, ep_cfg);
2732-
break;
2733-
case DWC2_DRV_EVT_HIBERNATION_EXIT:
2775+
}
2776+
2777+
if (evt & hibernation_exit_events) {
27342778
LOG_DBG("Hibernation exit event");
27352779
config->irq_disable_func(dev);
27362780

2781+
prev = k_event_clear(&priv->drv_evt, hibernation_exit_events);
2782+
27372783
if (priv->hibernated) {
27382784
dwc2_exit_hibernation(dev);
27392785

27402786
/* Let stack know we are no longer suspended */
27412787
udc_set_suspended(dev, false);
27422788
udc_submit_event(dev, UDC_EVT_RESUME, 0);
27432789

2744-
if (evt.exit_reason == DWC2_HIBERNATION_EXIT_BUS_RESET) {
2790+
if (prev & BIT(DWC2_DRV_EVT_HIBERNATION_EXIT_BUS_RESET)) {
27452791
dwc2_on_bus_reset(dev);
27462792
}
27472793
}
27482794

27492795
config->irq_enable_func(dev);
2750-
break;
2751-
}
2752-
2753-
if (ep_cfg->addr != USB_CONTROL_EP_OUT && !udc_ep_is_busy(dev, ep_cfg->addr)) {
2754-
dwc2_handle_xfer_next(dev, ep_cfg);
2755-
} else {
2756-
LOG_DBG("ep 0x%02x busy", ep_cfg->addr);
27572796
}
27582797
}
27592798

0 commit comments

Comments
 (0)