zephyr/drivers/usb/udc/udc_dwc2.c
Tomasz Moń 2368623f32 drivers: udc_dwc2: Avoid IN endpoint dequeue race
Flushing TxFIFO is racing with actual use of the TxFIFO. The software
controls only one side of the race (flush trigger) while the host
controls the other side. Therefore, locking interrupts before flushing
TxFIFO is not protecting against the race condition.

Disable the endpoint on dequeue to make sure that TxFIFO flushing won't
conflict with host actions (because the endpoint would be forced to NAK
the IN tokens before the TxFIFO is flushed).

Signed-off-by: Tomasz Moń <tomasz.mon@nordicsemi.no>
2024-05-29 12:00:08 +02:00

2013 lines
53 KiB
C

/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "udc_common.h"
#include "udc_dwc2.h"
#include <string.h>
#include <stdio.h>
#include <zephyr/kernel.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/drivers/usb/udc.h>
#include <zephyr/usb/usb_ch9.h>
#include <usb_dwc2_hw.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL);
#include "udc_dwc2_vendor_quirks.h"
enum dwc2_drv_event_type {
/* Trigger next transfer, must not be used for control OUT */
DWC2_DRV_EVT_XFER,
/* Setup packet received */
DWC2_DRV_EVT_SETUP,
/* OUT transaction for specific endpoint is finished */
DWC2_DRV_EVT_DOUT,
/* IN transaction for specific endpoint is finished */
DWC2_DRV_EVT_DIN,
};
struct dwc2_drv_event {
const struct device *dev;
enum dwc2_drv_event_type type;
uint32_t bcnt;
uint8_t ep;
};
K_MSGQ_DEFINE(drv_msgq, sizeof(struct dwc2_drv_event),
CONFIG_UDC_DWC2_MAX_QMESSAGES, sizeof(void *));
/* Minimum RX FIFO size in 32-bit words considering the largest used OUT packet
* of 512 bytes. The value must be adjusted according to the number of OUT
* endpoints.
*/
#define UDC_DWC2_GRXFSIZ_DEFAULT (15U + 512U/4U)
/* TX FIFO0 depth in 32-bit words (used by control IN endpoint) */
#define UDC_DWC2_FIFO0_DEPTH 16U
/* Get Data FIFO access register */
#define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1))
/* Driver private data per instance */
struct udc_dwc2_data {
struct k_thread thread_data;
uint32_t ghwcfg1;
uint32_t enumspd;
uint32_t txf_set;
uint32_t grxfsiz;
uint32_t dfifodepth;
uint32_t max_xfersize;
uint32_t max_pktcnt;
uint32_t tx_len[16];
unsigned int dynfifosizing : 1;
/* Number of endpoints including control endpoint */
uint8_t numdeveps;
/* Number of IN endpoints including control endpoint */
uint8_t ineps;
/* Number of OUT endpoints including control endpoint */
uint8_t outeps;
uint8_t setup[8];
};
#if defined(CONFIG_PINCTRL)
#include <zephyr/drivers/pinctrl.h>
static int dwc2_init_pinctrl(const struct device *dev)
{
const struct udc_dwc2_config *const config = dev->config;
const struct pinctrl_dev_config *const pcfg = config->pcfg;
int ret = 0;
if (pcfg == NULL) {
LOG_INF("Skip pinctrl configuration");
return 0;
}
ret = pinctrl_apply_state(pcfg, PINCTRL_STATE_DEFAULT);
if (ret) {
LOG_ERR("Failed to apply default pinctrl state (%d)", ret);
}
LOG_DBG("Apply pinctrl");
return ret;
}
#else
static int dwc2_init_pinctrl(const struct device *dev)
{
ARG_UNUSED(dev);
return 0;
}
#endif
static inline struct usb_dwc2_reg *dwc2_get_base(const struct device *dev)
{
const struct udc_dwc2_config *const config = dev->config;
return config->base;
}
/* Get DOEPCTLn or DIEPCTLn register address */
static mem_addr_t dwc2_get_dxepctl_reg(const struct device *dev, const uint8_t ep)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
uint8_t ep_idx = USB_EP_GET_IDX(ep);
if (USB_EP_DIR_IS_OUT(ep)) {
return (mem_addr_t)&base->out_ep[ep_idx].doepctl;
} else {
return (mem_addr_t)&base->in_ep[ep_idx].diepctl;
}
}
/* Get available FIFO space in bytes */
static uint32_t dwc2_ftx_avail(const struct device *dev, const uint32_t idx)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t reg = (mem_addr_t)&base->in_ep[idx].dtxfsts;
uint32_t dtxfsts;
dtxfsts = sys_read32(reg);
return usb_dwc2_get_dtxfsts_ineptxfspcavail(dtxfsts) * 4;
}
static uint32_t dwc2_get_iept_pktctn(const struct device *dev, const uint32_t idx)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
if (idx == 0) {
return usb_dwc2_get_dieptsiz0_pktcnt(UINT32_MAX);
} else {
return priv->max_pktcnt;
}
}
static uint32_t dwc2_get_iept_xfersize(const struct device *dev, const uint32_t idx)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
if (idx == 0) {
return usb_dwc2_get_dieptsiz0_xfersize(UINT32_MAX);
} else {
return priv->max_xfersize;
}
}
static void dwc2_flush_rx_fifo(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t grstctl_reg = (mem_addr_t)&base->grstctl;
sys_write32(USB_DWC2_GRSTCTL_RXFFLSH, grstctl_reg);
while (sys_read32(grstctl_reg) & USB_DWC2_GRSTCTL_RXFFLSH) {
}
}
static void dwc2_flush_tx_fifo(const struct device *dev, const uint8_t idx)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t grstctl_reg = (mem_addr_t)&base->grstctl;
/* TODO: use dwc2_get_dxepctl_reg() */
mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[idx].diepctl;
uint32_t grstctl;
uint32_t fnum;
fnum = usb_dwc2_get_depctl_txfnum(sys_read32(diepctl_reg));
grstctl = usb_dwc2_set_grstctl_txfnum(fnum) | USB_DWC2_GRSTCTL_TXFFLSH;
sys_write32(grstctl, grstctl_reg);
while (sys_read32(grstctl_reg) & USB_DWC2_GRSTCTL_TXFFLSH) {
}
}
/* Return TX FIFOi depth in 32-bit words (i = f_idx + 1) */
static uint32_t dwc2_get_txfdep(const struct device *dev, const uint32_t f_idx)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
uint32_t dieptxf;
dieptxf = sys_read32((mem_addr_t)&base->dieptxf[f_idx]);
return usb_dwc2_get_dieptxf_inepntxfdep(dieptxf);
}
/* Return TX FIFOi address (i = f_idx + 1) */
static uint32_t dwc2_get_txfaddr(const struct device *dev, const uint32_t f_idx)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
uint32_t dieptxf;
dieptxf = sys_read32((mem_addr_t)&base->dieptxf[f_idx]);
return usb_dwc2_get_dieptxf_inepntxfstaddr(dieptxf);
}
/* Set TX FIFOi address and depth (i = f_idx + 1) */
static void dwc2_set_txf(const struct device *dev, const uint32_t f_idx,
const uint32_t dep, const uint32_t addr)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
uint32_t dieptxf;
dieptxf = usb_dwc2_set_dieptxf_inepntxfdep(dep) |
usb_dwc2_set_dieptxf_inepntxfstaddr(addr);
sys_write32(dieptxf, (mem_addr_t)&base->dieptxf[f_idx]);
}
/* Enable/disable endpoint interrupt */
static void dwc2_set_epint(const struct device *dev,
struct udc_ep_config *const cfg, const bool enabled)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t reg = (mem_addr_t)&base->daintmsk;
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
uint32_t epmsk;
if (USB_EP_DIR_IS_IN(cfg->addr)) {
epmsk = USB_DWC2_DAINT_INEPINT(ep_idx);
} else {
epmsk = USB_DWC2_DAINT_OUTEPINT(ep_idx);
}
if (enabled) {
sys_set_bits(reg, epmsk);
} else {
sys_clear_bits(reg, epmsk);
}
}
/* Can be called from ISR context */
static int dwc2_tx_fifo_write(const struct device *dev,
struct udc_ep_config *const cfg, struct net_buf *const buf)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
mem_addr_t dieptsiz_reg = (mem_addr_t)&base->in_ep[ep_idx].dieptsiz;
/* TODO: use dwc2_get_dxepctl_reg() */
mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl;
mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint;
uint32_t max_xfersize, max_pktcnt, pktcnt, spcavail;
const size_t d = sizeof(uint32_t);
unsigned int key;
uint32_t len;
spcavail = dwc2_ftx_avail(dev, ep_idx);
/* Round down to multiple of endpoint MPS */
spcavail -= spcavail % cfg->mps;
/*
* Here, the available space should be equal to the FIFO space
* assigned/configured for that endpoint because we do not schedule another
* transfer until the previous one has not finished. For simplicity,
* we only check that the available space is not less than the endpoint
* MPS.
*/
if (spcavail < cfg->mps) {
LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)",
cfg->addr, spcavail, dwc2_ftx_avail(dev, ep_idx));
return -EAGAIN;
}
len = MIN(buf->len, spcavail);
if (len != 0U) {
max_pktcnt = dwc2_get_iept_pktctn(dev, ep_idx);
max_xfersize = dwc2_get_iept_xfersize(dev, ep_idx);
if (len > max_xfersize) {
/*
* Avoid short packets if the transfer size cannot be
* handled in one set.
*/
len = ROUND_DOWN(max_xfersize, cfg->mps);
}
/*
* Determine the number of packets for the current transfer;
* if the pktcnt is too large, truncate the actual transfer length.
*/
pktcnt = DIV_ROUND_UP(len, cfg->mps);
if (pktcnt > max_pktcnt) {
pktcnt = max_pktcnt;
len = pktcnt * cfg->mps;
}
} else {
/* ZLP */
pktcnt = 1U;
}
LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u spcavail %u",
cfg->addr, len, pktcnt, spcavail);
priv->tx_len[ep_idx] = len;
/* Lock and write to endpoint FIFO */
key = irq_lock();
/* Set number of packets and transfer size */
sys_write32((pktcnt << USB_DWC2_DEPTSIZN_PKTCNT_POS) | len, dieptsiz_reg);
/* Clear NAK and set endpoint enable */
sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK);
/* Clear IN Endpoint NAK Effective interrupt in case it was set */
sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg);
/* FIFO access is always in 32-bit words */
for (uint32_t i = 0UL; i < len; i += d) {
uint32_t val = buf->data[i];
if (i + 1 < len) {
val |= ((uint32_t)buf->data[i + 1UL]) << 8;
}
if (i + 2 < len) {
val |= ((uint32_t)buf->data[i + 2UL]) << 16;
}
if (i + 3 < len) {
val |= ((uint32_t)buf->data[i + 3UL]) << 24;
}
sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx));
}
irq_unlock(key);
return 0;
}
static inline int dwc2_read_fifo(const struct device *dev, const uint8_t ep,
struct net_buf *const buf, const size_t size)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
size_t len = buf ? MIN(size, net_buf_tailroom(buf)) : 0;
const size_t d = sizeof(uint32_t);
/* FIFO access is always in 32-bit words */
for (uint32_t n = 0; n < (len / d); n++) {
net_buf_add_le32(buf, sys_read32(UDC_DWC2_EP_FIFO(base, ep)));
}
if (len % d) {
uint8_t r[4];
/* Get the remaining */
sys_put_le32(sys_read32(UDC_DWC2_EP_FIFO(base, ep)), r);
for (uint32_t i = 0U; i < (len % d); i++) {
net_buf_add_u8(buf, r[i]);
}
}
if (unlikely(size > len)) {
for (uint32_t n = 0; n < DIV_ROUND_UP(size - len, d); n++) {
(void)sys_read32(UDC_DWC2_EP_FIFO(base, ep));
}
}
return 0;
}
/* Can be called from ISR and we call it only when there is a buffer in the queue */
static void dwc2_prep_rx(const struct device *dev,
struct udc_ep_config *const cfg, const bool ncnak)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
mem_addr_t doeptsiz_reg = (mem_addr_t)&base->out_ep[ep_idx].doeptsiz;
mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx);
uint32_t doeptsiz;
doeptsiz = (1 << USB_DWC2_DOEPTSIZ0_PKTCNT_POS) | cfg->mps;
if (cfg->addr == USB_CONTROL_EP_OUT) {
doeptsiz |= (3 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS);
}
sys_write32(doeptsiz, doeptsiz_reg);
if (ncnak) {
sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA);
} else {
sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK);
}
LOG_INF("Prepare RX 0x%02x doeptsiz 0x%x", cfg->addr, doeptsiz);
}
static void dwc2_handle_xfer_next(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct net_buf *buf;
buf = udc_buf_peek(dev, cfg->addr);
if (buf == NULL) {
return;
}
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
dwc2_prep_rx(dev, cfg, 0);
} else {
if (dwc2_tx_fifo_write(dev, cfg, buf)) {
LOG_ERR("Failed to start write to TX FIFO, ep 0x%02x",
cfg->addr);
}
}
udc_ep_set_busy(dev, cfg->addr, true);
}
static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length)
{
struct net_buf *buf;
buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length);
if (buf == NULL) {
return -ENOMEM;
}
udc_buf_put(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), buf);
dwc2_prep_rx(dev, udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), 0);
LOG_DBG("feed buf %p", buf);
return 0;
}
static int dwc2_handle_evt_setup(const struct device *dev)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
struct net_buf *buf;
int err;
buf = udc_buf_get(dev, USB_CONTROL_EP_OUT);
if (buf == NULL) {
LOG_ERR("No buffer queued for control ep");
return -ENODATA;
}
net_buf_add_mem(buf, priv->setup, sizeof(priv->setup));
udc_ep_buf_set_setup(buf);
LOG_HEXDUMP_DBG(buf->data, buf->len, "setup");
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
/* We always allocate and feed buffer large enough for a setup packet. */
if (udc_ctrl_stage_is_data_out(dev)) {
/* Allocate and feed buffer for data OUT stage */
LOG_DBG("s:%p|feed for -out-", buf);
err = dwc2_ctrl_feed_dout(dev, udc_data_stage_length(buf));
if (err == -ENOMEM) {
err = udc_submit_ep_event(dev, buf, err);
}
} else if (udc_ctrl_stage_is_data_in(dev)) {
LOG_DBG("s:%p|feed for -in-status", buf);
err = dwc2_ctrl_feed_dout(dev, 8);
if (err == -ENOMEM) {
err = udc_submit_ep_event(dev, buf, err);
}
err = udc_ctrl_submit_s_in_status(dev);
} else {
LOG_DBG("s:%p|feed >setup", buf);
err = dwc2_ctrl_feed_dout(dev, 8);
if (err == -ENOMEM) {
err = udc_submit_ep_event(dev, buf, err);
}
err = udc_ctrl_submit_s_status(dev);
}
return err;
}
static inline int dwc2_handle_evt_dout(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct net_buf *buf;
int err = 0;
buf = udc_buf_get(dev, cfg->addr);
if (buf == NULL) {
LOG_ERR("No buffer queued for control ep");
return -ENODATA;
}
udc_ep_set_busy(dev, cfg->addr, false);
if (cfg->addr == USB_CONTROL_EP_OUT) {
if (udc_ctrl_stage_is_status_out(dev)) {
/* s-in-status finished */
LOG_DBG("dout:%p| status, feed >s", buf);
/* Feed a buffer for the next setup packet */
err = dwc2_ctrl_feed_dout(dev, 8);
if (err == -ENOMEM) {
err = udc_submit_ep_event(dev, buf, err);
}
/* Status stage finished, notify upper layer */
udc_ctrl_submit_status(dev, buf);
} else {
/*
* For all other cases we feed with a buffer
* large enough for setup packet.
*/
LOG_DBG("dout:%p| data, feed >s", buf);
err = dwc2_ctrl_feed_dout(dev, 8);
if (err == -ENOMEM) {
err = udc_submit_ep_event(dev, buf, err);
}
}
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
if (udc_ctrl_stage_is_status_in(dev)) {
err = udc_ctrl_submit_s_out_status(dev, buf);
}
} else {
err = udc_submit_ep_event(dev, buf, 0);
}
return err;
}
static int dwc2_handle_evt_din(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct net_buf *buf;
buf = udc_buf_peek(dev, cfg->addr);
if (buf == NULL) {
LOG_ERR("No buffer for ep 0x%02x", cfg->addr);
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
return -ENOBUFS;
}
if (buf->len) {
/* Looks like we failed to continue in ISR, retry */
return dwc2_tx_fifo_write(dev, cfg, buf);
}
if (cfg->addr == USB_CONTROL_EP_IN && udc_ep_buf_has_zlp(buf)) {
udc_ep_buf_clear_zlp(buf);
return dwc2_tx_fifo_write(dev, cfg, buf);
}
buf = udc_buf_get(dev, cfg->addr);
udc_ep_set_busy(dev, cfg->addr, false);
if (cfg->addr == USB_CONTROL_EP_IN) {
if (udc_ctrl_stage_is_status_in(dev) ||
udc_ctrl_stage_is_no_data(dev)) {
/* Status stage finished, notify upper layer */
udc_ctrl_submit_status(dev, buf);
}
/* Update to next stage of control transfer */
udc_ctrl_update_stage(dev, buf);
if (udc_ctrl_stage_is_status_out(dev)) {
/*
* IN transfer finished, release buffer,
* control OUT buffer should be already fed.
*/
net_buf_unref(buf);
}
return 0;
}
return udc_submit_ep_event(dev, buf, 0);
}
static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)
{
const struct device *dev = (const struct device *)arg;
struct udc_ep_config *ep_cfg;
struct dwc2_drv_event evt;
/* This is the bottom-half of the ISR handler and the place where
* a new transfer can be fed.
*/
k_msgq_get(&drv_msgq, &evt, K_FOREVER);
ep_cfg = udc_get_ep_cfg(dev, evt.ep);
switch (evt.type) {
case DWC2_DRV_EVT_XFER:
LOG_DBG("New transfer in the queue");
break;
case DWC2_DRV_EVT_SETUP:
LOG_DBG("SETUP event");
dwc2_handle_evt_setup(dev);
break;
case DWC2_DRV_EVT_DOUT:
LOG_DBG("DOUT event ep 0x%02x", ep_cfg->addr);
dwc2_handle_evt_dout(dev, ep_cfg);
break;
case DWC2_DRV_EVT_DIN:
LOG_DBG("DIN event");
dwc2_handle_evt_din(dev, ep_cfg);
break;
}
if (ep_cfg->addr != USB_CONTROL_EP_OUT && !udc_ep_is_busy(dev, ep_cfg->addr)) {
dwc2_handle_xfer_next(dev, ep_cfg);
} else {
LOG_DBG("ep 0x%02x busy", ep_cfg->addr);
}
}
static void dwc2_on_bus_reset(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);
/* Set the NAK bit for all OUT endpoints */
for (uint8_t i = 0U; i < priv->numdeveps; i++) {
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i);
mem_addr_t doepctl_reg;
LOG_DBG("ep 0x%02x EPDIR %u", i, epdir);
if (epdir == USB_DWC2_GHWCFG1_EPDIR_OUT ||
epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) {
doepctl_reg = dwc2_get_dxepctl_reg(dev, i);
sys_write32(USB_DWC2_DEPCTL_SNAK, doepctl_reg);
}
}
sys_write32(USB_DWC2_DOEPINT_SETUP, (mem_addr_t)&base->doepmsk);
sys_set_bits((mem_addr_t)&base->gintmsk, USB_DWC2_GINTSTS_RXFLVL);
sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL);
/* Clear device address during reset. */
sys_clear_bits((mem_addr_t)&base->dcfg, USB_DWC2_DCFG_DEVADDR_MASK);
}
static void dwc2_handle_enumdone(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);
uint32_t dsts;
dsts = sys_read32((mem_addr_t)&base->dsts);
priv->enumspd = usb_dwc2_get_dsts_enumspd(dsts);
}
static inline int dwc2_read_fifo_setup(const struct device *dev, uint8_t ep,
const size_t size)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);
size_t offset;
/* FIFO access is always in 32-bit words */
if (size != 8) {
LOG_ERR("%d bytes SETUP", size);
}
/*
* We store the setup packet temporarily in the driver's private data
* because there is always a race risk after the status stage OUT
* packet from the host and the new setup packet. This is fine in
* bottom-half processing because the events arrive in a queue and
* there will be a next net_buf for the setup packet.
*/
for (offset = 0; offset < MIN(size, 8); offset += 4) {
sys_put_le32(sys_read32(UDC_DWC2_EP_FIFO(base, ep)),
&priv->setup[offset]);
}
/* On protocol error simply discard extra data */
while (offset < size) {
sys_read32(UDC_DWC2_EP_FIFO(base, ep));
offset += 4;
}
return 0;
}
static inline void dwc2_handle_rxflvl(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_ep_config *ep_cfg;
struct dwc2_drv_event evt;
struct net_buf *buf;
uint32_t grxstsp;
uint32_t pktsts;
grxstsp = sys_read32((mem_addr_t)&base->grxstsp);
evt.ep = usb_dwc2_get_grxstsp_epnum(grxstsp);
evt.bcnt = usb_dwc2_get_grxstsp_bcnt(grxstsp);
pktsts = usb_dwc2_get_grxstsp_pktsts(grxstsp);
LOG_DBG("ep 0x%02x: pktsts %u, bcnt %u", evt.ep, pktsts, evt.bcnt);
switch (pktsts) {
case USB_DWC2_GRXSTSR_PKTSTS_SETUP:
dwc2_read_fifo_setup(dev, evt.ep, evt.bcnt);
break;
case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA:
evt.type = DWC2_DRV_EVT_DOUT;
ep_cfg = udc_get_ep_cfg(dev, evt.ep);
buf = udc_buf_peek(dev, ep_cfg->addr);
/* RxFIFO data must be retrieved even when buf is NULL */
dwc2_read_fifo(dev, evt.ep, buf, evt.bcnt);
if (buf == NULL) {
LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr);
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
break;
}
if (net_buf_tailroom(buf) && evt.bcnt == ep_cfg->mps) {
dwc2_prep_rx(dev, ep_cfg, 0);
} else {
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
break;
case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA_DONE:
LOG_DBG("RX pktsts DONE");
break;
case USB_DWC2_GRXSTSR_PKTSTS_SETUP_DONE:
LOG_DBG("SETUP pktsts DONE");
case USB_DWC2_GRXSTSR_PKTSTS_GLOBAL_OUT_NAK:
LOG_DBG("Global OUT NAK");
break;
default:
break;
}
}
static inline void dwc2_handle_xfercompl(const struct device *dev,
const uint8_t ep_idx)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
struct udc_ep_config *ep_cfg;
struct dwc2_drv_event evt;
struct net_buf *buf;
ep_cfg = udc_get_ep_cfg(dev, ep_idx | USB_EP_DIR_IN);
buf = udc_buf_peek(dev, ep_cfg->addr);
if (buf == NULL) {
udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS);
return;
}
net_buf_pull(buf, priv->tx_len[ep_idx]);
if (buf->len && dwc2_tx_fifo_write(dev, ep_cfg, buf) == 0) {
return;
}
evt.dev = dev;
evt.ep = ep_cfg->addr;
evt.type = DWC2_DRV_EVT_DIN;
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
static inline void dwc2_handle_iepint(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
const uint8_t n_max = 16;
uint32_t diepmsk;
uint32_t daint;
diepmsk = sys_read32((mem_addr_t)&base->diepmsk);
daint = sys_read32((mem_addr_t)&base->daint);
for (uint8_t n = 0U; n < n_max; n++) {
mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[n].diepint;
uint32_t diepint;
uint32_t status;
if (daint & USB_DWC2_DAINT_INEPINT(n)) {
/* Read and clear interrupt status */
diepint = sys_read32(diepint_reg);
status = diepint & diepmsk;
sys_write32(status, diepint_reg);
LOG_DBG("ep 0x%02x interrupt status: 0x%x",
n | USB_EP_DIR_IN, status);
if (status & USB_DWC2_DIEPINT_XFERCOMPL) {
dwc2_handle_xfercompl(dev, n);
}
}
}
/* Clear IEPINT interrupt */
sys_write32(USB_DWC2_GINTSTS_IEPINT, (mem_addr_t)&base->gintsts);
}
static inline void dwc2_handle_oepint(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
const uint8_t n_max = 16;
uint32_t doepmsk;
uint32_t daint;
doepmsk = sys_read32((mem_addr_t)&base->doepmsk);
daint = sys_read32((mem_addr_t)&base->daint);
for (uint8_t n = 0U; n < n_max; n++) {
mem_addr_t doepint_reg = (mem_addr_t)&base->out_ep[n].doepint;
uint32_t doepint;
uint32_t status;
if (daint & USB_DWC2_DAINT_OUTEPINT(n)) {
/* Read and clear interrupt status */
doepint = sys_read32(doepint_reg);
status = doepint & doepmsk;
sys_write32(status, doepint_reg);
LOG_DBG("ep 0x%02x interrupt status: 0x%x", n, status);
if (status & USB_DWC2_DOEPINT_SETUP) {
struct dwc2_drv_event evt = {
.type = DWC2_DRV_EVT_SETUP,
.ep = USB_CONTROL_EP_OUT,
.bcnt = 8,
};
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
}
}
/* Clear OEPINT interrupt */
sys_write32(USB_DWC2_GINTSTS_OEPINT, (mem_addr_t)&base->gintsts);
}
static void udc_dwc2_isr_handler(const struct device *dev)
{
const struct udc_dwc2_config *const config = dev->config;
struct usb_dwc2_reg *const base = config->base;
mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts;
uint32_t int_status;
uint32_t gintmsk;
gintmsk = sys_read32((mem_addr_t)&base->gintmsk);
/* Read and handle interrupt status register */
while ((int_status = sys_read32(gintsts_reg) & gintmsk)) {
LOG_DBG("GINTSTS 0x%x", int_status);
if (int_status & USB_DWC2_GINTSTS_SOF) {
/* Clear USB SOF interrupt. */
sys_write32(USB_DWC2_GINTSTS_SOF, gintsts_reg);
udc_submit_event(dev, UDC_EVT_SOF, 0);
}
if (int_status & USB_DWC2_GINTSTS_USBRST) {
/* Clear and handle USB Reset interrupt. */
sys_write32(USB_DWC2_GINTSTS_USBRST, gintsts_reg);
dwc2_on_bus_reset(dev);
LOG_DBG("USB Reset interrupt");
}
if (int_status & USB_DWC2_GINTSTS_ENUMDONE) {
/* Clear and handle Enumeration Done interrupt. */
sys_write32(USB_DWC2_GINTSTS_ENUMDONE, gintsts_reg);
dwc2_handle_enumdone(dev);
udc_submit_event(dev, UDC_EVT_RESET, 0);
}
if (int_status & USB_DWC2_GINTSTS_USBSUSP) {
/* Clear USB Suspend interrupt. */
sys_write32(USB_DWC2_GINTSTS_USBSUSP, gintsts_reg);
udc_set_suspended(dev, true);
udc_submit_event(dev, UDC_EVT_SUSPEND, 0);
}
if (int_status & USB_DWC2_GINTSTS_WKUPINT) {
/* Clear Resume/Remote Wakeup Detected interrupt. */
sys_write32(USB_DWC2_GINTSTS_WKUPINT, gintsts_reg);
udc_set_suspended(dev, false);
udc_submit_event(dev, UDC_EVT_RESUME, 0);
}
if (int_status & USB_DWC2_GINTSTS_IEPINT) {
/* Handle IN Endpoints interrupt */
dwc2_handle_iepint(dev);
}
if (int_status & USB_DWC2_GINTSTS_RXFLVL) {
/* Handle RxFIFO Non-Empty interrupt */
dwc2_handle_rxflvl(dev);
}
if (int_status & USB_DWC2_GINTSTS_OEPINT) {
/* Handle OUT Endpoints interrupt */
dwc2_handle_oepint(dev);
}
}
(void)dwc2_quirk_irq_clear(dev);
}
static void dwc2_unset_unused_fifo(const struct device *dev)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
struct udc_ep_config *tmp;
for (uint8_t i = priv->ineps - 1U; i > 0; i--) {
tmp = udc_get_ep_cfg(dev, i | USB_EP_DIR_IN);
if (tmp->stat.enabled && (priv->txf_set & BIT(i))) {
return;
}
if (!tmp->stat.enabled && (priv->txf_set & BIT(i))) {
priv->txf_set &= ~BIT(i);
}
}
}
/*
* In dedicated FIFO mode there are i (i = 1 ... ineps - 1) FIFO size registers,
* e.g. DIEPTXF1, DIEPTXF2, ... DIEPTXF4. When dynfifosizing is enabled,
* the size register is mutable. The offset of DIEPTXF1 registers is 0.
*/
static int dwc2_set_dedicated_fifo(const struct device *dev,
struct udc_ep_config *const cfg,
uint32_t *const diepctl)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
uint32_t txfaddr;
uint32_t txfdep;
uint32_t tmp;
/* Keep everything but FIFO number */
tmp = *diepctl & ~USB_DWC2_DEPCTL_TXFNUM_MASK;
if (priv->dynfifosizing) {
if (priv->txf_set & ~BIT_MASK(ep_idx)) {
dwc2_unset_unused_fifo(dev);
}
if (priv->txf_set & ~BIT_MASK(ep_idx)) {
LOG_WRN("Some of the FIFOs higher than %u are set, %lx",
ep_idx, priv->txf_set & ~BIT_MASK(ep_idx));
return -EIO;
}
if ((ep_idx - 1) != 0U) {
txfaddr = dwc2_get_txfdep(dev, ep_idx - 2) +
dwc2_get_txfaddr(dev, ep_idx - 2);
} else {
txfaddr = UDC_DWC2_FIFO0_DEPTH + priv->grxfsiz;
}
/* Set FIFO depth (32-bit words) and address */
txfdep = cfg->mps / 4U;
dwc2_set_txf(dev, ep_idx - 1, txfdep, txfaddr);
} else {
txfdep = dwc2_get_txfdep(dev, ep_idx - 1);
txfaddr = dwc2_get_txfaddr(dev, ep_idx - 1);
if (cfg->mps < txfdep * 4U) {
return -ENOMEM;
}
LOG_DBG("Reuse FIFO%u addr 0x%08x depth %u", ep_idx, txfaddr, txfdep);
}
/* Assign FIFO to the IN endpoint */
*diepctl = tmp | usb_dwc2_set_depctl_txfnum(ep_idx);
priv->txf_set |= BIT(ep_idx);
dwc2_flush_tx_fifo(dev, ep_idx);
LOG_INF("Set FIFO%u (ep 0x%02x) addr 0x%04x depth %u size %u",
ep_idx, cfg->addr, txfaddr, txfdep, dwc2_ftx_avail(dev, ep_idx));
return 0;
}
static int dwc2_ep_control_enable(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);
mem_addr_t dxepctl0_reg;
uint32_t dxepctl0;
dxepctl0_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
dxepctl0 = sys_read32(dxepctl0_reg);
dxepctl0 &= ~USB_DWC2_DEPCTL0_MPS_MASK;
switch (cfg->mps) {
case 8:
dxepctl0 |= USB_DWC2_DEPCTL0_MPS_8 << USB_DWC2_DEPCTL_MPS_POS;
break;
case 16:
dxepctl0 |= USB_DWC2_DEPCTL0_MPS_16 << USB_DWC2_DEPCTL_MPS_POS;
break;
case 32:
dxepctl0 |= USB_DWC2_DEPCTL0_MPS_32 << USB_DWC2_DEPCTL_MPS_POS;
break;
case 64:
dxepctl0 |= USB_DWC2_DEPCTL0_MPS_64 << USB_DWC2_DEPCTL_MPS_POS;
break;
default:
return -EINVAL;
}
dxepctl0 |= USB_DWC2_DEPCTL_USBACTEP;
/*
* The following applies to the Control IN endpoint only.
*
* Set endpoint 0 TxFIFO depth when dynfifosizing is enabled.
* Note that only dedicated mode is supported at this time.
*/
if (cfg->addr == USB_CONTROL_EP_IN && priv->dynfifosizing) {
uint32_t gnptxfsiz;
gnptxfsiz = usb_dwc2_set_gnptxfsiz_nptxfdep(UDC_DWC2_FIFO0_DEPTH) |
usb_dwc2_set_gnptxfsiz_nptxfstaddr(priv->grxfsiz);
sys_write32(gnptxfsiz, (mem_addr_t)&base->gnptxfsiz);
}
if (cfg->addr == USB_CONTROL_EP_OUT) {
int ret;
dwc2_flush_rx_fifo(dev);
ret = dwc2_ctrl_feed_dout(dev, 8);
if (ret) {
return ret;
}
} else {
dwc2_flush_tx_fifo(dev, 0);
}
sys_write32(dxepctl0, dxepctl0_reg);
dwc2_set_epint(dev, cfg, true);
return 0;
}
static int udc_dwc2_ep_activate(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
struct udc_dwc2_data *const priv = udc_get_private(dev);
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
mem_addr_t dxepctl_reg;
uint32_t dxepctl;
LOG_DBG("Enable ep 0x%02x", cfg->addr);
if (ep_idx == 0U) {
return dwc2_ep_control_enable(dev, cfg);
}
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
/* TODO: use dwc2_get_dxepctl_reg() */
dxepctl_reg = (mem_addr_t)&base->out_ep[ep_idx].doepctl;
} else {
if (priv->ineps > 0U && ep_idx > (priv->ineps - 1U)) {
LOG_ERR("No resources available for ep 0x%02x", cfg->addr);
return -EINVAL;
}
dxepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl;
}
if (cfg->mps > usb_dwc2_get_depctl_mps(UINT16_MAX)) {
return -EINVAL;
}
dxepctl = sys_read32(dxepctl_reg);
/* Set max packet size */
dxepctl &= ~USB_DWC2_DEPCTL_MPS_MASK;
dxepctl |= cfg->mps << USB_DWC2_DEPCTL_MPS_POS;
/* Set endpoint type */
dxepctl &= ~USB_DWC2_DEPCTL_EPTYPE_MASK;
switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) {
case USB_EP_TYPE_BULK:
dxepctl |= USB_DWC2_DEPCTL_EPTYPE_BULK <<
USB_DWC2_DEPCTL_EPTYPE_POS;
dxepctl |= USB_DWC2_DEPCTL_SETD0PID;
break;
case USB_EP_TYPE_INTERRUPT:
dxepctl |= USB_DWC2_DEPCTL_EPTYPE_INTERRUPT <<
USB_DWC2_DEPCTL_EPTYPE_POS;
dxepctl |= USB_DWC2_DEPCTL_SETD0PID;
break;
case USB_EP_TYPE_ISO:
dxepctl |= USB_DWC2_DEPCTL_EPTYPE_ISO <<
USB_DWC2_DEPCTL_EPTYPE_POS;
break;
default:
return -EINVAL;
}
if (USB_EP_DIR_IS_IN(cfg->addr) && cfg->mps != 0U) {
int ret = dwc2_set_dedicated_fifo(dev, cfg, &dxepctl);
if (ret) {
return ret;
}
}
dxepctl |= USB_DWC2_DEPCTL_USBACTEP;
/* Enable endpoint interrupts */
dwc2_set_epint(dev, cfg, true);
sys_write32(dxepctl, dxepctl_reg);
for (uint8_t i = 1U; i < priv->ineps; i++) {
LOG_DBG("DIEPTXF%u %08x DIEPCTL%u %08x",
i, sys_read32((mem_addr_t)&base->dieptxf[i - 1U]), i, dxepctl);
}
return 0;
}
static int dwc2_unset_dedicated_fifo(const struct device *dev,
struct udc_ep_config *const cfg,
uint32_t *const diepctl)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
/* Clear FIFO number field */
*diepctl &= ~USB_DWC2_DEPCTL_TXFNUM_MASK;
if (priv->dynfifosizing) {
if (priv->txf_set & ~BIT_MASK(ep_idx)) {
LOG_WRN("Some of the FIFOs higher than %u are set, %lx",
ep_idx, priv->txf_set & ~BIT_MASK(ep_idx));
return 0;
}
dwc2_set_txf(dev, ep_idx - 1, 0, 0);
}
priv->txf_set &= ~BIT(ep_idx);
return 0;
}
static void dwc2_wait_for_bit(mem_addr_t addr, uint32_t bit)
{
k_timepoint_t timeout = sys_timepoint_calc(K_MSEC(100));
/* This could potentially be converted to use proper synchronization
* primitives instead of busy looping, but the number of interrupt bits
* this function can be waiting for is rather high.
*
* Busy looping is most likely fine unless profiling shows otherwise.
*/
while (!(sys_read32(addr) & bit)) {
if (sys_timepoint_expired(timeout)) {
LOG_ERR("Timeout waiting for bit 0x%08X at 0x%08X",
bit, (uint32_t)addr);
return;
}
}
}
/* Disabled IN endpoint means that device will send NAK (isochronous: ZLP) after
* receiving IN token from host even if there is packet available in TxFIFO.
* Disabled OUT endpoint means that device will NAK (isochronous: discard data)
* incoming OUT data (or HS PING) even if there is space available in RxFIFO.
*
* Set stall parameter to true if caller wants to send STALL instead of NAK.
*/
static void udc_dwc2_ep_disable(const struct device *dev,
struct udc_ep_config *const cfg, bool stall)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
mem_addr_t dxepctl_reg;
uint32_t dxepctl;
dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
dxepctl = sys_read32(dxepctl_reg);
if (dxepctl & USB_DWC2_DEPCTL_NAKSTS) {
/* Endpoint already sends forced NAKs. STALL if necessary. */
if (stall) {
dxepctl |= USB_DWC2_DEPCTL_STALL;
sys_write32(dxepctl, dxepctl_reg);
}
return;
}
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
mem_addr_t dctl_reg, gintsts_reg, doepint_reg;
uint32_t dctl;
dctl_reg = (mem_addr_t)&base->dctl;
gintsts_reg = (mem_addr_t)&base->gintsts;
doepint_reg = (mem_addr_t)&base->out_ep[ep_idx].doepint;
dctl = sys_read32(dctl_reg);
if (sys_read32(gintsts_reg) & USB_DWC2_GINTSTS_GOUTNAKEFF) {
LOG_ERR("GOUTNAKEFF already active");
} else {
dctl |= USB_DWC2_DCTL_SGOUTNAK;
sys_write32(dctl, dctl_reg);
dctl &= ~USB_DWC2_DCTL_SGOUTNAK;
}
dwc2_wait_for_bit(gintsts_reg, USB_DWC2_GINTSTS_GOUTNAKEFF);
/* The application cannot disable control OUT endpoint 0. */
if (ep_idx != 0) {
dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS;
}
if (stall) {
/* For OUT endpoints STALL is set instead of SNAK */
dxepctl |= USB_DWC2_DEPCTL_STALL;
} else {
dxepctl |= USB_DWC2_DEPCTL_SNAK;
}
sys_write32(dxepctl, dxepctl_reg);
if (ep_idx != 0) {
dwc2_wait_for_bit(doepint_reg, USB_DWC2_DOEPINT_EPDISBLD);
}
/* Clear Endpoint Disabled interrupt */
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, doepint_reg);
dctl |= USB_DWC2_DCTL_CGOUTNAK;
sys_write32(dctl, dctl_reg);
} else {
mem_addr_t diepint_reg;
diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint;
dxepctl |= USB_DWC2_DEPCTL_SNAK;
if (stall) {
/* For IN endpoints STALL is set in addition to SNAK */
dxepctl |= USB_DWC2_DEPCTL_STALL;
}
sys_write32(dxepctl, dxepctl_reg);
dwc2_wait_for_bit(diepint_reg, USB_DWC2_DIEPINT_INEPNAKEFF);
dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS;
sys_write32(dxepctl, dxepctl_reg);
dwc2_wait_for_bit(diepint_reg, USB_DWC2_DIEPINT_EPDISBLD);
/* Clear Endpoint Disabled interrupt */
sys_write32(USB_DWC2_DIEPINT_EPDISBLD, diepint_reg);
/* TODO: Read DIEPTSIZn here? Programming Guide suggest it to
* let application know how many bytes of interrupted transfer
* were transferred to the host.
*/
dwc2_flush_tx_fifo(dev, ep_idx);
}
udc_ep_set_busy(dev, cfg->addr, false);
}
/* Deactivated endpoint means that there will be a bus timeout when the host
* tries to access the endpoint.
*/
static int udc_dwc2_ep_deactivate(const struct device *dev,
struct udc_ep_config *const cfg)
{
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
mem_addr_t dxepctl_reg;
uint32_t dxepctl;
dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
dxepctl = sys_read32(dxepctl_reg);
if (dxepctl & USB_DWC2_DEPCTL_USBACTEP) {
LOG_DBG("Disable ep 0x%02x DxEPCTL%u %x",
cfg->addr, ep_idx, dxepctl);
udc_dwc2_ep_disable(dev, cfg, false);
dxepctl = sys_read32(dxepctl_reg);
dxepctl &= ~USB_DWC2_DEPCTL_USBACTEP;
} else {
LOG_WRN("ep 0x%02x is not active DxEPCTL%u %x",
cfg->addr, ep_idx, dxepctl);
}
if (USB_EP_DIR_IS_IN(cfg->addr) && cfg->mps != 0U && ep_idx != 0U) {
dwc2_unset_dedicated_fifo(dev, cfg, &dxepctl);
}
sys_write32(dxepctl, dxepctl_reg);
dwc2_set_epint(dev, cfg, false);
return 0;
}
static int udc_dwc2_ep_set_halt(const struct device *dev,
struct udc_ep_config *const cfg)
{
uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr);
udc_dwc2_ep_disable(dev, cfg, true);
LOG_DBG("Set halt ep 0x%02x", cfg->addr);
if (ep_idx != 0) {
cfg->stat.halted = true;
}
return 0;
}
static int udc_dwc2_ep_clear_halt(const struct device *dev,
struct udc_ep_config *const cfg)
{
mem_addr_t dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
uint32_t dxepctl;
struct dwc2_drv_event evt = {
.ep = cfg->addr,
.type = DWC2_DRV_EVT_XFER,
};
dxepctl = sys_read32(dxepctl_reg);
dxepctl &= ~USB_DWC2_DEPCTL_STALL;
dxepctl |= USB_DWC2_DEPCTL_SETD0PID;
sys_write32(dxepctl, dxepctl_reg);
LOG_DBG("Clear halt ep 0x%02x", cfg->addr);
cfg->stat.halted = false;
/* Resume queued transfers if any */
if (udc_buf_peek(dev, cfg->addr)) {
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
return 0;
}
static int udc_dwc2_ep_enqueue(const struct device *dev,
struct udc_ep_config *const cfg,
struct net_buf *const buf)
{
struct dwc2_drv_event evt = {
.ep = cfg->addr,
.type = DWC2_DRV_EVT_XFER,
};
LOG_DBG("%p enqueue %x %p", dev, cfg->addr, buf);
udc_buf_put(cfg, buf);
if (!cfg->stat.halted) {
k_msgq_put(&drv_msgq, &evt, K_NO_WAIT);
}
return 0;
}
static int udc_dwc2_ep_dequeue(const struct device *dev,
struct udc_ep_config *const cfg)
{
struct net_buf *buf;
udc_dwc2_ep_disable(dev, cfg, false);
buf = udc_buf_get_all(dev, cfg->addr);
if (buf) {
udc_submit_ep_event(dev, buf, -ECONNABORTED);
}
udc_ep_set_busy(dev, cfg->addr, false);
LOG_DBG("dequeue ep 0x%02x", cfg->addr);
return 0;
}
static int udc_dwc2_set_address(const struct device *dev, const uint8_t addr)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg;
if (addr > (USB_DWC2_DCFG_DEVADDR_MASK >> USB_DWC2_DCFG_DEVADDR_POS)) {
return -EINVAL;
}
sys_clear_bits(dcfg_reg, USB_DWC2_DCFG_DEVADDR_MASK);
sys_set_bits(dcfg_reg, usb_dwc2_set_dcfg_devaddr(addr));
LOG_DBG("Set new address %u for %p", addr, dev);
return 0;
}
static int udc_dwc2_test_mode(const struct device *dev,
const uint8_t mode, const bool dryrun)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t dctl_reg = (mem_addr_t)&base->dctl;
uint32_t tstctl;
if (mode == 0U || mode > USB_DWC2_DCTL_TSTCTL_TESTFE) {
return -EINVAL;
}
tstctl = usb_dwc2_get_dctl_tstctl(sys_read32(dctl_reg));
if (tstctl != USB_DWC2_DCTL_TSTCTL_DISABLED) {
return -EALREADY;
}
if (dryrun) {
LOG_DBG("Test Mode %u supported", mode);
return 0;
}
sys_set_bits(dctl_reg, usb_dwc2_set_dctl_tstctl(mode));
LOG_DBG("Enable Test Mode %u", mode);
return 0;
}
static int udc_dwc2_host_wakeup(const struct device *dev)
{
LOG_DBG("Remote wakeup from %p", dev);
return -ENOTSUP;
}
/* Return actual USB device speed */
static enum udc_bus_speed udc_dwc2_device_speed(const struct device *dev)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
switch (priv->enumspd) {
case USB_DWC2_DSTS_ENUMSPD_HS3060:
return UDC_BUS_SPEED_HS;
case USB_DWC2_DSTS_ENUMSPD_LS6:
__ASSERT(false, "Low speed mode not supported");
__fallthrough;
case USB_DWC2_DSTS_ENUMSPD_FS48:
__fallthrough;
case USB_DWC2_DSTS_ENUMSPD_FS3060:
__fallthrough;
default:
return UDC_BUS_SPEED_FS;
}
}
static int dwc2_core_soft_reset(const struct device *dev)
{
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t grstctl_reg = (mem_addr_t)&base->grstctl;
const unsigned int csr_timeout_us = 10000UL;
uint32_t cnt = 0UL;
/* Check AHB master idle state */
while (!(sys_read32(grstctl_reg) & USB_DWC2_GRSTCTL_AHBIDLE)) {
k_busy_wait(1);
if (++cnt > csr_timeout_us) {
LOG_ERR("Wait for AHB idle timeout, GRSTCTL 0x%08x",
sys_read32(grstctl_reg));
return -EIO;
}
}
/* Apply Core Soft Reset */
sys_write32(USB_DWC2_GRSTCTL_CSFTRST, grstctl_reg);
cnt = 0UL;
do {
if (++cnt > csr_timeout_us) {
LOG_ERR("Wait for CSR done timeout, GRSTCTL 0x%08x",
sys_read32(grstctl_reg));
return -EIO;
}
k_busy_wait(1);
} while (sys_read32(grstctl_reg) & USB_DWC2_GRSTCTL_CSFTRST &&
!(sys_read32(grstctl_reg) & USB_DWC2_GRSTCTL_CSFTRSTDONE));
sys_clear_bits(grstctl_reg, USB_DWC2_GRSTCTL_CSFTRST | USB_DWC2_GRSTCTL_CSFTRSTDONE);
return 0;
}
static int udc_dwc2_init_controller(const struct device *dev)
{
const struct udc_dwc2_config *const config = dev->config;
struct udc_dwc2_data *const priv = udc_get_private(dev);
struct usb_dwc2_reg *const base = config->base;
mem_addr_t gusbcfg_reg = (mem_addr_t)&base->gusbcfg;
mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg;
uint32_t gusbcfg;
uint32_t ghwcfg2;
uint32_t ghwcfg3;
uint32_t ghwcfg4;
int ret;
ret = dwc2_core_soft_reset(dev);
if (ret) {
return ret;
}
priv->ghwcfg1 = sys_read32((mem_addr_t)&base->ghwcfg1);
ghwcfg2 = sys_read32((mem_addr_t)&base->ghwcfg2);
ghwcfg3 = sys_read32((mem_addr_t)&base->ghwcfg3);
ghwcfg4 = sys_read32((mem_addr_t)&base->ghwcfg4);
if (!(ghwcfg4 & USB_DWC2_GHWCFG4_DEDFIFOMODE)) {
LOG_ERR("Only dedicated TX FIFO mode is supported");
return -ENOTSUP;
}
/*
* Force device mode as we do no support role changes.
* Wait 25ms for the change to take effect.
*/
gusbcfg = USB_DWC2_GUSBCFG_FORCEDEVMODE;
sys_write32(gusbcfg, gusbcfg_reg);
k_msleep(25);
if (ghwcfg2 & USB_DWC2_GHWCFG2_DYNFIFOSIZING) {
LOG_DBG("Dynamic FIFO Sizing is enabled");
priv->dynfifosizing = true;
}
/* Get the number or endpoints and IN endpoints we can use later */
priv->numdeveps = usb_dwc2_get_ghwcfg2_numdeveps(ghwcfg2) + 1U;
priv->ineps = usb_dwc2_get_ghwcfg4_ineps(ghwcfg4) + 1U;
LOG_DBG("Number of endpoints (NUMDEVEPS + 1) %u", priv->numdeveps);
LOG_DBG("Number of IN endpoints (INEPS + 1) %u", priv->ineps);
LOG_DBG("Number of periodic IN endpoints (NUMDEVPERIOEPS) %u",
usb_dwc2_get_ghwcfg4_numdevperioeps(ghwcfg4));
LOG_DBG("Number of additional control endpoints (NUMCTLEPS) %u",
usb_dwc2_get_ghwcfg4_numctleps(ghwcfg4));
LOG_DBG("OTG architecture (OTGARCH) %u, mode (OTGMODE) %u",
usb_dwc2_get_ghwcfg2_otgarch(ghwcfg2),
usb_dwc2_get_ghwcfg2_otgmode(ghwcfg2));
priv->dfifodepth = usb_dwc2_get_ghwcfg3_dfifodepth(ghwcfg3);
LOG_DBG("DFIFO depth (DFIFODEPTH) %u bytes", priv->dfifodepth * 4);
priv->max_pktcnt = GHWCFG3_PKTCOUNT(usb_dwc2_get_ghwcfg3_pktsizewidth(ghwcfg3));
priv->max_xfersize = GHWCFG3_XFERSIZE(usb_dwc2_get_ghwcfg3_xfersizewidth(ghwcfg3));
LOG_DBG("Max packet count %u, Max transfer size %u",
priv->max_pktcnt, priv->max_xfersize);
LOG_DBG("Vendor Control interface support enabled: %s",
(ghwcfg3 & USB_DWC2_GHWCFG3_VNDCTLSUPT) ? "true" : "false");
LOG_DBG("PHY interface type: FSPHYTYPE %u, HSPHYTYPE %u, DATAWIDTH %u",
usb_dwc2_get_ghwcfg2_fsphytype(ghwcfg2),
usb_dwc2_get_ghwcfg2_hsphytype(ghwcfg2),
usb_dwc2_get_ghwcfg4_phydatawidth(ghwcfg4));
LOG_DBG("LPM mode is %s",
(ghwcfg3 & USB_DWC2_GHWCFG3_LPMMODE) ? "enabled" : "disabled");
/* Configure PHY and device speed */
switch (usb_dwc2_get_ghwcfg2_hsphytype(ghwcfg2)) {
case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUSULPI:
__fallthrough;
case USB_DWC2_GHWCFG2_HSPHYTYPE_ULPI:
gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 |
USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_ULPI;
sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBHS20);
break;
case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUS:
gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 |
USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_UTMI;
sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBHS20);
break;
case USB_DWC2_GHWCFG2_HSPHYTYPE_NO_HS:
__fallthrough;
default:
if (usb_dwc2_get_ghwcfg2_fsphytype(ghwcfg2) !=
USB_DWC2_GHWCFG2_FSPHYTYPE_NO_FS) {
gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB11;
}
sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBFS1148);
}
if (usb_dwc2_get_ghwcfg4_phydatawidth(ghwcfg4)) {
gusbcfg |= USB_DWC2_GUSBCFG_PHYIF_16_BIT;
}
/* Update PHY configuration */
sys_set_bits(gusbcfg_reg, gusbcfg);
priv->outeps = 0U;
for (uint8_t i = 0U; i < priv->numdeveps; i++) {
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i);
if (epdir == USB_DWC2_GHWCFG1_EPDIR_OUT ||
epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) {
mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, i);
sys_write32(USB_DWC2_DEPCTL_SNAK, doepctl_reg);
priv->outeps++;
}
}
LOG_DBG("Number of OUT endpoints %u", priv->outeps);
if (priv->dynfifosizing) {
priv->grxfsiz = UDC_DWC2_GRXFSIZ_DEFAULT + priv->outeps * 2U;
sys_write32(usb_dwc2_set_grxfsiz(priv->grxfsiz), (mem_addr_t)&base->grxfsiz);
}
LOG_DBG("RX FIFO size %u bytes", priv->grxfsiz * 4);
for (uint8_t i = 1U; i < priv->ineps; i++) {
LOG_DBG("TX FIFO%u depth %u addr %u",
i, dwc2_get_txfdep(dev, i), dwc2_get_txfaddr(dev, i));
}
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT,
USB_EP_TYPE_CONTROL, 64, 0)) {
LOG_ERR("Failed to enable control endpoint");
return -EIO;
}
if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN,
USB_EP_TYPE_CONTROL, 64, 0)) {
LOG_ERR("Failed to enable control endpoint");
return -EIO;
}
/* Unmask interrupts */
sys_write32(USB_DWC2_GINTSTS_OEPINT | USB_DWC2_GINTSTS_IEPINT |
USB_DWC2_GINTSTS_ENUMDONE | USB_DWC2_GINTSTS_USBRST |
USB_DWC2_GINTSTS_WKUPINT | USB_DWC2_GINTSTS_USBSUSP |
USB_DWC2_GINTSTS_SOF,
(mem_addr_t)&base->gintmsk);
return 0;
}
static int udc_dwc2_enable(const struct device *dev)
{
const struct udc_dwc2_config *const config = dev->config;
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
int err;
err = dwc2_quirk_pre_enable(dev);
if (err) {
LOG_ERR("Quirk pre enable failed %d", err);
return err;
}
err = udc_dwc2_init_controller(dev);
if (err) {
return err;
}
err = dwc2_quirk_post_enable(dev);
if (err) {
LOG_ERR("Quirk post enable failed %d", err);
return err;
}
/* Enable global interrupt */
sys_set_bits((mem_addr_t)&base->gahbcfg, USB_DWC2_GAHBCFG_GLBINTRMASK);
config->irq_enable_func(dev);
/* Disable soft disconnect */
sys_clear_bits((mem_addr_t)&base->dctl, USB_DWC2_DCTL_SFTDISCON);
LOG_DBG("Enable device %p", base);
return 0;
}
static int udc_dwc2_disable(const struct device *dev)
{
const struct udc_dwc2_config *const config = dev->config;
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t dctl_reg = (mem_addr_t)&base->dctl;
int err;
/* Enable soft disconnect */
sys_set_bits(dctl_reg, USB_DWC2_DCTL_SFTDISCON);
LOG_DBG("Disable device %p", dev);
if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) {
LOG_DBG("Failed to disable control endpoint");
return -EIO;
}
if (udc_ep_disable_internal(dev, USB_CONTROL_EP_IN)) {
LOG_DBG("Failed to disable control endpoint");
return -EIO;
}
config->irq_disable_func(dev);
sys_clear_bits((mem_addr_t)&base->gahbcfg, USB_DWC2_GAHBCFG_GLBINTRMASK);
err = dwc2_quirk_disable(dev);
if (err) {
LOG_ERR("Quirk disable failed %d", err);
return err;
}
return 0;
}
static int udc_dwc2_init(const struct device *dev)
{
int ret;
ret = dwc2_quirk_init(dev);
if (ret) {
LOG_ERR("Quirk init failed %d", ret);
return ret;
}
return dwc2_init_pinctrl(dev);
}
static int udc_dwc2_shutdown(const struct device *dev)
{
int ret;
ret = dwc2_quirk_shutdown(dev);
if (ret) {
LOG_ERR("Quirk shutdown failed %d", ret);
return ret;
}
return 0;
}
static int dwc2_driver_preinit(const struct device *dev)
{
const struct udc_dwc2_config *config = dev->config;
struct udc_data *data = dev->data;
uint16_t mps = 1023;
uint32_t numdeveps;
uint32_t ineps;
int err;
k_mutex_init(&data->mutex);
data->caps.rwup = true;
data->caps.addr_before_status = true;
data->caps.mps0 = UDC_MPS0_64;
(void)dwc2_quirk_caps(dev);
if (data->caps.hs) {
mps = 1024;
}
/*
* At this point, we cannot or do not want to access the hardware
* registers to get GHWCFGn values. For now, we will use devicetree to
* get GHWCFGn values and use them to determine the number and type of
* configured endpoints in the hardware. This can be considered a
* workaround, and we may change the upper layer internals to avoid it
* in the future.
*/
ineps = usb_dwc2_get_ghwcfg4_ineps(config->ghwcfg4) + 1U;
numdeveps = usb_dwc2_get_ghwcfg2_numdeveps(config->ghwcfg2) + 1U;
LOG_DBG("Number of endpoints (NUMDEVEPS + 1) %u", numdeveps);
LOG_DBG("Number of IN endpoints (INEPS + 1) %u", ineps);
for (uint32_t i = 0, n = 0; i < numdeveps; i++) {
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(config->ghwcfg1, i);
if (epdir != USB_DWC2_GHWCFG1_EPDIR_OUT &&
epdir != USB_DWC2_GHWCFG1_EPDIR_BDIR) {
continue;
}
if (i == 0) {
config->ep_cfg_out[n].caps.control = 1;
config->ep_cfg_out[n].caps.mps = 64;
} else {
config->ep_cfg_out[n].caps.bulk = 1;
config->ep_cfg_out[n].caps.interrupt = 1;
config->ep_cfg_out[n].caps.iso = 1;
config->ep_cfg_out[n].caps.mps = mps;
}
config->ep_cfg_out[n].caps.out = 1;
config->ep_cfg_out[n].addr = USB_EP_DIR_OUT | i;
LOG_DBG("Register ep 0x%02x (%u)", i, n);
err = udc_register_ep(dev, &config->ep_cfg_out[n]);
if (err != 0) {
LOG_ERR("Failed to register endpoint");
return err;
}
n++;
/* Also check the number of desired OUT endpoints in devicetree. */
if (n >= config->num_out_eps) {
break;
}
}
for (uint32_t i = 0, n = 0; i < numdeveps; i++) {
uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(config->ghwcfg1, i);
if (epdir != USB_DWC2_GHWCFG1_EPDIR_IN &&
epdir != USB_DWC2_GHWCFG1_EPDIR_BDIR) {
continue;
}
if (i == 0) {
config->ep_cfg_in[n].caps.control = 1;
config->ep_cfg_in[n].caps.mps = 64;
} else {
config->ep_cfg_in[n].caps.bulk = 1;
config->ep_cfg_in[n].caps.interrupt = 1;
config->ep_cfg_in[n].caps.iso = 1;
config->ep_cfg_in[n].caps.mps = mps;
}
config->ep_cfg_in[n].caps.in = 1;
config->ep_cfg_in[n].addr = USB_EP_DIR_IN | i;
LOG_DBG("Register ep 0x%02x (%u)", USB_EP_DIR_IN | i, n);
err = udc_register_ep(dev, &config->ep_cfg_in[n]);
if (err != 0) {
LOG_ERR("Failed to register endpoint");
return err;
}
n++;
/* Also check the number of desired IN endpoints in devicetree. */
if (n >= MIN(ineps, config->num_in_eps)) {
break;
}
}
config->make_thread(dev);
return 0;
}
static int udc_dwc2_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
}
static int udc_dwc2_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
}
static const struct udc_api udc_dwc2_api = {
.lock = udc_dwc2_lock,
.unlock = udc_dwc2_unlock,
.device_speed = udc_dwc2_device_speed,
.init = udc_dwc2_init,
.enable = udc_dwc2_enable,
.disable = udc_dwc2_disable,
.shutdown = udc_dwc2_shutdown,
.set_address = udc_dwc2_set_address,
.test_mode = udc_dwc2_test_mode,
.host_wakeup = udc_dwc2_host_wakeup,
.ep_enable = udc_dwc2_ep_activate,
.ep_disable = udc_dwc2_ep_deactivate,
.ep_set_halt = udc_dwc2_ep_set_halt,
.ep_clear_halt = udc_dwc2_ep_clear_halt,
.ep_enqueue = udc_dwc2_ep_enqueue,
.ep_dequeue = udc_dwc2_ep_dequeue,
};
#define DT_DRV_COMPAT snps_dwc2
#define UDC_DWC2_VENDOR_QUIRK_GET(n) \
COND_CODE_1(DT_NODE_VENDOR_HAS_IDX(DT_DRV_INST(n), 1), \
(&dwc2_vendor_quirks_##n), \
(NULL))
#define UDC_DWC2_DT_INST_REG_ADDR(n) \
COND_CODE_1(DT_NUM_REGS(DT_DRV_INST(n)), (DT_INST_REG_ADDR(n)), \
(DT_INST_REG_ADDR_BY_NAME(n, core)))
#define UDC_DWC2_PINCTRL_DT_INST_DEFINE(n) \
COND_CODE_1(DT_INST_PINCTRL_HAS_NAME(n, default), \
(PINCTRL_DT_INST_DEFINE(n)), ())
#define UDC_DWC2_PINCTRL_DT_INST_DEV_CONFIG_GET(n) \
COND_CODE_1(DT_INST_PINCTRL_HAS_NAME(n, default), \
((void *)PINCTRL_DT_INST_DEV_CONFIG_GET(n)), (NULL))
#define UDC_DWC2_IRQ_FLAGS_TYPE0(n) 0
#define UDC_DWC2_IRQ_FLAGS_TYPE1(n) DT_INST_IRQ(n, type)
#define DW_IRQ_FLAGS(n) \
_CONCAT(UDC_DWC2_IRQ_FLAGS_TYPE, DT_INST_IRQ_HAS_CELL(n, type))(n)
/*
* A UDC driver should always be implemented as a multi-instance
* driver, even if your platform does not require it.
*/
#define UDC_DWC2_DEVICE_DEFINE(n) \
UDC_DWC2_PINCTRL_DT_INST_DEFINE(n); \
\
K_THREAD_STACK_DEFINE(udc_dwc2_stack_##n, CONFIG_UDC_DWC2_STACK_SIZE); \
\
static void udc_dwc2_thread_##n(void *dev, void *arg1, void *arg2) \
{ \
while (true) { \
dwc2_thread_handler(dev); \
} \
} \
\
static void udc_dwc2_make_thread_##n(const struct device *dev) \
{ \
struct udc_dwc2_data *priv = udc_get_private(dev); \
\
k_thread_create(&priv->thread_data, \
udc_dwc2_stack_##n, \
K_THREAD_STACK_SIZEOF(udc_dwc2_stack_##n), \
udc_dwc2_thread_##n, \
(void *)dev, NULL, NULL, \
K_PRIO_COOP(CONFIG_UDC_DWC2_THREAD_PRIORITY), \
K_ESSENTIAL, \
K_NO_WAIT); \
k_thread_name_set(&priv->thread_data, dev->name); \
} \
\
static void udc_dwc2_irq_enable_func_##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), \
DT_INST_IRQ(n, priority), \
udc_dwc2_isr_handler, \
DEVICE_DT_INST_GET(n), \
DW_IRQ_FLAGS(n)); \
\
irq_enable(DT_INST_IRQN(n)); \
} \
\
static void udc_dwc2_irq_disable_func_##n(const struct device *dev) \
{ \
irq_disable(DT_INST_IRQN(n)); \
} \
\
static struct udc_ep_config ep_cfg_out[DT_INST_PROP(n, num_out_eps)]; \
static struct udc_ep_config ep_cfg_in[DT_INST_PROP(n, num_in_eps)]; \
\
static const struct udc_dwc2_config udc_dwc2_config_##n = { \
.num_out_eps = DT_INST_PROP(n, num_out_eps), \
.num_in_eps = DT_INST_PROP(n, num_in_eps), \
.ep_cfg_in = ep_cfg_in, \
.ep_cfg_out = ep_cfg_out, \
.make_thread = udc_dwc2_make_thread_##n, \
.base = (struct usb_dwc2_reg *)UDC_DWC2_DT_INST_REG_ADDR(n), \
.pcfg = UDC_DWC2_PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.irq_enable_func = udc_dwc2_irq_enable_func_##n, \
.irq_disable_func = udc_dwc2_irq_disable_func_##n, \
.quirks = UDC_DWC2_VENDOR_QUIRK_GET(n), \
.ghwcfg1 = DT_INST_PROP(n, ghwcfg1), \
.ghwcfg2 = DT_INST_PROP(n, ghwcfg2), \
.ghwcfg4 = DT_INST_PROP(n, ghwcfg4), \
}; \
\
static struct udc_dwc2_data udc_priv_##n = { \
}; \
\
static struct udc_data udc_data_##n = { \
.mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \
.priv = &udc_priv_##n, \
}; \
\
DEVICE_DT_INST_DEFINE(n, dwc2_driver_preinit, NULL, \
&udc_data_##n, &udc_dwc2_config_##n, \
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&udc_dwc2_api);
DT_INST_FOREACH_STATUS_OKAY(UDC_DWC2_DEVICE_DEFINE)