drivers: udc_dwc2: Handle IN events before OUT events

DWC2 otg OUT transfers are being used for SETUP DATA0, OUT Data Stage
packets and OUT Status Stage ZLP. On High-Speed it is possible for IN
Data Stage, OUT Status Stage ZLP and subsequent SETUP DATA0 to happen
in very quick succession, making all the three events appear at the same
time to the handler thread.

The handler thread is picking up next endpoint to handle based on the
least significant bit set. When OUT endpoints were on bits 0-15 and IN
endpoints were on bits 16-31, the least significant bit policy favored
OUT endpoints over IN endpoints. This caused problems in Completer mode
(but suprisingly not in Buffer DMA mode) that lead to incorrect control
transfer handling.

The choice between least significant bit first or most significant bit
first is arbitrary. Switching from least to most significant bit first
would have resolved the issue. It would also favor higher numbered
endpoints over lower numbered endpoints.

Swap the order of endpoints in bitmaps to have IN on bits 0-15 and OUT
on bits 16-31 to keep handling lower numbered endpoints first and
resolve the control transfer handling in Completer mode.

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
This commit is contained in:
Tomasz Moń 2024-10-21 10:54:34 +02:00 committed by Carles Cufí
commit 94a6b82572

View file

@ -99,9 +99,9 @@ struct udc_dwc2_data {
struct k_thread thread_data;
/* Main events the driver thread waits for */
struct k_event drv_evt;
/* Transfer triggers (OUT on bits 0-15, IN on bits 16-31) */
/* Transfer triggers (IN on bits 0-15, OUT on bits 16-31) */
struct k_event xfer_new;
/* Finished transactions (OUT on bits 0-15, IN on bits 16-31) */
/* Finished transactions (IN on bits 0-15, OUT on bits 16-31) */
struct k_event xfer_finished;
struct dwc2_reg_backup backup;
uint32_t ghwcfg1;
@ -1554,9 +1554,9 @@ static int udc_dwc2_ep_clear_halt(const struct device *dev,
uint32_t ep_bit;
if (USB_EP_DIR_IS_IN(cfg->addr)) {
ep_bit = BIT(16 + USB_EP_GET_IDX(cfg->addr));
} else {
ep_bit = BIT(USB_EP_GET_IDX(cfg->addr));
} else {
ep_bit = BIT(16 + USB_EP_GET_IDX(cfg->addr));
}
k_event_post(&priv->xfer_new, ep_bit);
@ -1579,9 +1579,9 @@ static int udc_dwc2_ep_enqueue(const struct device *dev,
uint32_t ep_bit;
if (USB_EP_DIR_IS_IN(cfg->addr)) {
ep_bit = BIT(16 + USB_EP_GET_IDX(cfg->addr));
} else {
ep_bit = BIT(USB_EP_GET_IDX(cfg->addr));
} else {
ep_bit = BIT(16 + USB_EP_GET_IDX(cfg->addr));
}
k_event_post(&priv->xfer_new, ep_bit);
@ -2346,7 +2346,7 @@ static inline void dwc2_handle_in_xfercompl(const struct device *dev,
return;
}
k_event_post(&priv->xfer_finished, BIT(16 + ep_idx));
k_event_post(&priv->xfer_finished, BIT(ep_idx));
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
}
@ -2452,7 +2452,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev,
net_buf_tailroom(buf)) {
dwc2_prep_rx(dev, buf, ep_cfg);
} else {
k_event_post(&priv->xfer_finished, BIT(ep_idx));
k_event_post(&priv->xfer_finished, BIT(16 + ep_idx));
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
}
}
@ -2803,9 +2803,9 @@ static uint8_t pull_next_ep_from_bitmap(uint32_t *bitmap)
*bitmap &= ~BIT(bit);
if (bit >= 16) {
return USB_EP_DIR_IN | (bit - 16);
return USB_EP_DIR_OUT | (bit - 16);
} else {
return USB_EP_DIR_OUT | bit;
return USB_EP_DIR_IN | bit;
}
}