diff --git a/components/usb/CMakeLists.txt b/components/usb/CMakeLists.txt index fd3210c8ff..ba658c5f6a 100644 --- a/components/usb/CMakeLists.txt +++ b/components/usb/CMakeLists.txt @@ -19,12 +19,9 @@ if(CONFIG_SOC_USB_OTG_SUPPORTED) "usb_helpers.c" "usb_host.c" "usb_private.c" - "usbh.c") - if(NOT ${target} STREQUAL "esp32p4") - list(APPEND srcs "usb_phy.c") - else() - list(APPEND srcs "usb_phy_p4.c") - endif() + "usbh.c" + "usb_phy.c" + ) list(APPEND include "include") list(APPEND priv_includes "private_include") endif() diff --git a/components/usb/include/esp_private/usb_phy.h b/components/usb/include/esp_private/usb_phy.h index e8e892a856..74a25ccc34 100644 --- a/components/usb/include/esp_private/usb_phy.h +++ b/components/usb/include/esp_private/usb_phy.h @@ -11,6 +11,8 @@ #include "soc/soc_caps.h" #include "hal/usb_phy_types.h" +#define USB_PHY_SUPPORTS_P4_OTG11 1 // This version of usb_phy supports P4 OTG1.1 PHY + #ifdef __cplusplus extern "C" { #endif diff --git a/components/usb/include/usb/usb_host.h b/components/usb/include/usb/usb_host.h index d5036fdad9..0524674822 100644 --- a/components/usb/include/usb/usb_host.h +++ b/components/usb/include/usb/usb_host.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/components/usb/test_apps/.build-test-rules.yml b/components/usb/test_apps/.build-test-rules.yml index 44446d7b22..947b7394a3 100644 --- a/components/usb/test_apps/.build-test-rules.yml +++ b/components/usb/test_apps/.build-test-rules.yml @@ -21,10 +21,6 @@ components/usb/test_apps: components/usb/test_apps/phy: enable: - if: SOC_USB_OTG_SUPPORTED == 1 - disable_test: - - if: IDF_TARGET in ["esp32p4"] - temporary: true - reason: ESP32-P4 PHY driver not yet migrated depends_components: - usb depends_filepatterns: diff --git a/components/usb/test_apps/hcd/CMakeLists.txt b/components/usb/test_apps/hcd/CMakeLists.txt index 3294266f46..f22a5c347a 100644 --- a/components/usb/test_apps/hcd/CMakeLists.txt +++ b/components/usb/test_apps/hcd/CMakeLists.txt @@ -6,6 +6,6 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(EXTRA_COMPONENT_DIRS "../common") # "Trim" the build. Include the minimal set of components, main, and anything it depends on. -set(COMPONENTS main) +idf_build_set_property(MINIMAL_BUILD ON) project(test_app_usb_host) diff --git a/components/usb/test_apps/hcd/main/test_hcd_common.c b/components/usb/test_apps/hcd/main/test_hcd_common.c index 3b30beac50..c3f195dd72 100644 --- a/components/usb/test_apps/hcd/main/test_hcd_common.c +++ b/components/usb/test_apps/hcd/main/test_hcd_common.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -153,7 +153,11 @@ hcd_port_handle_t test_hcd_setup(void) // Initialize the internal USB PHY to connect to the USB OTG peripheral usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, +#if CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 has 2 USB-DWC peripherals, each with its dedicated PHY. We support HS+UTMI only ATM. + .target = USB_PHY_TARGET_UTMI, +#else .target = USB_PHY_TARGET_INT, +#endif .otg_mode = USB_OTG_MODE_HOST, .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device .ext_io_conf = NULL, diff --git a/components/usb/test_apps/phy/CMakeLists.txt b/components/usb/test_apps/phy/CMakeLists.txt index 5724e9e640..43d31c8c8c 100644 --- a/components/usb/test_apps/phy/CMakeLists.txt +++ b/components/usb/test_apps/phy/CMakeLists.txt @@ -4,6 +4,6 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. -set(COMPONENTS main) +idf_build_set_property(MINIMAL_BUILD ON) project(test_app_usb_phy) diff --git a/components/usb/test_apps/phy/main/test_app_main.c b/components/usb/test_apps/phy/main/test_app_main.c index a7994e8799..d8c9119f73 100644 --- a/components/usb/test_apps/phy/main/test_app_main.c +++ b/components/usb/test_apps/phy/main/test_app_main.c @@ -10,6 +10,7 @@ #include "esp_private/usb_phy.h" #include "hal/usb_wrap_ll.h" // For USB_WRAP_LL_EXT_PHY_SUPPORTED symbol #include "soc/soc_caps.h" // For SOC_USB_UTMI_PHY_NUM symbol +#include "sdkconfig.h" // For CONFIG_IDF_TARGET_*** #if USB_WRAP_LL_EXT_PHY_SUPPORTED #define EXT_PHY_SUPPORTED 1 @@ -55,9 +56,13 @@ void app_main(void) /** * Test init and deinit of internal FSLS PHY + * + * 1. Init + deinit in Host mode + * 2. Init + deinit in Device mode */ TEST_CASE("Init internal FSLS PHY", "[phy]") { + // Host mode usb_phy_handle_t phy_handle = NULL; const usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, @@ -70,6 +75,20 @@ TEST_CASE("Init internal FSLS PHY", "[phy]") TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle)); TEST_ASSERT_NOT_NULL(phy_handle); TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle)); + + // Device mode + usb_phy_handle_t phy_handle_2 = NULL; + const usb_phy_config_t phy_config_2 = { + .controller = USB_PHY_CTRL_OTG, + .target = USB_PHY_TARGET_INT, + .otg_mode = USB_OTG_MODE_DEVICE, + .otg_speed = USB_PHY_SPEED_FULL, + .ext_io_conf = NULL, + .otg_io_conf = NULL, + }; + TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config_2, &phy_handle_2)); + TEST_ASSERT_NOT_NULL(phy_handle_2); + TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle_2)); } /** @@ -137,44 +156,85 @@ TEST_CASE("Init internal UTMI PHY", "[phy]") } /** - * Test init and deinit of all PHYs at the same time + * Test init and deinit of all PHYs at the same time multiple times */ -TEST_CASE("Init all PHYs", "[phy]") +TEST_CASE("Init all PHYs in a loop", "[phy]") { + for (int i = 0; i < 2; i++) { + usb_phy_handle_t phy_handle = NULL; + usb_phy_handle_t phy_handle_2 = NULL; + usb_phy_config_t phy_config = { + .controller = USB_PHY_CTRL_OTG, + .target = USB_PHY_TARGET_INT, + .otg_mode = USB_OTG_MODE_HOST, + .otg_speed = USB_PHY_SPEED_UNDEFINED, + .ext_io_conf = NULL, + .otg_io_conf = NULL, + }; + TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle)); + TEST_ASSERT_NOT_NULL(phy_handle); + + // Our current targets support either UTMI or external PHY + // so if/else suffice here +#if UTMI_PHY_SUPPORTED + phy_config.target = USB_PHY_TARGET_UTMI; +#else + phy_config.target = USB_PHY_TARGET_EXT; + const usb_phy_ext_io_conf_t ext_io_conf = { // Some random values + .vp_io_num = 1, + .vm_io_num = 1, + .rcv_io_num = 1, + .suspend_n_io_num = 1, + .oen_io_num = 1, + .vpo_io_num = 1, + .vmo_io_num = 1, + .fs_edge_sel_io_num = 1, + }; + phy_config.ext_io_conf = &ext_io_conf; +#endif + TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle_2)); + TEST_ASSERT_NOT_NULL(phy_handle_2); + + TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle)); + TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle_2)); + } +} + +#if CONFIG_IDF_TARGET_ESP32P4 +/** + * Test backward compatibility of ESP32-P4 PHY + * + * Initial P4 device support was for USB-DWC HS and UTMI PHY. To maintain backward compatibility on ESP32-P4 in USB Device mode, + * we select UTMI PHY in case otg_speed is UNDEFINED or HIGH + */ +TEST_CASE("ESP32-P4 TinyUSB backward compatibility", "[phy]") +{ + // This configuration is used in esp_tinyusb usb_phy_handle_t phy_handle = NULL; - usb_phy_handle_t phy_handle_2 = NULL; usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, .target = USB_PHY_TARGET_INT, - .otg_mode = USB_OTG_MODE_HOST, + .otg_mode = USB_OTG_MODE_DEVICE, .otg_speed = USB_PHY_SPEED_UNDEFINED, .ext_io_conf = NULL, .otg_io_conf = NULL, }; TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle)); TEST_ASSERT_NOT_NULL(phy_handle); - - // Our current targets support either UTMI or external PHY - // so if/else suffice here -#if UTMI_PHY_SUPPORTED - phy_config.target = USB_PHY_TARGET_UTMI; -#else - const usb_phy_ext_io_conf_t ext_io_conf = { // Some random values - .vp_io_num = 1, - .vm_io_num = 1, - .rcv_io_num = 1, - .suspend_n_io_num = 1, - .oen_io_num = 1, - .vpo_io_num = 1, - .vmo_io_num = 1, - .fs_edge_sel_io_num = 1, - }; - phy_config.target = USB_PHY_TARGET_EXT; - phy_config.ext_io_conf = &ext_io_conf; -#endif - TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle_2)); - TEST_ASSERT_NOT_NULL(phy_handle_2); - TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle)); + + // This configuration is used in upstream tinyusb examples + usb_phy_handle_t phy_handle_2 = NULL; + usb_phy_config_t phy_config_2 = { + .controller = USB_PHY_CTRL_OTG, + .target = USB_PHY_TARGET_INT, + .otg_mode = USB_OTG_MODE_DEVICE, + .otg_speed = USB_PHY_SPEED_HIGH, + .ext_io_conf = NULL, + .otg_io_conf = NULL, + }; + TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config_2, &phy_handle_2)); + TEST_ASSERT_NOT_NULL(phy_handle_2); TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle_2)); } +#endif diff --git a/components/usb/test_apps/phy/pytest_usb_phy.py b/components/usb/test_apps/phy/pytest_usb_phy.py index 27cb20eca7..fef606d3f0 100644 --- a/components/usb/test_apps/phy/pytest_usb_phy.py +++ b/components/usb/test_apps/phy/pytest_usb_phy.py @@ -8,6 +8,5 @@ from pytest_embedded import Dut @pytest.mark.esp32s3 @pytest.mark.esp32p4 @pytest.mark.generic -@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='P4 PHY not yet migrated') def test_usb_phy(dut: Dut) -> None: dut.run_all_single_board_cases(group='phy') diff --git a/components/usb/test_apps/usb_host/CMakeLists.txt b/components/usb/test_apps/usb_host/CMakeLists.txt index 3294266f46..f22a5c347a 100644 --- a/components/usb/test_apps/usb_host/CMakeLists.txt +++ b/components/usb/test_apps/usb_host/CMakeLists.txt @@ -6,6 +6,6 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) set(EXTRA_COMPONENT_DIRS "../common") # "Trim" the build. Include the minimal set of components, main, and anything it depends on. -set(COMPONENTS main) +idf_build_set_property(MINIMAL_BUILD ON) project(test_app_usb_host) diff --git a/components/usb/usb_host.c b/components/usb/usb_host.c index e0844ad49f..0f9e017ecc 100644 --- a/components/usb/usb_host.c +++ b/components/usb/usb_host.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -469,7 +469,11 @@ esp_err_t usb_host_install(const usb_host_config_t *config) // Host Library defaults to internal PHY usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, +#if CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 has 2 USB-DWC peripherals, each with its dedicated PHY. We support HS+UTMI only ATM. + .target = USB_PHY_TARGET_UTMI, +#else .target = USB_PHY_TARGET_INT, +#endif .otg_mode = USB_OTG_MODE_HOST, .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device .ext_io_conf = NULL, diff --git a/components/usb/usb_phy.c b/components/usb/usb_phy.c index 5f1a6e7b22..5f8e89c963 100644 --- a/components/usb/usb_phy.c +++ b/components/usb/usb_phy.c @@ -6,6 +6,7 @@ #include #include +#include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "esp_log.h" #include "esp_check.h" @@ -13,6 +14,7 @@ #include "esp_private/usb_phy.h" #include "soc/usb_dwc_periph.h" #include "hal/usb_wrap_hal.h" +#include "hal/usb_utmi_hal.h" #include "esp_rom_gpio.h" #include "driver/gpio.h" #include "hal/gpio_ll.h" @@ -41,7 +43,8 @@ struct phy_context_t { }; typedef struct { - phy_context_t *internal_phy; /**< internal PHY context */ + phy_context_t *fsls_phy; /**< internal FSLS PHY context */ + phy_context_t *utmi_phy; /**< internal UTMI PHY context */ phy_context_t *external_phy; /**< external PHY context */ uint32_t ref_count; /**< reference count used to protect p_phy_ctrl_obj */ } phy_ctrl_obj_t; @@ -49,6 +52,13 @@ typedef struct { static phy_ctrl_obj_t *p_phy_ctrl_obj = NULL; static portMUX_TYPE phy_spinlock = portMUX_INITIALIZER_UNLOCKED; +// Mapping of OTG1.1 peripheral in usb_dwc_info struct +#if CONFIG_IDF_TARGET_ESP32P4 +static const int otg11_index = 1; +#else +static const int otg11_index = 0; +#endif + static esp_err_t phy_configure_pin_input(int gpio_pin, int signal_idx) { if (gpio_pin != GPIO_NUM_NC) { @@ -124,13 +134,21 @@ esp_err_t usb_phy_otg_set_mode(usb_phy_handle_t handle, usb_otg_mode_t mode) ESP_RETURN_ON_FALSE(handle->controller == USB_PHY_CTRL_OTG, ESP_FAIL, USBPHY_TAG, "phy source is not USB_OTG"); handle->otg_mode = mode; - const usb_otg_signal_conn_t *otg_sig = usb_dwc_info.controllers[0].otg_signals; + // On targets with multiple internal PHYs (FSLS and UTMI) + // we support only fixed PHY to USB-DWC mapping: + // USB-DWC2.0 <-> UTMI PHY + // USB-DWC1.1 <-> FSLS PHY + if (handle->target == USB_PHY_TARGET_UTMI) { + return ESP_OK; // No need to configure anything for UTMI PHY + } + + const usb_otg_signal_conn_t *otg_sig = usb_dwc_info.controllers[otg11_index].otg_signals; assert(otg_sig); if (mode == USB_OTG_MODE_HOST) { esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, otg_sig->iddig, false); // connected connector is A side esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, otg_sig->bvalid, false); esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, otg_sig->vbusvalid, false); // receiving a valid Vbus from host - esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, otg_sig->avalid, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, otg_sig->avalid, false); // HIGH to force USB host mode if (handle->target == USB_PHY_TARGET_INT) { // Configure pull resistors for host usb_wrap_pull_override_vals_t vals = { @@ -156,10 +174,15 @@ esp_err_t usb_phy_otg_dev_set_speed(usb_phy_handle_t handle, usb_phy_speed_t spe ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid"); ESP_RETURN_ON_FALSE(speed < USB_PHY_SPEED_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "speed argument is invalid"); ESP_RETURN_ON_FALSE(handle->controller == USB_PHY_CTRL_OTG, ESP_FAIL, USBPHY_TAG, "phy source is not USB_OTG"); - ESP_RETURN_ON_FALSE((handle->target == USB_PHY_TARGET_INT && handle->otg_mode == USB_OTG_MODE_DEVICE), ESP_FAIL, + ESP_RETURN_ON_FALSE((handle->target != USB_PHY_TARGET_EXT && handle->otg_mode == USB_OTG_MODE_DEVICE), ESP_FAIL, USBPHY_TAG, "set speed not supported"); + ESP_RETURN_ON_FALSE((handle->target == USB_PHY_TARGET_UTMI) == (speed == USB_PHY_SPEED_HIGH), ESP_ERR_NOT_SUPPORTED, USBPHY_TAG, "UTMI can be HighSpeed only"); // This is our software limitation handle->otg_speed = speed; + if (handle->target == USB_PHY_TARGET_UTMI) { + return ESP_OK; // No need to configure anything for UTMI PHY + } + // Configure pull resistors for device usb_wrap_pull_override_vals_t vals = { .dp_pd = false, @@ -179,13 +202,14 @@ esp_err_t usb_phy_otg_dev_set_speed(usb_phy_handle_t handle, usb_phy_speed_t spe esp_err_t usb_phy_action(usb_phy_handle_t handle, usb_phy_action_t action) { ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid"); + ESP_RETURN_ON_FALSE(handle->target != USB_PHY_TARGET_UTMI, ESP_ERR_NOT_SUPPORTED, USBPHY_TAG, "Operation not supported on UTMI PHY"); ESP_RETURN_ON_FALSE(action < USB_PHY_ACTION_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "action argument is invalid"); ESP_RETURN_ON_FALSE((action == USB_PHY_ACTION_HOST_ALLOW_CONN && handle->controller == USB_PHY_CTRL_OTG) || (action == USB_PHY_ACTION_HOST_FORCE_DISCONN && handle->controller == USB_PHY_CTRL_OTG), ESP_ERR_INVALID_ARG, USBPHY_TAG, "wrong target for the action"); esp_err_t ret = ESP_OK; - const usb_fsls_serial_signal_conn_t *fsls_sig = usb_dwc_info.controllers[0].fsls_signals; + const usb_fsls_serial_signal_conn_t *fsls_sig = usb_dwc_info.controllers[otg11_index].fsls_signals; assert(fsls_sig); switch (action) { @@ -277,15 +301,31 @@ cleanup: esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_ret) { + usb_phy_target_t phy_target = config->target; + + // Backward compatibility code: + // Initial P4 device support was for USB-DWC HS and UTMI PHY. + // To maintain backward compatibility on ESP32-P4 in USB Device mode, we select UTMI PHY + // In case otg_speed is UNDEFINED or HIGH +#if CONFIG_IDF_TARGET_ESP32P4 + if (config->otg_mode == USB_OTG_MODE_DEVICE && + (config->otg_speed == USB_PHY_SPEED_UNDEFINED || config->otg_speed == USB_PHY_SPEED_HIGH)) { + if (phy_target != USB_PHY_TARGET_UTMI) { + ESP_LOGW(USBPHY_TAG, "Using UTMI PHY instead of requested %s PHY", (phy_target == USB_PHY_TARGET_INT) ? "internal" : "external"); + phy_target = USB_PHY_TARGET_UTMI; + } + } +#endif + ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, USBPHY_TAG, "config argument is invalid"); - ESP_RETURN_ON_FALSE(config->target < USB_PHY_TARGET_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "specified PHY argument is invalid"); + ESP_RETURN_ON_FALSE(phy_target < USB_PHY_TARGET_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "specified PHY argument is invalid"); ESP_RETURN_ON_FALSE(config->controller < USB_PHY_CTRL_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "specified source argument is invalid"); - ESP_RETURN_ON_FALSE(config->target != USB_PHY_TARGET_EXT || config->ext_io_conf, ESP_ERR_INVALID_ARG, USBPHY_TAG, "ext_io_conf must be provided for ext PHY"); + ESP_RETURN_ON_FALSE(phy_target != USB_PHY_TARGET_EXT || config->ext_io_conf, ESP_ERR_INVALID_ARG, USBPHY_TAG, "ext_io_conf must be provided for ext PHY"); #if !USB_WRAP_LL_EXT_PHY_SUPPORTED - ESP_RETURN_ON_FALSE(config->target != USB_PHY_TARGET_EXT, ESP_ERR_NOT_SUPPORTED, USBPHY_TAG, "Ext PHY not supported on this target"); + ESP_RETURN_ON_FALSE(phy_target != USB_PHY_TARGET_EXT, ESP_ERR_NOT_SUPPORTED, USBPHY_TAG, "Ext PHY not supported on this target"); #endif #if !SOC_USB_UTMI_PHY_NUM - ESP_RETURN_ON_FALSE(config->target != USB_PHY_TARGET_UTMI, ESP_ERR_NOT_SUPPORTED, USBPHY_TAG, "UTMI PHY not supported on this target"); + ESP_RETURN_ON_FALSE(phy_target != USB_PHY_TARGET_UTMI, ESP_ERR_NOT_SUPPORTED, USBPHY_TAG, "UTMI PHY not supported on this target"); #endif ESP_RETURN_ON_ERROR(usb_phy_install(), USBPHY_TAG, "usb_phy driver installation failed"); @@ -299,39 +339,53 @@ esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_r if (phy_context->status == USB_PHY_STATUS_FREE) { new_phy = true; p_phy_ctrl_obj->ref_count++; - if (config->target == USB_PHY_TARGET_EXT) { + if (phy_target == USB_PHY_TARGET_EXT) { p_phy_ctrl_obj->external_phy = phy_context; - } else { - p_phy_ctrl_obj->internal_phy = phy_context; + } else if (phy_target == USB_PHY_TARGET_INT) { + p_phy_ctrl_obj->fsls_phy = phy_context; + } else { // USB_PHY_TARGET_UTMI + p_phy_ctrl_obj->utmi_phy = phy_context; } } portEXIT_CRITICAL(&phy_spinlock); ESP_GOTO_ON_FALSE(new_phy, ESP_ERR_INVALID_STATE, cleanup, USBPHY_TAG, "selected PHY is in use"); - phy_context->target = config->target; + phy_context->target = phy_target; phy_context->controller = config->controller; phy_context->status = USB_PHY_STATUS_IN_USE; - USB_PHY_RCC_ATOMIC() { - usb_wrap_hal_init(&phy_context->wrap_hal); + if (phy_target != USB_PHY_TARGET_UTMI) { + USB_PHY_RCC_ATOMIC() { + usb_wrap_hal_init(&phy_context->wrap_hal); + } + } else { +#if (SOC_USB_UTMI_PHY_NUM > 0) + usb_utmi_hal_context_t utmi_hal_context; // Unused for now + USB_PHY_RCC_ATOMIC() { + usb_utmi_hal_init(&utmi_hal_context); + } +#endif } if (config->controller == USB_PHY_CTRL_OTG) { #if USB_WRAP_LL_EXT_PHY_SUPPORTED - usb_wrap_hal_phy_set_external(&phy_context->wrap_hal, (config->target == USB_PHY_TARGET_EXT)); + usb_wrap_hal_phy_set_external(&phy_context->wrap_hal, (phy_target == USB_PHY_TARGET_EXT)); #endif } - if (config->target == USB_PHY_TARGET_INT) { + // For FSLS PHY that shares pads with GPIO peripheral, we must set drive capability to 3 (40mA) +#if !CONFIG_IDF_TARGET_ESP32P4 // TODO: We must set drive capability for FSLS PHY for P4 too, to pass Full Speed eye diagram test + if (phy_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); } +#endif *handle_ret = (usb_phy_handle_t) phy_context; - if (config->ext_io_conf && config->target == USB_PHY_TARGET_EXT) { + if (phy_target == USB_PHY_TARGET_EXT) { phy_context->iopins = (usb_phy_ext_io_conf_t *) calloc(1, sizeof(usb_phy_ext_io_conf_t)); ESP_GOTO_ON_FALSE(phy_context->iopins, ESP_ERR_NO_MEM, cleanup, USBPHY_TAG, "no mem for storing I/O pins"); memcpy(phy_context->iopins, config->ext_io_conf, sizeof(usb_phy_ext_io_conf_t)); - const usb_fsls_serial_signal_conn_t *fsls_sig = usb_dwc_info.controllers[0].fsls_signals; + const usb_fsls_serial_signal_conn_t *fsls_sig = usb_dwc_info.controllers[otg11_index].fsls_signals; ESP_ERROR_CHECK(phy_external_iopins_configure(phy_context->iopins, fsls_sig)); } if (config->otg_mode != USB_PHY_MODE_DEFAULT) { @@ -341,7 +395,7 @@ esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_r ESP_ERROR_CHECK(usb_phy_otg_dev_set_speed(*handle_ret, config->otg_speed)); } if (config->otg_io_conf && (phy_context->controller == USB_PHY_CTRL_OTG)) { - const usb_otg_signal_conn_t *otg_sig = usb_dwc_info.controllers[0].otg_signals; + const usb_otg_signal_conn_t *otg_sig = usb_dwc_info.controllers[otg11_index].otg_signals; ESP_ERROR_CHECK(phy_otg_iopins_configure(config->otg_io_conf, otg_sig)); } return ESP_OK; @@ -366,6 +420,9 @@ static void phy_uninstall(void) USB_PHY_RCC_ATOMIC() { // Disable USB peripheral without reset the module usb_wrap_hal_disable(); +#if (SOC_USB_UTMI_PHY_NUM > 0) + usb_utmi_hal_disable(); +#endif } } portEXIT_CRITICAL(&phy_spinlock); @@ -380,10 +437,12 @@ esp_err_t usb_del_phy(usb_phy_handle_t handle) p_phy_ctrl_obj->ref_count--; if (handle->target == USB_PHY_TARGET_EXT) { p_phy_ctrl_obj->external_phy = NULL; - } else { + } else if (handle->target == USB_PHY_TARGET_INT) { // Clear pullup and pulldown loads on D+ / D-, and disable the pads usb_wrap_hal_phy_disable_pull_override(&handle->wrap_hal); - p_phy_ctrl_obj->internal_phy = NULL; + p_phy_ctrl_obj->fsls_phy = NULL; + } else { // USB_PHY_TARGET_UTMI + p_phy_ctrl_obj->utmi_phy = NULL; } portEXIT_CRITICAL(&phy_spinlock); free(handle->iopins); @@ -399,8 +458,10 @@ esp_err_t usb_phy_get_phy_status(usb_phy_target_t target, usb_phy_status_t *stat if (target == USB_PHY_TARGET_EXT && p_phy_ctrl_obj->external_phy) { *status = p_phy_ctrl_obj->external_phy->status; - } else if (target == USB_PHY_TARGET_INT && p_phy_ctrl_obj->internal_phy) { - *status = p_phy_ctrl_obj->internal_phy->status; + } else if (target == USB_PHY_TARGET_INT && p_phy_ctrl_obj->fsls_phy) { + *status = p_phy_ctrl_obj->fsls_phy->status; + } else if (target == USB_PHY_TARGET_UTMI && p_phy_ctrl_obj->utmi_phy) { + *status = p_phy_ctrl_obj->utmi_phy->status; } else { *status = USB_PHY_STATUS_FREE; } diff --git a/components/usb/usb_phy_p4.c b/components/usb/usb_phy_p4.c deleted file mode 100644 index 43611c393c..0000000000 --- a/components/usb/usb_phy_p4.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -// TODO: Remove this file when proper support of P4 PHYs is implemented IDF-11144 -#include "hal/usb_utmi_hal.h" -#include "esp_private/usb_phy.h" - -#include "soc/soc_caps.h" -#if SOC_RCC_IS_INDEPENDENT -#define USB_UTMI_BUS_CLK_ATOMIC() -#else -#include "esp_private/periph_ctrl.h" -#define USB_UTMI_BUS_CLK_ATOMIC() PERIPH_RCC_ATOMIC() -#endif - -static usb_utmi_hal_context_t s_utmi_hal_context; - -esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_ret) -{ - USB_UTMI_BUS_CLK_ATOMIC() { - usb_utmi_hal_init(&s_utmi_hal_context); - } - return ESP_OK; -} - -esp_err_t usb_del_phy(usb_phy_handle_t handle) -{ - // Note: handle argument is not checked, because we don't have phy_handle for P4 yet - USB_UTMI_BUS_CLK_ATOMIC() { - usb_utmi_hal_disable(); - } - return ESP_OK; -} - -esp_err_t usb_phy_get_phy_status(usb_phy_target_t target, usb_phy_status_t *status) -{ - return ESP_OK; -} - -esp_err_t usb_phy_action(usb_phy_handle_t handle, usb_phy_action_t action) -{ - return ESP_OK; -} diff --git a/examples/peripherals/usb/README.md b/examples/peripherals/usb/README.md index 2d23a497d7..b7bbe88360 100644 --- a/examples/peripherals/usb/README.md +++ b/examples/peripherals/usb/README.md @@ -1,23 +1,28 @@ # USB-OTG Examples -See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples. +See the [README.md](../../README.md) file in the upper level [examples](../../) directory for more information about examples. ## Common Pin Assignments -Pin assignment is only needed for ESP chips that have an USB-OTG peripheral. +Pin assignment is only needed for ESP chips that have a USB-OTG peripheral. If your board doesn't have a USB connector connected to the USB-OTG dedicated GPIOs, you may have to DIY a cable and connect **D+** and **D-** to the pins listed below. ``` -ESP BOARD USB CONNECTOR (type A) - -- - | || VCC -[USBPHY_DM_NUM] ------> | || D- -[USBPHY_DP_NUM] ------> | || D+ - | || GND - -- +ESP BOARD USB CONNECTOR (type A) + -- + | || VBUS (5V) +[USB_DM] ------> | || D- +[USB_DP] ------> | || D+ + | || GND + -- ``` -Refer to `soc/usb_pins.h` to find the real GPIO number of **USBPHY_DP_NUM** and **USBPHY_DM_NUM**. -| | USB_DP | USB_DM | -| ----------- | ------ | ------ | -| ESP32-S2/S3 | GPIO20 | GPIO19 | +### USB PHY pin mapping + +| | USB_DP | USB_DM | +| ------------ | ------ | ------ | +| ESP32-S2/S3 | GPIO20 | GPIO19 | +| ESP32-P4 2.0 | pin 51 | pin 50 | +| ESP32-P4 1.1 | GPIO27 | GPIO26 | + +> Note: On the ESP32-P4, the USB 2.0 PHY pins are dedicated to USB-OTG functionality and cannot be used as general-purpose GPIOs.