diff --git a/boards/espressif/esp32s3_devkitc/esp32s3_devkitc_procpu.dts b/boards/espressif/esp32s3_devkitc/esp32s3_devkitc_procpu.dts index a7dec600f433..3edf6d74456b 100644 --- a/boards/espressif/esp32s3_devkitc/esp32s3_devkitc_procpu.dts +++ b/boards/espressif/esp32s3_devkitc/esp32s3_devkitc_procpu.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2024-2025 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,8 @@ aliases { i2c-0 = &i2c0; watchdog0 = &wdt0; + uart-0 = &uart0; + sw0 = &button0; }; chosen { @@ -29,11 +31,6 @@ zephyr,bt-hci = &esp32_bt_hci; }; - aliases { - uart-0 = &uart0; - sw0 = &button0; - }; - buttons { compatible = "gpio-keys"; button0: button_0 { @@ -151,3 +148,7 @@ &wifi { status = "okay"; }; + +zephyr_udc0: &usb_otg { + status = "okay"; +}; diff --git a/boards/espressif/esp32s3_devkitm/esp32s3_devkitm_procpu.dts b/boards/espressif/esp32s3_devkitm/esp32s3_devkitm_procpu.dts index c468871e25e3..14e07794ce83 100644 --- a/boards/espressif/esp32s3_devkitm/esp32s3_devkitm_procpu.dts +++ b/boards/espressif/esp32s3_devkitm/esp32s3_devkitm_procpu.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2022-2025 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,8 @@ aliases { i2c-0 = &i2c0; watchdog0 = &wdt0; + uart-0 = &uart0; + sw0 = &button0; }; chosen { @@ -29,11 +31,6 @@ zephyr,bt-hci = &esp32_bt_hci; }; - aliases { - uart-0 = &uart0; - sw0 = &button0; - }; - buttons { compatible = "gpio-keys"; button0: button_0 { @@ -151,3 +148,7 @@ &wifi { status = "okay"; }; + +zephyr_udc0: &usb_otg { + status = "okay"; +}; diff --git a/boards/espressif/esp32s3_eye/esp32s3_eye_procpu.dts b/boards/espressif/esp32s3_eye/esp32s3_eye_procpu.dts index 7391ad2357e4..518029c2e932 100644 --- a/boards/espressif/esp32s3_eye/esp32s3_eye_procpu.dts +++ b/boards/espressif/esp32s3_eye/esp32s3_eye_procpu.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2024-2025 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -208,3 +208,7 @@ &wifi { status = "okay"; }; + +zephyr_udc0: &usb_otg { + status = "okay"; +}; diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 5a47a23294c8..32068336d837 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -66,6 +66,9 @@ enum dwc2_drv_event_type { /* Get Data FIFO access register */ #define UDC_DWC2_EP_FIFO(base, idx) ((mem_addr_t)base + 0x1000 * (idx + 1)) +/* Percentage limit of how much SPRAM can be allocated for RxFIFO */ +#define MAX_RXFIFO_GDFIFO_PERCENTAGE 25 + enum dwc2_suspend_type { DWC2_SUSPEND_NO_POWER_SAVING, DWC2_SUSPEND_HIBERNATION, @@ -2099,6 +2102,13 @@ static int udc_dwc2_init_controller(const struct device *dev) if (priv->dynfifosizing) { uint32_t gnptxfsiz; uint32_t default_depth; + uint32_t spram_size; + uint32_t max_rxfifo; + + /* Get available SPRAM size and calculate max allocatable RX fifo size */ + val = sys_read32((mem_addr_t)&base->gdfifocfg); + spram_size = usb_dwc2_get_gdfifocfg_gdfifocfg(val); + max_rxfifo = ((spram_size * MAX_RXFIFO_GDFIFO_PERCENTAGE) / 100); /* TODO: For proper runtime FIFO sizing UDC driver would have to * have prior knowledge of the USB configurations. Only with the @@ -2119,7 +2129,7 @@ static int udc_dwc2_init_controller(const struct device *dev) * to store reset value. Read the reset value and make sure that * the programmed value is not greater than what driver sets. */ - priv->rxfifo_depth = MIN(priv->rxfifo_depth, default_depth); + priv->rxfifo_depth = MIN(MIN(priv->rxfifo_depth, default_depth), max_rxfifo); sys_write32(usb_dwc2_set_grxfsiz(priv->rxfifo_depth), grxfsiz_reg); /* Set TxFIFO 0 depth */ @@ -3289,6 +3299,30 @@ static const struct udc_api udc_dwc2_api = { COND_CODE_1(DT_NUM_REGS(DT_DRV_INST(n)), (DT_INST_REG_ADDR(n)), \ (DT_INST_REG_ADDR_BY_NAME(n, core))) +#if !defined(UDC_DWC2_IRQ_DT_INST_DEFINE) +#define UDC_DWC2_IRQ_FLAGS_TYPE0(n) 0 +#define UDC_DWC2_IRQ_FLAGS_TYPE1(n) DT_INST_IRQ(n, type) +#define DW_IRQ_FLAGS(n) \ + _CONCAT(UDC_DWC2_IRQ_FLAGS_TYPE, DT_INST_IRQ_HAS_CELL(n, type))(n) + +#define UDC_DWC2_IRQ_DT_INST_DEFINE(n) \ + static void udc_dwc2_irq_enable_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + udc_dwc2_isr_handler, \ + DEVICE_DT_INST_GET(n), \ + DW_IRQ_FLAGS(n)); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void udc_dwc2_irq_disable_func_##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } +#endif + #define UDC_DWC2_PINCTRL_DT_INST_DEFINE(n) \ COND_CODE_1(DT_INST_PINCTRL_HAS_NAME(n, default), \ (PINCTRL_DT_INST_DEFINE(n)), ()) @@ -3297,11 +3331,6 @@ static const struct udc_api udc_dwc2_api = { COND_CODE_1(DT_INST_PINCTRL_HAS_NAME(n, default), \ ((void *)PINCTRL_DT_INST_DEV_CONFIG_GET(n)), (NULL)) -#define UDC_DWC2_IRQ_FLAGS_TYPE0(n) 0 -#define UDC_DWC2_IRQ_FLAGS_TYPE1(n) DT_INST_IRQ(n, type) -#define DW_IRQ_FLAGS(n) \ - _CONCAT(UDC_DWC2_IRQ_FLAGS_TYPE, DT_INST_IRQ_HAS_CELL(n, type))(n) - /* * A UDC driver should always be implemented as a multi-instance * driver, even if your platform does not require it. @@ -3333,21 +3362,7 @@ static const struct udc_api udc_dwc2_api = { k_thread_name_set(&priv->thread_data, dev->name); \ } \ \ - static void udc_dwc2_irq_enable_func_##n(const struct device *dev) \ - { \ - IRQ_CONNECT(DT_INST_IRQN(n), \ - DT_INST_IRQ(n, priority), \ - udc_dwc2_isr_handler, \ - DEVICE_DT_INST_GET(n), \ - DW_IRQ_FLAGS(n)); \ - \ - irq_enable(DT_INST_IRQN(n)); \ - } \ - \ - static void udc_dwc2_irq_disable_func_##n(const struct device *dev) \ - { \ - irq_disable(DT_INST_IRQN(n)); \ - } \ + UDC_DWC2_IRQ_DT_INST_DEFINE(n) \ \ static struct udc_ep_config ep_cfg_out[DT_INST_PROP(n, num_out_eps)]; \ static struct udc_ep_config ep_cfg_in[DT_INST_PROP(n, num_in_eps)]; \ diff --git a/drivers/usb/udc/udc_dwc2_vendor_quirks.h b/drivers/usb/udc/udc_dwc2_vendor_quirks.h index f5a70063c2e2..8e71a84e11ac 100644 --- a/drivers/usb/udc/udc_dwc2_vendor_quirks.h +++ b/drivers/usb/udc/udc_dwc2_vendor_quirks.h @@ -312,6 +312,171 @@ DT_INST_FOREACH_STATUS_OKAY(QUIRK_NRF_USBHS_DEFINE) #endif /*DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_usbhs) */ +#if DT_HAS_COMPAT_STATUS_OKAY(espressif_esp32_usb_otg) + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct phy_context_t { + usb_phy_target_t target; + usb_phy_controller_t controller; + usb_phy_status_t status; + usb_otg_mode_t otg_mode; + usb_phy_speed_t otg_speed; + usb_phy_ext_io_conf_t *iopins; + usb_wrap_hal_context_t wrap_hal; +}; + +struct usb_dw_esp32_config { + const struct device *clock_dev; + const clock_control_subsys_t clock_subsys; + int irq_source; + int irq_priority; + int irq_flags; + struct phy_context_t *phy_ctx; +}; + +struct usb_dw_esp32_data { + struct intr_handle_data_t *int_handle; +}; + +static void udc_dwc2_isr_handler(const struct device *dev); + +static inline int esp32_usb_otg_init(const struct device *dev, + const struct usb_dw_esp32_config *cfg, + struct usb_dw_esp32_data *data) +{ + int ret; + + if (!device_is_ready(cfg->clock_dev)) { + return -ENODEV; + } + + ret = clock_control_on(cfg->clock_dev, cfg->clock_subsys); + + if (ret != 0) { + return ret; + } + + /* pinout config to work in USB_OTG_MODE_DEVICE */ + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, + false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, false); + + if (cfg->phy_ctx->target == USB_PHY_TARGET_INT) { + gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3); + gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3); + } + + /* allocate interrupt but keep it disabled to avoid + * spurious suspend/resume event at enumeration phase + */ + ret = esp_intr_alloc(cfg->irq_source, + ESP_INTR_FLAG_INTRDISABLED | + ESP_PRIO_TO_FLAGS(cfg->irq_priority) | + ESP_INT_FLAGS_CHECK(cfg->irq_flags), + (intr_handler_t)udc_dwc2_isr_handler, (void *)dev, &data->int_handle); + + return ret; +} + +static inline int esp32_usb_otg_enable_phy(struct phy_context_t *phy_ctx, bool enable) +{ + LOG_MODULE_DECLARE(udc_dwc2, CONFIG_UDC_DRIVER_LOG_LEVEL); + + if (enable) { + usb_wrap_ll_enable_bus_clock(true); + usb_wrap_hal_init(&phy_ctx->wrap_hal); + +#if USB_WRAP_LL_EXT_PHY_SUPPORTED + usb_wrap_hal_phy_set_external(&phy_ctx->wrap_hal, + (phy_ctx->target == USB_PHY_TARGET_EXT)); +#endif + + LOG_DBG("PHY enabled"); + } else { + usb_wrap_ll_enable_bus_clock(false); + usb_wrap_ll_phy_enable_pad(phy_ctx->wrap_hal.dev, false); + + LOG_DBG("PHY disabled"); + } + + return 0; +} + +#define QUIRK_ESP32_USB_OTG_DEFINE(n) \ + \ + static struct phy_context_t phy_ctx_##n = { \ + .target = USB_PHY_TARGET_INT, \ + .controller = USB_PHY_CTRL_OTG, \ + .otg_mode = USB_OTG_MODE_DEVICE, \ + .otg_speed = USB_PHY_SPEED_FULL, \ + .iopins = NULL, \ + .wrap_hal = {}, \ + }; \ + \ + static const struct usb_dw_esp32_config usb_otg_config_##n = { \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \ + .clock_subsys = (clock_control_subsys_t) \ + DT_INST_CLOCKS_CELL(n, offset), \ + .irq_source = DT_INST_IRQ_BY_IDX(n, 0, irq), \ + .irq_priority = DT_INST_IRQ_BY_IDX(n, 0, priority), \ + .irq_flags = DT_INST_IRQ_BY_IDX(n, 0, flags), \ + .phy_ctx = &phy_ctx_##n, \ + }; \ + \ + static struct usb_dw_esp32_data usb_otg_data_##n; \ + \ + static int esp32_usb_otg_init_##n(const struct device *dev) \ + { \ + return esp32_usb_otg_init(dev, \ + &usb_otg_config_##n, &usb_otg_data_##n); \ + } \ + \ + static int esp32_usb_otg_enable_phy_##n(const struct device *dev) \ + { \ + return esp32_usb_otg_enable_phy(&phy_ctx_##n, true); \ + } \ + \ + static int esp32_usb_otg_disable_phy_##n(const struct device *dev) \ + { \ + return esp32_usb_otg_enable_phy(&phy_ctx_##n, false); \ + } \ + \ + const struct dwc2_vendor_quirks dwc2_vendor_quirks_##n = { \ + .init = esp32_usb_otg_init_##n, \ + .post_enable = esp32_usb_otg_enable_phy_##n, \ + .disable = esp32_usb_otg_disable_phy_##n, \ + }; \ + +#define UDC_DWC2_IRQ_DT_INST_DEFINE(n) \ + static void udc_dwc2_irq_enable_func_##n(const struct device *dev) \ + { \ + esp_intr_enable(usb_otg_data_##n.int_handle); \ + } \ + \ + static void udc_dwc2_irq_disable_func_##n(const struct device *dev) \ + { \ + esp_intr_disable(usb_otg_data_##n.int_handle); \ + } + +DT_INST_FOREACH_STATUS_OKAY(QUIRK_ESP32_USB_OTG_DEFINE) + +#endif /*DT_HAS_COMPAT_STATUS_OKAY(espressif_esp32_usb_otg) */ + /* Add next vendor quirks definition above this line */ #endif /* ZEPHYR_DRIVERS_USB_UDC_DWC2_VENDOR_QUIRKS_H */ diff --git a/dts/bindings/usb/espressif,esp32-usb-otg.yaml b/dts/bindings/usb/espressif,esp32-usb-otg.yaml new file mode 100644 index 000000000000..0dd8c07d4ebd --- /dev/null +++ b/dts/bindings/usb/espressif,esp32-usb-otg.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 +# +description: ESP32 USB-OTG (DWC2 compatible controller) + +compatible: "espressif,esp32-usb-otg" + +include: ["snps,dwc2.yaml"] + +properties: + clocks: + required: true diff --git a/dts/xtensa/espressif/esp32s3/esp32s3_common.dtsi b/dts/xtensa/espressif/esp32s3/esp32s3_common.dtsi index 4d78a1127562..d35cecef2dcb 100644 --- a/dts/xtensa/espressif/esp32s3/esp32s3_common.dtsi +++ b/dts/xtensa/espressif/esp32s3/esp32s3_common.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. + * Copyright (c) 2022-2025 Espressif Systems (Shanghai) Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 */ @@ -405,6 +405,20 @@ clocks = <&clock ESP32_USB_MODULE>; }; + usb_otg: usb_otg@60080000 { + compatible = "espressif,esp32-usb-otg", "snps,dwc2"; + reg = <0x60080000 DT_SIZE_K(256)>; + status = "disabled"; + interrupts = ; + interrupt-parent = <&intc>; + clocks = <&clock ESP32_USB_MODULE>; + num-out-eps = <6>; + num-in-eps = <6>; + ghwcfg1 = <0x00000000>; + ghwcfg2 = <0x224dd930>; + ghwcfg4 = <0xd3f0a030>; + }; + timer0: counter@6001f000 { compatible = "espressif,esp32-timer"; reg = <0x6001f000 DT_SIZE_K(4)>; diff --git a/samples/subsys/usb/mass/boards/esp32s3_devkitm_procpu.overlay b/samples/subsys/usb/mass/boards/esp32s3_devkitm_procpu.overlay new file mode 100644 index 000000000000..66bff0e91bdb --- /dev/null +++ b/samples/subsys/usb/mass/boards/esp32s3_devkitm_procpu.overlay @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + msc_disk0 { + compatible = "zephyr,flash-disk"; + partition = <&storage_partition>; + disk-name = "NAND"; + cache-size = <4096>; + }; +};