refactor(usb/phy): Merge P4 and S2/S3 PHYs into one module

P4 had separate PHY implementation for initial bring-up,
now we can merge it with the original PHY driver.
This commit is contained in:
Tomas Rezucha
2025-01-08 13:00:02 +01:00
parent 19bdc77e55
commit 6d97cd0aa1
14 changed files with 208 additions and 126 deletions

View File

@ -19,12 +19,9 @@ if(CONFIG_SOC_USB_OTG_SUPPORTED)
"usb_helpers.c" "usb_helpers.c"
"usb_host.c" "usb_host.c"
"usb_private.c" "usb_private.c"
"usbh.c") "usbh.c"
if(NOT ${target} STREQUAL "esp32p4") "usb_phy.c"
list(APPEND srcs "usb_phy.c") )
else()
list(APPEND srcs "usb_phy_p4.c")
endif()
list(APPEND include "include") list(APPEND include "include")
list(APPEND priv_includes "private_include") list(APPEND priv_includes "private_include")
endif() endif()

View File

@ -11,6 +11,8 @@
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "hal/usb_phy_types.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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@ -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 * SPDX-License-Identifier: Apache-2.0
*/ */

View File

@ -21,10 +21,6 @@ components/usb/test_apps:
components/usb/test_apps/phy: components/usb/test_apps/phy:
enable: enable:
- if: SOC_USB_OTG_SUPPORTED == 1 - 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: depends_components:
- usb - usb
depends_filepatterns: depends_filepatterns:

View File

@ -6,6 +6,6 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS "../common") set(EXTRA_COMPONENT_DIRS "../common")
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. # "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) project(test_app_usb_host)

View File

