Skip to content

Commit 611d04d

Browse files
author
Victor Brzeski
committed
dwc2: support ISO transfer scheduling
1 parent c96d98d commit 611d04d

File tree

3 files changed

+154
-14
lines changed

3 files changed

+154
-14
lines changed

drivers/usb/common/usb_dwc2_hw.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ USB_DWC2_SET_FIELD_DEFINE(dctl_tstctl, DCTL_TSTCTL)
763763
#define USB_DWC2_DSTS_DEVLNSTS_MASK (0x3UL << USB_DWC2_DSTS_DEVLNSTS_POS)
764764
#define USB_DWC2_DSTS_SOFFN_POS 8UL
765765
#define USB_DWC2_DSTS_SOFFN_MASK (0x3FFFUL << USB_DWC2_DSTS_SOFFN_POS)
766+
#define USB_DWC2_DSTS_SOFFN_LIMIT 0x3FFFUL
766767
#define USB_DWC2_DSTS_ERRTICERR_POS 3UL
767768
#define USB_DWC2_DSTS_ERRTICERR BIT(USB_DWC2_DSTS_ERRTICERR_POS)
768769
#define USB_DWC2_DSTS_ENUMSPD_POS 1UL

drivers/usb/udc/udc_dwc2.c

Lines changed: 150 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ enum dwc2_drv_event_type {
6262
*/
6363
#define UDC_DWC2_FIFO0_DEPTH (2 * 16U)
6464

65+
#define UDC_DWC2_TARGET_FRAME_INITIAL UINT16_MAX
66+
6567
/* Get Data FIFO access register */
6668
#define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1))
6769

@@ -138,6 +140,9 @@ struct udc_dwc2_data {
138140
/* Number of OUT endpoints including control endpoint */
139141
uint8_t outeps;
140142
uint8_t setup[8];
143+
/* Target frame numbers for isochronous endpoints */
144+
uint16_t target_frames[32];
145+
uint32_t target_frame_overrun_mask;
141146
};
142147

143148
#if defined(CONFIG_PINCTRL)
@@ -381,6 +386,49 @@ static bool dwc2_ep_is_periodic(struct udc_ep_config *const cfg)
381386
}
382387
}
383388

389+
static void dwc2_ep_next_target_frame(const struct device *dev, struct udc_ep_config *const cfg) {
390+
struct udc_dwc2_data *const priv = udc_get_private(dev);
391+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr);
392+
uint16_t limit = USB_DWC2_DSTS_SOFFN_LIMIT;
393+
394+
if (priv->enumspd != USB_DWC2_DSTS_ENUMSPD_HS3060) {
395+
limit >>= 3;
396+
}
397+
398+
priv->target_frames[ep_bitmap_idx] += cfg->interval;
399+
if (priv->target_frames[ep_bitmap_idx] > limit) {
400+
priv->target_frames[ep_bitmap_idx] &= limit;
401+
priv->target_frame_overrun_mask &= BIT(ep_bitmap_idx);
402+
} else {
403+
priv->target_frame_overrun_mask &= ~BIT(ep_bitmap_idx);
404+
}
405+
}
406+
407+
static bool dwc2_target_frame_elapsed(const struct device *dev, struct udc_ep_config *const cfg)
408+
{
409+
struct udc_dwc2_data *const priv = udc_get_private(dev);
410+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr);
411+
const uint16_t target_frame = priv->target_frames[ep_bitmap_idx];
412+
const uint16_t current_frame = priv->sof_num;
413+
const bool frame_overrun = priv->target_frame_overrun_mask & BIT(ep_bitmap_idx);
414+
uint16_t limit = USB_DWC2_DSTS_SOFFN_LIMIT;
415+
416+
if (priv->enumspd != USB_DWC2_DSTS_ENUMSPD_HS3060) {
417+
limit >>= 3;
418+
}
419+
420+
if (!frame_overrun && current_frame >= target_frame) {
421+
return true;
422+
}
423+
424+
if (frame_overrun && current_frame >= target_frame &&
425+
((current_frame - target_frame) < limit / 2)) {
426+
return true;
427+
}
428+
429+
return false;
430+
}
431+
384432
static bool dwc2_ep_is_iso(struct udc_ep_config *const cfg)
385433
{
386434
return (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) == USB_EP_TYPE_ISO;
@@ -566,13 +614,14 @@ static int dwc2_tx_fifo_write(const struct device *dev,
566614
}
567615

568616
if (is_iso) {
569-
/* Queue transfer on next SOF. TODO: allow stack to explicitly
570-
* specify on which (micro-)frame the data should be sent.
571-
*/
572-
if (priv->sof_num & 1) {
573-
diepctl |= USB_DWC2_DEPCTL_SETEVENFR;
574-
} else {
575-
diepctl |= USB_DWC2_DEPCTL_SETODDFR;
617+
if (cfg->interval == 1) {
618+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr);
619+
620+
if (priv->target_frames[ep_bitmap_idx] & 1) {
621+
diepctl |= USB_DWC2_DEPCTL_SETODDFR;
622+
} else {
623+
diepctl |= USB_DWC2_DEPCTL_SETEVENFR;
624+
}
576625
}
577626
}
578627

@@ -706,10 +755,14 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf,
706755
}
707756

