Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_ambiq.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,14 +594,14 @@ static int udc_ambiq_shutdown(const struct device *dev)
return 0;
}

static int udc_ambiq_lock(const struct device *dev)
static void udc_ambiq_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int udc_ambiq_unlock(const struct device *dev)
static void udc_ambiq_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static void ambiq_handle_evt_setup(const struct device *dev)
Expand Down
125 changes: 103 additions & 22 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,12 @@ struct udc_dwc2_data {
struct k_event xfer_finished;
struct dwc2_reg_backup backup;
uint32_t ghwcfg1;
uint32_t txf_set;
uint32_t max_xfersize;
uint32_t max_pktcnt;
uint32_t tx_len[16];
uint32_t rx_siz[16];
uint16_t txf_set;
uint16_t pending_tx_flush;
uint16_t dfifodepth;
uint16_t rxfifo_depth;
uint16_t max_txfifo_depth[16];
Expand Down Expand Up @@ -668,7 +669,32 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf,

/* Clear NAK and set endpoint enable */
doepctl = sys_read32(doepctl_reg);
doepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK;
doepctl |= USB_DWC2_DEPCTL_EPENA;
if (cfg->addr == USB_CONTROL_EP_OUT) {
struct udc_data *data = dev->data;

/* During OUT Data Stage every packet has to have CNAK set.
* In Buffer DMA mode the OUT endpoint is armed during IN Data
* Stage to accept either Status stage or new SETUP token. The
* Status stage (premature or not) can only be received if CNAK
* was set. In Completer mode the OUT endpoint armed during OUT
* Status stage needs CNAK.
*
* Setting CNAK in other cases opens up possibility for Buffer
* DMA controller to lock up completely if the endpoint is
* enabled (to receive SETUP data) when the host is transmitting
* subsequent control transfer OUT Data Stage packet (SETUP DATA
* is unconditionally ACKed regardless of software state).
*/
if (data->stage == CTRL_PIPE_STAGE_DATA_OUT ||
data->stage == CTRL_PIPE_STAGE_DATA_IN ||
data->stage == CTRL_PIPE_STAGE_STATUS_OUT) {
doepctl |= USB_DWC2_DEPCTL_CNAK;
}
} else {
/* Non-control endpoint, set CNAK for all transfers */
doepctl |= USB_DWC2_DEPCTL_CNAK;
}

if (dwc2_ep_is_iso(cfg)) {
xfersize = USB_MPS_TO_TPL(cfg->mps);
Expand Down Expand Up @@ -1484,6 +1510,7 @@ static int dwc2_unset_dedicated_fifo(const struct device *dev,
static void udc_dwc2_ep_disable(const struct device *dev,
struct udc_ep_config *const cfg, bool stall)
{
struct udc_dwc2_data *const priv = udc_get_private(dev);
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;
Expand All @@ -1493,6 +1520,40 @@ static void udc_dwc2_ep_disable(const struct device *dev,
dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr);
dxepctl = sys_read32(dxepctl_reg);

if (priv->hibernated) {
/* Currently USB stack calls this function while hibernated,
* for example if usbd_disable() is called. We cannot access the
* real registers when hibernated, so just modify backup values
* that will be restored on hibernation exit.
*/
if (USB_EP_DIR_IS_OUT(cfg->addr)) {
dxepctl = priv->backup.doepctl[ep_idx];

dxepctl &= ~USB_DWC2_DEPCTL_EPENA;
if (stall) {
dxepctl |= USB_DWC2_DEPCTL_STALL;
} else {
dxepctl |= USB_DWC2_DEPCTL_SNAK;
}

priv->backup.doepctl[ep_idx] = dxepctl;
} else {
dxepctl = priv->backup.diepctl[ep_idx];

dxepctl &= ~USB_DWC2_DEPCTL_EPENA;
dxepctl |= USB_DWC2_DEPCTL_SNAK;
if (stall) {
dxepctl |= USB_DWC2_DEPCTL_STALL;
}

priv->backup.diepctl[ep_idx] = dxepctl;

priv->pending_tx_flush |= BIT(usb_dwc2_get_depctl_txfnum(dxepctl));
}

return;
}

if (!is_iso && (dxepctl & USB_DWC2_DEPCTL_NAKSTS)) {
/* Endpoint already sends forced NAKs. STALL if necessary. */
if (stall) {
Expand Down Expand Up @@ -1625,15 +1686,6 @@ static int udc_dwc2_ep_deactivate(const struct device *dev,
sys_write32(dxepctl, dxepctl_reg);
dwc2_set_epint(dev, cfg, false);

if (cfg->addr == USB_CONTROL_EP_OUT) {
struct net_buf *buf = udc_buf_get_all(dev, cfg->addr);

/* Release the buffer allocated in dwc2_ctrl_feed_dout() */
if (buf) {
net_buf_unref(buf);
}
}

return 0;
}

Expand Down Expand Up @@ -1839,10 +1891,16 @@ static int dwc2_core_soft_reset(const struct device *dev)
}

k_busy_wait(1);

if (dwc2_quirk_is_phy_clk_off(dev)) {
/* Software reset won't finish without PHY clock */
return -EIO;
}
} 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);
/* CSFTRSTDONE is W1C so the write must have the bit set to clear it */
sys_clear_bits(grstctl_reg, USB_DWC2_GRSTCTL_CSFTRST);

return 0;
}
Expand Down Expand Up @@ -2141,10 +2199,9 @@ static int udc_dwc2_disable(const struct device *dev)
struct udc_dwc2_data *const priv = udc_get_private(dev);
struct usb_dwc2_reg *const base = dwc2_get_base(dev);
mem_addr_t dctl_reg = (mem_addr_t)&base->dctl;
struct net_buf *buf;
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)) {
Expand All @@ -2162,10 +2219,27 @@ static int udc_dwc2_disable(const struct device *dev)
if (priv->hibernated) {
dwc2_exit_hibernation(dev, false, true);
priv->hibernated = 0;
priv->pending_tx_flush = 0;
}

sys_clear_bits((mem_addr_t)&base->gahbcfg, USB_DWC2_GAHBCFG_GLBINTRMASK);

/* Enable soft disconnect */
sys_set_bits(dctl_reg, USB_DWC2_DCTL_SFTDISCON);

/* OUT endpoint 0 cannot be disabled by software. The buffer allocated
* in dwc2_ctrl_feed_dout() can only be freed after core reset if the
* core was in Buffer DMA mode.
*
* Soft Reset does timeout if PHY clock is not running. However, just
* triggering Soft Reset seems to be enough on shutdown clean up.
*/
dwc2_core_soft_reset(dev);
buf = udc_buf_get_all(dev, USB_CONTROL_EP_OUT);
if (buf) {
net_buf_unref(buf);
}

err = dwc2_quirk_disable(dev);
if (err) {
LOG_ERR("Quirk disable failed %d", err);
Expand Down Expand Up @@ -2316,14 +2390,16 @@ static int dwc2_driver_preinit(const struct device *dev)
return 0;
}

static int udc_dwc2_lock(const struct device *dev)
static void udc_dwc2_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
k_sched_lock();
udc_lock_internal(dev, K_FOREVER);
}