@ -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 * 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 // Initialize the internal USB PHY to connect to the USB OTG peripheral
usb_phy_config_t phy_config = { usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG, .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, .target = USB_PHY_TARGET_INT,
#endif
.otg_mode = USB_OTG_MODE_HOST, .otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device
.ext_io_conf = NULL, .ext_io_conf = NULL,

View File

@ -4,6 +4,6 @@ cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. # "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) project(test_app_usb_phy)

View File

@ -10,6 +10,7 @@
#include "esp_private/usb_phy.h" #include "esp_private/usb_phy.h"
#include "hal/usb_wrap_ll.h" // For USB_WRAP_LL_EXT_PHY_SUPPORTED symbol #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 "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 #if USB_WRAP_LL_EXT_PHY_SUPPORTED
#define EXT_PHY_SUPPORTED 1 #define EXT_PHY_SUPPORTED 1
@ -55,9 +56,13 @@ void app_main(void)
/** /**
* Test init and deinit of internal FSLS PHY * 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]") TEST_CASE("Init internal FSLS PHY", "[phy]")
{ {
// Host mode
usb_phy_handle_t phy_handle = NULL; usb_phy_handle_t phy_handle = NULL;
const usb_phy_config_t phy_config = { const usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG, .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_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle));
TEST_ASSERT_NOT_NULL(phy_handle); TEST_ASSERT_NOT_NULL(phy_handle);
TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(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 = NULL;
usb_phy_handle_t phy_handle_2 = NULL;
usb_phy_config_t phy_config = { usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG, .controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_INT, .target = USB_PHY_TARGET_INT,
.otg_mode = USB_OTG_MODE_HOST, .otg_mode = USB_OTG_MODE_DEVICE,
.otg_speed = USB_PHY_SPEED_UNDEFINED, .otg_speed = USB_PHY_SPEED_UNDEFINED,
.ext_io_conf = NULL, .ext_io_conf = NULL,
.otg_io_conf = NULL, .otg_io_conf = NULL,
}; };
TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle)); TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_handle));
TEST_ASSERT_NOT_NULL(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)); 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)); TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_handle_2));
} }
#endif

View File

@ -8,6 +8,5 @@ from pytest_embedded import Dut
@pytest.mark.esp32s3 @pytest.mark.esp32s3
@pytest.mark.esp32p4 @pytest.mark.esp32p4
@pytest.mark.generic @pytest.mark.generic
@pytest.mark.temp_skip_ci(targets=['esp32p4'], reason='P4 PHY not yet migrated')
def test_usb_phy(dut: Dut) -> None: def test_usb_phy(dut: Dut) -> None:
dut.run_all_single_board_cases(group='phy') dut.run_all_single_board_cases(group='phy')

View File

@ -6,6 +6,6 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS "../common") set(EXTRA_COMPONENT_DIRS "../common")
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. # "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) project(test_app_usb_host)

View File

@ -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 * 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 // Host Library defaults to internal PHY
usb_phy_config_t phy_config = { usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG, .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, .target = USB_PHY_TARGET_INT,
#endif
.otg_mode = USB_OTG_MODE_HOST, .otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device
.ext_io_conf = NULL, .ext_io_conf = NULL,

View File

@ -6,6 +6,7 @@
#include <esp_types.h> #include <esp_types.h>
#include <string.h> #include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_check.h" #include "esp_check.h"
@ -13,6 +14,7 @@
#include "esp_private/usb_phy.h" #include "esp_private/usb_phy.h"
#include "soc/usb_dwc_periph.h" #include "soc/usb_dwc_periph.h"
#include "hal/usb_wrap_hal.h" #include "hal/usb_wrap_hal.h"
#include "hal/usb_utmi_hal.h"
#include "esp_rom_gpio.h" #include "esp_rom_gpio.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "hal/gpio_ll.h" #include "hal/gpio_ll.h"
@ -41,7 +43,8 @@ struct phy_context_t {
}; };
typedef struct { 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 */ phy_context_t *external_phy; /**< external PHY context */
uint32_t ref_count; /**< reference count used to protect p_phy_ctrl_obj */ uint32_t ref_count; /**< reference count used to protect p_phy_ctrl_obj */
} phy_ctrl_obj_t; } phy_ctrl_obj_t;
@ -49,6 +52,13 @@ typedef struct {
static phy_ctrl_obj_t *p_phy_ctrl_obj = NULL; static phy_ctrl_obj_t *p_phy_ctrl_obj = NULL;
static portMUX_TYPE phy_spinlock = portMUX_INITIALIZER_UNLOCKED; 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) static esp_err_t phy_configure_pin_input(int gpio_pin, int signal_idx)
{ {
if (gpio_pin != GPIO_NUM_NC) { 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"); ESP_RETURN_ON_FALSE(handle->controller == USB_PHY_CTRL_OTG, ESP_FAIL, USBPHY_TAG, "phy source is not USB_OTG");
handle->otg_mode = mode; 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); assert(otg_sig);
if (mode == USB_OTG_MODE_HOST) { 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->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_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->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) { if (handle->target == USB_PHY_TARGET_INT) {
// Configure pull resistors for host // Configure pull resistors for host
usb_wrap_pull_override_vals_t vals = { 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(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(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->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"); 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; 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 // Configure pull resistors for device
usb_wrap_pull_override_vals_t vals = { usb_wrap_pull_override_vals_t vals = {
.dp_pd = false, .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_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, 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_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) || 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), (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_INVALID_ARG, USBPHY_TAG, "wrong target for the action");
esp_err_t ret = ESP_OK; 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); assert(fsls_sig);
switch (action) { 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) 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, 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->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 #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 #endif
#if !SOC_USB_UTMI_PHY_NUM #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 #endif
ESP_RETURN_ON_ERROR(usb_phy_install(), USBPHY_TAG, "usb_phy driver installation failed"); 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) { if (phy_context->status == USB_PHY_STATUS_FREE) {
new_phy = true; new_phy = true;
p_phy_ctrl_obj->ref_count++; 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; p_phy_ctrl_obj->external_phy = phy_context;
} else { } else if (phy_target == USB_PHY_TARGET_INT) {
p_phy_ctrl_obj->internal_phy = phy_context; 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); portEXIT_CRITICAL(&phy_spinlock);
ESP_GOTO_ON_FALSE(new_phy, ESP_ERR_INVALID_STATE, cleanup, USBPHY_TAG, "selected PHY is in use"); 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->controller = config->controller;
phy_context->status = USB_PHY_STATUS_IN_USE; phy_context->status = USB_PHY_STATUS_IN_USE;
USB_PHY_RCC_ATOMIC() { if (phy_target != USB_PHY_TARGET_UTMI) {
usb_wrap_hal_init(&phy_context->wrap_hal); 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 (config->controller == USB_PHY_CTRL_OTG) {
#if USB_WRAP_LL_EXT_PHY_SUPPORTED #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 #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_DM_NUM, GPIO_DRIVE_CAP_3);
gpio_set_drive_capability(USBPHY_DP_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; *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)); 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"); 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)); 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)); ESP_ERROR_CHECK(phy_external_iopins_configure(phy_context->iopins, fsls_sig));
} }
if (config->otg_mode != USB_PHY_MODE_DEFAULT) { 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)); 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)) { 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)); ESP_ERROR_CHECK(phy_otg_iopins_configure(config->otg_io_conf, otg_sig));
} }
return ESP_OK; return ESP_OK;
@ -366,6 +420,9 @@ static void phy_uninstall(void)
USB_PHY_RCC_ATOMIC() { USB_PHY_RCC_ATOMIC() {
// Disable USB peripheral without reset the module // Disable USB peripheral without reset the module
usb_wrap_hal_disable(); usb_wrap_hal_disable();
#if (SOC_USB_UTMI_PHY_NUM > 0)
usb_utmi_hal_disable();
#endif
} }
} }
portEXIT_CRITICAL(&phy_spinlock); 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--; p_phy_ctrl_obj->ref_count--;
if (handle->target == USB_PHY_TARGET_EXT) { if (handle->target == USB_PHY_TARGET_EXT) {
p_phy_ctrl_obj->external_phy = NULL; 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 // Clear pullup and pulldown loads on D+ / D-, and disable the pads
usb_wrap_hal_phy_disable_pull_override(&handle->wrap_hal); 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); portEXIT_CRITICAL(&phy_spinlock);
free(handle->iopins); 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) { if (target == USB_PHY_TARGET_EXT && p_phy_ctrl_obj->external_phy) {
*status = p_phy_ctrl_obj->external_phy->status; *status = p_phy_ctrl_obj->external_phy->status;
} else if (target == USB_PHY_TARGET_INT && p_phy_ctrl_obj->internal_phy) { } else if (target == USB_PHY_TARGET_INT && p_phy_ctrl_obj->fsls_phy) {
*status = p_phy_ctrl_obj->internal_phy->status; *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 { } else {
*status = USB_PHY_STATUS_FREE; *status = USB_PHY_STATUS_FREE;
} }

View File

@ -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;
}

View File

@ -1,23 +1,28 @@
# USB-OTG Examples # 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 ## 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. 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) ESP BOARD USB CONNECTOR (type A)
-- --
| || VCC | || VBUS (5V)
[USBPHY_DM_NUM] ------> | || D- [USB_DM] ------> | || D-
[USBPHY_DP_NUM] ------> | || D+ [USB_DP] ------> | || D+
| || GND | || 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 | ### USB PHY pin mapping
| ----------- | ------ | ------ |
| ESP32-S2/S3 | GPIO20 | GPIO19 | | | 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.