usb: Add usb_phy driver to support operations on USB PHY

pull/7680/head
songruojing 2021-08-19 20:28:28 +08:00
rodzic 7fc071e133
commit 1fcd639224
18 zmienionych plików z 980 dodań i 6 usunięć

Wyświetl plik

@ -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"

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#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

Wyświetl plik

@ -0,0 +1,127 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -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}")

Wyświetl plik

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

Wyświetl plik

@ -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}")

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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")

Wyświetl plik

@ -0,0 +1,142 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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

Wyświetl plik

@ -0,0 +1,328 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <esp_types.h>
#include <string.h>
#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;
}