static int udc_dwc2_unlock(const struct device *dev)
static void udc_dwc2_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
k_sched_unlock();
}

static void dwc2_on_bus_reset(const struct device *dev)
Expand Down Expand Up @@ -2924,6 +3000,13 @@ static void dwc2_handle_hibernation_exit(const struct device *dev,
k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));
}
}

while (priv->pending_tx_flush) {
unsigned int fifo = find_lsb_set(priv->pending_tx_flush) - 1;

priv->pending_tx_flush &= ~BIT(fifo);
dwc2_flush_tx_fifo(dev, fifo);
}
}

static uint8_t pull_next_ep_from_bitmap(uint32_t *bitmap)
Expand Down Expand Up @@ -2968,8 +3051,7 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)

if (!priv->hibernated) {
LOG_DBG("New transfer(s) in the queue");
eps = k_event_test(&priv->xfer_new, UINT32_MAX);
k_event_clear(&priv->xfer_new, eps);
eps = k_event_clear(&priv->xfer_new, UINT32_MAX);
} else {
/* Events will be handled after hibernation exit */
eps = 0;
Expand All @@ -2991,8 +3073,7 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg)
k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED));

if (!priv->hibernated) {
eps = k_event_test(&priv->xfer_finished, UINT32_MAX);
k_event_clear(&priv->xfer_finished, eps);
eps = k_event_clear(&priv->xfer_finished, UINT32_MAX);
} else {
/* Events will be handled after hibernation exit */
eps = 0;
Expand Down
6 changes: 5 additions & 1 deletion drivers/usb/udc/udc_dwc2_vendor_quirks.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,11 @@ static inline int usbhs_enable_core(const struct device *dev)
}