708757
/* Set the Even/Odd (micro-)frame appropriately */
709-
if (priv->sof_num & 1) {
710-
doepctl |= USB_DWC2_DEPCTL_SETEVENFR;
711-
} else {
712-
doepctl |= USB_DWC2_DEPCTL_SETODDFR;
758+
if (cfg->interval == 1) {
759+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr);
760+
761+
if (priv->target_frames[ep_bitmap_idx] & 1) {
762+
doepctl |= USB_DWC2_DEPCTL_SETODDFR;
763+
} else {
764+
doepctl |= USB_DWC2_DEPCTL_SETEVENFR;
765+
}
713766
}
714767
} else {
715768
xfersize = net_buf_tailroom(buf);
@@ -1363,6 +1416,7 @@ static int dwc2_set_dedicated_fifo(const struct device *dev,
13631416
static int dwc2_ep_control_enable(const struct device *dev,
13641417
struct udc_ep_config *const cfg)
13651418
{
1419+
struct udc_dwc2_data *const priv = udc_get_private(dev);
13661420
mem_addr_t dxepctl0_reg;
13671421
uint32_t dxepctl0;
13681422

@@ -1416,6 +1470,12 @@ static int udc_dwc2_ep_activate(const struct device *dev,
14161470
return dwc2_ep_control_enable(dev, cfg);
14171471
}
14181472

1473+
if (dwc2_ep_is_iso(cfg)) {
1474+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(cfg->addr);
1475+
1476+
priv->target_frames[ep_bitmap_idx] = UDC_DWC2_TARGET_FRAME_INITIAL;
1477+
}
1478+
14191479
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
14201480
/* TODO: use dwc2_get_dxepctl_reg() */
14211481
dxepctl_reg = (mem_addr_t)&base->out_ep[ep_idx].doepctl;
@@ -2417,6 +2477,7 @@ static void dwc2_on_bus_reset(const struct device *dev)
24172477
uint32_t doepmsk;
24182478

24192479
/* Set the NAK bit for all OUT endpoints */
2480+
sys_clear_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_NAKINTRPT);
24202481
for (uint8_t i = 0U; i < priv->numdeveps; i++) {
24212482
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i);
24222483
mem_addr_t doepctl_reg;
@@ -2429,13 +2490,13 @@ static void dwc2_on_bus_reset(const struct device *dev)
24292490
}
24302491
}
24312492

2432-
doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL;
2493+
doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL | USB_DWC2_DOEPINT_OUTTKNEPDIS;
24332494
if (dwc2_in_buffer_dma_mode(dev)) {
24342495
doepmsk |= USB_DWC2_DOEPINT_STSPHSERCVD;
24352496
}
24362497

24372498
sys_write32(doepmsk, (mem_addr_t)&base->doepmsk);
2438-
sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL);
2499+
sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL | USB_DWC2_DIEPINT_NAKINTRPT);
24392500

24402501
/* Software has to handle RxFLvl interrupt only in Completer mode */
24412502
if (dwc2_in_completer_mode(dev)) {
@@ -2562,6 +2623,39 @@ static inline void dwc2_handle_in_xfercompl(const struct device *dev,
25622623
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
25632624
}
25642625

2626+
static inline void dwc2_handle_in_nakintrpt(const struct device *dev,
2627+
const uint8_t ep_idx)
2628+
{
2629+
struct udc_dwc2_data *const priv = udc_get_private(dev);
2630+
mem_addr_t diepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx | USB_EP_DIR_IN);
2631+
struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx | USB_EP_DIR_IN);
2632+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(ep_cfg->addr);
2633+
uint32_t diepctl;
2634+
2635+
if (!dwc2_ep_is_iso(ep_cfg)) {
2636+
return;
2637+
}
2638+
2639+
if (priv->target_frames[ep_bitmap_idx] == UDC_DWC2_TARGET_FRAME_INITIAL) {
2640+
priv->target_frames[ep_bitmap_idx] = priv->sof_num;
2641+
if (ep_cfg->interval > 1) {
2642+
diepctl = sys_read32(diepctl_reg);
2643+
if (priv->target_frames[ep_bitmap_idx] & 1) {
2644+
diepctl |= USB_DWC2_DEPCTL_SETODDFR;
2645+
} else {
2646+
diepctl |= USB_DWC2_DEPCTL_SETEVENFR;
2647+
}
2648+
2649+
sys_write32(diepctl, diepctl_reg);
2650+
}
2651+
}
2652+
2653+
// TODO this holy fuck
2654+
//diepctl = sys_read32(diepctl_reg);
2655+
//if (diepctl )
2656+
dwc2_ep_next_target_frame(dev, ep_cfg);
2657+
}
2658+
25652659
static inline void dwc2_handle_iepint(const struct device *dev)
25662660
{
25672661
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
@@ -2590,6 +2684,9 @@ static inline void dwc2_handle_iepint(const struct device *dev)
25902684
dwc2_handle_in_xfercompl(dev, n);
25912685
}
25922686

