From 3bd3fccc19307f96c94ec01faae16c03d82f8332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Thu, 15 May 2025 12:14:35 +0200 Subject: [PATCH 01/27] [nrf fromtree] samples: usb: uac2: Leave cache management up to drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not call cache management functions in sample because it does not work properly if driver uses bounce buffers. Signed-off-by: Tomasz Moń (cherry picked from commit 810118d2c64dc56d4cbcceded5bfd9cbb7dcadea) --- samples/subsys/usb/uac2_explicit_feedback/src/main.c | 2 -- samples/subsys/usb/uac2_implicit_feedback/src/main.c | 5 ----- 2 files changed, 7 deletions(-) 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/src/main.c b/samples/subsys/usb/uac2_implicit_feedback/src/main.c index f52d1227102..a41f4b3814d 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 @@ -164,7 +163,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 +257,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 +311,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 +420,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); From 6c7d6a23abd49fb9ce969440010931e5d073c9cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Thu, 15 May 2025 12:18:19 +0200 Subject: [PATCH 02/27] [nrf fromtree] samples: usb: uac2: Make I2S buffers UDC compliant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change I2S mem slabs to adhere to UDC padding and alignment requirements. Signed-off-by: Tomasz Moń (cherry picked from commit 45f1222474dd3b5a70dd8de166bf4c0aa517e8b6) --- samples/subsys/usb/uac2_implicit_feedback/src/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/samples/subsys/usb/uac2_implicit_feedback/src/main.c b/samples/subsys/usb/uac2_implicit_feedback/src/main.c index a41f4b3814d..b7720fe0331 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_implicit_feedback/src/main.c @@ -37,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; From cff82de0d122e2a62fabfdc934378ca9edd0882d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Thu, 15 May 2025 08:34:09 +0200 Subject: [PATCH 03/27] [nrf fromtree] samples: usb: uac2: Implement feedback on nRF54H20 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configuration and feedback implementation necessary to run UAC2 samples on nRF54H20. Limit nRF54H20 to Full-Speed only operation because the samples currently don't have necessary logic to support High-Speed. Signed-off-by: Tomasz Moń (cherry picked from commit e0c2372eebc7400676cfbc30838f2a84c9bcbca3) --- .../usb/uac2_explicit_feedback/CMakeLists.txt | 4 +- .../subsys/usb/uac2_explicit_feedback/Kconfig | 2 + .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 8 +++ .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 72 +++++++++++++++++++ .../usb/uac2_explicit_feedback/sample.yaml | 4 +- .../src/{feedback_nrf53.c => feedback_nrf.c} | 59 ++++++++++++--- .../usb/uac2_implicit_feedback/CMakeLists.txt | 4 +- .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 7 ++ .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 67 +++++++++++++++++ .../usb/uac2_implicit_feedback/sample.yaml | 4 +- .../src/{feedback_nrf53.c => feedback_nrf.c} | 43 +++++++++-- 11 files changed, 253 insertions(+), 21 deletions(-) create mode 100644 samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf create mode 100644 samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay rename samples/subsys/usb/uac2_explicit_feedback/src/{feedback_nrf53.c => feedback_nrf.c} (89%) create mode 100644 samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.conf create mode 100644 samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay rename samples/subsys/usb/uac2_implicit_feedback/src/{feedback_nrf53.c => feedback_nrf.c} (85%) 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..0e6b18cbae9 --- /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 0 for TDM130 MAXCNT */ +&dppic132 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <0>; + source-channels = <0>; + nonsecure-channels = <0>; + status = "okay"; +}; + +/* PPI channel 1 for GPIOTE used for feedback in edge counter mode */ +&gpiote130 { + status = "okay"; + owned-channels = <1>; +}; + +/* GPIOTE130 and TDM130 PPI needs routing to TIMER131 through main APB */ +&dppic130 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <0 1>; + sink-channels = <0 1>; + source-channels = <0 1>; + nonsecure-channels = <0 1>; + status = "okay"; +}; + +/* TIMER131 PPI channel 2 is used for SOF */ +&dppic133 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <0 1 2>; + sink-channels = <0 1 2>; + 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_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..7fec156ff7e --- /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 0 for TDM130 MAXCNT */ +&dppic132 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <0>; + source-channels = <0>; + nonsecure-channels = <0>; + status = "okay"; +}; + +/* TDM130 PPI needs routing to TIMER131 through main APB */ +&dppic130 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <0>; + sink-channels = <0>; + source-channels = <0>; + nonsecure-channels = <0>; + status = "okay"; +}; + +/* TIMER131 PPI channel 1 is used for SOF */ +&dppic133 { + compatible = "nordic,nrf-dppic-global"; + owned-channels = <0 1>; + sink-channels = <0 1>; + 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)); From a4d4a8fa83b02e802674e461d98faf2908245d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Thu, 29 May 2025 14:23:06 +0200 Subject: [PATCH 04/27] [nrf fromtree] drivers: udc_dwc2: Inline vendor quirks if possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Constify vendor quirks structure to not keep it in RAM. Use constified vendor quirks structure directly if there is only one snps,dwc2 instance to allow compiler inlining quirk implementation. Signed-off-by: Tomasz Moń Signed-off-by: Johann Fischer (cherry picked from commit 46b11f1fb2d0d463342487049a9d52775faf9f95) --- drivers/usb/udc/udc_dwc2.c | 10 ++-------- drivers/usb/udc/udc_dwc2.h | 16 +++++++++++++--- drivers/usb/udc/udc_dwc2_vendor_quirks.h | 17 +++++++---------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 269dfd56ee5..02652e0bfc1 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 */ @@ -3196,13 +3197,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..59a83d9d986 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,8 +310,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) */ #if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) From 927c406dc19e77070323346eff497c6db6fcce1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 9 Jun 2025 10:14:11 +0200 Subject: [PATCH 05/27] [nrf fromtree] drivers: udc_dwc2: Disable control IN endpoint on SETUP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New control transfer is started prematurely from device perspective when host timeout occurs. Any data transfer from previous control transfer have to be cancelled prior to handling SETUP data. Unconditionally disable control IN endpoint to prevent race for enqueued buffer between udc_buf_get_all() called in dwc2_handle_evt_setup() and udc_buf_peek() called in dwc2_handle_in_xfercompl(). Signed-off-by: Tomasz Moń (cherry picked from commit 89a81e3c1f9d17eaea403cc5329fcec9d48febbf) --- drivers/usb/udc/udc_dwc2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 02652e0bfc1..856324942d0 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -824,6 +824,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); atomic_and(&priv->xfer_finished, ~(BIT(0) | BIT(16))); buf = udc_buf_get_all(cfg_out); @@ -833,7 +834,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); } From 561bc552917c5658fe8d4c78667dbbd29c3f0a4a Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 22 Apr 2025 10:36:02 +0200 Subject: [PATCH 06/27] [nrf fromtree] drivers: udc: add SOF Kconfig option and SOF event helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add helper to handle SOF interrupts/events and new Kconfig option to disable SOF interrupt. Signed-off-by: Johann Fischer (cherry picked from commit 2d7995747e3a8ee6e894dc8f5dfae62a38bed03c) Signed-off-by: Tomasz Moń --- drivers/usb/udc/Kconfig | 6 ++++++ drivers/usb/udc/udc_common.h | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) 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_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. * From 162196798e83086d3d7d84928d883544e657a7e1 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Wed, 2 Apr 2025 10:43:02 +0200 Subject: [PATCH 07/27] [nrf fromtree] drivers: udc: disable SOF interrupt by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the new Kconfig option is disabled, no SOF events are passed to the higher layer. Signed-off-by: Johann Fischer (cherry picked from commit 7b287ec133cccf0649bc2989d20977b4a310e771) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_ambiq.c | 7 ++++--- drivers/usb/udc/udc_dwc2.c | 20 ++++++++++++-------- drivers/usb/udc/udc_it82xx2.c | 9 +++------ drivers/usb/udc/udc_kinetis.c | 4 ++-- drivers/usb/udc/udc_max32.c | 2 +- drivers/usb/udc/udc_mcux_ehci.c | 2 +- drivers/usb/udc/udc_mcux_ip3511.c | 2 +- drivers/usb/udc/udc_nrf.c | 4 ++-- drivers/usb/udc/udc_numaker.c | 6 ++++-- drivers/usb/udc/udc_renesas_ra.c | 2 +- drivers/usb/udc/udc_rpi_pico.c | 4 ++-- drivers/usb/udc/udc_sam0.c | 2 +- drivers/usb/udc/udc_skeleton.c | 4 ++++ drivers/usb/udc/udc_stm32.c | 2 +- subsys/usb/device_next/class/Kconfig.uac2 | 1 + tests/drivers/udc/prj.conf | 1 + tests/subsys/usb/device_next/prj.conf | 1 + 17 files changed, 42 insertions(+), 31 deletions(-) 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_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 856324942d0..cb52f711fcb 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -2163,11 +2163,12 @@ 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_WKUPINT | USB_DWC2_GINTSTS_USBSUSP, (mem_addr_t)&base->gintmsk); return 0; @@ -2891,7 +2892,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. */ @@ -2899,7 +2901,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) { @@ -2942,11 +2944,13 @@ 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); } 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/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/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 From 7ad84772e23c3d3675df78ca3ba63f415b1a3378 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 12 Jun 2025 18:59:44 +0200 Subject: [PATCH 08/27] [nrf fromtree] usb: device_next: add the missing const qualifier and save some RAM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the USBD_DESC_STRING_DEFINE macro defenition the const qualifier is missing. Also, do not reserve room for the UTF16LE conversion, as this is done on the fly in the stack. Signed-off-by: Johann Fischer (cherry picked from commit 0e127e6b106fab5018f5f0122133ca7aef623212) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 1545af66c81..27b684101b1 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, \ From 2f85d724143d515129f2cf14d6efd32b6f499944 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Mon, 16 Jun 2025 17:03:11 +0200 Subject: [PATCH 09/27] [nrf fromtree] usb: device_next: hide Kconfig option USBD_MSG_SLAB_COUNT if not used MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If deferred mode is not being used, hide the USBD_MSG_SLAB_COUNT option. Signed-off-by: Johann Fischer (cherry picked from commit 452a53ba1a28747ca48a86e16a77ea3d8e1e7185) Signed-off-by: Tomasz Moń --- subsys/usb/device_next/Kconfig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/subsys/usb/device_next/Kconfig b/subsys/usb/device_next/Kconfig index 987ef5e546e..b72de53f0b3 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 From 47e0e53f42363bab88194a34b1aab304c03e4ce1 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 17 Jun 2025 10:20:46 +0200 Subject: [PATCH 10/27] [nrf fromtree] usb: device_next: allow to limit number or digits in serial number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Kconfig option to limit the length requested from HWINFO to a meaningful number of digits. Also, check the length returned by the HWINFO driver and rename the variables to a more suitable name. Signed-off-by: Johann Fischer (cherry picked from commit 76579d21abb2ae22a7e19be1dd71af9cad10da4f) Signed-off-by: Tomasz Moń --- subsys/usb/device_next/Kconfig | 10 ++++++++++ subsys/usb/device_next/usbd_ch9.c | 25 +++++++++++++------------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/subsys/usb/device_next/Kconfig b/subsys/usb/device_next/Kconfig index b72de53f0b3..18b6860b0e6 100644 --- a/subsys/usb/device_next/Kconfig +++ b/subsys/usb/device_next/Kconfig @@ -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/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; From 8fcb7143134ace8e04892360602feb4e6e14078e Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 17 Jun 2025 13:30:18 +0200 Subject: [PATCH 11/27] [nrf fromtree] usb: update USBD_DESC_SERIAL_NUMBER_DEFINE macro description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update USBD_DESC_SERIAL_NUMBER_DEFINE macro description. Signed-off-by: Johann Fischer (cherry picked from commit c7b364e99a3646959c8ebeb2cecfb80764f620d7) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usbd.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 27b684101b1..029741f076d 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -629,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. */ From 9705935fe3dc2cfd68fe1943380d27f124c8e561 Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Thu, 12 Jun 2025 12:46:48 +0200 Subject: [PATCH 12/27] [nrf fromtree] usb: device_next: rework CDC ACM serial for flash usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow optimization if high speed is not supported. Unify strings for error logging. Allow the application to enable/disable the USB device. Signed-off-by: Johann Fischer (cherry picked from commit 09752374c5522644f9cd23991dc84e04bc64d98f) Signed-off-by: Tomasz Moń --- .../device_next/app/Kconfig.cdc_acm_serial | 7 ++++++ subsys/usb/device_next/app/cdc_acm_serial.c | 25 +++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) 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; From 42aa69f189066ce7999bf4ed58c68a8458a2aec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 10 Jun 2025 12:37:55 +0200 Subject: [PATCH 13/27] [nrf fromtree] drivers: udc_dwc2: Use spin lock for synchronization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace irq_lock() with spin lock which is proper synchronization primitive that should be used. Because non-SMP implementations are allowed to optimize spin lock to just locking interrupts the resulting code is the same on all currently supported DWC2 targets. Signed-off-by: Tomasz Moń (cherry picked from commit 70cd0ab56720f973255111ff38e4696669b4f936) --- drivers/usb/udc/udc_dwc2.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index cb52f711fcb..bebe7973a48 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -102,6 +102,7 @@ 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; @@ -462,7 +463,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); @@ -537,7 +538,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) | @@ -549,7 +550,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; } @@ -565,7 +566,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; } @@ -615,7 +616,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, } } - irq_unlock(key); + k_spin_unlock(&priv->lock, key); return 0; } From 141b58012d48bf5570ca456e294d76d410f4ba34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 10 Jun 2025 15:29:41 +0200 Subject: [PATCH 14/27] [nrf fromtree] drivers: udc_dwc2: Optimize incomplete iso handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At High-Speed there is at most 25 us handling window for incomplete iso IN/OUT and therefore determining which endpoints are isochronous is too wasteful. Add lookup variable holding which isochronous endpoints are enabled and limit the search to only enabled endpoints. For applications with just one OUT and one IN isochronous endpoint this is optimal. The lookup variable is updated only when mutex is held. Interrupt handler accesses the variable read-only and in general there is no problem is incomplete iso handling interrupt hits when the lookup variable is updated, because: * when endpoint is just activated, it cannot be source of incomplete iso interrupt because the endpoint is not armed yet * when endpoint is just deactivated, it was first disabled If there is more than one isochronous endpoint same direction then just relying on endpoint enabled is not necessarily optimal. However, in order to be able to limit the search to only armed endpoints, the lookup variable would have to be updated on every transfer preparation and completion which would require more time-expensive synchronization. Signed-off-by: Tomasz Moń (cherry picked from commit 9aff5da68abd9870ab1ee244aeb9633492c34c2c) --- drivers/usb/udc/udc_dwc2.c | 100 +++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index bebe7973a48..779600321e1 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -116,6 +116,8 @@ 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 txf_set; uint16_t pending_tx_flush; uint16_t dfifodepth; @@ -1475,6 +1477,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); @@ -1668,6 +1676,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; @@ -1696,6 +1705,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; } @@ -2306,6 +2321,7 @@ 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); + priv->iso_enabled = 0; data->caps.rwup = true; data->caps.addr_before_status = true; @@ -2762,38 +2778,37 @@ 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; - 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); - 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); - } + /* Try to queue next packet before SOF */ + dwc2_handle_xfer_next(dev, cfg); } } + + eps &= ~BIT(i); } sys_write32(USB_DWC2_GINTSTS_INCOMPISOIN, gintsts_reg); @@ -2817,34 +2832,33 @@ 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); + while (eps) { + uint8_t i = find_lsb_set(eps) - 1; + mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, i); + uint32_t doepctl; - 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; - - 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); - 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); From 79400fb56cb576908389e2ab58aa6e47e8884a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 20 Jan 2025 11:31:59 +0100 Subject: [PATCH 15/27] [nrf fromtree] drivers: udc_dwc2: Disable endpoints asynchronously MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not synchronously wait for endpoint interrupt bits when disarming endpoints. Introduce ep_disabled k_event that makes it possible for application to wait for the action to take effect which is necessary when handling SetFeature(ENDPOINT_HALT) or SetInterface() requests. This change improves incomplete iso IN and OUT handling performance, especially when there are multiple isochronous endpoints that need servicing. Signed-off-by: Tomasz Moń (cherry picked from commit d29719e27ed3615842d8df2d9aff30f63d2f384d) --- drivers/usb/udc/udc_dwc2.c | 230 +++++++++++++++++++++++-------------- 1 file changed, 146 insertions(+), 84 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 779600321e1..09ed5aaa014 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -106,6 +106,8 @@ struct udc_dwc2_data { 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) */ @@ -118,6 +120,9 @@ struct udc_dwc2_data { 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; @@ -145,7 +150,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 @@ -618,6 +624,7 @@ static int dwc2_tx_fifo_write(const struct device *dev, } } + k_event_clear(&priv->ep_disabled, BIT(ep_idx)); k_spin_unlock(&priv->lock, key); return 0; @@ -761,6 +768,8 @@ 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); } @@ -827,7 +836,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); + 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); @@ -1522,16 +1531,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); @@ -1567,104 +1579,55 @@ 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); - - 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); + /* Endpoint gets disabled in INEPNAKEFF handler */ + } - /* Clear Endpoint Disabled interrupt */ - sys_write32(USB_DWC2_DIEPINT_EPDISBLD | USB_DWC2_DIEPINT_XFERCOMPL, diepint_reg); + if (wait) { + uint32_t ret; - /* 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(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); @@ -1688,7 +1651,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; @@ -1719,7 +1682,7 @@ 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) { @@ -1794,7 +1757,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) { @@ -2184,7 +2147,8 @@ static int udc_dwc2_init_controller(const struct device *dev) 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_WKUPINT | USB_DWC2_GINTSTS_USBSUSP | + USB_DWC2_GINTSTS_GOUTNAKEFF, (mem_addr_t)&base->gintmsk); return 0; @@ -2321,6 +2285,10 @@ 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; @@ -2439,6 +2407,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++) { @@ -2453,13 +2422,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)) { @@ -2583,11 +2555,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; @@ -2598,6 +2572,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; @@ -2613,6 +2588,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); } @@ -2688,6 +2690,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)); } } @@ -2754,6 +2757,10 @@ static inline void dwc2_handle_oepint(const struct device *dev) dwc2_handle_out_xfercompl(dev, n); } + if (status & USB_DWC2_DOEPINT_EPDISBLD) { + k_event_post(&priv->ep_disabled, BIT(16 + n)); + } + epint &= ~BIT(n); } @@ -2779,6 +2786,7 @@ static void dwc2_handle_incompisoin(const struct device *dev) 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; while (eps) { uint8_t i = find_lsb_set(eps) - 1; @@ -2796,21 +2804,23 @@ static void dwc2_handle_incompisoin(const struct device *dev) __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); - /* 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); } @@ -2850,7 +2860,7 @@ static void dwc2_handle_incompisoout(const struct device *dev) __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) { @@ -2864,6 +2874,51 @@ static void dwc2_handle_incompisoout(const struct device *dev) 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; @@ -2969,6 +3024,13 @@ static void udc_dwc2_isr_handler(const struct device *dev) 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); From 04f5c8d2605206fd3cf303f469e6add4c686e517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 24 Jun 2025 14:47:44 +0200 Subject: [PATCH 16/27] [nrf fromtree] usb: device_next: uac2: Do not leak double buffered endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UDC drivers use udc_buf_get_all() when dequeueing endpoint. On drivers that require double buffering for isochronous IN endpoint, the dequeue on interface disable will result in class request API called on net_buf with frags set. Call release callback on the buffer pointed in frags because it was passed in using usbd_uac2_send(). Signed-off-by: Tomasz Moń (cherry picked from commit b3e80c5faadda64e1082fcbc492d78cc64a3c185) --- subsys/usb/device_next/class/usbd_uac2.c | 3 +++ 1 file changed, 3 insertions(+) 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); From 6e2ce989ba49c44353de44cda1eda89828b52efc Mon Sep 17 00:00:00 2001 From: Johann Fischer Date: Tue, 1 Jul 2025 17:15:43 +0200 Subject: [PATCH 17/27] [nrf fromtree] drivers: udc_dwc2: do not throw error when FIFO settings can be reused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the endpoint is re-enabled, check if the current FIFO settings can be reused. Further work is needed to improve FIFO memory handling for more advanced interface configurations. Signed-off-by: Johann Fischer (cherry picked from commit 56b359b8f52bbc90e7f31692ace488c9d68083a2) Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 09ed5aaa014..5a47a23294c 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -1328,12 +1328,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); @@ -1342,8 +1336,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; } From 529316feadd0e8e8cf4566acc16a34cc428af2f3 Mon Sep 17 00:00:00 2001 From: Victor Brzeski Date: Fri, 20 Jun 2025 16:16:01 -0700 Subject: [PATCH 18/27] [nrf fromtree] include: kernel: add macros to enable allocation from specific sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds two macros that allow the user to specify the section the memslab buffer should be allocated from. This is useful for systems where memory must reside in DMA-able memory like USB. Signed-off-by: Victor Brzeski (cherry picked from commit 3c47f91be4f879c9f1eac4c2be0a1bdd2de426ff) Signed-off-by: Tomasz Moń --- include/zephyr/kernel.h | 84 +++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 15 deletions(-) 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 \ From 0e816f84677214e29e5760d74fdd45aedd75d65c Mon Sep 17 00:00:00 2001 From: Victor Brzeski Date: Tue, 24 Jun 2025 21:47:06 -0700 Subject: [PATCH 19/27] [nrf fromtree] include: usb_ch9: fix USB_FS_ISO_EP_INTERVAL calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per section 5.6.4 of the USB 2.0 standard, Isochronous Endpoints are derived by the (2^(bInterval-1) * F) formula. The current implementation uses the formula intended for Interrupt Endpoints. Signed-off-by: Victor Brzeski (cherry picked from commit d9c7b19ae06ed134ab2150971e000ead42c32e7f) Signed-off-by: Tomasz Moń --- include/zephyr/usb/usb_ch9.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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) From 74b6be04dabd70cc111e1975a26e3738cb7a25c9 Mon Sep 17 00:00:00 2001 From: Victor Brzeski Date: Tue, 24 Jun 2025 22:47:45 -0700 Subject: [PATCH 20/27] [nrf fromtree] usb: device_next: uac2: support higher bInterval values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds a device-tree prop for the audio streaming terminals to specify the bInterval values for the Isochronous endpoints. Signed-off-by: Victor Brzeski (cherry picked from commit e8638befaf05604550865f3c4c6cecddbb1a409f) Signed-off-by: Tomasz Moń --- .../usb/uac2/zephyr,uac2-audio-streaming.yaml | 8 ++++++++ .../usb/device_next/class/usbd_uac2_macros.h | 20 +++++++++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) 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/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)[] = { \ From 7ba4adfefc0f13b5892a3e8c72601330989522d2 Mon Sep 17 00:00:00 2001 From: Zak Essadaoui Date: Thu, 26 Jun 2025 15:19:02 -0700 Subject: [PATCH 21/27] [nrf fromtree] usb: hid: Define sensor page usages IDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the following: * Sensor related usage IDs in the Sensors' page of the HID standard. * A HID usage macro that works with IDs of 2 bytes. * A macro to define the exponent in a report descriptor. The values and naming came from the following document: https://www.usb.org/sites/default/files/hutrr39b_0.pdf Signed-off-by: Zak Essadaoui (cherry picked from commit 41a83bd3a0a7216d6f1ce1d06f61060d4f66b590) Signed-off-by: Tomasz Moń --- include/zephyr/usb/class/hid.h | 49 +++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) 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 + /** * @} */ From 0db95e7e4083d1010e9952f95502e6cc3f0f14b8 Mon Sep 17 00:00:00 2001 From: Bjarki Arge Andreasen Date: Mon, 23 Jun 2025 11:50:44 +0200 Subject: [PATCH 22/27] [nrf fromtree] drivers: clock_control: nrf: add support for HFCLK24M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the HFCLK24M clock to the clock_control_nrf device driver. Signed-off-by: Bjarki Arge Andreasen (cherry picked from commit bb319603fddcd088a0b93606551d546f00f589a2) Signed-off-by: Tomasz Moń --- drivers/clock_control/clock_control_nrf.c | 25 ++++++++++++++++++- .../drivers/clock_control/nrf_clock_control.h | 5 ++++ 2 files changed, 29 insertions(+), 1 deletion(-) 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/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 \ From a22d3ac4e0a647a04a9367201efb9a5ed6fbc96f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Thu, 26 Jun 2025 10:31:27 +0200 Subject: [PATCH 23/27] [nrf fromtree] drivers: udc_dwc2: Set EP0 IN CNAK only when necessary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adhere to Programming Guide with regards to when the DIEPCTL0.CNAK is set. This allows the driver to survive abusive control transfers with extremely short host timeouts. Signed-off-by: Tomasz Moń (cherry picked from commit d590d27ec9902c1f9020221587ae344ad8597cc6) --- drivers/usb/udc/udc_dwc2.c | 72 ++++++++++++++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 5a47a23294c..8995ce295e2 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -139,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; @@ -428,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); + } } +} - if (!udc_buf_peek(udc_get_ep_cfg(dev, USB_CONTROL_EP_IN))) { - 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 (diepctl & USB_DWC2_DEPCTL_NAKSTS) { + diepctl &= ~USB_DWC2_DEPCTL_EPENA; + diepctl |= USB_DWC2_DEPCTL_CNAK; + sys_write32(diepctl, diepctl_reg); } } @@ -590,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 */ @@ -776,6 +799,7 @@ static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, 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); @@ -803,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); } } @@ -872,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); @@ -879,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; @@ -892,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; @@ -924,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) { @@ -1623,6 +1672,15 @@ static void udc_dwc2_ep_disable(const struct device *dev, /* For IN endpoints STALL is set in addition to SNAK */ dxepctl |= USB_DWC2_DEPCTL_STALL; } + + /* 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; + } + sys_write32(dxepctl, dxepctl_reg); /* Endpoint gets disabled in INEPNAKEFF handler */ @@ -2751,13 +2809,15 @@ 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) { From edee52c0f075680260941ca8ff9c08f5283a8ce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 8 Jul 2025 15:01:53 +0200 Subject: [PATCH 24/27] Revert "[nrf noup] drivers: udc_dwc2: Add nRF54L quirks" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f2576c9a67e790418b94f5c76085efe71def6fa9. Signed-off-by: Tomasz Moń --- drivers/usb/udc/udc_dwc2_vendor_quirks.h | 185 ----------------------- 1 file changed, 185 deletions(-) diff --git a/drivers/usb/udc/udc_dwc2_vendor_quirks.h b/drivers/usb/udc/udc_dwc2_vendor_quirks.h index 59a83d9d986..f5a70063c2e 100644 --- a/drivers/usb/udc/udc_dwc2_vendor_quirks.h +++ b/drivers/usb/udc/udc_dwc2_vendor_quirks.h @@ -312,191 +312,6 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) #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 - -#define NRF_DEFAULT_IRQ_PRIORITY 1 - -/* - * On USBHS, we cannot access the DWC2 register until VBUS is detected and - * valid. If the user tries to force usbd_enable() and the corresponding - * udc_enable() without a "VBUS ready" notification, the event wait will block - * until a valid VBUS signal is detected or until the - * CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT timeout expires. - */ -static K_EVENT_DEFINE(usbhs_events); -#define USBHS_VBUS_READY BIT(0) - -static void vregusb_isr(const void *arg) -{ - const struct device *dev = arg; - - if (NRF_VREGUSB->EVENTS_VBUSDETECTED) { - NRF_VREGUSB->EVENTS_VBUSDETECTED = 0; - k_event_post(&usbhs_events, USBHS_VBUS_READY); - udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); - } - - if (NRF_VREGUSB->EVENTS_VBUSREMOVED) { - NRF_VREGUSB->EVENTS_VBUSREMOVED = 0; - k_event_set_masked(&usbhs_events, 0, USBHS_VBUS_READY); - udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); - } -} - -static inline int usbhs_enable_vreg(const struct device *dev) -{ - IRQ_CONNECT(VREGUSB_IRQn, NRF_DEFAULT_IRQ_PRIORITY, - vregusb_isr, DEVICE_DT_INST_GET(0), 0); - - NRF_VREGUSB->INTEN = VREGUSB_INTEN_VBUSDETECTED_Msk | - VREGUSB_INTEN_VBUSREMOVED_Msk; - NRF_VREGUSB->TASKS_START = 1; - - /* TODO: Determine conditions when VBUSDETECTED is not generated */ - if (sys_read32((mem_addr_t)NRF_VREGUSB + 0x400) & BIT(2)) { - k_event_post(&usbhs_events, USBHS_VBUS_READY); - udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); - } - - irq_enable(VREGUSB_IRQn); - - return 0; -} - -static inline int usbhs_enable_core(const struct device *dev) -{ - NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); - k_timeout_t timeout = K_FOREVER; - - if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, K_NO_WAIT)) { - LOG_WRN("VBUS is not ready, block udc_enable()"); - if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, timeout)) { - return -ETIMEDOUT; - } - } - - /* TODO: Request PCLK24M using clock control driver */ - NRF_CLOCK->TASKS_XO24MSTART = 1; - while (NRF_CLOCK->EVENTS_XO24MSTARTED == 0) { - } - - /* Power up peripheral */ - wrapper->ENABLE = USBHS_ENABLE_CORE_Msk; - - /* Set ID to Device and force D+ pull-up off for now */ - wrapper->PHY.OVERRIDEVALUES = (1 << 31); - wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; - - /* Release PHY power-on reset */ - 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 */ - k_busy_wait(1); - - /* DWC2 opmode is now guaranteed to be Non-Driving, allow D+ pull-up to - * become active once driver clears DCTL SftDiscon bit. - */ - wrapper->PHY.INPUTOVERRIDE = (1 << 31); - - return 0; -} - -static inline int usbhs_disable_core(const struct device *dev) -{ - NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); - - /* Set ID to Device and forcefully disable D+ pull-up */ - wrapper->PHY.OVERRIDEVALUES = (1 << 31); - wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; - - wrapper->ENABLE = 0UL; - - /* TODO: Release PCLK24M using clock control driver */ - NRF_CLOCK->EVENTS_XO24MSTARTED = 0; - NRF_CLOCK->TASKS_XO24MSTOP = 1; - - return 0; -} - -static inline int usbhs_disable_vreg(const struct device *dev) -{ - NRF_VREGUSB->INTEN = 0; - NRF_VREGUSB->TASKS_STOP = 1; - - return 0; -} - -static inline int usbhs_init_caps(const struct device *dev) -{ - struct udc_data *data = dev->data; - - data->caps.can_detect_vbus = true; - data->caps.hs = true; - - return 0; -} - -static inline int usbhs_is_phy_clk_off(const struct device *dev) -{ - return !k_event_test(&usbhs_events, USBHS_VBUS_READY); -} - -static inline int usbhs_post_hibernation_entry(const struct device *dev) -{ - const struct udc_dwc2_config *const config = dev->config; - struct usb_dwc2_reg *const base = config->base; - NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); - - sys_set_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK); - - wrapper->TASKS_STOP = 1; - - return 0; -} - -static inline int usbhs_pre_hibernation_exit(const struct device *dev) -{ - const struct udc_dwc2_config *const config = dev->config; - struct usb_dwc2_reg *const base = config->base; - NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); - - sys_clear_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK); - - wrapper->TASKS_START = 1; - - return 0; -} - -#define QUIRK_NRF_USBHS_DEFINE(n) \ - struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ - .init = usbhs_enable_vreg, \ - .pre_enable = usbhs_enable_core, \ - .disable = usbhs_disable_core, \ - .shutdown = usbhs_disable_vreg, \ - .caps = usbhs_init_caps, \ - .is_phy_clk_off = usbhs_is_phy_clk_off, \ - .post_hibernation_entry = usbhs_post_hibernation_entry, \ - .pre_hibernation_exit = usbhs_pre_hibernation_exit, \ - }; - -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 */ #endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */ From 3730263ce9e8cd18650f1dbc367f8ecc9436ea2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Tue, 8 Apr 2025 15:34:37 +0200 Subject: [PATCH 25/27] [nrf fromlist] drivers: udc_dwc2: Add nRF54LM20A vendor quirks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial implementation of nRF54L quirks necessary for nRF54LM20A. Upstream PR #: 92842 Signed-off-by: Tomasz Moń Signed-off-by: Johann Fischer (cherry picked from commit f48fd8fad88f5cdb8a6600cfc3c8b66980fe0378) --- drivers/usb/udc/udc_dwc2_vendor_quirks.h | 198 +++++++++++++++++++++++ 1 file changed, 198 insertions(+) diff --git a/drivers/usb/udc/udc_dwc2_vendor_quirks.h b/drivers/usb/udc/udc_dwc2_vendor_quirks.h index f5a70063c2e..330ff8d113f 100644 --- a/drivers/usb/udc/udc_dwc2_vendor_quirks.h +++ b/drivers/usb/udc/udc_dwc2_vendor_quirks.h @@ -312,6 +312,204 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) #endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) */ +#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) + +#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 + +/* + * On USBHS, we cannot access the DWC2 register until VBUS is detected and + * valid. If the user tries to force usbd_enable() and the corresponding + * udc_enable() without a "VBUS ready" notification, the event wait will block + * until a valid VBUS signal is detected or until the + * CONFIG_UDC_DWC2_USBHS_VBUS_READY_TIMEOUT timeout expires. + */ +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; + + if (NRF_VREGUSB->EVENTS_VBUSDETECTED) { + NRF_VREGUSB->EVENTS_VBUSDETECTED = 0; + k_event_post(&usbhs_events, USBHS_VBUS_READY); + udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + } + + if (NRF_VREGUSB->EVENTS_VBUSREMOVED) { + NRF_VREGUSB->EVENTS_VBUSREMOVED = 0; + k_event_set_masked(&usbhs_events, 0, USBHS_VBUS_READY); + udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + } +} + +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); + + NRF_VREGUSB->INTEN = VREGUSB_INTEN_VBUSDETECTED_Msk | + VREGUSB_INTEN_VBUSREMOVED_Msk; + NRF_VREGUSB->TASKS_START = 1; + + /* TODO: Determine conditions when VBUSDETECTED is not generated */ + if (sys_read32((mem_addr_t)NRF_VREGUSB + 0x400) & BIT(2)) { + k_event_post(&usbhs_events, USBHS_VBUS_READY); + udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + } + + 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()"); + if (!k_event_wait(&usbhs_events, USBHS_VBUS_READY, false, timeout)) { + return -ETIMEDOUT; + } + } + + /* 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 */ + wrapper->ENABLE = USBHS_ENABLE_CORE_Msk; + + /* Set ID to Device and force D+ pull-up off for now */ + wrapper->PHY.OVERRIDEVALUES = (1 << 31); + wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; + + /* Release PHY power-on reset */ + 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 */ + k_busy_wait(1); + + /* DWC2 opmode is now guaranteed to be Non-Driving, allow D+ pull-up to + * become active once driver clears DCTL SftDiscon bit. + */ + wrapper->PHY.INPUTOVERRIDE = (1 << 31); + + return 0; +} + +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); + wrapper->PHY.INPUTOVERRIDE = (1 << 31) | USBHS_PHY_INPUTOVERRIDE_VBUSVALID_Msk; + + wrapper->ENABLE = 0UL; + + /* 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; +} + +static inline int usbhs_disable_vreg(const struct device *dev) +{ + NRF_VREGUSB->INTEN = 0; + NRF_VREGUSB->TASKS_STOP = 1; + + return 0; +} + +static inline int usbhs_init_caps(const struct device *dev) +{ + struct udc_data *data = dev->data; + + data->caps.can_detect_vbus = true; + data->caps.hs = true; + + return 0; +} + +static inline int usbhs_is_phy_clk_off(const struct device *dev) +{ + return !k_event_test(&usbhs_events, USBHS_VBUS_READY); +} + +static inline int usbhs_post_hibernation_entry(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); + + sys_set_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK); + + wrapper->TASKS_STOP = 1; + + return 0; +} + +static inline int usbhs_pre_hibernation_exit(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + NRF_USBHS_Type *wrapper = USBHS_DT_WRAPPER_REG_ADDR(0); + + sys_clear_bits((mem_addr_t)&base->pcgcctl, USB_DWC2_PCGCCTL_GATEHCLK); + + wrapper->TASKS_START = 1; + + return 0; +} + +#define QUIRK_NRF_USBHS_DEFINE(n) \ + struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ + .init = usbhs_init_vreg_and_clock, \ + .pre_enable = usbhs_enable_core, \ + .disable = usbhs_disable_core, \ + .shutdown = usbhs_disable_vreg, \ + .caps = usbhs_init_caps, \ + .is_phy_clk_off = usbhs_is_phy_clk_off, \ + .post_hibernation_entry = usbhs_post_hibernation_entry, \ + .pre_hibernation_exit = usbhs_pre_hibernation_exit, \ + }; + +DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) + +#endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs_nrf54l) */ + /* Add next vendor quirks definition above this line */ #endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */ From 3843d5f3c31862e6dcf1c533929a72ef341dda94 Mon Sep 17 00:00:00 2001 From: Grzegorz Swiderski Date: Wed, 9 Jul 2025 11:29:45 +0200 Subject: [PATCH 26/27] [nrf fromlist] samples: usb: uac2: Use different PPI channels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using the PPI channels that are currently reserved for Radiocore through the default configuration found in `uicr_periphconf_table.c`. Otherwise, the samples may build but will not work as expected. Upstream PR #: 91826 Signed-off-by: Grzegorz Swiderski (cherry picked from commit bc26475c262d6dcb1ec1b4d8bf53bf4f262382dc) Signed-off-by: Tomasz Moń --- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 26 +++++++++---------- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 22 ++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) 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 index 0e6b18cbae9..d1667d6e0e0 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/samples/subsys/usb/uac2_explicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -34,36 +34,36 @@ i2s_tx: &tdm130 { status = "okay"; }; -/* PPI channel 0 for TDM130 MAXCNT */ +/* PPI channel 4 for TDM130 MAXCNT */ &dppic132 { compatible = "nordic,nrf-dppic-global"; - owned-channels = <0>; - source-channels = <0>; - nonsecure-channels = <0>; + owned-channels = <4>; + source-channels = <4>; + nonsecure-channels = <4>; status = "okay"; }; -/* PPI channel 1 for GPIOTE used for feedback in edge counter mode */ +/* PPI channel 5 for GPIOTE used for feedback in edge counter mode */ &gpiote130 { status = "okay"; - owned-channels = <1>; + owned-channels = <5>; }; /* GPIOTE130 and TDM130 PPI needs routing to TIMER131 through main APB */ &dppic130 { compatible = "nordic,nrf-dppic-global"; - owned-channels = <0 1>; - sink-channels = <0 1>; - source-channels = <0 1>; - nonsecure-channels = <0 1>; + owned-channels = <4 5>; + sink-channels = <4 5>; + source-channels = <4 5>; + nonsecure-channels = <4 5>; status = "okay"; }; -/* TIMER131 PPI channel 2 is used for SOF */ +/* TIMER131 PPI channel 6 is used for SOF */ &dppic133 { compatible = "nordic,nrf-dppic-global"; - owned-channels = <0 1 2>; - sink-channels = <0 1 2>; + owned-channels = <4 5 6>; + sink-channels = <4 5 6>; status = "okay"; }; 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 index 7fec156ff7e..0f01b4ef91f 100644 --- a/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/samples/subsys/usb/uac2_implicit_feedback/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -35,30 +35,30 @@ i2s_rxtx: &tdm130 { status = "okay"; }; -/* PPI channel 0 for TDM130 MAXCNT */ +/* PPI channel 4 for TDM130 MAXCNT */ &dppic132 { compatible = "nordic,nrf-dppic-global"; - owned-channels = <0>; - source-channels = <0>; - nonsecure-channels = <0>; + 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 = <0>; - sink-channels = <0>; - source-channels = <0>; - nonsecure-channels = <0>; + owned-channels = <4>; + sink-channels = <4>; + source-channels = <4>; + nonsecure-channels = <4>; status = "okay"; }; -/* TIMER131 PPI channel 1 is used for SOF */ +/* TIMER131 PPI channel 5 is used for SOF */ &dppic133 { compatible = "nordic,nrf-dppic-global"; - owned-channels = <0 1>; - sink-channels = <0 1>; + owned-channels = <4 5>; + sink-channels = <4 5>; status = "okay"; }; From a54569d10195bf5278b1cba4ee49dc429d38c6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Mon, 14 Jul 2025 15:15:29 +0200 Subject: [PATCH 27/27] [nrf fromlist] drivers: udc_dwc2: Arm control out endpoint in DMA mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was observed that device ceases to work in Buffer DMA mode after GET DEVICE QUALIFIER request is STALLed (when USB stack is limited to Full-Speed only operation). The issue is due to missing dout feed. Clear pending dout feed flag after bus reset (enumeration done) and after stalled control read transfer to allow dout to be feed when necessary. Upstream PR #: 93103 Signed-off-by: Tomasz Moń (cherry picked from commit 83c6c402e206b7c68538594599ea495eb84ae967) --- drivers/usb/udc/udc_dwc2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 8995ce295e2..fca8f607ec7 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -1753,7 +1753,10 @@ static int udc_dwc2_ep_set_halt(const struct device *dev, 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); } @@ -3320,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); }