From 1fcd63922442d97a11bf6450e46991e128e1de1b Mon Sep 17 00:00:00 2001 From: songruojing Date: Thu, 19 Aug 2021 20:28:28 +0800 Subject: [PATCH] usb: Add usb_phy driver to support operations on USB PHY --- components/hal/CMakeLists.txt | 2 + components/hal/component.mk | 2 +- .../hal/esp32s2/include/hal/usb_phy_ll.h | 83 +++++ .../hal/esp32s3/include/hal/usb_phy_ll.h | 127 +++++++ components/hal/include/hal/usb_phy_hal.h | 81 +++++ components/hal/include/hal/usb_phy_types.h | 63 ++++ components/hal/usb_phy_hal.c | 63 ++++ components/soc/esp32s2/CMakeLists.txt | 3 +- components/soc/esp32s2/usb_phy_periph.c | 23 ++ components/soc/esp32s3/CMakeLists.txt | 3 +- components/soc/esp32s3/include/soc/soc_caps.h | 1 + .../soc/esp32s3/include/soc/usb_wrap_struct.h | 2 +- components/soc/esp32s3/usb_phy_periph.c | 23 ++ components/soc/include/soc/usb_phy_periph.h | 35 ++ components/tinyusb/CMakeLists.txt | 2 +- components/usb/CMakeLists.txt | 3 +- components/usb/include/esp_private/usb_phy.h | 142 ++++++++ components/usb/usb_phy.c | 328 ++++++++++++++++++ 18 files changed, 980 insertions(+), 6 deletions(-) create mode 100644 components/hal/esp32s2/include/hal/usb_phy_ll.h create mode 100644 components/hal/esp32s3/include/hal/usb_phy_ll.h create mode 100644 components/hal/include/hal/usb_phy_hal.h create mode 100644 components/hal/include/hal/usb_phy_types.h create mode 100644 components/hal/usb_phy_hal.c create mode 100644 components/soc/esp32s2/usb_phy_periph.c create mode 100644 components/soc/esp32s3/usb_phy_periph.c create mode 100644 components/soc/include/soc/usb_phy_periph.h create mode 100644 components/usb/include/esp_private/usb_phy.h create mode 100644 components/usb/usb_phy.c diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index 9c1488d493..d688177759 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -61,6 +61,7 @@ if(NOT BOOTLOADER_BUILD) "systimer_hal.c" "touch_sensor_hal.c" "usb_hal.c" + "usb_phy_hal.c" "xt_wdt_hal.c" "esp32s2/adc_hal.c" "esp32s2/brownout_hal.c" @@ -83,6 +84,7 @@ if(NOT BOOTLOADER_BUILD) "systimer_hal.c" "touch_sensor_hal.c" "usb_hal.c" + "usb_phy_hal.c" "xt_wdt_hal.c" "esp32s3/brownout_hal.c" "esp32s3/hmac_hal.c" diff --git a/components/hal/component.mk b/components/hal/component.mk index d979fa78b8..c5c1e84abc 100644 --- a/components/hal/component.mk +++ b/components/hal/component.mk @@ -2,7 +2,7 @@ COMPONENT_SRCDIRS := . esp32 COMPONENT_ADD_INCLUDEDIRS := esp32/include include platform_port/include COMPONENT_ADD_LDFRAGMENTS += linker.lf -COMPONENT_OBJEXCLUDE += ./spi_slave_hd_hal.o ./spi_flash_hal_gpspi.o ./spi_slave_hd_hal.o ./ds_hal.o ./gdma_hal.o ./lcd_hal.o ./systimer_hal.o ./usb_hal.o ./usbh_hal.o ./xt_wdt_hal.o +COMPONENT_OBJEXCLUDE += ./spi_slave_hd_hal.o ./spi_flash_hal_gpspi.o ./spi_slave_hd_hal.o ./ds_hal.o ./gdma_hal.o ./lcd_hal.o ./systimer_hal.o ./usb_hal.o ./usbh_hal.o ./usb_phy_hal.o ./xt_wdt_hal.o ifndef CONFIG_ETH_USE_ESP32_EMAC COMPONENT_OBJEXCLUDE += ./emac_hal.o diff --git a/components/hal/esp32s2/include/hal/usb_phy_ll.h b/components/hal/esp32s2/include/hal/usb_phy_ll.h new file mode 100644 index 0000000000..ed175cf9ce --- /dev/null +++ b/components/hal/esp32s2/include/hal/usb_phy_ll.h @@ -0,0 +1,83 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc.h" +#include "soc/system_reg.h" +#include "soc/usb_wrap_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configures the internal PHY for USB_OTG + * + * @param hw Start address of the USB Wrap registers + */ +static inline void usb_phy_ll_int_otg_enable(usb_wrap_dev_t *hw) +{ + //Enable internal PHY + hw->otg_conf.pad_enable = 1; + hw->otg_conf.phy_sel = 0; +} + +/** + * @brief Configures the external PHY for USB_OTG + * + * @param hw Start address of the USB Wrap registers + */ +static inline void usb_phy_ll_ext_otg_enable(usb_wrap_dev_t *hw) +{ + //Enable external PHY + hw->otg_conf.phy_sel = 1; +} + +/** + * @brief Configures port loads for the internal PHY + * + * @param hw Start address of the USB Wrap registers + * @param dp_pu D+ pullup load + * @param dp_pd D+ pulldown load + * @param dm_pu D- pullup load + * @param dm_pd D- pulldown load + */ +static inline void usb_phy_ll_int_load_conf(usb_wrap_dev_t *hw, bool dp_pu, bool dp_pd, bool dm_pu, bool dm_pd) +{ + usb_wrap_otg_conf_reg_t conf = hw->otg_conf; + conf.pad_pull_override = 1; + conf.dp_pullup = dp_pu; + conf.dp_pulldown = dp_pd; + conf.dm_pullup = dm_pu; + conf.dm_pulldown = dm_pd; + hw->otg_conf = conf; +} + +/** + * @brief Enable the internal PHY's test mode + * + * @param hw Start address of the USB Wrap registers + * @param en Whether to enable the internal PHY's test mode + */ +static inline void usb_phy_ll_int_enable_test_mode(usb_wrap_dev_t *hw, bool en) +{ + if (en) { + // Clear USB_WRAP_TEST_CONF_REG + hw->test_conf.val = 0; + // Set USB test pad oen + hw->test_conf.test_usb_wrap_oe = 1; + // Enable USB test mode + hw->test_conf.test_enable = 1; + } else { + hw->test_conf.test_enable = 0; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s3/include/hal/usb_phy_ll.h b/components/hal/esp32s3/include/hal/usb_phy_ll.h new file mode 100644 index 0000000000..fc665552d8 --- /dev/null +++ b/components/hal/esp32s3/include/hal/usb_phy_ll.h @@ -0,0 +1,127 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "soc/soc.h" +#include "soc/system_reg.h" +#include "soc/usb_wrap_struct.h" +#include "soc/rtc_cntl_struct.h" +#include "soc/usb_serial_jtag_struct.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configures the internal PHY for USB_OTG + * + * @param hw Start address of the USB Wrap registers + */ +static inline void usb_phy_ll_int_otg_enable(usb_wrap_dev_t *hw) +{ + hw->otg_conf.pad_enable = 1; + // USB_OTG use internal PHY + hw->otg_conf.phy_sel = 0; + // phy_sel is controlled by the following register value + RTCCNTL.usb_conf.sw_hw_usb_phy_sel = 1; + // phy_sel=sw_usb_phy_sel=1, USB_OTG is connected with internal PHY + RTCCNTL.usb_conf.sw_usb_phy_sel = 1; +} + +/** + * @brief Configures the external PHY for USB_OTG + * + * @param hw Start address of the USB Wrap registers + */ +static inline void usb_phy_ll_ext_otg_enable(usb_wrap_dev_t *hw) +{ + // USB_OTG use external PHY + hw->otg_conf.phy_sel = 1; + // phy_sel is controlled by the following register value + RTCCNTL.usb_conf.sw_hw_usb_phy_sel = 1; + // phy_sel=sw_usb_phy_sel=0, USB_OTG is connected with external PHY through GPIO Matrix + RTCCNTL.usb_conf.sw_usb_phy_sel = 0; +} + +/** + * @brief Configures the internal PHY for USB_Serial_JTAG + * + * @param hw Start address of the USB Serial_JTAG registers + */ +static inline void usb_phy_ll_int_jtag_enable(usb_serial_jtag_dev_t *hw) +{ + // USB_Serial_JTAG use internal PHY + hw->conf0.phy_sel = 0; + // Disable software control USB D+ D- pullup pulldown (Device FS: dp_pullup = 1) + hw->conf0.pad_pull_override = 0; + // Enable USB pad function + hw->conf0.usb_pad_enable = 1; + // phy_sel is controlled by the following register value + RTCCNTL.usb_conf.sw_hw_usb_phy_sel = 1; + // phy_sel=sw_usb_phy_sel=0, USB_Serial_JTAG is connected with internal PHY + RTCCNTL.usb_conf.sw_usb_phy_sel = 0; +} + +/** + * @brief Configures the external PHY for USB_Serial_JTAG + * + * @param hw Start address of the USB Serial_JTAG registers + */ +static inline void usb_phy_ll_ext_jtag_enable(usb_serial_jtag_dev_t *hw) +{ + // USB_Serial_JTAG use external PHY + hw->conf0.phy_sel = 1; + // phy_sel is controlled by the following register value + RTCCNTL.usb_conf.sw_hw_usb_phy_sel = 1; + // phy_sel=sw_usb_phy_sel=1, USB_Serial_JTAG is connected with external PHY + RTCCNTL.usb_conf.sw_usb_phy_sel = 1; +} + +/** + * @brief Configures port loads for the internal PHY + * + * @param hw Start address of the USB Wrap registers + * @param dp_pu D+ pullup load + * @param dp_pd D+ pulldown load + * @param dm_pu D- pullup load + * @param dm_pd D- pulldown load + */ +static inline void usb_phy_ll_int_load_conf(usb_wrap_dev_t *hw, bool dp_pu, bool dp_pd, bool dm_pu, bool dm_pd) +{ + usb_wrap_otg_conf_reg_t conf = hw->otg_conf; + conf.pad_pull_override = 1; + conf.dp_pullup = dp_pu; + conf.dp_pulldown = dp_pd; + conf.dm_pullup = dm_pu; + conf.dm_pulldown = dm_pd; + hw->otg_conf = conf; +} + +/** + * @brief Enable the internal PHY's test mode + * + * @param hw Start address of the USB Wrap registers + * @param en Whether to enable the internal PHY's test mode + */ +static inline void usb_phy_ll_int_enable_test_mode(usb_wrap_dev_t *hw, bool en) +{ + if (en) { + // Clear USB_WRAP_TEST_CONF_REG + hw->test_conf.val = 0; + // Set USB test pad oen + hw->test_conf.test_usb_wrap_oe = 1; + // Enable USB test mode + hw->test_conf.test_enable = 1; + } else { + hw->test_conf.test_enable = 0; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/usb_phy_hal.h b/components/hal/include/hal/usb_phy_hal.h new file mode 100644 index 0000000000..fe77eb8744 --- /dev/null +++ b/components/hal/include/hal/usb_phy_hal.h @@ -0,0 +1,81 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "usb_types_private.h" +#include "usb_phy_types.h" +#include "soc/soc_caps.h" +#include "soc/usb_wrap_struct.h" +#if SOC_USB_SERIAL_JTAG_SUPPORTED +#include "soc/usb_serial_jtag_struct.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Context that should be maintained by both the driver and the HAL + */ +typedef struct { + usb_wrap_dev_t *wrap_dev; /**< Pointer to base address of USB Wrapper registers */ +#if SOC_USB_SERIAL_JTAG_SUPPORTED + usb_serial_jtag_dev_t *jtag_dev; /**< Pointer to base address of USB Serial JTAG registers */ +#endif +} usb_phy_hal_context_t; + +/** + * @brief Init the USB PHY hal. This function should be called first before other hal layer function is called + * + * @param hal Context of the HAL layer + */ +void usb_phy_hal_init(usb_phy_hal_context_t *hal); + +/** + * @brief Configure internal/external PHY for USB_OTG + * + * @param hal Context of the HAL layer + * @param phy_target USB PHY target + */ +void usb_phy_hal_otg_conf(usb_phy_hal_context_t *hal, usb_phy_target_t phy_target); + +#if SOC_USB_SERIAL_JTAG_SUPPORTED +/** + * @brief Configure internal/external PHY for USB_Serial_JTAG + * + * @param hal Context of the HAL layer + * @param phy_target USB PHY target + */ +void usb_phy_hal_jtag_conf(usb_phy_hal_context_t *hal, usb_phy_target_t phy_target); +#endif + +/** + * @brief Configure pullup/pulldown loads for the D+/D- as a host + * + * @param hal Context of the HAL layer + */ +void usb_phy_hal_int_load_conf_host(usb_phy_hal_context_t *hal); + +/** + * @brief Configure pullup/pulldown loads for the D+/D- as a device + * + * @param hal Context of the HAL layer + * @param speed USB speed + */ +void usb_phy_hal_int_load_conf_dev(usb_phy_hal_context_t *hal, usb_priv_speed_t speed); + +/** + * @brief Enable/Disable test mode for internal PHY to mimick host-device disconnection + * + * @param hal Context of the HAL layer + * @param disconn Whether to disconnect + */ +void usb_phy_hal_int_mimick_disconn(usb_phy_hal_context_t *hal, bool disconn); + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/usb_phy_types.h b/components/hal/include/hal/usb_phy_types.h new file mode 100644 index 0000000000..1f5ac431ef --- /dev/null +++ b/components/hal/include/hal/usb_phy_types.h @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* +Note: This header file contains USB2.0 related types and macros that can be used by code specific to the DWC_OTG +controller (i.e., the HW specific layers of the USB host stack). Thus, this header is only meant to be used below (and +including) the HAL layer. For types and macros that are HW implementation agnostic (i.e., HCD layer and above), add them +to the "usb/usb_types_ch9.h" header instead. +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @brief USB PHY target + */ +typedef enum { + USB_PHY_TARGET_INT, /**< USB target is internal PHY */ + USB_PHY_TARGET_EXT, /**< USB target is external PHY */ + USB_PHY_TARGET_MAX, +} usb_phy_target_t; + +/** + * @brief USB PHY source + */ +typedef enum { + USB_PHY_CTRL_OTG, /**< PHY controller is USB OTG */ +#if SOC_USB_SERIAL_JTAG_SUPPORTED + USB_PHY_CTRL_SERIAL_JTAG, /**< PHY controller is USB Serial JTAG */ +#endif + USB_PHY_CTRL_MAX, +} usb_phy_controller_t; + +/** + * @brief USB OTG mode + */ +typedef enum { + USB_PHY_MODE_DEFAULT, /**< USB OTG default mode */ + USB_OTG_MODE_HOST, /**< USB OTG host mode */ + USB_OTG_MODE_DEVICE, /**< USB OTG device mode */ + USB_OTG_MODE_MAX, +} usb_otg_mode_t; + +/** + * @brief USB speed + */ +typedef enum { + USB_PHY_SPEED_UNDEFINED, + USB_PHY_SPEED_LOW, /**< USB Low Speed (1.5 Mbit/s) */ + USB_PHY_SPEED_FULL, /**< USB Full Speed (12 Mbit/s) */ + USB_PHY_SPEED_MAX, +} usb_phy_speed_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/usb_phy_hal.c b/components/hal/usb_phy_hal.c new file mode 100644 index 0000000000..16a8f60d81 --- /dev/null +++ b/components/hal/usb_phy_hal.c @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/usb_phy_ll.h" +#include "hal/usb_phy_hal.h" + +void usb_phy_hal_init(usb_phy_hal_context_t *hal) +{ + hal->wrap_dev = &USB_WRAP; +#if SOC_USB_SERIAL_JTAG_SUPPORTED + hal->jtag_dev = &USB_SERIAL_JTAG; +#endif +} + +void usb_phy_hal_otg_conf(usb_phy_hal_context_t *hal, usb_phy_target_t phy_target) +{ + if (phy_target == USB_PHY_TARGET_EXT) { + usb_phy_ll_ext_otg_enable(hal->wrap_dev); + } else if (phy_target == USB_PHY_TARGET_INT) { + usb_phy_ll_int_otg_enable(hal->wrap_dev); + } +} + +#if SOC_USB_SERIAL_JTAG_SUPPORTED +void usb_phy_hal_jtag_conf(usb_phy_hal_context_t *hal, usb_phy_target_t phy_target) +{ + if (phy_target == USB_PHY_TARGET_EXT) { + usb_phy_ll_ext_jtag_enable(hal->jtag_dev); + } else if (phy_target == USB_PHY_TARGET_INT) { + usb_phy_ll_int_jtag_enable(hal->jtag_dev); + } +} +#endif + +void usb_phy_hal_int_load_conf_host(usb_phy_hal_context_t *hal) +{ + // HOST - upstream: dp_pd = 1, dm_pd = 1 + usb_phy_ll_int_load_conf(hal->wrap_dev, false, true, false, true); +} + +void usb_phy_hal_int_load_conf_dev(usb_phy_hal_context_t *hal, usb_priv_speed_t speed) +{ + // DEVICE - downstream + if (speed == USB_PRIV_SPEED_LOW) { + // LS: dm_pu = 1 + usb_phy_ll_int_load_conf(hal->wrap_dev, false, false, true, false); + } else { + // FS: dp_pu = 1 + usb_phy_ll_int_load_conf(hal->wrap_dev, true, false, false, false); + } +} + +void usb_phy_hal_int_mimick_disconn(usb_phy_hal_context_t *hal, bool disconn) +{ + /* + We mimick a disconnect by enabling the internal PHY's test mode, then forcing the output_enable to HIGH. This will: + A HIGH output_enable will cause the received VP and VM to be zero, thus mimicking a disconnection. + */ + usb_phy_ll_int_enable_test_mode(hal->wrap_dev, disconn); +} diff --git a/components/soc/esp32s2/CMakeLists.txt b/components/soc/esp32s2/CMakeLists.txt index 47c9135a1a..218b4bdbb8 100644 --- a/components/soc/esp32s2/CMakeLists.txt +++ b/components/soc/esp32s2/CMakeLists.txt @@ -16,7 +16,8 @@ set(srcs "timer_periph.c" "touch_sensor_periph.c" "uart_periph.c" - "usb_periph.c") + "usb_periph.c" + "usb_phy_periph.c") add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}") diff --git a/components/soc/esp32s2/usb_phy_periph.c b/components/soc/esp32s2/usb_phy_periph.c new file mode 100644 index 0000000000..596db0417c --- /dev/null +++ b/components/soc/esp32s2/usb_phy_periph.c @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/usb_phy_periph.h" +#include "soc/gpio_sig_map.h" + +/* + Bunch of constants for USB peripheral: GPIO signals +*/ +const usb_phy_signal_conn_t usb_phy_periph_signal = { + .extphy_vp_in = USB_EXTPHY_VP_IDX, + .extphy_vm_in = USB_EXTPHY_VM_IDX, + .extphy_rcv_in = USB_EXTPHY_RCV_IDX, + .extphy_oen_out = USB_EXTPHY_OEN_IDX, + .extphy_vpo_out = USB_EXTPHY_VPO_IDX, + .extphy_vmo_out = USB_EXTPHY_VMO_IDX, + .extphy_suspend_in = USB_EXTPHY_SUSPND_IDX, + .extphy_speed_in = USB_EXTPHY_SPEED_IDX, + .module = PERIPH_USB_MODULE +}; diff --git a/components/soc/esp32s3/CMakeLists.txt b/components/soc/esp32s3/CMakeLists.txt index 99ed3ecc79..53914f113e 100644 --- a/components/soc/esp32s3/CMakeLists.txt +++ b/components/soc/esp32s3/CMakeLists.txt @@ -19,7 +19,8 @@ set(srcs "timer_periph.c" "touch_sensor_periph.c" "uart_periph.c" - "usb_periph.c") + "usb_periph.c" + "usb_phy_periph.c") add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}") diff --git a/components/soc/esp32s3/include/soc/soc_caps.h b/components/soc/esp32s3/include/soc/soc_caps.h index 73ecbfdb8c..e5b196ffbc 100644 --- a/components/soc/esp32s3/include/soc/soc_caps.h +++ b/components/soc/esp32s3/include/soc/soc_caps.h @@ -16,6 +16,7 @@ #define SOC_CACHE_SUPPORT_WRAP 1 #define SOC_ULP_SUPPORTED 1 #define SOC_USB_OTG_SUPPORTED 1 +#define SOC_USB_SERIAL_JTAG_SUPPORTED 1 #define SOC_RTC_SLOW_MEM_SUPPORTED 1 #define SOC_CCOMP_TIMER_SUPPORTED 1 #define SOC_DIG_SIGN_SUPPORTED 1 diff --git a/components/soc/esp32s3/include/soc/usb_wrap_struct.h b/components/soc/esp32s3/include/soc/usb_wrap_struct.h index 91b79d9119..3df4106cd5 100644 --- a/components/soc/esp32s3/include/soc/usb_wrap_struct.h +++ b/components/soc/esp32s3/include/soc/usb_wrap_struct.h @@ -154,7 +154,7 @@ typedef union { * USB D- rx value in test. */ uint32_t test_rx_dm:1; - uint32_t reserved7: 25; + uint32_t reserved7:25; }; uint32_t val; } usb_wrap_test_conf_reg_t; diff --git a/components/soc/esp32s3/usb_phy_periph.c b/components/soc/esp32s3/usb_phy_periph.c new file mode 100644 index 0000000000..596db0417c --- /dev/null +++ b/components/soc/esp32s3/usb_phy_periph.c @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/usb_phy_periph.h" +#include "soc/gpio_sig_map.h" + +/* + Bunch of constants for USB peripheral: GPIO signals +*/ +const usb_phy_signal_conn_t usb_phy_periph_signal = { + .extphy_vp_in = USB_EXTPHY_VP_IDX, + .extphy_vm_in = USB_EXTPHY_VM_IDX, + .extphy_rcv_in = USB_EXTPHY_RCV_IDX, + .extphy_oen_out = USB_EXTPHY_OEN_IDX, + .extphy_vpo_out = USB_EXTPHY_VPO_IDX, + .extphy_vmo_out = USB_EXTPHY_VMO_IDX, + .extphy_suspend_in = USB_EXTPHY_SUSPND_IDX, + .extphy_speed_in = USB_EXTPHY_SPEED_IDX, + .module = PERIPH_USB_MODULE +}; diff --git a/components/soc/include/soc/usb_phy_periph.h b/components/soc/include/soc/usb_phy_periph.h new file mode 100644 index 0000000000..802d1720ce --- /dev/null +++ b/components/soc/include/soc/usb_phy_periph.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#include +#include "soc/soc_caps.h" +#include "soc/periph_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Stores a bunch of USB-peripheral data. +*/ +typedef struct { + const uint8_t extphy_vp_in; + const uint8_t extphy_vm_in; + const uint8_t extphy_rcv_in; + const uint8_t extphy_oen_out; + const uint8_t extphy_vpo_out; + const uint8_t extphy_vmo_out; + const uint8_t extphy_suspend_in; + const uint8_t extphy_speed_in; + const periph_module_t module; +} usb_phy_signal_conn_t; + +extern const usb_phy_signal_conn_t usb_phy_periph_signal; + +#ifdef __cplusplus +} +#endif diff --git a/components/tinyusb/CMakeLists.txt b/components/tinyusb/CMakeLists.txt index b180c75e23..e7c1b7edbc 100644 --- a/components/tinyusb/CMakeLists.txt +++ b/components/tinyusb/CMakeLists.txt @@ -73,7 +73,7 @@ endif() # CONFIG_TINYUSB idf_component_register(SRCS ${srcs} INCLUDE_DIRS ${includes_public} PRIV_INCLUDE_DIRS ${includes_private} - PRIV_REQUIRES "vfs" + PRIV_REQUIRES "vfs" "usb" ) if(CONFIG_TINYUSB) diff --git a/components/usb/CMakeLists.txt b/components/usb/CMakeLists.txt index 76aeb05ee7..5bf8229d5c 100644 --- a/components/usb/CMakeLists.txt +++ b/components/usb/CMakeLists.txt @@ -9,7 +9,8 @@ if(CONFIG_USB_OTG_SUPPORTED) "usb_helpers.c" "usb_host.c" "usb_private.c" - "usbh.c") + "usbh.c" + "usb_phy.c") list(APPEND include "include") list(APPEND priv_include "private_include") list(APPEND priv_require "hal" "driver") diff --git a/components/usb/include/esp_private/usb_phy.h b/components/usb/include/esp_private/usb_phy.h new file mode 100644 index 0000000000..5e8f9c8df1 --- /dev/null +++ b/components/usb/include/esp_private/usb_phy.h @@ -0,0 +1,142 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc_caps.h" +#include "hal/usb_phy_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief USB PHY status + */ +typedef enum { + USB_PHY_STATUS_FREE, /**< PHY is not being used */ + USB_PHY_STATUS_IN_USE, /**< PHY is in use */ +} usb_phy_status_t; + +/** + * @brief USB PHY available actions + */ +typedef enum { + USB_PHY_ACTION_HOST_ALLOW_CONN, /**< Enable physical connection when operating as an OTG Host */ + USB_PHY_ACTION_HOST_FORCE_DISCONN, /**< Disable physical connection when operating as an OTG Host */ + USB_PHY_ACTION_MAX, +} usb_phy_action_t; + +/** + * @brief USB external PHY iopins configure struct + */ +typedef struct { + int vp_io_num; /**< GPIO pin to USB_EXTPHY_VP_IDX */ + int vm_io_num; /**< GPIO pin to USB_EXTPHY_VM_IDX */ + int rcv_io_num; /**< GPIO pin to USB_EXTPHY_RCV_IDX */ + int oen_io_num; /**< GPIO pin to USB_EXTPHY_OEN_IDX */ + int vpo_io_num; /**< GPIO pin to USB_EXTPHY_VPO_IDX */ + int vmo_io_num; /**< GPIO pin to USB_EXTPHY_VMO_IDX */ +} usb_phy_gpio_conf_t; + +/** + * @brief USB PHY configure struct + * + * At minimum the PHY controller and PHY target must be initialized. + */ +typedef struct { + usb_phy_controller_t controller; /**< USB PHY controller */ + usb_phy_target_t target; /**< USB PHY target INT/EXT */ + usb_otg_mode_t otg_mode; /**< USB OTG mode */ + usb_phy_speed_t otg_speed; /**< USB OTG speed */ + usb_phy_gpio_conf_t *gpio_conf; /**< USB external PHY iopins configure */ +} usb_phy_config_t; + +typedef struct phy_context_t *usb_phy_handle_t; /**< USB PHY context handle */ + +/** + * @brief Initialize a new USB PHY + * Configure at least PHY source. + * + * @param[in] config USB PHY configurtion struct + * @param[out] handle_ret USB PHY context handle + * + * @return + * - ESP_OK Success + * - ESP_FAIL USB PHY init error. + * - ESP_ERR_INVALID_STATE USB PHY not installed. + * - ESP_ERR_NO_MEM USB_OTG installation failed due to no mem. + */ +esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_ret); + +/** + * @brief Configure OTG mode for a USB PHY + * + * @param handle Pointer of USB PHY context handle + * @param mode USB OTG mode + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error. + * - ESP_FAIL OTG set mode fail. + */ +esp_err_t usb_phy_otg_set_mode(usb_phy_handle_t handle, usb_otg_mode_t mode); + +/** + * @brief Configure USB speed for a USB PHY that is operating as an OTG Device + * + * @param handle Pointer of USB PHY context handle + * @param mode USB speed + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error. + * - ESP_FAIL OTG set speed fail. + */ +esp_err_t usb_phy_otg_dev_set_speed(usb_phy_handle_t handle, usb_phy_speed_t speed); + +/** + * @brief Take a action for a USB PHY + * + * @param handle Pointer of USB PHY context handle + * @param action USB PHY action + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error. + * - ESP_FAIL Action cannot be performed. + */ +esp_err_t usb_phy_action(usb_phy_handle_t handle, usb_phy_action_t action); + +/** + * @brief Delete a USB PHY + * + * @param handle Pointer of USB PHY context handle + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error. + */ +esp_err_t usb_del_phy(usb_phy_handle_t handle); + +/** + * @brief Get status of a USB PHY + * + * @param[in] target The specific PHY target to check + * @param[out] status Status of the PHY + * + * @return + * - ESP_OK Success + * - ESP_ERR_INVALID_ARG Parameter error. + * - ESP_ERR_INVALID_STATE USB PHY not installed. + */ +esp_err_t usb_phy_get_phy_status(usb_phy_target_t target, usb_phy_status_t *status); + +#ifdef __cplusplus +} +#endif diff --git a/components/usb/usb_phy.c b/components/usb/usb_phy.c new file mode 100644 index 0000000000..62fbb78f56 --- /dev/null +++ b/components/usb/usb_phy.c @@ -0,0 +1,328 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_check.h" +#include "driver/periph_ctrl.h" +#include "esp_private/usb_phy.h" +#include "soc/usb_phy_periph.h" +#include "hal/usb_phy_hal.h" +#include "hal/usb_phy_ll.h" +#include "esp_rom_gpio.h" +#include "driver/gpio.h" +#include "hal/gpio_ll.h" +#include "soc/usb_pins.h" + +static const char *USBPHY_TAG = "usb_phy"; + +#define USBPHY_NOT_INIT_ERR_STR "USB_PHY is not initialized" + +typedef struct phy_context_t phy_context_t; + +struct phy_context_t { + usb_phy_target_t target; /**< PHY target */ + usb_phy_controller_t controller; /**< PHY controller */ + usb_phy_status_t status; /**< PHY status */ + usb_otg_mode_t otg_mode; /**< USB OTG mode */ + usb_phy_speed_t otg_speed; /**< USB speed */ + usb_phy_gpio_conf_t *iopins; /**< external PHY I/O pins */ + usb_phy_hal_context_t hal_context; /**< USB_PHY hal context */ +}; + +typedef struct { + phy_context_t *internal_phy; /**< internal 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; + +/** + * @brief A pin descriptor for initialize external PHY I/O pins + */ +typedef struct { + int pin; /**< GPIO pin num */ + const int func; /**< GPIO matrix signal */ + const bool is_output; /**< input/output signal */ +} usb_iopin_dsc_t; + +static phy_ctrl_obj_t *p_phy_ctrl_obj = NULL; +static portMUX_TYPE phy_spinlock = portMUX_INITIALIZER_UNLOCKED; + +static esp_err_t phy_external_iopins_configure(usb_phy_gpio_conf_t *gpio_conf) +{ + const usb_iopin_dsc_t usb_periph_iopins[] = { + {gpio_conf->vp_io_num, usb_phy_periph_signal.extphy_vp_in, 0}, + {gpio_conf->vm_io_num, usb_phy_periph_signal.extphy_vm_in, 0}, + {gpio_conf->rcv_io_num, usb_phy_periph_signal.extphy_rcv_in, 0}, + {gpio_conf->oen_io_num, usb_phy_periph_signal.extphy_oen_out, 1}, + {gpio_conf->vpo_io_num, usb_phy_periph_signal.extphy_vpo_out, 1}, + {gpio_conf->vmo_io_num, usb_phy_periph_signal.extphy_vmo_out, 1}, + }; + + for (int i = 0; i < sizeof(usb_periph_iopins)/sizeof(usb_iopin_dsc_t); i++) { + const usb_iopin_dsc_t iopin = usb_periph_iopins[i]; + if (iopin.pin != -1) { + ESP_RETURN_ON_FALSE((iopin.is_output && GPIO_IS_VALID_OUTPUT_GPIO(iopin.pin)) || + (!iopin.is_output && GPIO_IS_VALID_GPIO(iopin.pin)), + ESP_ERR_INVALID_ARG, USBPHY_TAG, "io_num argument is invalid"); + esp_rom_gpio_pad_select_gpio(iopin.pin); + if (iopin.is_output) { + esp_rom_gpio_connect_out_signal(iopin.pin, iopin.func, false, false); + } else { + esp_rom_gpio_connect_in_signal(iopin.pin, iopin.func, false); + gpio_ll_input_enable(&GPIO, iopin.pin); + } + esp_rom_gpio_pad_unhold(iopin.pin); + } + } + return ESP_OK; +} + +esp_err_t usb_phy_otg_set_mode(usb_phy_handle_t handle, usb_otg_mode_t mode) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid"); + ESP_RETURN_ON_FALSE(mode < USB_OTG_MODE_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "mode argument is invalid"); + ESP_RETURN_ON_FALSE(handle->controller == USB_PHY_CTRL_OTG, ESP_FAIL, USBPHY_TAG, "phy source is not USB_OTG"); + + handle->otg_mode = mode; + if (mode == USB_OTG_MODE_HOST) { + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_IDDIG_IN_IDX, false); //connected connector is A side + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_SRP_BVALID_IN_IDX, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, false); //receiving a valid Vbus from host + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_AVALID_IN_IDX, false); //HIGH to force USB host mode + if (handle->target == USB_PHY_TARGET_INT) { + usb_phy_hal_int_load_conf_host(&(handle->hal_context)); + } + } else if (mode == USB_OTG_MODE_DEVICE) { + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, false); //connected connector is mini-B side + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, false); //HIGH to force USB device mode + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, false); //receiving a valid Vbus from device + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, false); + } + + return ESP_OK; +} + +esp_err_t usb_phy_otg_dev_set_speed(usb_phy_handle_t handle, usb_phy_speed_t speed) +{ + 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, + USBPHY_TAG, "set speed not supported"); + + handle->otg_speed = speed; + usb_priv_speed_t hal_speed = 0; + if (speed == USB_PHY_SPEED_LOW) { + hal_speed = USB_PRIV_SPEED_LOW; + } else if (speed == USB_PHY_SPEED_FULL) { + hal_speed = USB_PRIV_SPEED_FULL; + } + usb_phy_hal_int_load_conf_dev(&(handle->hal_context), hal_speed); + return ESP_OK; +} + +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(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; + switch (action) { + case USB_PHY_ACTION_HOST_ALLOW_CONN: + if (handle->target == USB_PHY_TARGET_INT) { + usb_phy_hal_int_mimick_disconn(&(handle->hal_context), false); + } else { + if (!handle->iopins) { + ret = ESP_FAIL; + ESP_LOGE(USBPHY_TAG, "no I/O pins provided for connection"); + break; + } + /* + Allow for connections on the external PHY by connecting the VP and VM signals to the external PHY. + */ + esp_rom_gpio_connect_in_signal(handle->iopins->vp_io_num, USB_EXTPHY_VP_IDX, false); + esp_rom_gpio_connect_in_signal(handle->iopins->vm_io_num, USB_EXTPHY_VM_IDX, false); + } + break; + + case USB_PHY_ACTION_HOST_FORCE_DISCONN: + if (handle->target == USB_PHY_TARGET_INT) { + usb_phy_hal_int_mimick_disconn(&(handle->hal_context), true); + } else { + /* + Disable connections on the external PHY by connecting the VP and VM signals to the constant LOW signal. + */ + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_EXTPHY_VP_IDX, false); + esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_EXTPHY_VM_IDX, false); + } + break; + + default: + break; + } + + return ret; +} + +static esp_err_t usb_phy_install(void) +{ + portENTER_CRITICAL(&phy_spinlock); + if (p_phy_ctrl_obj) { + // p_phy_ctrl_obj already installed, return immediately + portEXIT_CRITICAL(&phy_spinlock); + return ESP_OK; + } + portEXIT_CRITICAL(&phy_spinlock); + + esp_err_t ret = ESP_OK; + phy_ctrl_obj_t *phy_ctrl_obj = (phy_ctrl_obj_t *) calloc(1, sizeof(phy_ctrl_obj_t)); + ESP_GOTO_ON_FALSE(phy_ctrl_obj, ESP_ERR_NO_MEM, cleanup, USBPHY_TAG, "no mem for USB_PHY driver"); + + portENTER_CRITICAL(&phy_spinlock); + if (!p_phy_ctrl_obj) { + p_phy_ctrl_obj = phy_ctrl_obj; + p_phy_ctrl_obj->ref_count = 0; + } else { + // p_phy_ctrl_obj already installed, need to free resource + portEXIT_CRITICAL(&phy_spinlock); + goto cleanup; + } + portEXIT_CRITICAL(&phy_spinlock); + periph_module_enable(usb_phy_periph_signal.module); + periph_module_reset(usb_phy_periph_signal.module); + return ESP_OK; + +cleanup: + free(phy_ctrl_obj); + return ret; +} + +esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_ret) +{ + 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(config->controller < USB_PHY_CTRL_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "specified source argument is invalid"); + + ESP_RETURN_ON_ERROR(usb_phy_install(), USBPHY_TAG, "usb_phy driver installation failed"); + esp_err_t ret = ESP_OK; + bool new_phy = false; + phy_context_t *phy_context = (phy_context_t *) calloc(1, sizeof(phy_context_t)); + ESP_GOTO_ON_FALSE(phy_context, ESP_ERR_NO_MEM, cleanup, USBPHY_TAG, "no mem for phy context"); + + portENTER_CRITICAL(&phy_spinlock); + usb_phy_get_phy_status(config->target, &phy_context->status); + if (phy_context->status == USB_PHY_STATUS_FREE) { + new_phy = true; + p_phy_ctrl_obj->ref_count++; + if (config->target == USB_PHY_TARGET_EXT) { + p_phy_ctrl_obj->external_phy = phy_context; + } else { + p_phy_ctrl_obj->internal_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->controller = config->controller; + phy_context->status = USB_PHY_STATUS_IN_USE; + + usb_phy_hal_init(&(phy_context->hal_context)); + if (config->controller == USB_PHY_CTRL_OTG) { + usb_phy_hal_otg_conf(&(phy_context->hal_context), config->target == USB_PHY_TARGET_EXT); + } +#if SOC_USB_SERIAL_JTAG_SUPPORTED + else if (config->controller == USB_PHY_CTRL_SERIAL_JTAG) { + usb_phy_hal_jtag_conf(&(phy_context->hal_context), config->target == USB_PHY_TARGET_EXT); + phy_context->otg_mode = USB_OTG_MODE_DEVICE; + phy_context->otg_speed = USB_PHY_SPEED_FULL; + } +#endif + + if (config->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); + } + + *handle_ret = (usb_phy_handle_t) phy_context; + if (config->gpio_conf && config->target == USB_PHY_TARGET_EXT) { + phy_context->iopins = (usb_phy_gpio_conf_t *) calloc(1, sizeof(usb_phy_gpio_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->gpio_conf, sizeof(usb_phy_gpio_conf_t)); + ESP_ERROR_CHECK(phy_external_iopins_configure(phy_context->iopins)); + } + if (config->otg_mode != USB_PHY_MODE_DEFAULT) { + ESP_ERROR_CHECK(usb_phy_otg_set_mode(*handle_ret, config->otg_mode)); + } + if (config->otg_speed != USB_PHY_SPEED_UNDEFINED) { + ESP_ERROR_CHECK(usb_phy_otg_dev_set_speed(*handle_ret, config->otg_speed)); + } + return ESP_OK; + +cleanup: + free(phy_context->iopins); + free(phy_context); + if (p_phy_ctrl_obj->ref_count == 0) { + free(p_phy_ctrl_obj); + p_phy_ctrl_obj = NULL; + } + return ret; +} + +static void phy_uninstall(void) +{ + phy_ctrl_obj_t *p_phy_ctrl_obj_free = NULL; + portENTER_CRITICAL(&phy_spinlock); + if (p_phy_ctrl_obj->ref_count == 0) { + p_phy_ctrl_obj_free = p_phy_ctrl_obj; + p_phy_ctrl_obj = NULL; + // Disable USB peripheral + periph_module_disable(usb_phy_periph_signal.module); + } + portEXIT_CRITICAL(&phy_spinlock); + free(p_phy_ctrl_obj_free); +} + +esp_err_t usb_del_phy(usb_phy_handle_t handle) +{ + ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, USBPHY_TAG, "handle argument is invalid"); + + portENTER_CRITICAL(&phy_spinlock); + p_phy_ctrl_obj->ref_count--; + if (handle->target == USB_PHY_TARGET_EXT) { + p_phy_ctrl_obj->external_phy = NULL; + } else { + // Clear pullup and pulldown loads on D+ / D- + usb_phy_ll_int_load_conf(handle->hal_context.wrap_dev, false, false, false, false); + p_phy_ctrl_obj->internal_phy = NULL; + } + portEXIT_CRITICAL(&phy_spinlock); + free(handle->iopins); + free(handle); + phy_uninstall(); + return ESP_OK; +} + +esp_err_t usb_phy_get_phy_status(usb_phy_target_t target, usb_phy_status_t *status) +{ + ESP_RETURN_ON_FALSE(target < USB_PHY_TARGET_MAX, ESP_ERR_INVALID_ARG, USBPHY_TAG, "argument is invalid"); + ESP_RETURN_ON_FALSE(p_phy_ctrl_obj, ESP_ERR_INVALID_STATE, USBPHY_TAG, USBPHY_NOT_INIT_ERR_STR); + + 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 { + *status = USB_PHY_STATUS_FREE; + } + return ESP_OK; +}