diff --git a/drivers/clock_control/clock_control_nrf.c b/drivers/clock_control/clock_control_nrf.c index d148cd49e4e..3d5d0951d32 100644 --- a/drivers/clock_control/clock_control_nrf.c +++ b/drivers/clock_control/clock_control_nrf.c @@ -329,6 +329,18 @@ static void hfclk_stop(void) nrfx_clock_hfclk_stop(); } +#if NRF_CLOCK_HAS_HFCLK24M +static void hfclk24m_start(void) +{ + nrfx_clock_start(NRF_CLOCK_DOMAIN_HFCLK24M); +} + +static void hfclk24m_stop(void) +{ + nrfx_clock_stop(NRF_CLOCK_DOMAIN_HFCLK24M); +} +#endif + #if NRF_CLOCK_HAS_HFCLK192M static void hfclk192m_start(void) { @@ -714,7 +726,11 @@ static void clock_event_handler(nrfx_clock_evt_type_t event) break; } #endif - +#if NRF_CLOCK_HAS_HFCLK24M + case NRFX_CLOCK_EVT_HFCLK24M_STARTED: + clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_HFCLK24M); + break; +#endif #if NRF_CLOCK_HAS_HFCLK192M case NRFX_CLOCK_EVT_HFCLK192M_STARTED: clkstarted_handle(dev, CLOCK_CONTROL_NRF_TYPE_HFCLK192M); @@ -843,6 +859,13 @@ static const struct nrf_clock_control_config config = { .stop = lfclk_stop, IF_ENABLED(CONFIG_LOG, (.name = "lfclk",)) }, +#if NRF_CLOCK_HAS_HFCLK24M + [CLOCK_CONTROL_NRF_TYPE_HFCLK24M] = { + .start = hfclk24m_start, + .stop = hfclk24m_stop, + IF_ENABLED(CONFIG_LOG, (.name = "hfclk24m",)) + }, +#endif #if NRF_CLOCK_HAS_HFCLK192M [CLOCK_CONTROL_NRF_TYPE_HFCLK192M] = { .start = hfclk192m_start, diff --git a/drivers/usb/udc/Kconfig b/drivers/usb/udc/Kconfig index b6c49e6b3e7..b2c64634315 100644 --- a/drivers/usb/udc/Kconfig +++ b/drivers/usb/udc/Kconfig @@ -42,6 +42,12 @@ config UDC_BUF_FORCE_NOCACHE Place the buffer pools in the nocache memory region if the driver cannot handle buffers in cached memory. +config UDC_ENABLE_SOF + bool "SOF interrupt processing" + help + Enabled SoF interrupts can cause a very high CPU load on high-speed + controllers because the interrupt rate would be 125 µs. + config UDC_WORKQUEUE bool "Use a dedicate work queue for UDC drivers" help diff --git a/drivers/usb/udc/udc_ambiq.c b/drivers/usb/udc/udc_ambiq.c index 6e5f2603edd..9b2fdc0addb 100644 --- a/drivers/usb/udc/udc_ambiq.c +++ b/drivers/usb/udc/udc_ambiq.c @@ -156,8 +156,9 @@ static void udc_ambiq_evt_callback(const struct device *dev, am_hal_usb_dev_even case AM_HAL_USB_DEV_EVT_BUS_RESET: /* enable usb bus interrupts */ am_hal_usb_intr_usb_enable(priv->usb_handle, - USB_CFG2_SOFE_Msk | USB_CFG2_ResumeE_Msk | - USB_CFG2_SuspendE_Msk | USB_CFG2_ResetE_Msk); + IF_ENABLED(CONFIG_UDC_ENABLE_SOF, (USB_CFG2_SOFE_Msk |)) + USB_CFG2_ResumeE_Msk | + USB_CFG2_SuspendE_Msk | USB_CFG2_ResetE_Msk); /* init the endpoint */ am_hal_usb_ep_init(priv->usb_handle, 0, 0, EP0_MPS); /* Set USB device speed to HAL */ @@ -174,7 +175,7 @@ static void udc_ambiq_evt_callback(const struct device *dev, am_hal_usb_dev_even udc_submit_event(dev, UDC_EVT_RESUME, 0); break; case AM_HAL_USB_DEV_EVT_SOF: - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); break; case AM_HAL_USB_DEV_EVT_SUSPEND: /* Handle USB Suspend event, then set device state to suspended */ diff --git a/drivers/usb/udc/udc_common.h b/drivers/usb/udc/udc_common.h index 8cad6b6dd1c..ed6712e95ae 100644 --- a/drivers/usb/udc/udc_common.h +++ b/drivers/usb/udc/udc_common.h @@ -156,6 +156,29 @@ int udc_submit_event(const struct device *dev, int udc_submit_ep_event(const struct device *dev, struct net_buf *const buf, const int err); + +/** + * @brief Helper function to send UDC SOF event to a higher level. + * + * Type of this event is hardcoded to UDC_EVT_SOF. + * + * @param[in] dev Pointer to device struct of the driver instance + */ +#if defined(CONFIG_UDC_ENABLE_SOF) +static inline void udc_submit_sof_event(const struct device *dev) +{ + struct udc_data *data = dev->data; + struct udc_event drv_evt = { + .type = UDC_EVT_SOF, + .dev = dev, + }; + + (void)data->event_cb(dev, &drv_evt); +} +#else +#define udc_submit_sof_event(dev) ARG_UNUSED(dev) +#endif + /** * @brief Helper function to enable endpoint. * diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 269dfd56ee5..fca8f607ec7 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#define DT_DRV_COMPAT snps_dwc2 + #include "udc_common.h" #include "udc_dwc2.h" @@ -22,7 +24,6 @@ #include LOG_MODULE_REGISTER(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); -#include "udc_dwc2_vendor_quirks.h" enum dwc2_drv_event_type { /* USB connection speed determined after bus reset */ @@ -101,9 +102,12 @@ struct dwc2_reg_backup { /* Driver private data per instance */ struct udc_dwc2_data { + struct k_spinlock lock; struct k_thread thread_data; /* Main events the driver thread waits for */ struct k_event drv_evt; + /* Endpoint is considered disabled when there is no active transfer */ + struct k_event ep_disabled; /* Transfer triggers (IN on bits 0-15, OUT on bits 16-31) */ atomic_t xfer_new; /* Finished transactions (IN on bits 0-15, OUT on bits 16-31) */ @@ -114,6 +118,11 @@ struct udc_dwc2_data { uint32_t max_pktcnt; uint32_t tx_len[16]; uint32_t rx_siz[16]; + /* Isochronous endpoint enabled (IN on bits 0-15, OUT on bits 16-31) */ + uint32_t iso_enabled; + uint16_t iso_in_rearm; + uint16_t ep_out_disable; + uint16_t ep_out_stall; uint16_t txf_set; uint16_t pending_tx_flush; uint16_t dfifodepth; @@ -130,6 +139,7 @@ struct udc_dwc2_data { unsigned int hibernated : 1; unsigned int enumdone : 1; unsigned int enumspd : 2; + unsigned int pending_dout_feed : 1; enum dwc2_suspend_type suspend_type; /* Number of endpoints including control endpoint */ uint8_t numdeveps; @@ -141,7 +151,8 @@ struct udc_dwc2_data { }; static void udc_dwc2_ep_disable(const struct device *dev, - struct udc_ep_config *const cfg, bool stall); + struct udc_ep_config *const cfg, + bool stall, bool wait); #if defined(CONFIG_PINCTRL) #include @@ -418,10 +429,28 @@ static void dwc2_ensure_setup_ready(const struct device *dev) if (dwc2_in_completer_mode(dev)) { /* In Completer mode EP0 can always receive SETUP data */ return; + } else { + struct udc_dwc2_data *const priv = udc_get_private(dev); + + /* Enable EP0 OUT only if there is no pending EP0 IN transfer + * after which the stack has to enable EP0 OUT. + */ + if (!priv->pending_dout_feed) { + dwc2_ctrl_feed_dout(dev, 8); + } } +} + +static void dwc2_clear_control_in_nak(const struct device *dev) +{ + struct usb_dwc2_reg *const base = dwc2_get_base(dev); + mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[0].diepctl; + uint32_t diepctl = sys_read32(diepctl_reg); - if (!udc_buf_peek(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN))) { - dwc2_ctrl_feed_dout(dev, 8); + if (diepctl & USB_DWC2_DEPCTL_NAKSTS) { + diepctl &= ~USB_DWC2_DEPCTL_EPENA; + diepctl |= USB_DWC2_DEPCTL_CNAK; + sys_write32(diepctl, diepctl_reg); } } @@ -461,7 +490,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, uint32_t max_xfersize, max_pktcnt, pktcnt; const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); const size_t d = sizeof(uint32_t); - unsigned int key; + k_spinlock_key_t key; uint32_t len; const bool is_periodic = dwc2_ep_is_periodic(cfg); const bool is_iso = dwc2_ep_is_iso(cfg); @@ -536,7 +565,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, priv->tx_len[ep_idx] = len; /* Lock and write to endpoint FIFO */ - key = irq_lock(); + key = k_spin_lock(&priv->lock); /* Set number of packets and transfer size */ sys_write32((is_periodic ? usb_dwc2_set_dieptsizn_mc(1 + addnl) : 0) | @@ -548,7 +577,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, /* Cannot continue unless buffer is bounced. Device will * cease to function. Is fatal error appropriate here? */ - irq_unlock(key); + k_spin_unlock(&priv->lock, key); return -ENOTSUP; } @@ -564,7 +593,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, * no fifo is assigned to inactive endpoint and therefore it is * possible that the write will corrupt other endpoint fifo. */ - irq_unlock(key); + k_spin_unlock(&priv->lock, key); return -ENOENT; } @@ -580,7 +609,11 @@ static int dwc2_tx_fifo_write(const struct device *dev, } /* Clear NAK and set endpoint enable */ - diepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK; + diepctl |= USB_DWC2_DEPCTL_EPENA; + if (cfg->addr != USB_CONTROL_EP_IN) { + /* Non-control endpoint, set CNAK for all transfers */ + diepctl |= USB_DWC2_DEPCTL_CNAK; + } sys_write32(diepctl, diepctl_reg); /* Clear IN Endpoint NAK Effective interrupt in case it was set */ @@ -614,7 +647,8 @@ static int dwc2_tx_fifo_write(const struct device *dev, } } - irq_unlock(key); + k_event_clear(&priv->ep_disabled, BIT(ep_idx)); + k_spin_unlock(&priv->lock, key); return 0; } @@ -757,12 +791,15 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, sys_write32(doepctl, doepctl_reg); + k_event_clear(&priv->ep_disabled, BIT(16 + ep_idx)); + 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 udc_dwc2_data *const priv = udc_get_private(dev); struct net_buf *buf; buf = udc_buf_peek(cfg); @@ -790,7 +827,8 @@ static void dwc2_handle_xfer_next(const struct device *dev, * avoid race condition where the next Control Write * Transfer Data Stage is received into the buffer. */ - if (dwc2_in_buffer_dma_mode(dev)) { + if (dwc2_in_buffer_dma_mode(dev) && priv->pending_dout_feed) { + priv->pending_dout_feed = 0; dwc2_ctrl_feed_dout(dev, 8); } } @@ -823,6 +861,7 @@ static int dwc2_handle_evt_setup(const struct device *dev) * transfer beforehand. In Buffer DMA the SETUP can be copied to any EP0 * OUT buffer. If there is any buffer queued, it is obsolete now. */ + udc_dwc2_ep_disable(dev, cfg_in, false, true); atomic_and(&priv->xfer_finished, ~(BIT(0) | BIT(16))); buf = udc_buf_get_all(cfg_out); @@ -832,7 +871,6 @@ static int dwc2_handle_evt_setup(const struct device *dev) buf = udc_buf_get_all(cfg_in); if (buf) { - udc_dwc2_ep_disable(dev, cfg_in, false); net_buf_unref(buf); } @@ -859,6 +897,19 @@ static int dwc2_handle_evt_setup(const struct device *dev) /* Allocate and feed buffer for data OUT stage */ LOG_DBG("s:%p|feed for -out-", buf); + priv->pending_dout_feed = 0; + + if (dwc2_in_completer_mode(dev)) { + /* Programming Guide does not clearly describe to clear + * control IN endpoint NAK for Control Write Transfers + * when operating in Completer mode. Set CNAK here, + * because IN endpoint is not armed at this point and + * forced NAKs are not necessary. IN Status stage will + * only finish after IN endpoint is armed. + */ + dwc2_clear_control_in_nak(dev); + } + err = dwc2_ctrl_feed_dout(dev, udc_data_stage_length(buf)); if (err == -ENOMEM) { err = udc_submit_ep_event(dev, buf, err); @@ -866,11 +917,19 @@ static int dwc2_handle_evt_setup(const struct device *dev) } else if (udc_ctrl_stage_is_data_in(dev)) { LOG_DBG("s:%p|feed for -in-status", buf); + dwc2_clear_control_in_nak(dev); + err = udc_ctrl_submit_s_in_status(dev); + + priv->pending_dout_feed = 1; } else { LOG_DBG("s:%p|feed >setup", buf); + dwc2_clear_control_in_nak(dev); + err = udc_ctrl_submit_s_status(dev); + + priv->pending_dout_feed = 1; } return err; @@ -879,6 +938,7 @@ static int dwc2_handle_evt_setup(const struct device *dev) static inline int dwc2_handle_evt_dout(const struct device *dev, struct udc_ep_config *const cfg) { + struct udc_dwc2_data *const priv = udc_get_private(dev); struct udc_data *data = dev->data; struct net_buf *buf; int err = 0; @@ -911,6 +971,8 @@ static inline int dwc2_handle_evt_dout(const struct device *dev, if (udc_ctrl_stage_is_status_in(dev)) { err = udc_ctrl_submit_s_out_status(dev, buf); + + priv->pending_dout_feed = 1; } if (data->stage == CTRL_PIPE_STAGE_ERROR) { @@ -1315,12 +1377,6 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, 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); @@ -1329,8 +1385,21 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, MIN(UDC_DWC2_FIFO0_DEPTH, priv->max_txfifo_depth[0]); } + if (priv->txf_set & BIT(ep_idx)) { + uint32_t curaddr; + + curaddr = dwc2_get_txfaddr(dev, ep_idx - 1); + txfdep = dwc2_get_txfdep(dev, ep_idx - 1); + if (txfaddr != curaddr || reqdep > txfdep) { + LOG_ERR("FIFO%u cannot be reused, new addr 0x%04x depth %u", + ep_idx, txfaddr, reqdep); + return -ENOMEM; + } + } else { + txfdep = reqdep; + } + /* Make sure to not set TxFIFO greater than hardware allows */ - txfdep = reqdep; if (txfdep > priv->max_txfifo_depth[ep_idx]) { return -ENOMEM; } @@ -1473,6 +1542,12 @@ static int udc_dwc2_ep_activate(const struct device *dev, dwc2_set_epint(dev, cfg, true); sys_write32(dxepctl, dxepctl_reg); + if (dwc2_ep_is_iso(cfg)) { + uint8_t ep_bit = ep_idx + (USB_EP_DIR_IS_OUT(cfg->addr) ? 16 : 0); + + priv->iso_enabled |= BIT(ep_bit); + } + 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); @@ -1512,16 +1587,19 @@ static int dwc2_unset_dedicated_fifo(const struct device *dev, * 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. + * Set wait parameter to true to caller wants the function to return only after + * the requested operation is effective (or after 100 ms timeout). */ static void udc_dwc2_ep_disable(const struct device *dev, - struct udc_ep_config *const cfg, bool stall) + struct udc_ep_config *const cfg, + bool stall, bool wait) { 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); + uint8_t ep_bit = ep_idx + (USB_EP_DIR_IS_OUT(cfg->addr) ? 16 : 0); mem_addr_t dxepctl_reg; uint32_t dxepctl; - const bool is_iso = dwc2_ep_is_iso(cfg); dxepctl_reg = dwc2_get_dxepctl_reg(dev, cfg->addr); dxepctl = sys_read32(dxepctl_reg); @@ -1557,104 +1635,64 @@ static void udc_dwc2_ep_disable(const struct device *dev, priv->pending_tx_flush |= BIT(usb_dwc2_get_depctl_txfnum(dxepctl)); } + k_event_post(&priv->ep_disabled, BIT(ep_bit)); udc_ep_set_busy(cfg, false); return; } - if (!is_iso && (dxepctl & USB_DWC2_DEPCTL_NAKSTS) && - !(dxepctl & USB_DWC2_DEPCTL_EPENA)) { - /* Endpoint already sends forced NAKs. STALL if necessary. */ + if (k_event_test(&priv->ep_disabled, BIT(ep_bit))) { + /* There is no active transfer, STALL if necessary */ if (stall) { dxepctl |= USB_DWC2_DEPCTL_STALL; sys_write32(dxepctl, dxepctl_reg); } + /* Do not start pending transfers (if any) */ + atomic_clear_bit(&priv->xfer_new, ep_bit); + return; } - /* FIXME: This function needs to be changed to not synchronously wait - * for the events to happen because the actions here are racing against - * the USB host packets. It is possible that the IN token or OUT DATA - * gets sent shortly before this function disables the endpoint. If this - * happens, the XferCompl would be set and driver will incorrectly think - * that either: - * * never queued transfer finished, OR - * * transfer queued in incompisoin handler finished (before it really - * does and then it'll "double"-finish when it actually finishes) - * - * For the time being XferCompl is cleared as a workaround. - */ - 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(dev, 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; - } + k_spinlock_key_t key = k_spin_lock(&priv->lock); + priv->ep_out_disable |= BIT(ep_idx); if (stall) { - /* For OUT endpoints STALL is set instead of SNAK */ - dxepctl |= USB_DWC2_DEPCTL_STALL; - } else { - dxepctl |= USB_DWC2_DEPCTL_SNAK; + priv->ep_out_stall |= BIT(ep_idx); } - sys_write32(dxepctl, dxepctl_reg); - if (ep_idx != 0) { - dwc2_wait_for_bit(dev, doepint_reg, USB_DWC2_DOEPINT_EPDISBLD); - } + sys_set_bits((mem_addr_t)&base->dctl, USB_DWC2_DCTL_SGOUTNAK); - /* Clear Endpoint Disabled interrupt */ - sys_write32(USB_DWC2_DOEPINT_EPDISBLD | USB_DWC2_DOEPINT_XFERCOMPL, doepint_reg); + k_spin_unlock(&priv->lock, key); - dctl |= USB_DWC2_DCTL_CGOUTNAK; - sys_write32(dctl, dctl_reg); + /* Endpoint gets disabled in GOUTNAKEFF handler */ } else { - mem_addr_t diepint_reg; - - diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint; - - dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_SNAK; + 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(dev, diepint_reg, USB_DWC2_DIEPINT_INEPNAKEFF); + /* EP0 status stage enabled, but CNAK not yet written. + * Set EPDIS to prevent timeout. + */ + if ((dxepctl & USB_DWC2_DEPCTL_NAKSTS) && + (dxepctl & USB_DWC2_DEPCTL_EPENA)) { + dxepctl |= USB_DWC2_DEPCTL_EPDIS; + } - dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPDIS; sys_write32(dxepctl, dxepctl_reg); - dwc2_wait_for_bit(dev, diepint_reg, USB_DWC2_DIEPINT_EPDISBLD); - - /* Clear Endpoint Disabled interrupt */ - sys_write32(USB_DWC2_DIEPINT_EPDISBLD | USB_DWC2_DIEPINT_XFERCOMPL, diepint_reg); + /* Endpoint gets disabled in INEPNAKEFF handler */ + } - /* TODO: Read DIEPTSIZn here? Programming Guide suggest it to - * let application know how many bytes of interrupted transfer - * were transferred to the host. - */ + if (wait) { + uint32_t ret; - dwc2_flush_tx_fifo(dev, usb_dwc2_get_depctl_txfnum(dxepctl)); + ret = k_event_wait(&priv->ep_disabled, BIT(ep_bit), false, K_MSEC(100)); + if (ret == 0) { + LOG_ERR("Endpoint 0x%02x disable timeout", cfg->addr); + } } udc_ep_set_busy(cfg, false); @@ -1666,6 +1704,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, static int udc_dwc2_ep_deactivate(const struct device *dev, struct udc_ep_config *const cfg) { + 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; @@ -1677,7 +1716,7 @@ static int udc_dwc2_ep_deactivate(const struct device *dev, LOG_DBG("Disable ep 0x%02x DxEPCTL%u %x", cfg->addr, ep_idx, dxepctl); - udc_dwc2_ep_disable(dev, cfg, false); + udc_dwc2_ep_disable(dev, cfg, false, true); dxepctl = sys_read32(dxepctl_reg); dxepctl &= ~USB_DWC2_DEPCTL_USBACTEP; @@ -1694,6 +1733,12 @@ static int udc_dwc2_ep_deactivate(const struct device *dev, sys_write32(dxepctl, dxepctl_reg); dwc2_set_epint(dev, cfg, false); + if (dwc2_ep_is_iso(cfg)) { + uint8_t ep_bit = ep_idx + (USB_EP_DIR_IS_OUT(cfg->addr) ? 16 : 0); + + priv->iso_enabled &= ~BIT(ep_bit); + } + return 0; } @@ -1702,13 +1747,16 @@ static int udc_dwc2_ep_set_halt(const struct device *dev, { uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); - udc_dwc2_ep_disable(dev, cfg, true); + udc_dwc2_ep_disable(dev, cfg, true, true); LOG_DBG("Set halt ep 0x%02x", cfg->addr); if (ep_idx != 0) { cfg->stat.halted = true; } else { + struct udc_dwc2_data *const priv = udc_get_private(dev); + /* Data/Status stage is STALLed, allow receiving next SETUP */ + priv->pending_dout_feed = 0; dwc2_ensure_setup_ready(dev); } @@ -1777,7 +1825,7 @@ static int udc_dwc2_ep_dequeue(const struct device *dev, { struct net_buf *buf; - udc_dwc2_ep_disable(dev, cfg, false); + udc_dwc2_ep_disable(dev, cfg, false, true); buf = udc_buf_get_all(cfg); if (buf) { @@ -2162,11 +2210,13 @@ static int udc_dwc2_init_controller(const struct device *dev) } /* Unmask interrupts */ - sys_write32(USB_DWC2_GINTSTS_OEPINT | USB_DWC2_GINTSTS_IEPINT | + sys_write32(IF_ENABLED(CONFIG_UDC_ENABLE_SOF, (USB_DWC2_GINTSTS_SOF | + USB_DWC2_GINTSTS_INCOMPISOOUT | + USB_DWC2_GINTSTS_INCOMPISOIN |)) + 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_INCOMPISOOUT | USB_DWC2_GINTSTS_INCOMPISOIN | - USB_DWC2_GINTSTS_SOF, + USB_DWC2_GINTSTS_GOUTNAKEFF, (mem_addr_t)&base->gintmsk); return 0; @@ -2303,6 +2353,11 @@ static int dwc2_driver_preinit(const struct device *dev) k_event_init(&priv->drv_evt); atomic_clear(&priv->xfer_new); atomic_clear(&priv->xfer_finished); + k_event_init(&priv->ep_disabled); + k_event_set(&priv->ep_disabled, UINT32_MAX); + priv->ep_out_disable = 0; + priv->ep_out_stall = 0; + priv->iso_enabled = 0; data->caps.rwup = true; data->caps.addr_before_status = true; @@ -2420,6 +2475,7 @@ 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); uint32_t doepmsk; + uint32_t diepmsk; /* Set the NAK bit for all OUT endpoints */ for (uint8_t i = 0U; i < priv->numdeveps; i++) { @@ -2434,13 +2490,16 @@ static void dwc2_on_bus_reset(const struct device *dev) } } - doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL; + doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_EPDISBLD | USB_DWC2_DOEPINT_XFERCOMPL; if (dwc2_in_buffer_dma_mode(dev)) { doepmsk |= USB_DWC2_DOEPINT_STSPHSERCVD; } sys_write32(doepmsk, (mem_addr_t)&base->doepmsk); - sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL); + + diepmsk = USB_DWC2_DIEPINT_INEPNAKEFF | USB_DWC2_DIEPINT_EPDISBLD | + USB_DWC2_DIEPINT_XFERCOMPL; + sys_write32(diepmsk, (mem_addr_t)&base->diepmsk); /* Software has to handle RxFLvl interrupt only in Completer mode */ if (dwc2_in_completer_mode(dev)) { @@ -2564,11 +2623,13 @@ static inline void dwc2_handle_in_xfercompl(const struct device *dev, } atomic_set_bit(&priv->xfer_finished, ep_idx); + k_event_post(&priv->ep_disabled, BIT(ep_idx)); k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED)); } static inline void dwc2_handle_iepint(const struct device *dev) { + struct udc_dwc2_data *const priv = udc_get_private(dev); struct usb_dwc2_reg *const base = dwc2_get_base(dev); uint32_t diepmsk; uint32_t epint; @@ -2579,6 +2640,7 @@ static inline void dwc2_handle_iepint(const struct device *dev) while (epint) { uint8_t n = find_lsb_set(epint) - 1; mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[n].diepint; + mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[n].diepctl; uint32_t diepint; uint32_t status; @@ -2594,6 +2656,33 @@ static inline void dwc2_handle_iepint(const struct device *dev) dwc2_handle_in_xfercompl(dev, n); } + if (status & USB_DWC2_DIEPINT_INEPNAKEFF) { + sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPDIS); + } + + if (status & USB_DWC2_DIEPINT_EPDISBLD) { + uint32_t diepctl = sys_read32(diepctl_reg); + + k_event_post(&priv->ep_disabled, BIT(n)); + + /* 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, usb_dwc2_get_depctl_txfnum(diepctl)); + + if ((usb_dwc2_get_depctl_eptype(diepctl) == USB_DWC2_DEPCTL_EPTYPE_ISO) && + (priv->iso_in_rearm & BIT(n))) { + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, n | USB_EP_DIR_IN); + + /* Try to queue next packet before SOF */ + dwc2_handle_xfer_next(dev, cfg); + + priv->iso_in_rearm &= ~BIT(n); + } + } + epint &= ~BIT(n); } @@ -2669,6 +2758,7 @@ static inline void dwc2_handle_out_xfercompl(const struct device *dev, dwc2_prep_rx(dev, buf, ep_cfg); } else { atomic_set_bit(&priv->xfer_finished, 16 + ep_idx); + k_event_post(&priv->ep_disabled, BIT(16 + ep_idx)); k_event_post(&priv->drv_evt, BIT(DWC2_DRV_EVT_EP_FINISHED)); } } @@ -2722,19 +2812,25 @@ static inline void dwc2_handle_oepint(const struct device *dev) } if (status & USB_DWC2_DOEPINT_STSPHSERCVD) { - /* Driver doesn't need any special handling, but it is - * mandatory that the bit is cleared in Buffer DMA mode. + /* Allow IN Status stage after control transfer with + * data stage from device to host. Clear the CNAK and + * this interrupt bit is mandatory in Buffer DMA mode. * If the bit is not cleared (i.e. when this interrupt * bit is masked), then SETUP interrupts will cease * after first control transfer with data stage from * device to host. */ + dwc2_clear_control_in_nak(dev); } if (status & USB_DWC2_DOEPINT_XFERCOMPL) { dwc2_handle_out_xfercompl(dev, n); } + if (status & USB_DWC2_DOEPINT_EPDISBLD) { + k_event_post(&priv->ep_disabled, BIT(16 + n)); + } + epint &= ~BIT(n); } @@ -2759,40 +2855,42 @@ static void dwc2_handle_incompisoin(const struct device *dev) USB_DWC2_DEPCTL_EPENA | usb_dwc2_set_depctl_eptype(USB_DWC2_DEPCTL_EPTYPE_ISO) | USB_DWC2_DEPCTL_USBACTEP; + uint32_t eps = priv->iso_enabled & 0x0000FFFFUL; + uint16_t rearm = 0; - for (uint8_t i = 1U; i < priv->numdeveps; i++) { - uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i); - - if (epdir == USB_DWC2_GHWCFG1_EPDIR_IN || - epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) { - mem_addr_t diepctl_reg = dwc2_get_dxepctl_reg(dev, i | USB_EP_DIR_IN); - uint32_t diepctl; + while (eps) { + uint8_t i = find_lsb_set(eps) - 1; + mem_addr_t diepctl_reg = dwc2_get_dxepctl_reg(dev, i | USB_EP_DIR_IN); + uint32_t diepctl; - diepctl = sys_read32(diepctl_reg); + diepctl = sys_read32(diepctl_reg); - /* Check if endpoint didn't receive ISO OUT data */ - if ((diepctl & mask) == val) { - struct udc_ep_config *cfg; - struct net_buf *buf; + /* Check if endpoint didn't receive ISO IN data */ + if ((diepctl & mask) == val) { + struct udc_ep_config *cfg; + struct net_buf *buf; - cfg = udc_get_ep_cfg(dev, i | USB_EP_DIR_IN); - __ASSERT_NO_MSG(cfg && cfg->stat.enabled && - dwc2_ep_is_iso(cfg)); + cfg = udc_get_ep_cfg(dev, i | USB_EP_DIR_IN); + __ASSERT_NO_MSG(cfg && cfg->stat.enabled && + dwc2_ep_is_iso(cfg)); - udc_dwc2_ep_disable(dev, cfg, false); + udc_dwc2_ep_disable(dev, cfg, false, false); - buf = udc_buf_get(cfg); - if (buf) { - /* Data is no longer relevant */ - udc_submit_ep_event(dev, buf, 0); + buf = udc_buf_get(cfg); + if (buf) { + /* Data is no longer relevant */ + udc_submit_ep_event(dev, buf, 0); - /* Try to queue next packet before SOF */ - dwc2_handle_xfer_next(dev, cfg); - } + rearm |= BIT(i); } } + + eps &= ~BIT(i); } + /* Mark endpoints to re-arm in EPDISBLD handler */ + priv->iso_in_rearm = rearm; + sys_write32(USB_DWC2_GINTSTS_INCOMPISOIN, gintsts_reg); } @@ -2814,39 +2912,83 @@ static void dwc2_handle_incompisoout(const struct device *dev) usb_dwc2_set_depctl_eptype(USB_DWC2_DEPCTL_EPTYPE_ISO) | ((priv->sof_num & 1) ? USB_DWC2_DEPCTL_DPID : 0) | USB_DWC2_DEPCTL_USBACTEP; + uint32_t eps = (priv->iso_enabled & 0xFFFF0000UL) >> 16; - for (uint8_t i = 1U; 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); - uint32_t doepctl; + while (eps) { + uint8_t i = find_lsb_set(eps) - 1; + mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, i); + uint32_t doepctl; - doepctl = sys_read32(doepctl_reg); + doepctl = sys_read32(doepctl_reg); - /* Check if endpoint didn't receive ISO OUT data */ - if ((doepctl & mask) == val) { - struct udc_ep_config *cfg; - struct net_buf *buf; + /* Check if endpoint didn't receive ISO OUT data */ + if ((doepctl & mask) == val) { + struct udc_ep_config *cfg; + struct net_buf *buf; - cfg = udc_get_ep_cfg(dev, i); - __ASSERT_NO_MSG(cfg && cfg->stat.enabled && - dwc2_ep_is_iso(cfg)); + cfg = udc_get_ep_cfg(dev, i); + __ASSERT_NO_MSG(cfg && cfg->stat.enabled && + dwc2_ep_is_iso(cfg)); - udc_dwc2_ep_disable(dev, cfg, false); + udc_dwc2_ep_disable(dev, cfg, false, false); - buf = udc_buf_get(cfg); - if (buf) { - udc_submit_ep_event(dev, buf, 0); - } + buf = udc_buf_get(cfg); + if (buf) { + udc_submit_ep_event(dev, buf, 0); } } + + eps &= ~BIT(i); } sys_write32(USB_DWC2_GINTSTS_INCOMPISOOUT, gintsts_reg); } +static void dwc2_handle_goutnakeff(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); + k_spinlock_key_t key; + mem_addr_t doepctl_reg; + uint32_t doepctl; + + key = k_spin_lock(&priv->lock); + + while (priv->ep_out_disable) { + uint8_t ep_idx = find_lsb_set(priv->ep_out_disable) - 1; + + priv->ep_out_disable &= ~BIT(ep_idx); + + doepctl_reg = (mem_addr_t)&base->out_ep[ep_idx].doepctl; + doepctl = sys_read32(doepctl_reg); + + /* The application cannot disable control OUT endpoint 0. */ + if (ep_idx != 0) { + doepctl |= USB_DWC2_DEPCTL_EPDIS; + } + + if (priv->ep_out_stall & BIT(ep_idx)) { + /* For OUT endpoints STALL is set instead of SNAK */ + doepctl |= USB_DWC2_DEPCTL_STALL; + priv->ep_out_stall &= ~BIT(ep_idx); + } else { + doepctl |= USB_DWC2_DEPCTL_SNAK; + } + + sys_write32(doepctl, doepctl_reg); + + if (ep_idx == 0) { + /* Consider endpoint to be disabled */ + k_event_post(&priv->ep_disabled, BIT(16)); + } + } + + sys_set_bits((mem_addr_t)&base->dctl, USB_DWC2_DCTL_CGOUTNAK); + + k_spin_unlock(&priv->lock, key); +} + static void udc_dwc2_isr_handler(const struct device *dev) { const struct udc_dwc2_config *const config = dev->config; @@ -2890,7 +3032,8 @@ static void udc_dwc2_isr_handler(const struct device *dev) LOG_DBG("GINTSTS 0x%x", int_status); - if (int_status & USB_DWC2_GINTSTS_SOF) { + if (IS_ENABLED(CONFIG_UDC_ENABLE_SOF) && + int_status & USB_DWC2_GINTSTS_SOF) { uint32_t dsts; /* Clear USB SOF interrupt. */ @@ -2898,7 +3041,7 @@ static void udc_dwc2_isr_handler(const struct device *dev) dsts = sys_read32((mem_addr_t)&base->dsts); priv->sof_num = usb_dwc2_get_dsts_soffn(dsts); - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); } if (int_status & USB_DWC2_GINTSTS_USBRST) { @@ -2941,14 +3084,23 @@ static void udc_dwc2_isr_handler(const struct device *dev) dwc2_handle_oepint(dev); } - if (int_status & USB_DWC2_GINTSTS_INCOMPISOIN) { + if (IS_ENABLED(CONFIG_UDC_ENABLE_SOF) && + int_status & USB_DWC2_GINTSTS_INCOMPISOIN) { dwc2_handle_incompisoin(dev); } - if (int_status & USB_DWC2_GINTSTS_INCOMPISOOUT) { + if (IS_ENABLED(CONFIG_UDC_ENABLE_SOF) && + int_status & USB_DWC2_GINTSTS_INCOMPISOOUT) { dwc2_handle_incompisoout(dev); } + if (int_status & USB_DWC2_GINTSTS_GOUTNAKEFF) { + /* Clear Global OUT NAK Effective interrupt. */ + sys_write32(USB_DWC2_GINTSTS_GOUTNAKEFF, gintsts_reg); + + dwc2_handle_goutnakeff(dev); + } + if (int_status & USB_DWC2_GINTSTS_USBSUSP) { /* Clear USB Suspend interrupt. */ sys_write32(USB_DWC2_GINTSTS_USBSUSP, gintsts_reg); @@ -3171,6 +3323,8 @@ static ALWAYS_INLINE void dwc2_thread_handler(void *const arg) if (evt & BIT(DWC2_DRV_EVT_ENUM_DONE)) { k_event_clear(&priv->drv_evt, BIT(DWC2_DRV_EVT_ENUM_DONE)); + /* Any potential transfer on control IN endpoint is cancelled */ + priv->pending_dout_feed = 0; dwc2_ensure_setup_ready(dev); } @@ -3196,13 +3350,6 @@ static const struct udc_api udc_dwc2_api = { .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))) diff --git a/drivers/usb/udc/udc_dwc2.h b/drivers/usb/udc/udc_dwc2.h index 87e71f81899..77be96adaa5 100644 --- a/drivers/usb/udc/udc_dwc2.h +++ b/drivers/usb/udc/udc_dwc2.h @@ -46,7 +46,7 @@ struct udc_dwc2_config { /* Pointer to pin control configuration or NULL */ struct pinctrl_dev_config *const pcfg; /* Pointer to vendor quirks or NULL */ - struct dwc2_vendor_quirks *const quirks; + const struct dwc2_vendor_quirks *const quirks; void (*make_thread)(const struct device *dev); void (*irq_enable_func)(const struct device *dev); void (*irq_disable_func)(const struct device *dev); @@ -55,13 +55,23 @@ struct udc_dwc2_config { uint32_t ghwcfg4; }; +#include "udc_dwc2_vendor_quirks.h" + +#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 DWC2_QUIRK_FUNC_DEFINE(fname) \ static inline int dwc2_quirk_##fname(const struct device *dev) \ { \ const struct udc_dwc2_config *const config = dev->config; \ - struct dwc2_vendor_quirks *quirks = config->quirks; \ + const struct dwc2_vendor_quirks *const quirks = \ + COND_CODE_1(IS_EQ(DT_NUM_INST_STATUS_OKAY(snps_dwc2), 1), \ + (UDC_DWC2_VENDOR_QUIRK_GET(0); ARG_UNUSED(config);), \ + (config->quirks;)) \ \ - if (quirks != NULL && config->quirks->fname != NULL) { \ + if (quirks != NULL && quirks->fname != NULL) { \ return quirks->fname(dev); \ } \ \ diff --git a/drivers/usb/udc/udc_dwc2_vendor_quirks.h b/drivers/usb/udc/udc_dwc2_vendor_quirks.h index 1f450c430c2..330ff8d113f 100644 --- a/drivers/usb/udc/udc_dwc2_vendor_quirks.h +++ b/drivers/usb/udc/udc_dwc2_vendor_quirks.h @@ -25,8 +25,6 @@ struct usb_dw_stm32_clk { size_t pclken_len; }; -#define DT_DRV_COMPAT snps_dwc2 - static inline int stm32f4_fsotg_enable_clk(const struct usb_dw_stm32_clk *const clk) { int ret; @@ -94,7 +92,7 @@ static inline int stm32f4_fsotg_disable_phy(const struct device *dev) return stm32f4_fsotg_enable_clk(&stm32f4_clk_##n); \ } \ \ - struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ + const struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ .pre_enable = stm32f4_fsotg_enable_clk_##n, \ .post_enable = stm32f4_fsotg_enable_phy, \ .disable = stm32f4_fsotg_disable_phy, \ @@ -104,14 +102,11 @@ static inline int stm32f4_fsotg_disable_phy(const struct device *dev) DT_INST_FOREACH_STATUS_OKAY(QUIRK_STM32F4_FSOTG_DEFINE) -#undef DT_DRV_COMPAT - #endif /*DT_HAS_COMPAT_STATUS_OKAY(st_stm32f4_fsotg) */ #if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) -#define DT_DRV_COMPAT snps_dwc2 - +#include #include #include @@ -129,6 +124,7 @@ static K_EVENT_DEFINE(usbhs_events); static void usbhs_vbus_handler(nrfs_usb_evt_t const *p_evt, void *const context) { + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); const struct device *dev = context; switch (p_evt->type) { @@ -156,6 +152,7 @@ static void usbhs_vbus_handler(nrfs_usb_evt_t const *p_evt, void *const context) static inline int usbhs_enable_nrfs_service(const struct device *dev) { + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); nrfs_err_t nrfs_err; int err; @@ -182,6 +179,7 @@ static inline int usbhs_enable_nrfs_service(const struct device *dev) static inline int usbhs_enable_core(const struct device *dev) { + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); k_timeout_t timeout = K_FOREVER; @@ -229,6 +227,7 @@ static inline int usbhs_disable_core(const struct device *dev) static inline int usbhs_disable_nrfs_service(const struct device *dev) { + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); nrfs_err_t nrfs_err; nrfs_err = nrfs_usb_disable_request((void *)dev); @@ -297,7 +296,7 @@ static inline int usbhs_pre_hibernation_exit(const struct device *dev) } #define QUIRK_NRF_USBHS_DEFINE(n) \ - struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ + const struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ .init = usbhs_enable_nrfs_service, \ .pre_enable = usbhs_enable_core, \ .disable = usbhs_disable_core, \ @@ -311,17 +310,16 @@ static inline int usbhs_pre_hibernation_exit(const struct device *dev) DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) -#undef DT_DRV_COMPAT - #endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) */ #if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) -#define DT_DRV_COMPAT snps_dwc2 - #define USBHS_DT_WRAPPER_REG_ADDR(n) UINT_TO_POINTER(DT_INST_REG_ADDR_BY_NAME(n, wrapper)) #include +#include +#include +#include #define NRF_DEFAULT_IRQ_PRIORITY 1 @@ -335,6 +333,9 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) static K_EVENT_DEFINE(usbhs_events); #define USBHS_VBUS_READY BIT(0) +static struct onoff_manager *pclk24m_mgr; +static struct onoff_client pclk24m_cli; + static void vregusb_isr(const void *arg) { const struct device *dev = arg; @@ -352,7 +353,7 @@ static void vregusb_isr(const void *arg) } } -static inline int usbhs_enable_vreg(const struct device *dev) +static inline int usbhs_init_vreg_and_clock(const struct device *dev) { IRQ_CONNECT(VREGUSB_IRQn, NRF_DEFAULT_IRQ_PRIORITY, vregusb_isr, DEVICE_DT_INST_GET(0), 0); @@ -368,14 +369,17 @@ static inline int usbhs_enable_vreg(const struct device *dev) } irq_enable(VREGUSB_IRQn); + pclk24m_mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF24M); return 0; } static inline int usbhs_enable_core(const struct device *dev) { + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); k_timeout_t timeout = K_FOREVER; + int err; if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, K_NO_WAIT)) { LOG_WRN("VBUS is not ready, block udc_enable()"); @@ -384,9 +388,12 @@ static inline int usbhs_enable_core(const struct device *dev) } } - /* TODO: Request PCLK24M using clock control driver */ - NRF_CLOCK->TASKS_XO24MSTART = 1; - while (NRF_CLOCK->EVENTS_XO24MSTARTED == 0) { + /* Request PCLK24M using clock control driver */ + sys_notify_init_spinwait(&pclk24m_cli.notify); + err = onoff_request(pclk24m_mgr, &pclk24m_cli); + if (err < 0) { + LOG_ERR("Failed to start PCLK24M %d", err); + return err; } /* Power up peripheral */ @@ -418,7 +425,9 @@ static inline int usbhs_enable_core(const struct device *dev) static inline int usbhs_disable_core(const struct device *dev) { + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); + int err; /* Set ID to Device and forcefully disable D+ pull-up */ wrapper->PHY.OVERRIDEVALUES = (1 << 31); @@ -426,9 +435,12 @@ static inline int usbhs_disable_core(const struct device *dev) wrapper->ENABLE = 0UL; - /* TODO: Release PCLK24M using clock control driver */ - NRF_CLOCK->EVENTS_XO24MSTARTED = 0; - NRF_CLOCK->TASKS_XO24MSTOP = 1; + /* Release PCLK24M using clock control driver */ + err = onoff_cancel_or_release(pclk24m_mgr, &pclk24m_cli); + if (err < 0) { + LOG_ERR("Failed to stop PCLK24M %d", err); + return err; + } return 0; } @@ -484,7 +496,7 @@ static inline int usbhs_pre_hibernation_exit(const struct device *dev) #define QUIRK_NRF_USBHS_DEFINE(n) \ struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ - .init = usbhs_enable_vreg, \ + .init = usbhs_init_vreg_and_clock, \ .pre_enable = usbhs_enable_core, \ .disable = usbhs_disable_core, \ .shutdown = usbhs_disable_vreg, \ @@ -496,8 +508,6 @@ static inline int usbhs_pre_hibernation_exit(const struct device *dev) DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) -#undef DT_DRV_COMPAT - #endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) */ /* Add next vendor quirks definition above this line */ diff --git a/drivers/usb/udc/udc_it82xx2.c b/drivers/usb/udc/udc_it82xx2.c index 85ec9431c63..6b41cb162d4 100644 --- a/drivers/usb/udc/udc_it82xx2.c +++ b/drivers/usb/udc/udc_it82xx2.c @@ -17,9 +17,6 @@ LOG_MODULE_REGISTER(udc_it82xx2, CONFIG_UDC_DRIVER_LOG_LEVEL); #define DT_DRV_COMPAT ite_it82xx2_usb -/* TODO: Replace this definition by Kconfig option */ -#define USB_DEVICE_CONFIG_SOF_NOTIFICATIONS (0U) - #define IT8XXX2_IS_EXTEND_ENDPOINT(n) (USB_EP_GET_IDX(n) >= 4) #define IT82xx2_STATE_OUT_SHARED_FIFO_BUSY 0 @@ -1357,11 +1354,11 @@ static void it82xx2_usb_dc_isr(const void *arg) /* sof received */ if (status & DC_SOF_RECEIVED) { - if (!USB_DEVICE_CONFIG_SOF_NOTIFICATIONS) { + if (!IS_ENABLED(CONFIG_UDC_ENABLE_SOF)) { it82xx2_enable_sof_int(dev, false); } else { usb_regs->dc_interrupt_status = DC_SOF_RECEIVED; - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); } it82xx2_enable_resume_int(dev, false); emit_resume_event(dev); @@ -1411,7 +1408,7 @@ static void suspended_handler(struct k_work *item) it82xx2_enable_resume_int(dev, true); - if (!USB_DEVICE_CONFIG_SOF_NOTIFICATIONS) { + if (!IS_ENABLED(CONFIG_UDC_ENABLE_SOF)) { it82xx2_enable_sof_int(dev, true); } diff --git a/drivers/usb/udc/udc_kinetis.c b/drivers/usb/udc/udc_kinetis.c index 8858ee129c4..7d227301603 100644 --- a/drivers/usb/udc/udc_kinetis.c +++ b/drivers/usb/udc/udc_kinetis.c @@ -653,7 +653,7 @@ static void usbfsotg_isr_handler(const struct device *dev) } if (istatus == USB_ISTAT_SOFTOK_MASK) { - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); } if (istatus == USB_ISTAT_ERROR_MASK) { @@ -1016,7 +1016,7 @@ static int usbfsotg_init(const struct device *dev) base->INTEN = (USB_INTEN_SLEEPEN_MASK | USB_INTEN_STALLEN_MASK | USB_INTEN_TOKDNEEN_MASK | - USB_INTEN_SOFTOKEN_MASK | + IF_ENABLED(CONFIG_UDC_ENABLE_SOF, (USB_INTEN_SOFTOKEN_MASK |)) USB_INTEN_ERROREN_MASK | USB_INTEN_USBRSTEN_MASK); diff --git a/drivers/usb/udc/udc_max32.c b/drivers/usb/udc/udc_max32.c index 1e09f5ef7c6..526a49b8ba9 100644 --- a/drivers/usb/udc/udc_max32.c +++ b/drivers/usb/udc/udc_max32.c @@ -533,7 +533,7 @@ static int udc_max32_event_callback(maxusb_event_t event, void *cbdata) case MAXUSB_EVENT_DPACT: LOG_DBG("DPACT event occurred"); udc_set_suspended(dev, false); - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); break; case MAXUSB_EVENT_BRST: LOG_DBG("BRST event occurred"); diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c index fc95f7a91d7..041f9b759b1 100644 --- a/drivers/usb/udc/udc_mcux_ehci.c +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -531,7 +531,7 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); break; case kUSB_DeviceNotifySOF: - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); break; default: udc_mcux_event_submit(dev, mcux_msg); diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c index 312acae1784..c6aab0d0bb1 100644 --- a/drivers/usb/udc/udc_mcux_ip3511.c +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -531,7 +531,7 @@ usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); break; case kUSB_DeviceNotifySOF: - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); break; default: udc_mcux_event_submit(dev, mcux_msg); diff --git a/drivers/usb/udc/udc_nrf.c b/drivers/usb/udc/udc_nrf.c index 71eebbd70d3..7b18862fa59 100644 --- a/drivers/usb/udc/udc_nrf.c +++ b/drivers/usb/udc/udc_nrf.c @@ -436,7 +436,7 @@ static void ev_sof_handler(void) m_ep_armed &= ~USBD_EPISO_BIT_MASK; - udc_submit_event(udc_nrf_dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(udc_nrf_dev); } static void usbd_in_packet_sent(uint8_t ep) @@ -1547,7 +1547,7 @@ static void udc_nrf_power_handler(nrfx_power_usb_evt_t pwr_evt) break; case NRFX_POWER_USB_EVT_READY: LOG_DBG("POWER event ready"); - nrf_usbd_legacy_start(true); + nrf_usbd_legacy_start(IS_ENABLED(CONFIG_UDC_ENABLE_SOF)); break; case NRFX_POWER_USB_EVT_REMOVED: LOG_DBG("POWER event removed"); diff --git a/drivers/usb/udc/udc_numaker.c b/drivers/usb/udc/udc_numaker.c index bbc016ecf70..43630010caa 100644 --- a/drivers/usb/udc/udc_numaker.c +++ b/drivers/usb/udc/udc_numaker.c @@ -171,7 +171,9 @@ static inline void numaker_usbd_sw_connect(const struct device *dev) base->INTSTS = base->INTSTS; /* Enable relevant interrupts */ - base->INTEN = USBD_INT_BUS | USBD_INT_USB | USBD_INT_FLDET | USBD_INT_WAKEUP | USBD_INT_SOF; + base->INTEN = USBD_INT_BUS | USBD_INT_USB | USBD_INT_FLDET | + IF_ENABLED(CONFIG_UDC_ENABLE_SOF, (USBD_INT_SOF |)) + USBD_INT_WAKEUP; /* Clear SE0 for connect */ base->SE0 &= ~USBD_DRVSE0; @@ -1298,7 +1300,7 @@ static void numaker_udbd_isr(const struct device *dev) base->INTSTS = USBD_INTSTS_SOFIF_Msk; /* UDC stack would handle bottom-half processing */ - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); } /* USB Setup/EP */ diff --git a/drivers/usb/udc/udc_renesas_ra.c b/drivers/usb/udc/udc_renesas_ra.c index fa98fda46ac..4a3123ec2b9 100644 --- a/drivers/usb/udc/udc_renesas_ra.c +++ b/drivers/usb/udc/udc_renesas_ra.c @@ -79,7 +79,7 @@ static void udc_renesas_ra_event_handler(usbd_callback_arg_t *p_args) break; case USBD_EVENT_SOF: - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); break; default: diff --git a/drivers/usb/udc/udc_rpi_pico.c b/drivers/usb/udc/udc_rpi_pico.c index 31561f6fce6..70ebf047399 100644 --- a/drivers/usb/udc/udc_rpi_pico.c +++ b/drivers/usb/udc/udc_rpi_pico.c @@ -678,7 +678,7 @@ static void rpi_pico_isr_handler(const struct device *dev) if (status & USB_INTS_DEV_SOF_BITS) { handled |= USB_INTS_DEV_SOF_BITS; - sys_read32((mm_reg_t)&base->sof_rd); + udc_submit_sof_event(dev); } if (status & USB_INTS_DEV_CONN_DIS_BITS) { @@ -1048,7 +1048,7 @@ static int udc_rpi_pico_enable(const struct device *dev) sys_write32(USB_SIE_CTRL_EP0_INT_1BUF_BITS, (mm_reg_t)&base->sie_ctrl); /* Enable interrupts */ - sys_write32(USB_INTE_DEV_SOF_BITS | + sys_write32(IF_ENABLED(CONFIG_UDC_ENABLE_SOF, (USB_INTE_DEV_SOF_BITS |)) USB_INTE_SETUP_REQ_BITS | USB_INTE_DEV_RESUME_FROM_HOST_BITS | USB_INTE_DEV_SUSPEND_BITS | diff --git a/drivers/usb/udc/udc_sam0.c b/drivers/usb/udc/udc_sam0.c index dab462a03fa..e351c98e284 100644 --- a/drivers/usb/udc/udc_sam0.c +++ b/drivers/usb/udc/udc_sam0.c @@ -693,7 +693,7 @@ static void sam0_isr_handler(const struct device *dev) base->INTFLAG.reg = intflag; if (intflag & USB_DEVICE_INTFLAG_SOF) { - udc_submit_event(dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(dev); } if (intflag & USB_DEVICE_INTFLAG_EORST) { diff --git a/drivers/usb/udc/udc_skeleton.c b/drivers/usb/udc/udc_skeleton.c index c808cc98ad8..2c9be371dec 100644 --- a/drivers/usb/udc/udc_skeleton.c +++ b/drivers/usb/udc/udc_skeleton.c @@ -233,6 +233,10 @@ static int udc_skeleton_init(const struct device *dev) return -EIO; } + if (IS_ENABLED(CONFIG_UDC_ENABLE_SOF)) { + LOG_INF("Enable SOF interrupt"); + } + return 0; } diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index 6cc8c4add1c..f773a1c641d 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -182,7 +182,7 @@ void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) { struct udc_stm32_data *priv = hpcd2data(hpcd); - udc_submit_event(priv->dev, UDC_EVT_SOF, 0); + udc_submit_sof_event(priv->dev); } static int usbd_ctrl_feed_dout(const struct device *dev, const size_t length) diff --git a/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml b/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml index 6e661d1c73d..d1742344855 100644 --- a/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml +++ b/dts/bindings/usb/uac2/zephyr,uac2-audio-streaming.yaml @@ -78,3 +78,11 @@ properties: type: int description: | Number of effectively used bits in audio subslot. + + polling-period-us: + type: int + description: | + Input or output endpoint polling period in microseconds. For USB full + speed this could be clamped to 1ms or 32768ms depending on the value. For + USB high speed this could be clamped to 125us or 4096ms depending on the + value. diff --git a/include/zephyr/drivers/clock_control/nrf_clock_control.h b/include/zephyr/drivers/clock_control/nrf_clock_control.h index f407be61d4d..31c1dea047b 100644 --- a/include/zephyr/drivers/clock_control/nrf_clock_control.h +++ b/include/zephyr/drivers/clock_control/nrf_clock_control.h @@ -27,6 +27,9 @@ extern "C" { enum clock_control_nrf_type { CLOCK_CONTROL_NRF_TYPE_HFCLK, CLOCK_CONTROL_NRF_TYPE_LFCLK, +#if NRF_CLOCK_HAS_HFCLK24M + CLOCK_CONTROL_NRF_TYPE_HFCLK24M, +#endif #if NRF_CLOCK_HAS_HFCLK192M CLOCK_CONTROL_NRF_TYPE_HFCLK192M, #endif @@ -43,6 +46,8 @@ enum clock_control_nrf_type { ((clock_control_subsys_t)CLOCK_CONTROL_NRF_TYPE_HFCLK) #define CLOCK_CONTROL_NRF_SUBSYS_LF \ ((clock_control_subsys_t)CLOCK_CONTROL_NRF_TYPE_LFCLK) +#define CLOCK_CONTROL_NRF_SUBSYS_HF24M \ + ((clock_control_subsys_t)CLOCK_CONTROL_NRF_TYPE_HFCLK24M) #define CLOCK_CONTROL_NRF_SUBSYS_HF192M \ ((clock_control_subsys_t)CLOCK_CONTROL_NRF_TYPE_HFCLK192M) #define CLOCK_CONTROL_NRF_SUBSYS_HFAUDIO \ diff --git a/include/zephyr/kernel.h b/include/zephyr/kernel.h index a407678bcaa..e7968bd2f85 100644 --- a/include/zephyr/kernel.h +++ b/include/zephyr/kernel.h @@ -5440,6 +5440,41 @@ struct k_mem_slab { * @{ */ +/** + * @brief Statically define and initialize a memory slab in a user-provided memory section with + * public (non-static) scope. + * + * The memory slab's buffer contains @a slab_num_blocks memory blocks + * that are @a slab_block_size bytes long. The buffer is aligned to a + * @a slab_align -byte boundary. To ensure that each memory block is similarly + * aligned to this boundary, @a slab_block_size must also be a multiple of + * @a slab_align. + * + * The memory slab can be accessed outside the module where it is defined + * using: + * + * @code extern struct k_mem_slab ; @endcode + * + * @note This macro cannot be used together with a static keyword. + * If such a use-case is desired, use @ref K_MEM_SLAB_DEFINE_IN_SECT_STATIC + * instead. + * + * @param name Name of the memory slab. + * @param in_section Section attribute specifier such as Z_GENERIC_SECTION. + * @param slab_block_size Size of each memory block (in bytes). + * @param slab_num_blocks Number memory blocks. + * @param slab_align Alignment of the memory slab's buffer (power of 2). + */ +#define K_MEM_SLAB_DEFINE_IN_SECT(name, in_section, slab_block_size, slab_num_blocks, slab_align) \ + BUILD_ASSERT(((slab_block_size) % (slab_align)) == 0, \ + "slab_block_size must be a multiple of slab_align"); \ + BUILD_ASSERT((((slab_align) & ((slab_align) - 1)) == 0), \ + "slab_align must be a power of 2"); \ + char in_section __aligned(WB_UP( \ + slab_align)) _k_mem_slab_buf_##name[(slab_num_blocks) * WB_UP(slab_block_size)]; \ + STRUCT_SECTION_ITERABLE(k_mem_slab, name) = Z_MEM_SLAB_INITIALIZER( \ + name, _k_mem_slab_buf_##name, WB_UP(slab_block_size), slab_num_blocks) + /** * @brief Statically define and initialize a memory slab in a public (non-static) scope. * @@ -5463,13 +5498,36 @@ struct k_mem_slab { * @param slab_num_blocks Number memory blocks. * @param slab_align Alignment of the memory slab's buffer (power of 2). */ -#define K_MEM_SLAB_DEFINE(name, slab_block_size, slab_num_blocks, slab_align) \ - char __noinit_named(k_mem_slab_buf_##name) \ - __aligned(WB_UP(slab_align)) \ - _k_mem_slab_buf_##name[(slab_num_blocks) * WB_UP(slab_block_size)]; \ - STRUCT_SECTION_ITERABLE(k_mem_slab, name) = \ - Z_MEM_SLAB_INITIALIZER(name, _k_mem_slab_buf_##name, \ - WB_UP(slab_block_size), slab_num_blocks) +#define K_MEM_SLAB_DEFINE(name, slab_block_size, slab_num_blocks, slab_align) \ + K_MEM_SLAB_DEFINE_IN_SECT(name, __noinit_named(k_mem_slab_buf_##name), slab_block_size, \ + slab_num_blocks, slab_align) + +/** + * @brief Statically define and initialize a memory slab in a user-provided memory section with + * private (static) scope. + * + * The memory slab's buffer contains @a slab_num_blocks memory blocks + * that are @a slab_block_size bytes long. The buffer is aligned to a + * @a slab_align -byte boundary. To ensure that each memory block is similarly + * aligned to this boundary, @a slab_block_size must also be a multiple of + * @a slab_align. + * + * @param name Name of the memory slab. + * @param in_section Section attribute specifier such as Z_GENERIC_SECTION. + * @param slab_block_size Size of each memory block (in bytes). + * @param slab_num_blocks Number memory blocks. + * @param slab_align Alignment of the memory slab's buffer (power of 2). + */ +#define K_MEM_SLAB_DEFINE_IN_SECT_STATIC(name, in_section, slab_block_size, slab_num_blocks, \ + slab_align) \ + BUILD_ASSERT(((slab_block_size) % (slab_align)) == 0, \ + "slab_block_size must be a multiple of slab_align"); \ + BUILD_ASSERT((((slab_align) & ((slab_align) - 1)) == 0), \ + "slab_align must be a power of 2"); \ + static char in_section __aligned(WB_UP( \ + slab_align)) _k_mem_slab_buf_##name[(slab_num_blocks) * WB_UP(slab_block_size)]; \ + static STRUCT_SECTION_ITERABLE(k_mem_slab, name) = Z_MEM_SLAB_INITIALIZER( \ + name, _k_mem_slab_buf_##name, WB_UP(slab_block_size), slab_num_blocks) /** * @brief Statically define and initialize a memory slab in a private (static) scope. @@ -5485,13 +5543,9 @@ struct k_mem_slab { * @param slab_num_blocks Number memory blocks. * @param slab_align Alignment of the memory slab's buffer (power of 2). */ -#define K_MEM_SLAB_DEFINE_STATIC(name, slab_block_size, slab_num_blocks, slab_align) \ - static char __noinit_named(k_mem_slab_buf_##name) \ - __aligned(WB_UP(slab_align)) \ - _k_mem_slab_buf_##name[(slab_num_blocks) * WB_UP(slab_block_size)]; \ - static STRUCT_SECTION_ITERABLE(k_mem_slab, name) = \ - Z_MEM_SLAB_INITIALIZER(name, _k_mem_slab_buf_##name, \ - WB_UP(slab_block_size), slab_num_blocks) +#define K_MEM_SLAB_DEFINE_STATIC(name, slab_block_size, slab_num_blocks, slab_align) \ + K_MEM_SLAB_DEFINE_IN_SECT_STATIC(name, __noinit_named(k_mem_slab_buf_##name), \ + slab_block_size, slab_num_blocks, slab_align) /** * @brief Initialize a memory slab. @@ -5790,7 +5844,7 @@ void k_heap_free(struct k_heap *h, void *mem) __attribute_nonnull(1); * * @param name Symbol name for the struct k_heap object * @param bytes Size of memory region, in bytes - * @param in_section __attribute__((section(name)) + * @param in_section Section attribute specifier such as Z_GENERIC_SECTION. */ #define Z_HEAP_DEFINE_IN_SECT(name, bytes, in_section) \ char in_section \ diff --git a/include/zephyr/usb/class/hid.h b/include/zephyr/usb/class/hid.h index 96bb8271ed7..c9561e750c7 100644 --- a/include/zephyr/usb/class/hid.h +++ b/include/zephyr/usb/class/hid.h @@ -141,6 +141,8 @@ extern "C" { #define HID_USAGE_GEN_LEDS 0x08 /** HID Button Usage page */ #define HID_USAGE_GEN_BUTTON 0x09 +/** HID Sensors Usage page */ +#define HID_USAGE_SENSORS 0x20 /** HID Generic Desktop Undefined Usage ID */ #define HID_USAGE_GEN_DESKTOP_UNDEFINED 0x00 @@ -163,6 +165,33 @@ extern "C" { /** HID Generic Desktop Wheel Usage ID */ #define HID_USAGE_GEN_DESKTOP_WHEEL 0x38 +/** HID Sensors Collection Usage ID */ +#define HID_USAGE_SENSOR_TYPE_COLLECTION 0x001 +/** HID Sensors Environmental Temperature Type Usage ID */ +#define HID_USAGE_SENSORS_TYPE_ENVIRONMENTAL_TEMPERATURE 0x033 +/** HID Sensors Event Sensor State Usage ID */ +#define HID_USAGE_SENSORS_EVENT_SENSOR_STATE 0x201 +/** HID Sensors Friendly Name Property Usage ID */ +#define HID_USAGE_SENSORS_PROPERTY_FRIENDLY_NAME 0x301 +/** HID Sensors Enviromental Temperature Data Usage ID */ +#define HID_USAGE_SENSORS_DATA_ENVIRONMENTAL_TEMPERATURE 0x434 +/** HID Sensors Timestamp Property Usage ID */ +#define HID_USAGE_SENSORS_PROPERTY_TIMESTAMP 0x529 +/** HID Sensors Sensor State Undefined Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_UNDEFINED 0x800 +/** HID Sensors Sensor State Ready Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_READY 0x801 +/** HID Sensors Sensor State Not Available Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_NOT_AVAILABLE 0x802 +/** HID Sensors Sensor State No Data Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_NO_DATA 0x803 +/** HID Sensors Sensor State Initializing Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_INITIALIZING 0x804 +/** HID Sensors Sensor State Access Denied Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_ACCESS_DENIED 0x805 +/** HID Sensors Sensor State Error Usage ID */ +#define HID_USAGE_SENSORS_SENSOR_STATE_ERROR 0x806 + /** * @} */ @@ -357,12 +386,21 @@ extern "C" { * For usage examples, see @ref HID_MOUSE_REPORT_DESC(), * @ref HID_KEYBOARD_REPORT_DESC() * - * @param idx Number of data fields included in the report + * @param idx HID Usage ID per the HID Usage Table * @return HID Usage Index item */ #define HID_USAGE(idx) \ HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 1), idx +/** + * @brief Define HID Usage Index item with the data length of two bytes. + * + * @param idx HID Usage ID per the HID Usage Table + * @return HID Usage Index item + */ +#define HID_USAGE16(idx) \ + HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), (uint8_t)idx, (idx >> 8) + /** * @brief Define HID Usage Minimum item with the data length of one byte. * @@ -413,6 +451,15 @@ extern "C" { #define HID_USAGE_MAX16(a, b) \ HID_ITEM(HID_ITEM_TAG_USAGE_MAX, HID_ITEM_TYPE_LOCAL, 2), a, b +/** + * @brief Define HID Unit Exponent item. + * + * @param exp Unit exponent, refer to the HID Unit Exponent table + * in the specification for usage + * @return HID Unit Exponent item + */ +#define HID_UNIT_EXPONENT(exp) HID_ITEM(HID_ITEM_TAG_UNIT_EXPONENT, HID_ITEM_TYPE_GLOBAL, 1), exp + /** * @} */ diff --git a/include/zephyr/usb/usb_ch9.h b/include/zephyr/usb/usb_ch9.h index 8ecadf64bd8..0c4a3ac0608 100644 --- a/include/zephyr/usb/usb_ch9.h +++ b/include/zephyr/usb/usb_ch9.h @@ -354,8 +354,8 @@ struct usb_association_descriptor { /** Calculate high speed interrupt endpoint bInterval from a value in microseconds */ #define USB_HS_INT_EP_INTERVAL(us) CLAMP((ilog2((us) / 125U) + 1U), 1U, 16U) -/** Calculate high speed isochronous endpoint bInterval from a value in microseconds */ -#define USB_FS_ISO_EP_INTERVAL(us) CLAMP(((us) / 1000U), 1U, 16U) +/** Calculate full speed isochronous endpoint bInterval from a value in microseconds */ +#define USB_FS_ISO_EP_INTERVAL(us) CLAMP((ilog2((us) / 1000U) + 1U), 1U, 16U) /** Calculate high speed isochronous endpoint bInterval from a value in microseconds */ #define USB_HS_ISO_EP_INTERVAL(us) CLAMP((ilog2((us) / 125U) + 1U), 1U, 16U) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 1545af66c81..029741f076d 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -43,18 +43,6 @@ extern "C" { /* Maximum bulk max packet size the stack supports */ #define USBD_MAX_BULK_MPS COND_CODE_1(USBD_SUPPORTS_HIGH_SPEED, (512), (64)) -/* - * The USB Unicode bString is encoded in UTF16LE, which means it takes up - * twice the amount of bytes than the same string encoded in ASCII7. - * Use this macro to determine the length of the bString array. - * - * bString length without null character: - * bString_length = (sizeof(initializer_string) - 1) * 2 - * or: - * bString_length = sizeof(initializer_string) * 2 - 2 - */ -#define USB_BSTRING_LENGTH(s) (sizeof(s) * 2 - 2) - /* * The length of the string descriptor (bLength) is calculated from the * size of the two octets bLength and bDescriptorType plus the @@ -574,7 +562,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c * @param name Language string descriptor node identifier. */ #define USBD_DESC_LANG_DEFINE(name) \ - static uint16_t langid_##name = sys_cpu_to_le16(0x0409); \ + static const uint16_t langid_##name = sys_cpu_to_le16(0x0409); \ static struct usbd_desc_node name = { \ .str = { \ .idx = 0, \ @@ -597,7 +585,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c * @param d_utype String descriptor usage type */ #define USBD_DESC_STRING_DEFINE(d_name, d_string, d_utype) \ - static uint8_t ascii_##d_name[USB_BSTRING_LENGTH(d_string)] = d_string; \ + static const uint8_t ascii_##d_name[sizeof(d_string)] = d_string; \ static struct usbd_desc_node d_name = { \ .str = { \ .utype = d_utype, \ @@ -641,8 +629,11 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c * * This macro defines a descriptor node that, when added to the device context, * is automatically used as the serial number string descriptor. A valid serial - * number is generated from HWID (HWINFO= whenever this string descriptor is - * requested. + * number is obtained from @ref hwinfo_interface whenever this string + * descriptor is requested. + * + * @note The HWINFO driver must be available and the Kconfig option HWINFO + * enabled. * * @param d_name String descriptor node identifier. */ diff --git a/samples/subsys/usb/uac2_explicit_feedback/CMakeLists.txt b/samples/subsys/usb/uac2_explicit_feedback/CMakeLists.txt index 26ce264c421..56bba10717c 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/CMakeLists.txt +++ b/samples/subsys/usb/uac2_explicit_feedback/CMakeLists.txt @@ -7,8 +7,8 @@ project(usb_audio_async_i2s) include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) target_sources(app PRIVATE src/main.c) -if (CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP) - target_sources(app PRIVATE src/feedback_nrf53.c) +if (CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP OR CONFIG_SOC_SERIES_NRF54HX) + target_sources(app PRIVATE src/feedback_nrf.c) else() target_sources(app PRIVATE src/feedback_dummy.c) endif() diff --git a/samples/subsys/usb/uac2_explicit_feedback/Kconfig b/samples/subsys/usb/uac2_explicit_feedback/Kconfig index bee6118a00d..76373b61469 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/Kconfig +++ b/samples/subsys/usb/uac2_explicit_feedback/Kconfig @@ -9,6 +9,8 @@ config APP_USE_I2S_LRCLK_EDGES_COUNTER Use this to use I2S LRCLK edge counting for calculating feedback. On nRF53 this option requires externally connecting I2S LRCLK back to separate GPIOTE input pin (P1.09). + On nRF54 this option requires externally connecting TDM FSYNC back to + separate GPIOTE input pin (P0.08). endmenu # Source common USB sample options used to initialize new experimental USB diff --git a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 00000000000..0e35fa1f491 --- /dev/null +++ b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1,8 @@ +# Enable timer for asynchronous feedback +CONFIG_NRFX_GPPI=y +CONFIG_NRFX_TIMER131=y +CONFIG_NRFX_GPIOTE130=y + +# Sample is Full-Speed only, prevent High-Speed enumeration +CONFIG_UDC_DRIVER_HIGH_SPEED_SUPPORT_ENABLED=n +CONFIG_USBD_MAX_SPEED_FULL=y diff --git a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..d1667d6e0e0 --- /dev/null +++ b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "../app.overlay" + +&pinctrl { + tdm130_default_alt: tdm130_default_alt { + group1 { + psels = , + , + ; + }; + }; +}; + +i2s_tx: &tdm130 { + status = "okay"; + pinctrl-0 = <&tdm130_default_alt>; + pinctrl-names = "default"; + memory-regions = <&cpuapp_dma_region>; + mck-clock-source = "ACLK"; + sck-clock-source = "ACLK"; +}; + +&audiopll { + frequency = ; + status = "okay"; +}; + +&cpuapp_dma_region { + status = "okay"; +}; + +/* PPI channel 4 for TDM130 MAXCNT */ +&dppic132 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <4>; + source-channels = <4>; + nonsecure-channels = <4>; + status = "okay"; +}; + +/* PPI channel 5 for GPIOTE used for feedback in edge counter mode */ +&gpiote130 { + status = "okay"; + owned-channels = <5>; +}; + +/* GPIOTE130 and TDM130 PPI needs routing to TIMER131 through main APB */ +&dppic130 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <4 5>; + sink-channels = <4 5>; + source-channels = <4 5>; + nonsecure-channels = <4 5>; + status = "okay"; +}; + +/* TIMER131 PPI channel 6 is used for SOF */ +&dppic133 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <4 5 6>; + sink-channels = <4 5 6>; + status = "okay"; +}; + +&timer131 { + status = "okay"; +}; diff --git a/samples/subsys/usb/uac2_explicit_feedback/sample.yaml b/samples/subsys/usb/uac2_explicit_feedback/sample.yaml index bc0797a9812..9a58d1b6443 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/sample.yaml +++ b/samples/subsys/usb/uac2_explicit_feedback/sample.yaml @@ -8,5 +8,7 @@ tests: tags: - usb - i2s - platform_allow: nrf5340dk/nrf5340/cpuapp + platform_allow: + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp harness: TBD diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf53.c b/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c similarity index 89% rename from samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf53.c rename to samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c index 7e949d61211..62a7ad05ef9 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf53.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/feedback_nrf.c @@ -12,18 +12,56 @@ #include #include #include -#include -#include #include LOG_MODULE_REGISTER(feedback, LOG_LEVEL_INF); -static const nrfx_gpiote_t gpiote = NRFX_GPIOTE_INSTANCE(0); +#define FEEDBACK_TIMER_USBD_SOF_CAPTURE 0 +#define FEEDBACK_TIMER_I2S_FRAMESTART_CAPTURE 1 + +#if IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP) + +#include +#include #define FEEDBACK_PIN NRF_GPIO_PIN_MAP(1, 9) +#define FEEDBACK_GPIOTE_INSTANCE_NUMBER 0 #define FEEDBACK_TIMER_INSTANCE_NUMBER 2 -#define FEEDBACK_TIMER_USBD_SOF_CAPTURE 0 -#define FEEDBACK_TIMER_I2S_FRAMESTART_CAPTURE 1 +#define USB_SOF_EVENT_ADDRESS nrf_usbd_event_address_get(NRF_USBD, NRF_USBD_EVENT_SOF) +#define I2S_FRAMESTART_EVENT_ADDRESS nrf_i2s_event_address_get(NRF_I2S0, NRF_I2S_EVENT_FRAMESTART) + +static inline void feedback_target_init(void) +{ + if (IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) { + /* App core is using feedback pin */ + nrf_gpio_pin_control_select(FEEDBACK_PIN, NRF_GPIO_PIN_SEL_APP); + } +} + +#elif IS_ENABLED(CONFIG_SOC_SERIES_NRF54HX) + +#include + +#define FEEDBACK_PIN NRF_GPIO_PIN_MAP(0, 8) +#define FEEDBACK_GPIOTE_INSTANCE_NUMBER 130 +#define FEEDBACK_TIMER_INSTANCE_NUMBER 131 +#define USB_SOF_EVENT_ADDRESS nrf_timer_event_address_get(NRF_TIMER131, NRF_TIMER_EVENT_COMPARE5) +#define I2S_FRAMESTART_EVENT_ADDRESS nrf_tdm_event_address_get(NRF_TDM130, NRF_TDM_EVENT_MAXCNT) + +static inline void feedback_target_init(void) +{ + /* Enable Start-of-Frame workaround in TIMER131 */ + *(volatile uint32_t *)0x5F9A3C04 = 0x00000002; + *(volatile uint32_t *)0x5F9A3C04 = 0x00000003; + *(volatile uint32_t *)0x5F9A3C80 = 0x00000082; +} + +#else +#error "Unsupported target" +#endif + +static const nrfx_gpiote_t gpiote = + NRFX_GPIOTE_INSTANCE(FEEDBACK_GPIOTE_INSTANCE_NUMBER); static const nrfx_timer_t feedback_timer_instance = NRFX_TIMER_INSTANCE(FEEDBACK_TIMER_INSTANCE_NUMBER); @@ -80,13 +118,12 @@ static nrfx_err_t feedback_edge_counter_setup(void) .trigger = NRFX_GPIOTE_TRIGGER_TOGGLE, .p_in_channel = &feedback_gpiote_channel, }; + nrf_gpio_pin_pull_t pull = NRF_GPIO_PIN_PULLUP; nrfx_gpiote_input_pin_config_t input_pin_config = { + .p_pull_config = &pull, .p_trigger_config = &trigger_config, }; - /* App core is using feedback pin */ - nrf_gpio_pin_control_select(FEEDBACK_PIN, NRF_GPIO_PIN_SEL_APP); - err = nrfx_gpiote_channel_alloc(&gpiote, &feedback_gpiote_channel); if (err != NRFX_SUCCESS) { return err; @@ -151,6 +188,8 @@ struct feedback_ctx *feedback_init(void) uint8_t usbd_sof_gppi_channel; uint8_t i2s_framestart_gppi_channel; + feedback_target_init(); + feedback_reset_ctx(&fb_ctx); if (IS_ENABLED(CONFIG_APP_USE_I2S_LRCLK_EDGES_COUNTER)) { @@ -171,7 +210,7 @@ struct feedback_ctx *feedback_init(void) } nrfx_gppi_channel_endpoints_setup(usbd_sof_gppi_channel, - nrf_usbd_event_address_get(NRF_USBD, NRF_USBD_EVENT_SOF), + USB_SOF_EVENT_ADDRESS, nrfx_timer_capture_task_address_get(&feedback_timer_instance, FEEDBACK_TIMER_USBD_SOF_CAPTURE)); nrfx_gppi_fork_endpoint_setup(usbd_sof_gppi_channel, @@ -188,7 +227,7 @@ struct feedback_ctx *feedback_init(void) } nrfx_gppi_channel_endpoints_setup(i2s_framestart_gppi_channel, - nrf_i2s_event_address_get(NRF_I2S0, NRF_I2S_EVENT_FRAMESTART), + I2S_FRAMESTART_EVENT_ADDRESS, nrfx_timer_capture_task_address_get(&feedback_timer_instance, FEEDBACK_TIMER_I2S_FRAMESTART_CAPTURE)); diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/main.c b/samples/subsys/usb/uac2_explicit_feedback/src/main.c index e448416c745..3f4f596f35c 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/main.c @@ -10,7 +10,6 @@ #include #include "feedback.h" -#include #include #include #include @@ -117,7 +116,6 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, */ size = BLOCK_SIZE; memset(buf, 0, size); - sys_cache_data_flush_range(buf, size); } LOG_DBG("Received %d data to input terminal %d", size, terminal); diff --git a/samples/subsys/usb/uac2_implicit_feedback/CMakeLists.txt b/samples/subsys/usb/uac2_implicit_feedback/CMakeLists.txt index 26ce264c421..56bba10717c 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/CMakeLists.txt +++ b/samples/subsys/usb/uac2_implicit_feedback/CMakeLists.txt @@ -7,8 +7,8 @@ project(usb_audio_async_i2s) include(${ZEPHYR_BASE}/samples/subsys/usb/common/common.cmake) target_sources(app PRIVATE src/main.c) -if (CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP) - target_sources(app PRIVATE src/feedback_nrf53.c) +if (CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP OR CONFIG_SOC_SERIES_NRF54HX) + target_sources(app PRIVATE src/feedback_nrf.c) else() target_sources(app PRIVATE src/feedback_dummy.c) endif() diff --git a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 00000000000..1b1edb40666 --- /dev/null +++ b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1,7 @@ +# Enable timer for asynchronous feedback +CONFIG_NRFX_GPPI=y +CONFIG_NRFX_TIMER131=y + +# Sample is Full-Speed only, prevent High-Speed enumeration +CONFIG_UDC_DRIVER_HIGH_SPEED_SUPPORT_ENABLED=n +CONFIG_USBD_MAX_SPEED_FULL=y diff --git a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..0f01b4ef91f --- /dev/null +++ b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "../app.overlay" + +&pinctrl { + tdm130_default_alt: tdm130_default_alt { + group1 { + psels = , + , + , + ; + }; + }; +}; + +i2s_rxtx: &tdm130 { + status = "okay"; + pinctrl-0 = <&tdm130_default_alt>; + pinctrl-names = "default"; + memory-regions = <&cpuapp_dma_region>; + mck-clock-source = "ACLK"; + sck-clock-source = "ACLK"; +}; + +&audiopll { + frequency = ; + status = "okay"; +}; + +&cpuapp_dma_region { + status = "okay"; +}; + +/* PPI channel 4 for TDM130 MAXCNT */ +&dppic132 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <4>; + source-channels = <4>; + nonsecure-channels = <4>; + status = "okay"; +}; + +/* TDM130 PPI needs routing to TIMER131 through main APB */ +&dppic130 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <4>; + sink-channels = <4>; + source-channels = <4>; + nonsecure-channels = <4>; + status = "okay"; +}; + +/* TIMER131 PPI channel 5 is used for SOF */ +&dppic133 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <4 5>; + sink-channels = <4 5>; + status = "okay"; +}; + +&timer131 { + status = "okay"; +}; diff --git a/samples/subsys/usb/uac2_implicit_feedback/sample.yaml b/samples/subsys/usb/uac2_implicit_feedback/sample.yaml index 6682ac0ed83..03cf7145e89 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/sample.yaml +++ b/samples/subsys/usb/uac2_implicit_feedback/sample.yaml @@ -8,5 +8,7 @@ tests: tags: - usb - i2s - platform_allow: nrf5340dk/nrf5340/cpuapp + platform_allow: + - nrf5340dk/nrf5340/cpuapp + - nrf54h20dk/nrf54h20/cpuapp harness: TBD diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf53.c b/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c similarity index 85% rename from samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf53.c rename to samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c index ecc59f1a35a..a7dfad370b8 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf53.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/feedback_nrf.c @@ -10,16 +10,47 @@ #include #include -#include -#include #include LOG_MODULE_REGISTER(feedback, LOG_LEVEL_INF); -#define FEEDBACK_TIMER_INSTANCE_NUMBER 2 #define FEEDBACK_TIMER_USBD_SOF_CAPTURE 0 #define FEEDBACK_TIMER_I2S_FRAMESTART_CAPTURE 1 +#if IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF5340_CPUAPP) + +#include +#include + +#define FEEDBACK_TIMER_INSTANCE_NUMBER 2 +#define USB_SOF_EVENT_ADDRESS nrf_usbd_event_address_get(NRF_USBD, NRF_USBD_EVENT_SOF) +#define I2S_FRAMESTART_EVENT_ADDRESS nrf_i2s_event_address_get(NRF_I2S0, NRF_I2S_EVENT_FRAMESTART) + +static inline void feedback_target_init(void) +{ + /* No target specific init necessary */ +} + +#elif IS_ENABLED(CONFIG_SOC_SERIES_NRF54HX) + +#include + +#define FEEDBACK_TIMER_INSTANCE_NUMBER 131 +#define USB_SOF_EVENT_ADDRESS nrf_timer_event_address_get(NRF_TIMER131, NRF_TIMER_EVENT_COMPARE5) +#define I2S_FRAMESTART_EVENT_ADDRESS nrf_tdm_event_address_get(NRF_TDM130, NRF_TDM_EVENT_MAXCNT) + +static inline void feedback_target_init(void) +{ + /* Enable Start-of-Frame workaround in TIMER131 */ + *(volatile uint32_t *)0x5F9A3C04 = 0x00000002; + *(volatile uint32_t *)0x5F9A3C04 = 0x00000003; + *(volatile uint32_t *)0x5F9A3C80 = 0x00000082; +} + +#else +#error "Unsupported target" +#endif + static const nrfx_timer_t feedback_timer_instance = NRFX_TIMER_INSTANCE(FEEDBACK_TIMER_INSTANCE_NUMBER); @@ -57,6 +88,8 @@ struct feedback_ctx *feedback_init(void) .p_context = NULL, }; + feedback_target_init(); + feedback_reset_ctx(&fb_ctx); err = nrfx_timer_init(&feedback_timer_instance, &cfg, NULL); @@ -73,7 +106,7 @@ struct feedback_ctx *feedback_init(void) } nrfx_gppi_channel_endpoints_setup(usbd_sof_gppi_channel, - nrf_usbd_event_address_get(NRF_USBD, NRF_USBD_EVENT_SOF), + USB_SOF_EVENT_ADDRESS, nrfx_timer_capture_task_address_get(&feedback_timer_instance, FEEDBACK_TIMER_USBD_SOF_CAPTURE)); nrfx_gppi_fork_endpoint_setup(usbd_sof_gppi_channel, @@ -90,7 +123,7 @@ struct feedback_ctx *feedback_init(void) } nrfx_gppi_channel_endpoints_setup(i2s_framestart_gppi_channel, - nrf_i2s_event_address_get(NRF_I2S0, NRF_I2S_EVENT_FRAMESTART), + I2S_FRAMESTART_EVENT_ADDRESS, nrfx_timer_capture_task_address_get(&feedback_timer_instance, FEEDBACK_TIMER_I2S_FRAMESTART_CAPTURE)); diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/main.c b/samples/subsys/usb/uac2_implicit_feedback/src/main.c index f52d1227102..b7720fe0331 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/main.c @@ -10,7 +10,6 @@ #include #include "feedback.h" -#include #include #include #include @@ -38,8 +37,10 @@ LOG_MODULE_REGISTER(uac2_sample, LOG_LEVEL_INF); * errors when USB host decides to perform rapid terminal enable/disable cycles. */ #define I2S_BLOCKS 7 -K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, MAX_BLOCK_SIZE, I2S_BLOCKS, 4); -K_MEM_SLAB_DEFINE_STATIC(i2s_rx_slab, MAX_BLOCK_SIZE, I2S_BLOCKS, 4); +K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, ROUND_UP(MAX_BLOCK_SIZE, UDC_BUF_GRANULARITY), + I2S_BLOCKS, UDC_BUF_ALIGN); +K_MEM_SLAB_DEFINE_STATIC(i2s_rx_slab, ROUND_UP(MAX_BLOCK_SIZE, UDC_BUF_GRANULARITY), + I2S_BLOCKS, UDC_BUF_ALIGN); struct usb_i2s_ctx { const struct device *i2s_dev; @@ -164,7 +165,6 @@ static void uac2_data_recv_cb(const struct device *dev, uint8_t terminal, size = SAMPLES_PER_SOF * BYTES_PER_SLOT; } memset(buf, 0, size); - sys_cache_data_flush_range(buf, size); } LOG_DBG("Received %d data to input terminal %d", size, terminal); @@ -259,7 +259,6 @@ static void process_mic_data(const struct device *dev, struct usb_i2s_ctx *ctx) /* No data available, I2S will restart soon */ return; } - sys_cache_data_invd_range(rx_block, num_bytes); /* I2S operates on 2 channels (stereo) */ rx_samples = num_bytes / (BYTES_PER_SAMPLE * 2); @@ -314,7 +313,6 @@ static void process_mic_data(const struct device *dev, struct usb_i2s_ctx *ctx) ctx->pending_mic_samples = mic_samples; return; } - sys_cache_data_invd_range(rx_block, num_bytes); src = rx_block; rx_samples = num_bytes / (BYTES_PER_SAMPLE * 2); @@ -424,7 +422,6 @@ static void process_mic_data(const struct device *dev, struct usb_i2s_ctx *ctx) } /* Finally send the microphone samples to host */ - sys_cache_data_flush_range(mic_buf, mic_samples * BYTES_PER_SAMPLE); if (usbd_uac2_send(dev, MICROPHONE_IN_TERMINAL_ID, mic_buf, mic_samples * BYTES_PER_SAMPLE) < 0) { k_mem_slab_free(&i2s_rx_slab, mic_buf); diff --git a/subsys/usb/device_next/Kconfig b/subsys/usb/device_next/Kconfig index 987ef5e546e..18b6860b0e6 100644 --- a/subsys/usb/device_next/Kconfig +++ b/subsys/usb/device_next/Kconfig @@ -74,13 +74,6 @@ config USBD_MAX_UDC_MSG help Maximum number of USB device controller events that can be queued. -config USBD_MSG_SLAB_COUNT - int "Maximum number of USB device notification messages" - range 4 64 - default 8 - help - Maximum number of USB device notification messages that can be queued. - config USBD_MSG_DEFERRED_MODE bool "Execute message callback from system workqueue" default y @@ -88,6 +81,13 @@ config USBD_MSG_DEFERRED_MODE Execute message callback from system workqueue. If disabled, message callback will be executed in the device stack context. +config USBD_MSG_SLAB_COUNT + int "Maximum number of USB device notification messages" if USBD_MSG_DEFERRED_MODE + range 4 64 + default 8 + help + Maximum number of USB device notification messages that can be queued. + config USBD_MSG_WORK_DELAY int "USB device notification messages work delay" if USBD_MSG_DEFERRED_MODE range 1 100 @@ -96,6 +96,16 @@ config USBD_MSG_WORK_DELAY Message work may need to be delayed because the device stack is not yet ready to publish the message. The delay unit is milliseconds. +config USBD_HWINFO_DEVID_LENGTH + int "The length of the device ID requested from HWINFO in bytes" + depends on HWINFO + range 8 128 + default 16 + help + Each byte represents two digits in the serial number string + descriptor. This option can be used to limit the length requested + from HWINFO to a meaningful number of digits. + rsource "class/Kconfig" rsource "app/Kconfig.cdc_acm_serial" diff --git a/subsys/usb/device_next/app/Kconfig.cdc_acm_serial b/subsys/usb/device_next/app/Kconfig.cdc_acm_serial index dba212ba9ba..5192d5636d9 100644 --- a/subsys/usb/device_next/app/Kconfig.cdc_acm_serial +++ b/subsys/usb/device_next/app/Kconfig.cdc_acm_serial @@ -16,6 +16,13 @@ menuconfig CDC_ACM_SERIAL_INITIALIZE_AT_BOOT if CDC_ACM_SERIAL_INITIALIZE_AT_BOOT +config CDC_ACM_SERIAL_ENABLE_AT_BOOT + bool "USB device will be enabled at boot" + default y + help + When disabled, the application is responsible for enabling/disabling + the USB device. + config CDC_ACM_SERIAL_MANUFACTURER_STRING string "USB device manufacturer string descriptor" default "Zephyr Project" diff --git a/subsys/usb/device_next/app/cdc_acm_serial.c b/subsys/usb/device_next/app/cdc_acm_serial.c index 6f8e64b3c8c..af081469303 100644 --- a/subsys/usb/device_next/app/cdc_acm_serial.c +++ b/subsys/usb/device_next/app/cdc_acm_serial.c @@ -12,7 +12,7 @@ #include #include -LOG_MODULE_REGISTER(cdc_acm_serial, LOG_LEVEL_DBG); +LOG_MODULE_REGISTER(cdc_acm_serial, CONFIG_USBD_LOG_LEVEL); /* * This is intended for use with cdc-acm-snippet or as a default serial backend @@ -79,19 +79,19 @@ static int cdc_acm_serial_init_device(void) err = usbd_add_descriptor(&cdc_acm_serial, &cdc_acm_serial_lang); if (err) { - LOG_ERR("Failed to initialize language descriptor (%d)", err); + LOG_ERR("Failed to initialize %s (%d)", "language descriptor", err); return err; } err = usbd_add_descriptor(&cdc_acm_serial, &cdc_acm_serial_mfr); if (err) { - LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); + LOG_ERR("Failed to initialize %s (%d)", "manufacturer descriptor", err); return err; } err = usbd_add_descriptor(&cdc_acm_serial, &cdc_acm_serial_product); if (err) { - LOG_ERR("Failed to initialize product descriptor (%d)", err); + LOG_ERR("Failed to initialize %s (%d)", "product descriptor", err); return err; } @@ -99,11 +99,12 @@ static int cdc_acm_serial_init_device(void) err = usbd_add_descriptor(&cdc_acm_serial, &cdc_acm_serial_sn); )) if (err) { - LOG_ERR("Failed to initialize SN descriptor (%d)", err); + LOG_ERR("Failed to initialize %s (%d)", "SN descriptor", err); return err; } - if (usbd_caps_speed(&cdc_acm_serial) == USBD_SPEED_HS) { + if (USBD_SUPPORTS_HIGH_SPEED && + usbd_caps_speed(&cdc_acm_serial) == USBD_SPEED_HS) { err = register_cdc_acm_0(&cdc_acm_serial, USBD_SPEED_HS); if (err) { return err; @@ -117,14 +118,16 @@ static int cdc_acm_serial_init_device(void) err = usbd_init(&cdc_acm_serial); if (err) { - LOG_ERR("Failed to initialize device support"); + LOG_ERR("Failed to initialize %s (%d)", "device support", err); return err; } - err = usbd_enable(&cdc_acm_serial); - if (err) { - LOG_ERR("Failed to enable device support"); - return err; + if (IS_ENABLED(CONFIG_CDC_ACM_SERIAL_ENABLE_AT_BOOT)) { + err = usbd_enable(&cdc_acm_serial); + if (err) { + LOG_ERR("Failed to enable %s (%d)", "device support", err); + return err; + } } return 0; diff --git a/subsys/usb/device_next/class/Kconfig.uac2 b/subsys/usb/device_next/class/Kconfig.uac2 index ad6fb54b38a..5c19e0542c3 100644 --- a/subsys/usb/device_next/class/Kconfig.uac2 +++ b/subsys/usb/device_next/class/Kconfig.uac2 @@ -4,6 +4,7 @@ config USBD_AUDIO2_CLASS bool "USB Audio 2 class support [EXPERIMENTAL]" + select UDC_ENABLE_SOF help USB Audio 2 device class support. diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index 2e55a9a017a..efa3fd6cdf3 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -816,6 +816,9 @@ static int uac2_request(struct usbd_class_data *const c_data, struct net_buf *bu ctx->user_data); } else if (!is_feedback) { ctx->ops->buf_release_cb(dev, terminal, buf->__buf, ctx->user_data); + if (buf->frags) { + ctx->ops->buf_release_cb(dev, terminal, buf->frags->__buf, ctx->user_data); + } } usbd_ep_buf_free(uds_ctx, buf); diff --git a/subsys/usb/device_next/class/usbd_uac2_macros.h b/subsys/usb/device_next/class/usbd_uac2_macros.h index a31278d9cf8..bd83a3c42a2 100644 --- a/subsys/usb/device_next/class/usbd_uac2_macros.h +++ b/subsys/usb/device_next/class/usbd_uac2_macros.h @@ -707,14 +707,22 @@ #define AS_BYTES_PER_SAMPLE(node) \ DT_PROP(node, subslot_size) +#define AS_FS_DATA_EP_BINTERVAL(node) \ + USB_FS_ISO_EP_INTERVAL(DT_PROP_OR(node, polling_period_us, 1000)) + +#define AS_HS_DATA_EP_BINTERVAL(node) \ + USB_HS_ISO_EP_INTERVAL(DT_PROP_OR(node, polling_period_us, 125)) + /* Asynchronous endpoints needs space for 1 extra sample */ #define AS_SAMPLES_PER_FRAME(node) \ - ((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 1000) / 1000) + \ - UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) + (((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 1000) / 1000) \ + << (AS_FS_DATA_EP_BINTERVAL(node) - 1)) + \ + UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) #define AS_SAMPLES_PER_MICROFRAME(node) \ - ((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 8000) / 8000) + \ - UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) + (((ROUND_UP(AS_CLK_MAX_FREQUENCY(node), 8000) / 8000) \ + << (AS_HS_DATA_EP_BINTERVAL(node) - 1)) + \ + UTIL_NOT(AS_IS_SOF_SYNCHRONIZED(node))) #define AS_DATA_EP_SYNC_TYPE(node) \ COND_CODE_1(AS_IS_SOF_SYNCHRONIZED(node), (0x3 << 2), (0x1 << 2)) @@ -745,7 +753,7 @@ AS_DATA_EP_ADDR(node), /* bEndpointAddress */ \ AS_DATA_EP_ATTR(node), /* bmAttributes */ \ U16_LE(AS_FS_DATA_EP_MAX_PACKET_SIZE(node)), /* wMaxPacketSize */ \ - 0x01, /* bInterval */ + AS_FS_DATA_EP_BINTERVAL(node), /* bInterval */ #define AS_ISOCHRONOUS_DATA_ENDPOINT_FS_DESCRIPTORS_ARRAYS(node) \ static uint8_t DESCRIPTOR_NAME(fs_std_data_ep, node)[] = { \ @@ -758,7 +766,7 @@ AS_DATA_EP_ADDR(node), /* bEndpointAddress */ \ AS_DATA_EP_ATTR(node), /* bmAttributes */ \ U16_LE(AS_HS_DATA_EP_MAX_PACKET_SIZE(node)), /* wMaxPacketSize */ \ - 0x01, /* bInterval */ + AS_HS_DATA_EP_BINTERVAL(node), /* bInterval */ #define AS_ISOCHRONOUS_DATA_ENDPOINT_HS_DESCRIPTORS_ARRAYS(node) \ static uint8_t DESCRIPTOR_NAME(hs_std_data_ep, node)[] = { \ diff --git a/subsys/usb/device_next/usbd_ch9.c b/subsys/usb/device_next/usbd_ch9.c index 3adb3574f0c..599eb161178 100644 --- a/subsys/usb/device_next/usbd_ch9.c +++ b/subsys/usb/device_next/usbd_ch9.c @@ -533,13 +533,13 @@ static int sreq_get_desc_cfg(struct usbd_context *const uds_ctx, return 0; } -#define USBD_HWID_SN_MAX 32U +#define USBD_SN_ASCII7_LENGTH (CONFIG_USBD_HWINFO_DEVID_LENGTH * 2) /* Generate valid USB device serial number from hwid */ -static ssize_t get_sn_from_hwid(uint8_t sn[static USBD_HWID_SN_MAX]) +static ssize_t get_sn_from_hwid(uint8_t sn[static USBD_SN_ASCII7_LENGTH]) { static const char hex[] = "0123456789ABCDEF"; - uint8_t hwid[USBD_HWID_SN_MAX / 2U]; + uint8_t hwid[USBD_SN_ASCII7_LENGTH / 2U]; ssize_t hwid_len = -ENOSYS; if (IS_ENABLED(CONFIG_HWINFO)) { @@ -554,36 +554,37 @@ static ssize_t get_sn_from_hwid(uint8_t sn[static USBD_HWID_SN_MAX]) return hwid_len; } - for (ssize_t i = 0; i < hwid_len; i++) { + for (ssize_t i = 0; i < MIN(hwid_len, sizeof(hwid)); i++) { sn[i * 2] = hex[hwid[i] >> 4]; sn[i * 2 + 1] = hex[hwid[i] & 0xF]; } - return hwid_len * 2; + return MIN(hwid_len, sizeof(hwid)) * 2; } /* Copy and convert ASCII-7 string descriptor to UTF16-LE */ static void string_ascii7_to_utf16le(struct usbd_desc_node *const dn, struct net_buf *const buf, const uint16_t wLength) { - uint8_t hwid_sn[USBD_HWID_SN_MAX]; + uint8_t sn_ascii7_str[USBD_SN_ASCII7_LENGTH]; struct usb_desc_header head = { .bDescriptorType = dn->bDescriptorType, }; - uint8_t *ascii7_str; + const uint8_t *ascii7_str; size_t len; size_t i; - if (dn->str.utype == USBD_DUT_STRING_SERIAL_NUMBER && dn->str.use_hwinfo) { - ssize_t hwid_len = get_sn_from_hwid(hwid_sn); + if (IS_ENABLED(CONFIG_HWINFO) && + dn->str.utype == USBD_DUT_STRING_SERIAL_NUMBER && dn->str.use_hwinfo) { + ssize_t sn_ascii7_str_len = get_sn_from_hwid(sn_ascii7_str); - if (hwid_len < 0) { + if (sn_ascii7_str_len < 0) { errno = -ENOTSUP; return; } - head.bLength = sizeof(head) + hwid_len * 2; - ascii7_str = hwid_sn; + head.bLength = sizeof(head) + sn_ascii7_str_len * 2; + ascii7_str = sn_ascii7_str; } else { head.bLength = dn->bLength; ascii7_str = (uint8_t *)dn->ptr; diff --git a/tests/drivers/udc/prj.conf b/tests/drivers/udc/prj.conf index 380d5e74b99..975887e579c 100644 --- a/tests/drivers/udc/prj.conf +++ b/tests/drivers/udc/prj.conf @@ -5,6 +5,7 @@ CONFIG_LOG=y CONFIG_ZTEST=y CONFIG_UDC_DRIVER=y +CONFIG_UDC_ENABLE_SOF=y CONFIG_UDC_BUF_COUNT=16 CONFIG_UDC_BUF_POOL_SIZE=16384 CONFIG_UDC_DRIVER_LOG_LEVEL_INF=y diff --git a/tests/subsys/usb/device_next/prj.conf b/tests/subsys/usb/device_next/prj.conf index 54321edc952..05fc7b0b028 100644 --- a/tests/subsys/usb/device_next/prj.conf +++ b/tests/subsys/usb/device_next/prj.conf @@ -6,6 +6,7 @@ CONFIG_ZTEST=y CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USBD_LOOPBACK_CLASS=y +CONFIG_UDC_ENABLE_SOF=y CONFIG_UHC_DRIVER=y CONFIG_USB_HOST_STACK=y