2687+
if (status & USB_DWC2_DIEPINT_NAKINTRPT) {
2688+
dwc2_handle_in_nakintrpt(dev, n);
2689+
}
25932690
}
25942691
}
25952692

@@ -2669,6 +2766,33 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev,
26692766
}
26702767
}
26712768

2769+
static inline void dwc2_handle_out_token_ep_disabled(const struct device *dev, const uint8_t ep_idx) {
2770+
struct udc_dwc2_data *const priv = udc_get_private(dev);
2771+
struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx);
2772+
const uint8_t ep_bitmap_idx = USB_EP_GET_BITMAP_IDX(ep_cfg->addr);
2773+
2774+
if (!USB_EP_DIR_IS_OUT(ep_cfg->addr) || !dwc2_ep_is_iso(ep_cfg)) {
2775+
return;
2776+
}
2777+
2778+
2779+
if (priv->target_frames[ep_bitmap_idx] == UDC_DWC2_TARGET_FRAME_INITIAL) {
2780+
priv->target_frames[ep_bitmap_idx] = priv->sof_num;
2781+
if (ep_cfg->interval > 1) {
2782+
mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx);
2783+
uint32_t doepctl = sys_read32(doepctl_reg);
2784+
if (priv->target_frames[ep_bitmap_idx] & 1) {
2785+
doepctl |= USB_DWC2_DEPCTL_SETODDFR;
2786+
} else {
2787+
doepctl |= USB_DWC2_DEPCTL_SETEVENFR;
2788+
}
2789+
sys_write32(doepctl, doepctl_reg);
2790+
}
2791+
}
2792+
2793+
dwc2_ep_next_target_frame(dev, ep_cfg);
2794+
}
2795+
26722796
static inline void dwc2_handle_oepint(const struct device *dev)
26732797
{
26742798
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
@@ -2734,6 +2858,10 @@ static inline void dwc2_handle_oepint(const struct device *dev)
27342858
if (status & USB_DWC2_DOEPINT_XFERCOMPL) {
27352859
dwc2_handle_out_xfercompl(dev, n);
27362860
}
2861+
2862+
if (status & USB_DWC2_DOEPINT_OUTTKNEPDIS) {
2863+
dwc2_handle_out_token_ep_disabled(dev, n);
2864+
}
27372865
}
27382866

27392867
/* Clear OEPINT interrupt */
@@ -2768,7 +2896,7 @@ static void dwc2_handle_incompisoin(const struct device *dev)
27682896

27692897
diepctl = sys_read32(diepctl_reg);
27702898

2771-
/* Check if endpoint didn't receive ISO OUT data */
2899+
/* Check if endpoint didn't receive ISO IN data */
27722900
if ((diepctl & mask) == val) {
27732901
struct udc_ep_config *cfg;
27742902
struct net_buf *buf;
@@ -2777,6 +2905,10 @@ static void dwc2_handle_incompisoin(const struct device *dev)
27772905
__ASSERT_NO_MSG(cfg && cfg->stat.enabled &&
27782906
dwc2_ep_is_iso(cfg));
27792907

2908+
if (!dwc2_target_frame_elapsed(dev, cfg)) {
2909+
continue;
2910+
}
2911+
27802912
udc_dwc2_ep_disable(dev, cfg, false);
27812913

27822914
buf = udc_buf_get(cfg);
@@ -2832,6 +2964,10 @@ static void dwc2_handle_incompisoout(const struct device *dev)
28322964
__ASSERT_NO_MSG(cfg && cfg->stat.enabled &&
28332965
dwc2_ep_is_iso(cfg));
28342966

2967+
if (!dwc2_target_frame_elapsed(dev, cfg)) {
2968+
continue;
2969+
}
2970+
28352971
udc_dwc2_ep_disable(dev, cfg, false);
28362972

28372973
buf = udc_buf_get(cfg);

include/zephyr/usb/usb_ch9.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ struct usb_association_descriptor {
321321
/** Get endpoint address from endpoint index and direction */
322322
#define USB_EP_GET_ADDR(idx, dir) ((idx) | ((dir) & USB_EP_DIR_MASK))
323323

324+
/** Get endpoint bitmap index mapping */
325+
#define USB_EP_GET_BITMAP_IDX(ep) (USB_EP_GET_IDX(ep) | (((ep) & USB_EP_DIR_MASK) >> 3U))
326+
324327
/** True if the endpoint is an IN endpoint */
325328
#define USB_EP_DIR_IS_IN(ep) (USB_EP_GET_DIR(ep) == USB_EP_DIR_IN)
326329

0 commit comments

Comments
 (0)