wrapper->ENABLE = USBHS_ENABLE_PHY_Msk | USBHS_ENABLE_CORE_Msk;

/* Wait for PHY clock to start */
k_busy_wait(45);

/* Release DWC2 reset */
wrapper->TASKS_START = 1UL;

/* Wait for clock to start to avoid hang on too early register read */
Expand All @@ -218,7 +223,6 @@ static inline int usbhs_disable_core(const struct device *dev)
wrapper->INTENCLR = 1UL;

wrapper->ENABLE = 0UL;
wrapper->TASKS_START = 1UL;

return 0;
}
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_it82xx2.c
Original file line number Diff line number Diff line change
Expand Up @@ -1495,14 +1495,14 @@ static int it82xx2_shutdown(const struct device *dev)
return 0;
}

static int it82xx2_lock(const struct device *dev)
static void it82xx2_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int it82xx2_unlock(const struct device *dev)
static void it82xx2_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static const struct udc_api it82xx2_api = {
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_kinetis.c
Original file line number Diff line number Diff line change
Expand Up @@ -1069,14 +1069,14 @@ static int usbfsotg_shutdown(const struct device *dev)
return 0;
}

static int usbfsotg_lock(const struct device *dev)
static void usbfsotg_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int usbfsotg_unlock(const struct device *dev)
static void usbfsotg_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static int usbfsotg_driver_preinit(const struct device *dev)
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_mcux_ehci.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ struct udc_mcux_event {
K_MEM_SLAB_DEFINE(udc_event_slab, sizeof(struct udc_mcux_event),
CONFIG_UDC_NXP_EVENT_COUNT, sizeof(void *));

static int udc_mcux_lock(const struct device *dev)
static void udc_mcux_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int udc_mcux_unlock(const struct device *dev)
static void udc_mcux_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command,
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_mcux_ip3511.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ struct udc_mcux_event {
K_MEM_SLAB_DEFINE(udc_event_slab, sizeof(struct udc_mcux_event),
CONFIG_UDC_NXP_EVENT_COUNT, sizeof(void *));

static int udc_mcux_lock(const struct device *dev)
static void udc_mcux_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int udc_mcux_unlock(const struct device *dev)
static void udc_mcux_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command,
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_nrf.c
Original file line number Diff line number Diff line change
Expand Up @@ -909,14 +909,14 @@ static int udc_nrf_driver_init(const struct device *dev)
return 0;
}

static int udc_nrf_lock(const struct device *dev)
static void udc_nrf_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int udc_nrf_unlock(const struct device *dev)
static void udc_nrf_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static const struct udc_nrf_config udc_nrf_cfg = {
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_numaker.c
Original file line number Diff line number Diff line change
Expand Up @@ -1622,14 +1622,14 @@ static int udc_numaker_shutdown(const struct device *dev)
return 0;
}

static int udc_numaker_lock(const struct device *dev)
static void udc_numaker_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int udc_numaker_unlock(const struct device *dev)
static void udc_numaker_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static int udc_numaker_driver_preinit(const struct device *dev)
Expand Down
8 changes: 4 additions & 4 deletions drivers/usb/udc/udc_renesas_ra.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,14 +641,14 @@ static int udc_renesas_ra_driver_preinit(const struct device *dev)
return 0;
}

static int udc_renesas_ra_lock(const struct device *dev)
static void udc_renesas_ra_lock(const struct device *dev)
{
return udc_lock_internal(dev, K_FOREVER);
udc_lock_internal(dev, K_FOREVER);
}

static int udc_renesas_ra_unlock(const struct device *dev)
static void udc_renesas_ra_unlock(const struct device *dev)
{
return udc_unlock_internal(dev);
udc_unlock_internal(dev);
}

static const struct udc_api udc_renesas_ra_api = {
Expand Down
Loading