From 7019a9f61cceb36fc15c398640488115b19d67b8 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Wed, 3 Apr 2024 00:48:24 +0800 Subject: [PATCH 1/3] feat(soc/usb): Add USB related changes to soc_caps and usb_dwc_periph This commit changes the following: - Add types and data structures indicating the available USB controllers for each target. --- components/soc/CMakeLists.txt | 5 +- .../esp32p4/include/soc/Kconfig.soc_caps.in | 8 ++ components/soc/esp32p4/include/soc/soc_caps.h | 9 ++- components/soc/esp32p4/usb_dwc_periph.c | 50 ++++++++++++ components/soc/esp32s2/usb_dwc_periph.c | 72 ++++++++++++++++- components/soc/esp32s2/usb_periph.c | 30 ------- components/soc/esp32s3/usb_dwc_periph.c | 73 ++++++++++++++++- components/soc/esp32s3/usb_periph.c | 30 ------- components/soc/include/soc/usb_dwc_periph.h | 79 ++++++++++++++++++- components/usb/hcd_dwc.c | 26 ++---- 10 files changed, 295 insertions(+), 87 deletions(-) create mode 100644 components/soc/esp32p4/usb_dwc_periph.c delete mode 100644 components/soc/esp32s2/usb_periph.c delete mode 100644 components/soc/esp32s3/usb_periph.c diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 2b21132528..2e3d92bd6c 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -138,10 +138,7 @@ if(CONFIG_SOC_IEEE802154_SUPPORTED) endif() if(CONFIG_SOC_USB_OTG_SUPPORTED) - if(NOT ${target} STREQUAL "esp32p4") - list(APPEND srcs "${target_folder}/usb_periph.c" - "${target_folder}/usb_dwc_periph.c") - endif() + list(APPEND srcs "${target_folder}/usb_dwc_periph.c") endif() if(CONFIG_SOC_DAC_SUPPORTED) diff --git a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in index c613b53ff7..c7f0ab8fc1 100644 --- a/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32p4/include/soc/Kconfig.soc_caps.in @@ -1087,6 +1087,14 @@ config SOC_MCPWM_CAPTURE_CLK_FROM_GROUP bool default y +config SOC_USB_OTG_PERIPH_NUM + int + default 2 + +config SOC_USB_UTMI_PHY_NUM + int + default 1 + config SOC_PARLIO_GROUPS int default 1 diff --git a/components/soc/esp32p4/include/soc/soc_caps.h b/components/soc/esp32p4/include/soc/soc_caps.h index 040af5af46..e6b155f135 100644 --- a/components/soc/esp32p4/include/soc/soc_caps.h +++ b/components/soc/esp32p4/include/soc/soc_caps.h @@ -411,9 +411,16 @@ #define SOC_MCPWM_SUPPORT_EVENT_COMPARATOR (1) ///< Support event comparator (based on ETM) #define SOC_MCPWM_CAPTURE_CLK_FROM_GROUP (1) ///< Capture timer shares clock with other PWM timers -/*------------------------ USB SERIAL JTAG CAPS ------------------------------*/ +/*-------------------------- USB CAPS ----------------------------------------*/ +// USB Serial JTAG Caps // #define SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP (1) /*!< Support to maintain minimum usb communication during light sleep */ // TODO: IDF-6395 +// USB OTG Caps +#define SOC_USB_OTG_PERIPH_NUM (2U) + +// USB PHY Caps +#define SOC_USB_UTMI_PHY_NUM (1U) + /*-------------------------- PARLIO CAPS --------------------------------------*/ #define SOC_PARLIO_GROUPS 1U /*!< Number of parallel IO peripherals */ #define SOC_PARLIO_TX_UNITS_PER_GROUP 1U /*!< number of TX units in each group */ diff --git a/components/soc/esp32p4/usb_dwc_periph.c b/components/soc/esp32p4/usb_dwc_periph.c new file mode 100644 index 0000000000..23fecc8afe --- /dev/null +++ b/components/soc/esp32p4/usb_dwc_periph.c @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "soc/interrupts.h" +#include "soc/gpio_sig_map.h" +#include "soc/usb_dwc_periph.h" +#include "soc/usb_dwc_struct.h" + +/* -------------------------------- Private --------------------------------- */ + +static const usb_utmi_otg_signal_conn_t dwc_fs_otg_signals = { + // Inputs + .iddig = USB_OTG11_IDDIG_PAD_IN_IDX, + .avalid = USB_OTG11_AVALID_PAD_IN_IDX, + .bvalid = USB_SRP_BVALID_PAD_IN_IDX, + .vbusvalid = USB_OTG11_VBUSVALID_PAD_IN_IDX, + .sessend = USB_SRP_SESSEND_PAD_IN_IDX, + // Outputs + .idpullup = USB_OTG11_IDPULLUP_PAD_OUT_IDX, + .dppulldown = USB_OTG11_DPPULLDOWN_PAD_OUT_IDX, + .dmpulldown = USB_OTG11_DMPULLDOWN_PAD_OUT_IDX, + .drvvbus = USB_OTG11_DRVVBUS_PAD_OUT_IDX, + .chrgvbus = USB_SRP_CHRGVBUS_PAD_OUT_IDX, + .dischrgvbus = USB_SRP_DISCHRGVBUS_PAD_OUT_IDX, +}; + +/* --------------------------------- Public --------------------------------- */ + +const usb_dwc_info_t usb_dwc_info = { + .controllers = { + // High-Speed USB-DWC + [0] = { + .fsls_signals = NULL, + .otg_signals = NULL, + .irq = ETS_USB_OTG_INTR_SOURCE, + .irq_2nd_cpu = ETS_USB_OTG_ENDP_MULTI_PROC_INTR_SOURCE, + }, + // Full-Speed USB-DWC + [1] = { + .fsls_signals = NULL, + .otg_signals = &dwc_fs_otg_signals, + .irq = ETS_USB_OTG11_CH0_INTR_SOURCE, + .irq_2nd_cpu = -1, + }, + }, +}; diff --git a/components/soc/esp32s2/usb_dwc_periph.c b/components/soc/esp32s2/usb_dwc_periph.c index eba3eaaf60..2cb76077e7 100644 --- a/components/soc/esp32s2/usb_dwc_periph.c +++ b/components/soc/esp32s2/usb_dwc_periph.c @@ -4,8 +4,78 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "soc/usb_dwc_periph.h" #include "soc/gpio_sig_map.h" +#include "soc/usb_periph.h" +#include "soc/interrupts.h" +#include "soc/usb_dwc_periph.h" + +/* -------------------------------- Private --------------------------------- */ + +static const usb_fsls_serial_signal_conn_t fsls_signals = { + // Inputs + .rx_dp = USB_EXTPHY_VP_IDX, + .rx_dm = USB_EXTPHY_VM_IDX, + .rx_rcv = USB_EXTPHY_RCV_IDX, + // Outputs + .suspend_n = USB_EXTPHY_SUSPND_IDX, + .tx_enable_n = USB_EXTPHY_OEN_IDX, + .tx_dp = USB_EXTPHY_VPO_IDX, + .tx_dm = USB_EXTPHY_VMO_IDX, + .fs_edge_sel = USB_EXTPHY_SPEED_IDX, +}; + +static const usb_utmi_otg_signal_conn_t otg_signals = { + // Inputs + .iddig = USB_OTG_IDDIG_IN_IDX, + .avalid = USB_OTG_AVALID_IN_IDX, + .bvalid = USB_SRP_BVALID_IN_IDX, + .vbusvalid = USB_OTG_VBUSVALID_IN_IDX, + .sessend = USB_SRP_SESSEND_IN_IDX, + // Outputs + .idpullup = USB_OTG_IDPULLUP_IDX, + .dppulldown = USB_OTG_DPPULLDOWN_IDX, + .dmpulldown = USB_OTG_DMPULLDOWN_IDX, + .drvvbus = USB_OTG_DRVVBUS_IDX, + .chrgvbus = USB_SRP_CHRGVBUS_IDX, + .dischrgvbus = USB_SRP_DISCHRGVBUS_IDX, +}; + +/* --------------------------------- Public --------------------------------- */ + +const usb_dwc_info_t usb_dwc_info = { + .controllers = { + [0] = { + .fsls_signals = &fsls_signals, + .otg_signals = &otg_signals, + .irq = ETS_USB_INTR_SOURCE, + .irq_2nd_cpu = -1, + }, + }, +}; + +/* ------------------------------- Deprecated ------------------------------- */ + +/* +Note: These IO pins are deprecated. When connecting USB OTG to an external FSLS +PHY, the FSLS Serial Interface signals can be routed to any GPIO via the GPIO +matrix. Thus, this mapping of signals to IO pins is meaningless. + +Todo: Remove in IDF v6.0 (IDF-9029) +*/ +const usb_iopin_dsc_t usb_periph_iopins[] = { + {USBPHY_VP_NUM, USB_EXTPHY_VP_IDX, 0, 1}, + {USBPHY_VM_NUM, USB_EXTPHY_VM_IDX, 0, 1}, + {USBPHY_RCV_NUM, USB_EXTPHY_RCV_IDX, 0, 1}, + {USBPHY_OEN_NUM, USB_EXTPHY_OEN_IDX, 1, 1}, + {USBPHY_VPO_NUM, USB_EXTPHY_VPO_IDX, 1, 1}, + {USBPHY_VMO_NUM, USB_EXTPHY_VMO_IDX, 1, 1}, + {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, 0, 0}, //connected connector is mini-B + //connected connector is mini-B + {GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, 0, 0}, //HIGH to force USB device mode + {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, 0, 0}, //receiving a valid Vbus from host + {GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, 0, 0}, + {-1, -1, 0, 0} +}; /* Bunch of constants for USB peripheral: GPIO signals diff --git a/components/soc/esp32s2/usb_periph.c b/components/soc/esp32s2/usb_periph.c deleted file mode 100644 index a3fda7193e..0000000000 --- a/components/soc/esp32s2/usb_periph.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "soc/soc_caps.h" -#include "soc/usb_periph.h" - -/* -Note: These IO pins are deprecated. When connecting USB OTG to an external FSLS -PHY, the FSLS Serial Interface signals can be routed to any GPIO via the GPIO -matrix. Thus, this mapping of signals to IO pins is meaningless. - -Todo: Remove in IDF v6.0 (IDF-9029) -*/ -const usb_iopin_dsc_t usb_periph_iopins[] = { - {USBPHY_VP_NUM, USB_EXTPHY_VP_IDX, 0, 1}, - {USBPHY_VM_NUM, USB_EXTPHY_VM_IDX, 0, 1}, - {USBPHY_RCV_NUM, USB_EXTPHY_RCV_IDX, 0, 1}, - {USBPHY_OEN_NUM, USB_EXTPHY_OEN_IDX, 1, 1}, - {USBPHY_VPO_NUM, USB_EXTPHY_VPO_IDX, 1, 1}, - {USBPHY_VMO_NUM, USB_EXTPHY_VMO_IDX, 1, 1}, - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, 0, 0}, //connected connector is mini-B - //connected connector is mini-B - {GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, 0, 0}, //HIGH to force USB device mode - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, 0, 0}, //receiving a valid Vbus from host - {GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, 0, 0}, - {-1, -1, 0, 0} -}; diff --git a/components/soc/esp32s3/usb_dwc_periph.c b/components/soc/esp32s3/usb_dwc_periph.c index eba3eaaf60..51386dde8b 100644 --- a/components/soc/esp32s3/usb_dwc_periph.c +++ b/components/soc/esp32s3/usb_dwc_periph.c @@ -4,8 +4,79 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "soc/usb_dwc_periph.h" +#include #include "soc/gpio_sig_map.h" +#include "soc/usb_periph.h" +#include "soc/interrupts.h" +#include "soc/usb_dwc_periph.h" + +/* -------------------------------- Private --------------------------------- */ + +static const usb_fsls_serial_signal_conn_t fsls_signals = { + // Inputs + .rx_dp = USB_EXTPHY_VP_IDX, + .rx_dm = USB_EXTPHY_VM_IDX, + .rx_rcv = USB_EXTPHY_RCV_IDX, + // Outputs + .suspend_n = USB_EXTPHY_SUSPND_IDX, + .tx_enable_n = USB_EXTPHY_OEN_IDX, + .tx_dp = USB_EXTPHY_VPO_IDX, + .tx_dm = USB_EXTPHY_VMO_IDX, + .fs_edge_sel = USB_EXTPHY_SPEED_IDX, +}; + +static const usb_utmi_otg_signal_conn_t otg_signals = { + // Inputs + .iddig = USB_OTG_IDDIG_IN_IDX, + .avalid = USB_OTG_AVALID_IN_IDX, + .bvalid = USB_SRP_BVALID_IN_IDX, + .vbusvalid = USB_OTG_VBUSVALID_IN_IDX, + .sessend = USB_SRP_SESSEND_IN_IDX, + // Outputs + .idpullup = USB_OTG_IDPULLUP_IDX, + .dppulldown = USB_OTG_DPPULLDOWN_IDX, + .dmpulldown = USB_OTG_DMPULLDOWN_IDX, + .drvvbus = USB_OTG_DRVVBUS_IDX, + .chrgvbus = USB_SRP_CHRGVBUS_IDX, + .dischrgvbus = USB_SRP_DISCHRGVBUS_IDX, +}; + +/* --------------------------------- Public --------------------------------- */ + +const usb_dwc_info_t usb_dwc_info = { + .controllers = { + [0] = { + .fsls_signals = &fsls_signals, + .otg_signals = &otg_signals, + .irq = ETS_USB_INTR_SOURCE, + .irq_2nd_cpu = -1, + }, + }, +}; + +/* ------------------------------- Deprecated ------------------------------- */ + +/* +Note: These IO pins are deprecated. When connecting USB OTG to an external FSLS +PHY, the FSLS Serial Interface signals can be routed to any GPIO via the GPIO +matrix. Thus, this mapping of signals to IO pins is meaningless. + +Todo: Remove in IDF v6.0 (IDF-9029) +*/ +const usb_iopin_dsc_t usb_periph_iopins[] = { + {USBPHY_VP_NUM, USB_EXTPHY_VP_IDX, 0, 1}, + {USBPHY_VM_NUM, USB_EXTPHY_VM_IDX, 0, 1}, + {USBPHY_RCV_NUM, USB_EXTPHY_RCV_IDX, 0, 1}, + {USBPHY_OEN_NUM, USB_EXTPHY_OEN_IDX, 1, 1}, + {USBPHY_VPO_NUM, USB_EXTPHY_VPO_IDX, 1, 1}, + {USBPHY_VMO_NUM, USB_EXTPHY_VMO_IDX, 1, 1}, + {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, 0, 0}, //connected connector is mini-B + //connected connector is mini-B + {GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, 0, 0}, //HIGH to force USB device mode + {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, 0, 0}, //receiving a valid Vbus from host + {GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, 0, 0}, + {-1, -1, 0, 0} +}; /* Bunch of constants for USB peripheral: GPIO signals diff --git a/components/soc/esp32s3/usb_periph.c b/components/soc/esp32s3/usb_periph.c deleted file mode 100644 index a3fda7193e..0000000000 --- a/components/soc/esp32s3/usb_periph.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "soc/soc_caps.h" -#include "soc/usb_periph.h" - -/* -Note: These IO pins are deprecated. When connecting USB OTG to an external FSLS -PHY, the FSLS Serial Interface signals can be routed to any GPIO via the GPIO -matrix. Thus, this mapping of signals to IO pins is meaningless. - -Todo: Remove in IDF v6.0 (IDF-9029) -*/ -const usb_iopin_dsc_t usb_periph_iopins[] = { - {USBPHY_VP_NUM, USB_EXTPHY_VP_IDX, 0, 1}, - {USBPHY_VM_NUM, USB_EXTPHY_VM_IDX, 0, 1}, - {USBPHY_RCV_NUM, USB_EXTPHY_RCV_IDX, 0, 1}, - {USBPHY_OEN_NUM, USB_EXTPHY_OEN_IDX, 1, 1}, - {USBPHY_VPO_NUM, USB_EXTPHY_VPO_IDX, 1, 1}, - {USBPHY_VMO_NUM, USB_EXTPHY_VMO_IDX, 1, 1}, - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, 0, 0}, //connected connector is mini-B - //connected connector is mini-B - {GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, 0, 0}, //HIGH to force USB device mode - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, 0, 0}, //receiving a valid Vbus from host - {GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, 0, 0}, - {-1, -1, 0, 0} -}; diff --git a/components/soc/include/soc/usb_dwc_periph.h b/components/soc/include/soc/usb_dwc_periph.h index d13286aa1d..8e1832c2ce 100644 --- a/components/soc/include/soc/usb_dwc_periph.h +++ b/components/soc/include/soc/usb_dwc_periph.h @@ -1,18 +1,93 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once + #include +#include +#include "soc/soc_pins.h" #include "soc/soc_caps.h" #include "soc/periph_defs.h" +#include "soc/gpio_sig_map.h" #ifdef __cplusplus extern "C" { #endif +#if SOC_USB_OTG_SUPPORTED + +/* ---------------------------------- Types --------------------------------- */ + +/** + * @brief USB PHY FSLS Serial Interface Signals + * + * Structure to store the GPIO matrix signal indexes for a USB PHY FSLS Serial + * interface's signals. + * + * @note Refer to section "2.2.1.13 FsLsSerialMode" of the UTMI+ for more + * details regarding the FSLS Serial Interface. + */ +typedef struct { + // Inputs + int rx_dp; + int rx_dm; + int rx_rcv; + // Outputs + int suspend_n; + int tx_enable_n; + int tx_dp; + int tx_dm; + int fs_edge_sel; +} usb_fsls_serial_signal_conn_t; + +/** + * @brief USB PHY UTMI OTG Interface Signal Index Type + * + * Structure to store the GPIO matrix signal indexes for a UTMI PHY interface's + * OTG signals. + */ +typedef struct { + // Inputs + int iddig; + int avalid; + int bvalid; + int vbusvalid; + int sessend; + // Outputs + int idpullup; + int dppulldown; + int dmpulldown; + int drvvbus; + int chrgvbus; + int dischrgvbus; +} usb_utmi_otg_signal_conn_t; + +/** + * @brief USB Controller Information + * + * Structure to store information for all USB-DWC instances + */ +typedef struct { + struct { + const usb_fsls_serial_signal_conn_t * const fsls_signals; // Must be set if external PHY is supported by controller + const usb_utmi_otg_signal_conn_t * const otg_signals; + const int irq; + const int irq_2nd_cpu; // The USB-DWC can provide 2nd interrupt so each CPU can have its own interrupt line. Set to -1 if not supported + } controllers [SOC_USB_OTG_PERIPH_NUM]; +} usb_dwc_info_t; + +extern const usb_dwc_info_t usb_dwc_info; + +#endif // SOC_USB_OTG_SUPPORTED + +/* ------------------------------- Deprecated ------------------------------- */ +/* Todo: Remove in ESP-IDF v6.0 (IDF-9052) */ + +#if SOC_USB_OTG_SUPPORTED + /* Stores a bunch of USB-peripheral data. */ @@ -41,6 +116,8 @@ typedef struct { extern const usb_phy_signal_conn_t usb_otg_periph_signal; +#endif // SOC_USB_OTG_SUPPORTED + #ifdef __cplusplus } #endif diff --git a/components/usb/hcd_dwc.c b/components/usb/hcd_dwc.c index 8754f273cb..2005a963a0 100644 --- a/components/usb/hcd_dwc.c +++ b/components/usb/hcd_dwc.c @@ -12,11 +12,11 @@ #include "freertos/semphr.h" #include "esp_heap_caps.h" #include "esp_intr_alloc.h" -#include "soc/interrupts.h" // For interrupt index #include "esp_err.h" #include "esp_log.h" + +#include "soc/usb_dwc_periph.h" #include "hal/usb_dwc_hal.h" -#include "hal/usb_dwc_types.h" #include "hcd.h" #include "usb_private.h" #include "usb/usb_types_ch9.h" @@ -28,15 +28,6 @@ // ----------------------------------------------------- Macros -------------------------------------------------------- -// ------------------ Target specific ---------------------- -// TODO: Remove target specific section after support for multiple USB peripherals is implemented -#include "sdkconfig.h" -#if (CONFIG_IDF_TARGET_ESP32P4) -#define USB_INTR ETS_USB_OTG_INTR_SOURCE -#else -#define USB_INTR ETS_USB_INTR_SOURCE -#endif - // --------------------- Constants ------------------------- #define INIT_DELAY_MS 30 // A delay of at least 25ms to enter Host mode. Make it 30ms to be safe @@ -64,22 +55,19 @@ #define XFER_LIST_LEN_ISOC 64 // Implement longer ISOC transfer list to give us enough space for additional timing margin #define XFER_LIST_ISOC_MARGIN 3 // The 1st ISOC transfer is scheduled 3 (micro)frames later so we have enough timing margin -// ------------------------ Flags -------------------------- +// ------------------------ Internal -------------------------- /** - * @brief Bit masks for the HCD to use in the URBs reserved_flags field + * @brief Values for the HCD to use in the URBs hcd_var field * - * The URB object has a reserved_flags member for host stack's internal use. The following flags will be set in - * reserved_flags in order to keep track of state of an URB within the HCD. + * The URB object has a hcd_var member for host stack's internal use. The following values will be set in + * hcd_var in order to keep track of state of an URB within the HCD. */ #define URB_HCD_STATE_IDLE 0 // The URB is not enqueued in an HCD pipe #define URB_HCD_STATE_PENDING 1 // The URB is enqueued and pending execution #define URB_HCD_STATE_INFLIGHT 2 // The URB is currently in flight #define URB_HCD_STATE_DONE 3 // The URB has completed execution or is retired, and is waiting to be dequeued -#define URB_HCD_STATE_SET(reserved_flags, state) (reserved_flags = (reserved_flags & ~URB_HCD_STATE_MASK) | state) -#define URB_HCD_STATE_GET(reserved_flags) (reserved_flags & URB_HCD_STATE_MASK) - // -------------------- Convenience ------------------------ const char *HCD_DWC_TAG = "HCD DWC"; @@ -1005,7 +993,7 @@ esp_err_t hcd_install(const hcd_config_t *config) goto port_alloc_err; } // Allocate interrupt - err_ret = esp_intr_alloc(USB_INTR, + err_ret = esp_intr_alloc(usb_dwc_info.controllers[0].irq, config->intr_flags | ESP_INTR_FLAG_INTRDISABLED, // The interrupt must be disabled until the port is initialized intr_hdlr_main, (void *)p_hcd_obj_dmy->port_obj, From 1d5a8f6952f4139edd4895334afcda91b68c162c Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Wed, 3 Apr 2024 01:03:29 +0800 Subject: [PATCH 2/3] feat(hal/usb): Add USB UTMI PHY HAL * Add a bare-bones HAL API for the USB UTMI PHY * Split USB-DWC LL per target --- components/hal/CMakeLists.txt | 4 + .../{ => esp32p4}/include/hal/usb_dwc_ll.h | 35 +- .../hal/esp32p4/include/hal/usb_utmi_ll.h | 4 +- .../hal/esp32s2/include/hal/usb_dwc_ll.h | 993 ++++++++++++++++++ .../hal/esp32s3/include/hal/usb_dwc_ll.h | 993 ++++++++++++++++++ components/hal/include/hal/usb_utmi_hal.h | 64 ++ components/hal/include/hal/usb_wrap_hal.h | 8 +- components/hal/include/hal/usb_wrap_types.h | 4 +- components/hal/usb_dwc_hal.c | 25 +- components/hal/usb_utmi_hal.c | 29 + components/usb/usb_phy_p4.c | 39 +- 11 files changed, 2135 insertions(+), 63 deletions(-) rename components/hal/{ => esp32p4}/include/hal/usb_dwc_ll.h (97%) create mode 100644 components/hal/esp32s2/include/hal/usb_dwc_ll.h create mode 100644 components/hal/esp32s3/include/hal/usb_dwc_ll.h create mode 100644 components/hal/include/hal/usb_utmi_hal.h create mode 100644 components/hal/usb_utmi_hal.c diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index b00cdad58c..77ebb8393d 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -274,6 +274,10 @@ if(NOT BOOTLOADER_BUILD) list(APPEND srcs "usb_serial_jtag_hal.c") endif() + if(CONFIG_SOC_USB_UTMI_PHY_NUM GREATER 0) + list(APPEND srcs "usb_utmi_hal.c") + endif() + if(CONFIG_SOC_USB_OTG_SUPPORTED) list(APPEND srcs "usb_dwc_hal.c" diff --git a/components/hal/include/hal/usb_dwc_ll.h b/components/hal/esp32p4/include/hal/usb_dwc_ll.h similarity index 97% rename from components/hal/include/hal/usb_dwc_ll.h rename to components/hal/esp32p4/include/hal/usb_dwc_ll.h index 49ade50405..f93db6623c 100644 --- a/components/hal/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32p4/include/hal/usb_dwc_ll.h @@ -6,25 +6,22 @@ #pragma once -#include "soc/soc_caps.h" -/* -This header is shared across all targets. Resolve to an empty header for targets -that don't support USB OTG. -*/ -#if SOC_USB_OTG_SUPPORTED #include #include #include "soc/usb_dwc_struct.h" #include "soc/usb_dwc_cfg.h" #include "hal/usb_dwc_types.h" #include "hal/misc.h" -#endif // SOC_USB_OTG_SUPPORTED #ifdef __cplusplus extern "C" { #endif -#if SOC_USB_OTG_SUPPORTED +/* ----------------------------- Helper Macros ------------------------------ */ + +// Get USB hardware instance +// TODO: extend this macros when we have support for both FS and HS hardware on P4 +#define USB_DWC_LL_GET_HW(num) (&USB_DWC_HS) /* ----------------------------------------------------------------------------- --------------------------------- DWC Constants -------------------------------- @@ -222,14 +219,14 @@ static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t hw->gusbcfg_reg.toutcal = tout_cal; } -#if (OTG_HSPHY_INTERFACE != 0) static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw) { +#if (OTG_HSPHY_INTERFACE != 0) hw->gusbcfg_reg.phyif = 1; // 16 bits interface hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+ hw->gusbcfg_reg.physel = 0; // HS PHY -} #endif // (OTG_HSPHY_INTERFACE != 0) +} // --------------------------- GRSTCTL Register -------------------------------- @@ -356,9 +353,9 @@ static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw) // --------------------------- GHWCFGx Register -------------------------------- /** - * @brief Get the hardware configuration regiters of the DWC_OTG controller + * @brief Get the hardware configuration registers of the DWC_OTG controller * - * The hardware configuraiton regitsers are read only and indicate the various + * The hardware configuration regitsers are read only and indicate the various * features of the DWC_OTG core. * * @param hw Start address of the DWC_OTG registers @@ -405,7 +402,7 @@ static inline void usb_dwc_ll_hcfg_dis_perio_sched(usb_dwc_dev_t *hw) /** * Sets the length of the frame list * - * @param num_entires Number of entires in the frame list + * @param num_entires Number of entries in the frame list */ static inline void usb_dwc_ll_hcfg_set_num_frame_list_entries(usb_dwc_dev_t *hw, usb_hal_frame_list_len_t num_entries) { @@ -903,7 +900,7 @@ static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t * // ------------------------------ QTD related ---------------------------------- #define USB_DWC_LL_QTD_STATUS_SUCCESS 0x0 //If QTD was processed, it indicates the data was transmitted/received successfully -#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data trasnmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK). +#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data transmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK). //Note: 0x2 is reserved #define USB_DWC_LL_QTD_STATUS_BUFFER 0x3 //AHB error occurred. #define USB_DWC_LL_QTD_STATUS_NOT_EXECUTED 0x4 //QTD as never processed @@ -914,7 +911,7 @@ static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t * * @param qtd Pointer to the QTD * @param data_buff Pointer to buffer containing the data to transfer * @param xfer_len Number of bytes in transfer. Setting 0 will do a zero length IN transfer. - * Non zero length must be mulitple of the endpoint's MPS. + * Non zero length must be multiple of the endpoint's MPS. * @param hoc Halt on complete (will generate an interrupt and halt the channel) */ static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc) @@ -932,7 +929,7 @@ static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *dat /** * @brief Set a QTD for a non isochronous OUT transfer * - * @param qtd Poitner to the QTD + * @param qtd Pointer to the QTD * @param data_buff Pointer to buffer containing the data to transfer * @param xfer_len Number of bytes to transfer. Setting 0 will do a zero length transfer. * For ctrl setup packets, this should be set to 8. @@ -972,9 +969,9 @@ static inline void usb_dwc_ll_qtd_set_null(usb_dwc_ll_dma_qtd_t *qtd) /** * @brief Get the status of a QTD * - * When a channel get's halted, call this to check whether each QTD was executed successfully + * When a channel gets halted, call this to check whether each QTD was executed successfully * - * @param qtd Poitner to the QTD + * @param qtd Pointer to the QTD * @param[out] rem_len Number of bytes ramining in the QTD * @param[out] status Status of the QTD */ @@ -992,8 +989,6 @@ static inline void usb_dwc_ll_qtd_get_status(usb_dwc_ll_dma_qtd_t *qtd, int *rem qtd->buffer_status_val = 0; } -#endif // SOC_USB_OTG_SUPPORTED - #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/usb_utmi_ll.h b/components/hal/esp32p4/include/hal/usb_utmi_ll.h index 027bb2e613..adebd1cdec 100644 --- a/components/hal/esp32p4/include/hal/usb_utmi_ll.h +++ b/components/hal/esp32p4/include/hal/usb_utmi_ll.h @@ -53,7 +53,7 @@ FORCE_INLINE_ATTR void usb_utmi_ll_enable_bus_clock(bool clk_en) /** * @brief Reset the USB UTMI PHY and USB_DWC_HS controller */ -FORCE_INLINE_ATTR void usb_utmi_ll_reset_register(void) +FORCE_INLINE_ATTR void _usb_utmi_ll_reset_register(void) { // Reset the USB_UTMI and USB_DWC_HS LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg20 = 1; @@ -63,7 +63,7 @@ FORCE_INLINE_ATTR void usb_utmi_ll_reset_register(void) } // P_AON_CLKRST.hp_usb_clkrst_ctrlx is shared register, so this function must be used in an atomic way -#define usb_utmi_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; usb_utmi_ll_reset_register(__VA_ARGS__) +#define usb_utmi_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_ll_reset_register(__VA_ARGS__) /** * @brief Enable precise detection of VBUS diff --git a/components/hal/esp32s2/include/hal/usb_dwc_ll.h b/components/hal/esp32s2/include/hal/usb_dwc_ll.h new file mode 100644 index 0000000000..954a54bf80 --- /dev/null +++ b/components/hal/esp32s2/include/hal/usb_dwc_ll.h @@ -0,0 +1,993 @@ +/* + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/usb_dwc_struct.h" +#include "soc/usb_dwc_cfg.h" +#include "hal/usb_dwc_types.h" +#include "hal/misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------- Helper Macros ------------------------------ */ + +// Get USB hardware instance +#define USB_DWC_LL_GET_HW(num) (&USB_DWC) + +/* ----------------------------------------------------------------------------- +--------------------------------- DWC Constants -------------------------------- +----------------------------------------------------------------------------- */ + +#define USB_DWC_QTD_LIST_MEM_ALIGN 512 +#define USB_DWC_FRAME_LIST_MEM_ALIGN 512 // The frame list needs to be 512 bytes aligned (contrary to the databook) + +/* ----------------------------------------------------------------------------- +------------------------------- Global Registers ------------------------------- +----------------------------------------------------------------------------- */ + +/* + * Interrupt bit masks of the GINTSTS and GINTMSK registers + */ +#define USB_DWC_LL_INTR_CORE_WKUPINT (1 << 31) +#define USB_DWC_LL_INTR_CORE_SESSREQINT (1 << 30) +#define USB_DWC_LL_INTR_CORE_DISCONNINT (1 << 29) +#define USB_DWC_LL_INTR_CORE_CONIDSTSCHNG (1 << 28) +#define USB_DWC_LL_INTR_CORE_PTXFEMP (1 << 26) +#define USB_DWC_LL_INTR_CORE_HCHINT (1 << 25) +#define USB_DWC_LL_INTR_CORE_PRTINT (1 << 24) +#define USB_DWC_LL_INTR_CORE_RESETDET (1 << 23) +#define USB_DWC_LL_INTR_CORE_FETSUSP (1 << 22) +#define USB_DWC_LL_INTR_CORE_INCOMPIP (1 << 21) +#define USB_DWC_LL_INTR_CORE_INCOMPISOIN (1 << 20) +#define USB_DWC_LL_INTR_CORE_OEPINT (1 << 19) +#define USB_DWC_LL_INTR_CORE_IEPINT (1 << 18) +#define USB_DWC_LL_INTR_CORE_EPMIS (1 << 17) +#define USB_DWC_LL_INTR_CORE_EOPF (1 << 15) +#define USB_DWC_LL_INTR_CORE_ISOOUTDROP (1 << 14) +#define USB_DWC_LL_INTR_CORE_ENUMDONE (1 << 13) +#define USB_DWC_LL_INTR_CORE_USBRST (1 << 12) +#define USB_DWC_LL_INTR_CORE_USBSUSP (1 << 11) +#define USB_DWC_LL_INTR_CORE_ERLYSUSP (1 << 10) +#define USB_DWC_LL_INTR_CORE_GOUTNAKEFF (1 << 7) +#define USB_DWC_LL_INTR_CORE_GINNAKEFF (1 << 6) +#define USB_DWC_LL_INTR_CORE_NPTXFEMP (1 << 5) +#define USB_DWC_LL_INTR_CORE_RXFLVL (1 << 4) +#define USB_DWC_LL_INTR_CORE_SOF (1 << 3) +#define USB_DWC_LL_INTR_CORE_OTGINT (1 << 2) +#define USB_DWC_LL_INTR_CORE_MODEMIS (1 << 1) +#define USB_DWC_LL_INTR_CORE_CURMOD (1 << 0) + +/* + * Bit mask of interrupt generating bits of the the HPRT register. These bits + * are ORd into the USB_DWC_LL_INTR_CORE_PRTINT interrupt. + * + * Note: Some fields of the HPRT are W1C (write 1 clear), this we cannot do a + * simple read and write-back to clear the HPRT interrupt bits. Instead we need + * a W1C mask the non-interrupt related bits + */ +#define USB_DWC_LL_HPRT_W1C_MSK (0x2E) +#define USB_DWC_LL_HPRT_ENA_MSK (0x04) +#define USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG (1 << 5) +#define USB_DWC_LL_INTR_HPRT_PRTENCHNG (1 << 3) +#define USB_DWC_LL_INTR_HPRT_PRTCONNDET (1 << 1) + +/* + * Bit mask of channel interrupts (HCINTi and HCINTMSKi registers) + * + * Note: Under Scatter/Gather DMA mode, only the following interrupts can be unmasked + * - DESC_LS_ROLL + * - XCS_XACT_ERR (always unmasked) + * - BNAINTR + * - CHHLTD + * - XFERCOMPL + * The remaining interrupt bits will still be set (when the corresponding event occurs) + * but will not generate an interrupt. Therefore we must proxy through the + * USB_DWC_LL_INTR_CHAN_CHHLTD interrupt to check the other interrupt bits. + */ +#define USB_DWC_LL_INTR_CHAN_DESC_LS_ROLL (1 << 13) +#define USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR (1 << 12) +#define USB_DWC_LL_INTR_CHAN_BNAINTR (1 << 11) +#define USB_DWC_LL_INTR_CHAN_DATATGLERR (1 << 10) +#define USB_DWC_LL_INTR_CHAN_FRMOVRUN (1 << 9) +#define USB_DWC_LL_INTR_CHAN_BBLEER (1 << 8) +#define USB_DWC_LL_INTR_CHAN_XACTERR (1 << 7) +#define USB_DWC_LL_INTR_CHAN_NYET (1 << 6) +#define USB_DWC_LL_INTR_CHAN_ACK (1 << 5) +#define USB_DWC_LL_INTR_CHAN_NAK (1 << 4) +#define USB_DWC_LL_INTR_CHAN_STALL (1 << 3) +#define USB_DWC_LL_INTR_CHAN_AHBERR (1 << 2) +#define USB_DWC_LL_INTR_CHAN_CHHLTD (1 << 1) +#define USB_DWC_LL_INTR_CHAN_XFERCOMPL (1 << 0) + +/* + * QTD (Queue Transfer Descriptor) structure used in Scatter/Gather DMA mode. + * Each QTD describes one transfer. Scatter gather mode will automatically split + * a transfer into multiple MPS packets. Each QTD is 64bits in size + * + * Note: The status information part of the QTD is interpreted differently depending + * on IN or OUT, and ISO or non-ISO + */ +typedef struct { + union { + struct { + uint32_t xfer_size: 17; + uint32_t aqtd_offset: 6; + uint32_t aqtd_valid: 1; + uint32_t reserved_24: 1; + uint32_t intr_cplt: 1; + uint32_t eol: 1; + uint32_t reserved_27: 1; + uint32_t rx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } in_non_iso; + struct { + uint32_t xfer_size: 12; + uint32_t reserved_12_24: 13; + uint32_t intr_cplt: 1; + uint32_t reserved_26_27: 2; + uint32_t rx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } in_iso; + struct { + uint32_t xfer_size: 17; + uint32_t reserved_17_23: 7; + uint32_t is_setup: 1; + uint32_t intr_cplt: 1; + uint32_t eol: 1; + uint32_t reserved_27: 1; + uint32_t tx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } out_non_iso; + struct { + uint32_t xfer_size: 12; + uint32_t reserved_12_24: 13; + uint32_t intr_cplt: 1; + uint32_t eol: 1; + uint32_t reserved_27: 1; + uint32_t tx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } out_iso; + uint32_t buffer_status_val; + }; + uint8_t *buffer; +} usb_dwc_ll_dma_qtd_t; + + +/* ----------------------------------------------------------------------------- +------------------------------- Global Registers ------------------------------- +----------------------------------------------------------------------------- */ + +// --------------------------- GAHBCFG Register -------------------------------- + +static inline void usb_dwc_ll_gahbcfg_en_dma_mode(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.dmaen = 1; +} + +static inline void usb_dwc_ll_gahbcfg_en_slave_mode(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.dmaen = 0; +} + +static inline void usb_dwc_ll_gahbcfg_set_hbstlen(usb_dwc_dev_t *hw, uint32_t burst_len) +{ + hw->gahbcfg_reg.hbstlen = burst_len; +} + +static inline void usb_dwc_ll_gahbcfg_en_global_intr(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.glbllntrmsk = 1; +} + +static inline void usb_dwc_ll_gahbcfg_dis_global_intr(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.glbllntrmsk = 0; +} + +// --------------------------- GUSBCFG Register -------------------------------- + +static inline void usb_dwc_ll_gusbcfg_force_host_mode(usb_dwc_dev_t *hw) +{ + hw->gusbcfg_reg.forcehstmode = 1; +} + +static inline void usb_dwc_ll_gusbcfg_dis_hnp_cap(usb_dwc_dev_t *hw) +{ + hw->gusbcfg_reg.hnpcap = 0; +} + +static inline void usb_dwc_ll_gusbcfg_dis_srp_cap(usb_dwc_dev_t *hw) +{ + hw->gusbcfg_reg.srpcap = 0; +} + +static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t tout_cal) +{ + hw->gusbcfg_reg.toutcal = tout_cal; +} + +static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw) +{ +#if (OTG_HSPHY_INTERFACE != 0) + hw->gusbcfg_reg.phyif = 1; // 16 bits interface + hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+ + hw->gusbcfg_reg.physel = 0; // HS PHY +#endif // (OTG_HSPHY_INTERFACE != 0) +} + +// --------------------------- GRSTCTL Register -------------------------------- + +static inline bool usb_dwc_ll_grstctl_is_ahb_idle(usb_dwc_dev_t *hw) +{ + return hw->grstctl_reg.ahbidle; +} + +static inline bool usb_dwc_ll_grstctl_is_dma_req_in_progress(usb_dwc_dev_t *hw) +{ + return hw->grstctl_reg.dmareq; +} + +static inline void usb_dwc_ll_grstctl_flush_nptx_fifo(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.txfnum = 0; //Set the TX FIFO number to 0 to select the non-periodic TX FIFO + hw->grstctl_reg.txfflsh = 1; //Flush the selected TX FIFO + //Wait for the flushing to complete + while (hw->grstctl_reg.txfflsh) { + ; + } +} + +static inline void usb_dwc_ll_grstctl_flush_ptx_fifo(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.txfnum = 1; //Set the TX FIFO number to 1 to select the periodic TX FIFO + hw->grstctl_reg.txfflsh = 1; //FLush the select TX FIFO + //Wait for the flushing to complete + while (hw->grstctl_reg.txfflsh) { + ; + } +} + +static inline void usb_dwc_ll_grstctl_flush_rx_fifo(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.rxfflsh = 1; + //Wait for the flushing to complete + while (hw->grstctl_reg.rxfflsh) { + ; + } +} + +static inline void usb_dwc_ll_grstctl_reset_frame_counter(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.frmcntrrst = 1; +} + +static inline void usb_dwc_ll_grstctl_core_soft_reset(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.csftrst = 1; +} + +static inline bool usb_dwc_ll_grstctl_is_core_soft_reset_in_progress(usb_dwc_dev_t *hw) +{ + return hw->grstctl_reg.csftrst; +} + +// --------------------------- GINTSTS Register -------------------------------- + +/** + * @brief Reads and clears the global interrupt register + * + * @param hw Start address of the DWC_OTG registers + * @return uint32_t Mask of interrupts + */ +static inline uint32_t usb_dwc_ll_gintsts_read_and_clear_intrs(usb_dwc_dev_t *hw) +{ + usb_dwc_gintsts_reg_t gintsts; + gintsts.val = hw->gintsts_reg.val; + hw->gintsts_reg.val = gintsts.val; //Write back to clear + return gintsts.val; +} + +/** + * @brief Clear specific interrupts + * + * @param hw Start address of the DWC_OTG registers + * @param intr_msk Mask of interrupts to clear + */ +static inline void usb_dwc_ll_gintsts_clear_intrs(usb_dwc_dev_t *hw, uint32_t intr_msk) +{ + //All GINTSTS fields are either W1C or read only. So safe to write directly + hw->gintsts_reg.val = intr_msk; +} + +// --------------------------- GINTMSK Register -------------------------------- + +static inline void usb_dwc_ll_gintmsk_en_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask) +{ + hw->gintmsk_reg.val |= intr_mask; +} + +static inline void usb_dwc_ll_gintmsk_dis_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask) +{ + hw->gintmsk_reg.val &= ~intr_mask; +} + +// --------------------------- GRXFSIZ Register -------------------------------- + +static inline void usb_dwc_ll_grxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t num_lines) +{ + //Set size in words + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->grxfsiz_reg, rxfdep, num_lines); +} + +// -------------------------- GNPTXFSIZ Register ------------------------------- + +static inline void usb_dwc_ll_gnptxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines) +{ + usb_dwc_gnptxfsiz_reg_t gnptxfsiz; + gnptxfsiz.val = hw->gnptxfsiz_reg.val; + HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfstaddr, addr); + HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfdep, num_lines); + hw->gnptxfsiz_reg.val = gnptxfsiz.val; +} + +// --------------------------- GSNPSID Register -------------------------------- + +static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw) +{ + return hw->gsnpsid_reg.val; +} + +// --------------------------- GHWCFGx Register -------------------------------- + +/** + * @brief Get the hardware configuration registers of the DWC_OTG controller + * + * The hardware configuration regitsers are read only and indicate the various + * features of the DWC_OTG core. + * + * @param hw Start address of the DWC_OTG registers + * @param[out] ghwcfg1 Hardware configuration registesr 1 + * @param[out] ghwcfg2 Hardware configuration registesr 2 + * @param[out] ghwcfg3 Hardware configuration registesr 3 + * @param[out] ghwcfg4 Hardware configuration registesr 4 + */ +static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4) +{ + *ghwcfg1 = hw->ghwcfg1_reg.val; + *ghwcfg2 = hw->ghwcfg2_reg.val; + *ghwcfg3 = hw->ghwcfg3_reg.val; + *ghwcfg4 = hw->ghwcfg4_reg.val; +} + +// --------------------------- HPTXFSIZ Register ------------------------------- + +static inline void usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines) +{ + usb_dwc_hptxfsiz_reg_t hptxfsiz; + hptxfsiz.val = hw->hptxfsiz_reg.val; + HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfstaddr, addr); + HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfsize, num_lines); + hw->hptxfsiz_reg.val = hptxfsiz.val; +} + +/* ----------------------------------------------------------------------------- +-------------------------------- Host Registers -------------------------------- +----------------------------------------------------------------------------- */ + +// ----------------------------- HCFG Register --------------------------------- + +static inline void usb_dwc_ll_hcfg_en_perio_sched(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.perschedena = 1; +} + +static inline void usb_dwc_ll_hcfg_dis_perio_sched(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.perschedena = 0; +} + +/** + * Sets the length of the frame list + * + * @param num_entires Number of entries in the frame list + */ +static inline void usb_dwc_ll_hcfg_set_num_frame_list_entries(usb_dwc_dev_t *hw, usb_hal_frame_list_len_t num_entries) +{ + uint32_t frlisten; + switch (num_entries) { + case USB_HAL_FRAME_LIST_LEN_8: + frlisten = 0; + break; + case USB_HAL_FRAME_LIST_LEN_16: + frlisten = 1; + break; + case USB_HAL_FRAME_LIST_LEN_32: + frlisten = 2; + break; + default: //USB_HAL_FRAME_LIST_LEN_64 + frlisten = 3; + break; + } + hw->hcfg_reg.frlisten = frlisten; +} + +static inline void usb_dwc_ll_hcfg_en_scatt_gatt_dma(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.descdma = 1; +} + +static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.fslssupp = 1; +} + +static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.fslspclksel = 1; +} + +/** + * @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA + * + * @param[in] hw Start address of the DWC_OTG registers + * @param[in] speed Speed to initialize the host port at + */ +static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +{ + hw->hcfg_reg.descdma = 1; //Enable scatt/gatt +#if (OTG_HSPHY_INTERFACE == 0) + /* + Indicate to the OTG core what speed the PHY clock is at + Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode, + so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. + */ + hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only +#endif // (OTG_HSPHY_INTERFACE == 0) + hw->hcfg_reg.perschedena = 0; //Disable perio sched +} + +// ----------------------------- HFIR Register --------------------------------- + +static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +{ +#if (OTG_HSPHY_INTERFACE == 0) + usb_dwc_hfir_reg_t hfir; + hfir.val = hw->hfir_reg.val; + hfir.hfirrldctrl = 0; //Disable dynamic loading + /* + Set frame interval to be equal to 1ms + Note: It seems like our PHY has an implicit 8 divider applied when in LS mode, + so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. + */ + hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS + hw->hfir_reg.val = hfir.val; +#endif // (OTG_HSPHY_INTERFACE == 0) +} + +// ----------------------------- HFNUM Register -------------------------------- + +static inline uint32_t usb_dwc_ll_hfnum_get_frame_time_rem(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->hfnum_reg, frrem); +} + +static inline uint32_t usb_dwc_ll_hfnum_get_frame_num(usb_dwc_dev_t *hw) +{ + return hw->hfnum_reg.frnum; +} + +// ---------------------------- HPTXSTS Register ------------------------------- + +static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_top(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxqtop); +} + +static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_space_avail(usb_dwc_dev_t *hw) +{ + return hw->hptxsts_reg.ptxqspcavail; +} + +static inline uint32_t usb_dwc_ll_ptxsts_get_ptxf_space_avail(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxfspcavail); +} + +// ----------------------------- HAINT Register -------------------------------- + +static inline uint32_t usb_dwc_ll_haint_get_chan_intrs(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->haint_reg, haint); +} + +// --------------------------- HAINTMSK Register ------------------------------- + +static inline void usb_dwc_ll_haintmsk_en_chan_intr(usb_dwc_dev_t *hw, uint32_t mask) +{ + + hw->haintmsk_reg.val |= mask; +} + +static inline void usb_dwc_ll_haintmsk_dis_chan_intr(usb_dwc_dev_t *hw, uint32_t mask) +{ + hw->haintmsk_reg.val &= ~mask; +} + +// --------------------------- HFLBAddr Register ------------------------------- + +/** + * @brief Set the base address of the scheduling frame list + * + * @note For some reason, this address must be 512 bytes aligned or else a bunch of frames will not be scheduled when + * the frame list rolls over. However, according to the databook, there is no mention of the HFLBAddr needing to + * be aligned. + * + * @param hw Start address of the DWC_OTG registers + * @param addr Base address of the scheduling frame list + */ +static inline void usb_dwc_ll_hflbaddr_set_base_addr(usb_dwc_dev_t *hw, uint32_t addr) +{ + hw->hflbaddr_reg.hflbaddr = addr; +} + +/** + * @brief Get the base address of the scheduling frame list + * + * @param hw Start address of the DWC_OTG registers + * @return uint32_t Base address of the scheduling frame list + */ +static inline uint32_t usb_dwc_ll_hflbaddr_get_base_addr(usb_dwc_dev_t *hw) +{ + return hw->hflbaddr_reg.hflbaddr; +} + +// ----------------------------- HPRT Register --------------------------------- + +static inline usb_dwc_speed_t usb_dwc_ll_hprt_get_speed(usb_dwc_dev_t *hw) +{ + return (usb_dwc_speed_t)hw->hprt_reg.prtspd; +} + +static inline uint32_t usb_dwc_ll_hprt_get_test_ctl(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prttstctl; +} + +static inline void usb_dwc_ll_hprt_set_test_ctl(usb_dwc_dev_t *hw, uint32_t test_mode) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prttstctl = test_mode; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline void usb_dwc_ll_hprt_en_pwr(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtpwr = 1; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline void usb_dwc_ll_hprt_dis_pwr(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtpwr = 0; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline uint32_t usb_dwc_ll_hprt_get_pwr_line_status(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtlnsts; +} + +static inline void usb_dwc_ll_hprt_set_port_reset(usb_dwc_dev_t *hw, bool reset) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtrst = reset; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_port_reset(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtrst; +} + +static inline void usb_dwc_ll_hprt_set_port_suspend(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtsusp = 1; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_port_suspend(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtsusp; +} + +static inline void usb_dwc_ll_hprt_set_port_resume(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtres = 1; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline void usb_dwc_ll_hprt_clr_port_resume(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtres = 0; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_port_resume(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtres; +} + +static inline bool usb_dwc_ll_hprt_get_port_overcur(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtovrcurract; +} + +static inline bool usb_dwc_ll_hprt_get_port_en(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtena; +} + +static inline void usb_dwc_ll_hprt_port_dis(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtena = 1; //W1C to disable + //we want to W1C ENA but not W1C the interrupt bits + hw->hprt_reg.val = hprt.val & ((~USB_DWC_LL_HPRT_W1C_MSK) | USB_DWC_LL_HPRT_ENA_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_conn_status(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtconnsts; +} + +static inline uint32_t usb_dwc_ll_hprt_intr_read_and_clear(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + //We want to W1C the interrupt bits but not that ENA + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_ENA_MSK); + //Return only the interrupt bits + return (hprt.val & (USB_DWC_LL_HPRT_W1C_MSK & ~(USB_DWC_LL_HPRT_ENA_MSK))); +} + +static inline void usb_dwc_ll_hprt_intr_clear(usb_dwc_dev_t *hw, uint32_t intr_mask) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hw->hprt_reg.val = ((hprt.val & ~USB_DWC_LL_HPRT_ENA_MSK) & ~USB_DWC_LL_HPRT_W1C_MSK) | intr_mask; +} + +//Per Channel registers + +// --------------------------- HCCHARi Register -------------------------------- + +static inline void usb_dwc_ll_hcchar_enable_chan(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.chena = 1; +} + +static inline bool usb_dwc_ll_hcchar_chan_is_enabled(volatile usb_dwc_host_chan_regs_t *chan) +{ + return chan->hcchar_reg.chena; +} + +static inline void usb_dwc_ll_hcchar_disable_chan(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.chdis = 1; +} + +static inline void usb_dwc_ll_hcchar_set_odd_frame(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.oddfrm = 1; +} + +static inline void usb_dwc_ll_hcchar_set_even_frame(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.oddfrm = 0; +} + +static inline void usb_dwc_ll_hcchar_set_dev_addr(volatile usb_dwc_host_chan_regs_t *chan, uint32_t addr) +{ + chan->hcchar_reg.devaddr = addr; +} + +static inline void usb_dwc_ll_hcchar_set_ep_type(volatile usb_dwc_host_chan_regs_t *chan, usb_dwc_xfer_type_t type) +{ + chan->hcchar_reg.eptype = (uint32_t)type; +} + +//Indicates whether channel is commuunicating with a LS device connected via a FS hub. Setting this bit to 1 will cause +//each packet to be preceded by a PREamble packet +static inline void usb_dwc_ll_hcchar_set_lspddev(volatile usb_dwc_host_chan_regs_t *chan, bool is_ls) +{ + chan->hcchar_reg.lspddev = is_ls; +} + +static inline void usb_dwc_ll_hcchar_set_dir(volatile usb_dwc_host_chan_regs_t *chan, bool is_in) +{ + chan->hcchar_reg.epdir = is_in; +} + +static inline void usb_dwc_ll_hcchar_set_ep_num(volatile usb_dwc_host_chan_regs_t *chan, uint32_t num) +{ + chan->hcchar_reg.epnum = num; +} + +static inline void usb_dwc_ll_hcchar_set_mps(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mps) +{ + chan->hcchar_reg.mps = mps; +} + +static inline void usb_dwc_ll_hcchar_init(volatile usb_dwc_host_chan_regs_t *chan, int dev_addr, int ep_num, int mps, usb_dwc_xfer_type_t type, bool is_in, bool is_ls) +{ + //Sets all persistent fields of the channel over its lifetimez + usb_dwc_ll_hcchar_set_dev_addr(chan, dev_addr); + usb_dwc_ll_hcchar_set_ep_type(chan, type); + usb_dwc_ll_hcchar_set_lspddev(chan, is_ls); + usb_dwc_ll_hcchar_set_dir(chan, is_in); + usb_dwc_ll_hcchar_set_ep_num(chan, ep_num); + usb_dwc_ll_hcchar_set_mps(chan, mps); +} + +// ---------------------------- HCINTi Register -------------------------------- + +static inline uint32_t usb_dwc_ll_hcint_read_and_clear_intrs(volatile usb_dwc_host_chan_regs_t *chan) +{ + usb_dwc_hcint_reg_t hcint; + hcint.val = chan->hcint_reg.val; + chan->hcint_reg.val = hcint.val; + return hcint.val; +} + +// --------------------------- HCINTMSKi Register ------------------------------ + +static inline void usb_dwc_ll_hcintmsk_set_intr_mask(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mask) +{ + chan->hcintmsk_reg.val = mask; +} + +// ---------------------------- HCTSIZi Register ------------------------------- + +static inline void usb_dwc_ll_hctsiz_set_pid(volatile usb_dwc_host_chan_regs_t *chan, uint32_t data_pid) +{ + if (data_pid == 0) { + chan->hctsiz_reg.pid = 0; + } else { + chan->hctsiz_reg.pid = 2; + } +} + +static inline uint32_t usb_dwc_ll_hctsiz_get_pid(volatile usb_dwc_host_chan_regs_t *chan) +{ + if (chan->hctsiz_reg.pid == 0) { + return 0; //DATA0 + } else { + return 1; //DATA1 + } +} + +static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan_regs_t *chan, int qtd_list_len) +{ + usb_dwc_hctsiz_reg_t hctsiz; + hctsiz.val = chan->hctsiz_reg.val; + //Set the length of the descriptor list. NTD occupies xfersize[15:8] + hctsiz.xfersize &= ~(0xFF << 8); + hctsiz.xfersize |= ((qtd_list_len - 1) & 0xFF) << 8; + chan->hctsiz_reg.val = hctsiz.val; +} + +static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan) +{ + usb_dwc_hctsiz_reg_t hctsiz; + hctsiz.val = chan->hctsiz_reg.val; + hctsiz.dopng = 0; //Don't do ping + /* + Set SCHED_INFO which occupies xfersize[7:0] + It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels + */ + hctsiz.xfersize |= 0xFF; + chan->hctsiz_reg.val = hctsiz.val; +} + +static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset) +{ + // @see USB-OTG databook: Table 5-47 + // This function is relevant only for HS + usb_dwc_hctsiz_reg_t hctsiz; + hctsiz.val = chan->hctsiz_reg.val; + uint8_t sched_info_val; + switch (tokens_per_frame) { + case 1: + offset %= 8; // If the required offset > 8, we must wrap around to SCHED_INFO size = 8 + sched_info_val = 0b00000001; + break; + case 2: + offset %= 4; + sched_info_val = 0b00010001; + break; + case 4: + offset %= 2; + sched_info_val = 0b01010101; + break; + case 8: + offset = 0; + sched_info_val = 0b11111111; + break; + default: + abort(); + break; + } + sched_info_val <<= offset; + hctsiz.xfersize &= ~(0xFF); + hctsiz.xfersize |= sched_info_val; + chan->hctsiz_reg.val = hctsiz.val; +} + +// ---------------------------- HCDMAi Register -------------------------------- + +static inline void usb_dwc_ll_hcdma_set_qtd_list_addr(volatile usb_dwc_host_chan_regs_t *chan, void *dmaaddr, uint32_t qtd_idx) +{ + usb_dwc_hcdma_reg_t hcdma; + /* + Set the base address portion of the field which is dmaaddr[31:9]. This is + the based address of the QTD list and must be 512 bytes aligned + */ + hcdma.dmaaddr = ((uint32_t)dmaaddr) & 0xFFFFFE00; + //Set the current QTD index in the QTD list which is dmaaddr[8:3] + hcdma.dmaaddr |= (qtd_idx & 0x3F) << 3; + //dmaaddr[2:0] is reserved thus doesn't not need to be set + + chan->hcdma_reg.val = hcdma.val; +} + +static inline int usb_dwc_ll_hcdam_get_cur_qtd_idx(usb_dwc_host_chan_regs_t *chan) +{ + //The current QTD index is dmaaddr[8:3] + return (chan->hcdma_reg.dmaaddr >> 3) & 0x3F; +} + +// ---------------------------- HCDMABi Register ------------------------------- + +static inline void *usb_dwc_ll_hcdmab_get_buff_addr(volatile usb_dwc_host_chan_regs_t *chan) +{ + return (void *)chan->hcdmab_reg.hcdmab; +} + +/* ----------------------------------------------------------------------------- +---------------------------- Scatter/Gather DMA QTDs --------------------------- +----------------------------------------------------------------------------- */ + +// ---------------------------- Helper Functions ------------------------------- + +/** + * @brief Get the base address of a channel's register based on the channel's index + * + * @param dev Start address of the DWC_OTG registers + * @param chan_idx The channel's index + * @return usb_dwc_host_chan_regs_t* Pointer to channel's registers + */ +static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t *dev, int chan_idx) +{ + return &dev->host_chans[chan_idx]; +} + +// ------------------------------ QTD related ---------------------------------- + +#define USB_DWC_LL_QTD_STATUS_SUCCESS 0x0 //If QTD was processed, it indicates the data was transmitted/received successfully +#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data transmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK). +//Note: 0x2 is reserved +#define USB_DWC_LL_QTD_STATUS_BUFFER 0x3 //AHB error occurred. +#define USB_DWC_LL_QTD_STATUS_NOT_EXECUTED 0x4 //QTD as never processed + +/** + * @brief Set a QTD for a non isochronous IN transfer + * + * @param qtd Pointer to the QTD + * @param data_buff Pointer to buffer containing the data to transfer + * @param xfer_len Number of bytes in transfer. Setting 0 will do a zero length IN transfer. + * Non zero length must be multiple of the endpoint's MPS. + * @param hoc Halt on complete (will generate an interrupt and halt the channel) + */ +static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc) +{ + qtd->buffer = data_buff; //Set pointer to data buffer + qtd->buffer_status_val = 0; //Reset all flags to zero + qtd->in_non_iso.xfer_size = xfer_len; + if (hoc) { + qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD + qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd + } + qtd->in_non_iso.active = 1; +} + +/** + * @brief Set a QTD for a non isochronous OUT transfer + * + * @param qtd Pointer to the QTD + * @param data_buff Pointer to buffer containing the data to transfer + * @param xfer_len Number of bytes to transfer. Setting 0 will do a zero length transfer. + * For ctrl setup packets, this should be set to 8. + * @param hoc Halt on complete (will generate an interrupt) + * @param is_setup Indicates whether this is a control transfer setup packet or a normal OUT Data transfer. + * (As per the USB protocol, setup packets cannot be STALLd or NAKd by the device) + */ +static inline void usb_dwc_ll_qtd_set_out(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc, bool is_setup) +{ + qtd->buffer = data_buff; //Set pointer to data buffer + qtd->buffer_status_val = 0; //Reset all flags to zero + qtd->out_non_iso.xfer_size = xfer_len; + if (is_setup) { + qtd->out_non_iso.is_setup = 1; + } + if (hoc) { + qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD + qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd + } + qtd->out_non_iso.active = 1; +} + +/** + * @brief Set a QTD as NULL + * + * This sets the QTD to a value of 0. This is only useful when you need to insert + * blank QTDs into a list of QTDs + * + * @param qtd Pointer to the QTD + */ +static inline void usb_dwc_ll_qtd_set_null(usb_dwc_ll_dma_qtd_t *qtd) +{ + qtd->buffer = NULL; + qtd->buffer_status_val = 0; //Disable qtd by clearing it to zero. Used by interrupt/isoc as an unscheudled frame +} + +/** + * @brief Get the status of a QTD + * + * When a channel gets halted, call this to check whether each QTD was executed successfully + * + * @param qtd Pointer to the QTD + * @param[out] rem_len Number of bytes ramining in the QTD + * @param[out] status Status of the QTD + */ +static inline void usb_dwc_ll_qtd_get_status(usb_dwc_ll_dma_qtd_t *qtd, int *rem_len, int *status) +{ + //Status is the same regardless of IN or OUT + if (qtd->in_non_iso.active) { + //QTD was never processed + *status = USB_DWC_LL_QTD_STATUS_NOT_EXECUTED; + } else { + *status = qtd->in_non_iso.rx_status; + } + *rem_len = qtd->in_non_iso.xfer_size; + //Clear the QTD just for safety + qtd->buffer_status_val = 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32s3/include/hal/usb_dwc_ll.h b/components/hal/esp32s3/include/hal/usb_dwc_ll.h new file mode 100644 index 0000000000..954a54bf80 --- /dev/null +++ b/components/hal/esp32s3/include/hal/usb_dwc_ll.h @@ -0,0 +1,993 @@ +/* + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/usb_dwc_struct.h" +#include "soc/usb_dwc_cfg.h" +#include "hal/usb_dwc_types.h" +#include "hal/misc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------- Helper Macros ------------------------------ */ + +// Get USB hardware instance +#define USB_DWC_LL_GET_HW(num) (&USB_DWC) + +/* ----------------------------------------------------------------------------- +--------------------------------- DWC Constants -------------------------------- +----------------------------------------------------------------------------- */ + +#define USB_DWC_QTD_LIST_MEM_ALIGN 512 +#define USB_DWC_FRAME_LIST_MEM_ALIGN 512 // The frame list needs to be 512 bytes aligned (contrary to the databook) + +/* ----------------------------------------------------------------------------- +------------------------------- Global Registers ------------------------------- +----------------------------------------------------------------------------- */ + +/* + * Interrupt bit masks of the GINTSTS and GINTMSK registers + */ +#define USB_DWC_LL_INTR_CORE_WKUPINT (1 << 31) +#define USB_DWC_LL_INTR_CORE_SESSREQINT (1 << 30) +#define USB_DWC_LL_INTR_CORE_DISCONNINT (1 << 29) +#define USB_DWC_LL_INTR_CORE_CONIDSTSCHNG (1 << 28) +#define USB_DWC_LL_INTR_CORE_PTXFEMP (1 << 26) +#define USB_DWC_LL_INTR_CORE_HCHINT (1 << 25) +#define USB_DWC_LL_INTR_CORE_PRTINT (1 << 24) +#define USB_DWC_LL_INTR_CORE_RESETDET (1 << 23) +#define USB_DWC_LL_INTR_CORE_FETSUSP (1 << 22) +#define USB_DWC_LL_INTR_CORE_INCOMPIP (1 << 21) +#define USB_DWC_LL_INTR_CORE_INCOMPISOIN (1 << 20) +#define USB_DWC_LL_INTR_CORE_OEPINT (1 << 19) +#define USB_DWC_LL_INTR_CORE_IEPINT (1 << 18) +#define USB_DWC_LL_INTR_CORE_EPMIS (1 << 17) +#define USB_DWC_LL_INTR_CORE_EOPF (1 << 15) +#define USB_DWC_LL_INTR_CORE_ISOOUTDROP (1 << 14) +#define USB_DWC_LL_INTR_CORE_ENUMDONE (1 << 13) +#define USB_DWC_LL_INTR_CORE_USBRST (1 << 12) +#define USB_DWC_LL_INTR_CORE_USBSUSP (1 << 11) +#define USB_DWC_LL_INTR_CORE_ERLYSUSP (1 << 10) +#define USB_DWC_LL_INTR_CORE_GOUTNAKEFF (1 << 7) +#define USB_DWC_LL_INTR_CORE_GINNAKEFF (1 << 6) +#define USB_DWC_LL_INTR_CORE_NPTXFEMP (1 << 5) +#define USB_DWC_LL_INTR_CORE_RXFLVL (1 << 4) +#define USB_DWC_LL_INTR_CORE_SOF (1 << 3) +#define USB_DWC_LL_INTR_CORE_OTGINT (1 << 2) +#define USB_DWC_LL_INTR_CORE_MODEMIS (1 << 1) +#define USB_DWC_LL_INTR_CORE_CURMOD (1 << 0) + +/* + * Bit mask of interrupt generating bits of the the HPRT register. These bits + * are ORd into the USB_DWC_LL_INTR_CORE_PRTINT interrupt. + * + * Note: Some fields of the HPRT are W1C (write 1 clear), this we cannot do a + * simple read and write-back to clear the HPRT interrupt bits. Instead we need + * a W1C mask the non-interrupt related bits + */ +#define USB_DWC_LL_HPRT_W1C_MSK (0x2E) +#define USB_DWC_LL_HPRT_ENA_MSK (0x04) +#define USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG (1 << 5) +#define USB_DWC_LL_INTR_HPRT_PRTENCHNG (1 << 3) +#define USB_DWC_LL_INTR_HPRT_PRTCONNDET (1 << 1) + +/* + * Bit mask of channel interrupts (HCINTi and HCINTMSKi registers) + * + * Note: Under Scatter/Gather DMA mode, only the following interrupts can be unmasked + * - DESC_LS_ROLL + * - XCS_XACT_ERR (always unmasked) + * - BNAINTR + * - CHHLTD + * - XFERCOMPL + * The remaining interrupt bits will still be set (when the corresponding event occurs) + * but will not generate an interrupt. Therefore we must proxy through the + * USB_DWC_LL_INTR_CHAN_CHHLTD interrupt to check the other interrupt bits. + */ +#define USB_DWC_LL_INTR_CHAN_DESC_LS_ROLL (1 << 13) +#define USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR (1 << 12) +#define USB_DWC_LL_INTR_CHAN_BNAINTR (1 << 11) +#define USB_DWC_LL_INTR_CHAN_DATATGLERR (1 << 10) +#define USB_DWC_LL_INTR_CHAN_FRMOVRUN (1 << 9) +#define USB_DWC_LL_INTR_CHAN_BBLEER (1 << 8) +#define USB_DWC_LL_INTR_CHAN_XACTERR (1 << 7) +#define USB_DWC_LL_INTR_CHAN_NYET (1 << 6) +#define USB_DWC_LL_INTR_CHAN_ACK (1 << 5) +#define USB_DWC_LL_INTR_CHAN_NAK (1 << 4) +#define USB_DWC_LL_INTR_CHAN_STALL (1 << 3) +#define USB_DWC_LL_INTR_CHAN_AHBERR (1 << 2) +#define USB_DWC_LL_INTR_CHAN_CHHLTD (1 << 1) +#define USB_DWC_LL_INTR_CHAN_XFERCOMPL (1 << 0) + +/* + * QTD (Queue Transfer Descriptor) structure used in Scatter/Gather DMA mode. + * Each QTD describes one transfer. Scatter gather mode will automatically split + * a transfer into multiple MPS packets. Each QTD is 64bits in size + * + * Note: The status information part of the QTD is interpreted differently depending + * on IN or OUT, and ISO or non-ISO + */ +typedef struct { + union { + struct { + uint32_t xfer_size: 17; + uint32_t aqtd_offset: 6; + uint32_t aqtd_valid: 1; + uint32_t reserved_24: 1; + uint32_t intr_cplt: 1; + uint32_t eol: 1; + uint32_t reserved_27: 1; + uint32_t rx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } in_non_iso; + struct { + uint32_t xfer_size: 12; + uint32_t reserved_12_24: 13; + uint32_t intr_cplt: 1; + uint32_t reserved_26_27: 2; + uint32_t rx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } in_iso; + struct { + uint32_t xfer_size: 17; + uint32_t reserved_17_23: 7; + uint32_t is_setup: 1; + uint32_t intr_cplt: 1; + uint32_t eol: 1; + uint32_t reserved_27: 1; + uint32_t tx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } out_non_iso; + struct { + uint32_t xfer_size: 12; + uint32_t reserved_12_24: 13; + uint32_t intr_cplt: 1; + uint32_t eol: 1; + uint32_t reserved_27: 1; + uint32_t tx_status: 2; + uint32_t reserved_30: 1; + uint32_t active: 1; + } out_iso; + uint32_t buffer_status_val; + }; + uint8_t *buffer; +} usb_dwc_ll_dma_qtd_t; + + +/* ----------------------------------------------------------------------------- +------------------------------- Global Registers ------------------------------- +----------------------------------------------------------------------------- */ + +// --------------------------- GAHBCFG Register -------------------------------- + +static inline void usb_dwc_ll_gahbcfg_en_dma_mode(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.dmaen = 1; +} + +static inline void usb_dwc_ll_gahbcfg_en_slave_mode(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.dmaen = 0; +} + +static inline void usb_dwc_ll_gahbcfg_set_hbstlen(usb_dwc_dev_t *hw, uint32_t burst_len) +{ + hw->gahbcfg_reg.hbstlen = burst_len; +} + +static inline void usb_dwc_ll_gahbcfg_en_global_intr(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.glbllntrmsk = 1; +} + +static inline void usb_dwc_ll_gahbcfg_dis_global_intr(usb_dwc_dev_t *hw) +{ + hw->gahbcfg_reg.glbllntrmsk = 0; +} + +// --------------------------- GUSBCFG Register -------------------------------- + +static inline void usb_dwc_ll_gusbcfg_force_host_mode(usb_dwc_dev_t *hw) +{ + hw->gusbcfg_reg.forcehstmode = 1; +} + +static inline void usb_dwc_ll_gusbcfg_dis_hnp_cap(usb_dwc_dev_t *hw) +{ + hw->gusbcfg_reg.hnpcap = 0; +} + +static inline void usb_dwc_ll_gusbcfg_dis_srp_cap(usb_dwc_dev_t *hw) +{ + hw->gusbcfg_reg.srpcap = 0; +} + +static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t tout_cal) +{ + hw->gusbcfg_reg.toutcal = tout_cal; +} + +static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw) +{ +#if (OTG_HSPHY_INTERFACE != 0) + hw->gusbcfg_reg.phyif = 1; // 16 bits interface + hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+ + hw->gusbcfg_reg.physel = 0; // HS PHY +#endif // (OTG_HSPHY_INTERFACE != 0) +} + +// --------------------------- GRSTCTL Register -------------------------------- + +static inline bool usb_dwc_ll_grstctl_is_ahb_idle(usb_dwc_dev_t *hw) +{ + return hw->grstctl_reg.ahbidle; +} + +static inline bool usb_dwc_ll_grstctl_is_dma_req_in_progress(usb_dwc_dev_t *hw) +{ + return hw->grstctl_reg.dmareq; +} + +static inline void usb_dwc_ll_grstctl_flush_nptx_fifo(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.txfnum = 0; //Set the TX FIFO number to 0 to select the non-periodic TX FIFO + hw->grstctl_reg.txfflsh = 1; //Flush the selected TX FIFO + //Wait for the flushing to complete + while (hw->grstctl_reg.txfflsh) { + ; + } +} + +static inline void usb_dwc_ll_grstctl_flush_ptx_fifo(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.txfnum = 1; //Set the TX FIFO number to 1 to select the periodic TX FIFO + hw->grstctl_reg.txfflsh = 1; //FLush the select TX FIFO + //Wait for the flushing to complete + while (hw->grstctl_reg.txfflsh) { + ; + } +} + +static inline void usb_dwc_ll_grstctl_flush_rx_fifo(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.rxfflsh = 1; + //Wait for the flushing to complete + while (hw->grstctl_reg.rxfflsh) { + ; + } +} + +static inline void usb_dwc_ll_grstctl_reset_frame_counter(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.frmcntrrst = 1; +} + +static inline void usb_dwc_ll_grstctl_core_soft_reset(usb_dwc_dev_t *hw) +{ + hw->grstctl_reg.csftrst = 1; +} + +static inline bool usb_dwc_ll_grstctl_is_core_soft_reset_in_progress(usb_dwc_dev_t *hw) +{ + return hw->grstctl_reg.csftrst; +} + +// --------------------------- GINTSTS Register -------------------------------- + +/** + * @brief Reads and clears the global interrupt register + * + * @param hw Start address of the DWC_OTG registers + * @return uint32_t Mask of interrupts + */ +static inline uint32_t usb_dwc_ll_gintsts_read_and_clear_intrs(usb_dwc_dev_t *hw) +{ + usb_dwc_gintsts_reg_t gintsts; + gintsts.val = hw->gintsts_reg.val; + hw->gintsts_reg.val = gintsts.val; //Write back to clear + return gintsts.val; +} + +/** + * @brief Clear specific interrupts + * + * @param hw Start address of the DWC_OTG registers + * @param intr_msk Mask of interrupts to clear + */ +static inline void usb_dwc_ll_gintsts_clear_intrs(usb_dwc_dev_t *hw, uint32_t intr_msk) +{ + //All GINTSTS fields are either W1C or read only. So safe to write directly + hw->gintsts_reg.val = intr_msk; +} + +// --------------------------- GINTMSK Register -------------------------------- + +static inline void usb_dwc_ll_gintmsk_en_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask) +{ + hw->gintmsk_reg.val |= intr_mask; +} + +static inline void usb_dwc_ll_gintmsk_dis_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask) +{ + hw->gintmsk_reg.val &= ~intr_mask; +} + +// --------------------------- GRXFSIZ Register -------------------------------- + +static inline void usb_dwc_ll_grxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t num_lines) +{ + //Set size in words + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->grxfsiz_reg, rxfdep, num_lines); +} + +// -------------------------- GNPTXFSIZ Register ------------------------------- + +static inline void usb_dwc_ll_gnptxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines) +{ + usb_dwc_gnptxfsiz_reg_t gnptxfsiz; + gnptxfsiz.val = hw->gnptxfsiz_reg.val; + HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfstaddr, addr); + HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfdep, num_lines); + hw->gnptxfsiz_reg.val = gnptxfsiz.val; +} + +// --------------------------- GSNPSID Register -------------------------------- + +static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw) +{ + return hw->gsnpsid_reg.val; +} + +// --------------------------- GHWCFGx Register -------------------------------- + +/** + * @brief Get the hardware configuration registers of the DWC_OTG controller + * + * The hardware configuration regitsers are read only and indicate the various + * features of the DWC_OTG core. + * + * @param hw Start address of the DWC_OTG registers + * @param[out] ghwcfg1 Hardware configuration registesr 1 + * @param[out] ghwcfg2 Hardware configuration registesr 2 + * @param[out] ghwcfg3 Hardware configuration registesr 3 + * @param[out] ghwcfg4 Hardware configuration registesr 4 + */ +static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4) +{ + *ghwcfg1 = hw->ghwcfg1_reg.val; + *ghwcfg2 = hw->ghwcfg2_reg.val; + *ghwcfg3 = hw->ghwcfg3_reg.val; + *ghwcfg4 = hw->ghwcfg4_reg.val; +} + +// --------------------------- HPTXFSIZ Register ------------------------------- + +static inline void usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines) +{ + usb_dwc_hptxfsiz_reg_t hptxfsiz; + hptxfsiz.val = hw->hptxfsiz_reg.val; + HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfstaddr, addr); + HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfsize, num_lines); + hw->hptxfsiz_reg.val = hptxfsiz.val; +} + +/* ----------------------------------------------------------------------------- +-------------------------------- Host Registers -------------------------------- +----------------------------------------------------------------------------- */ + +// ----------------------------- HCFG Register --------------------------------- + +static inline void usb_dwc_ll_hcfg_en_perio_sched(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.perschedena = 1; +} + +static inline void usb_dwc_ll_hcfg_dis_perio_sched(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.perschedena = 0; +} + +/** + * Sets the length of the frame list + * + * @param num_entires Number of entries in the frame list + */ +static inline void usb_dwc_ll_hcfg_set_num_frame_list_entries(usb_dwc_dev_t *hw, usb_hal_frame_list_len_t num_entries) +{ + uint32_t frlisten; + switch (num_entries) { + case USB_HAL_FRAME_LIST_LEN_8: + frlisten = 0; + break; + case USB_HAL_FRAME_LIST_LEN_16: + frlisten = 1; + break; + case USB_HAL_FRAME_LIST_LEN_32: + frlisten = 2; + break; + default: //USB_HAL_FRAME_LIST_LEN_64 + frlisten = 3; + break; + } + hw->hcfg_reg.frlisten = frlisten; +} + +static inline void usb_dwc_ll_hcfg_en_scatt_gatt_dma(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.descdma = 1; +} + +static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.fslssupp = 1; +} + +static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw) +{ + hw->hcfg_reg.fslspclksel = 1; +} + +/** + * @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA + * + * @param[in] hw Start address of the DWC_OTG registers + * @param[in] speed Speed to initialize the host port at + */ +static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +{ + hw->hcfg_reg.descdma = 1; //Enable scatt/gatt +#if (OTG_HSPHY_INTERFACE == 0) + /* + Indicate to the OTG core what speed the PHY clock is at + Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode, + so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. + */ + hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only +#endif // (OTG_HSPHY_INTERFACE == 0) + hw->hcfg_reg.perschedena = 0; //Disable perio sched +} + +// ----------------------------- HFIR Register --------------------------------- + +static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +{ +#if (OTG_HSPHY_INTERFACE == 0) + usb_dwc_hfir_reg_t hfir; + hfir.val = hw->hfir_reg.val; + hfir.hfirrldctrl = 0; //Disable dynamic loading + /* + Set frame interval to be equal to 1ms + Note: It seems like our PHY has an implicit 8 divider applied when in LS mode, + so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. + */ + hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS + hw->hfir_reg.val = hfir.val; +#endif // (OTG_HSPHY_INTERFACE == 0) +} + +// ----------------------------- HFNUM Register -------------------------------- + +static inline uint32_t usb_dwc_ll_hfnum_get_frame_time_rem(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->hfnum_reg, frrem); +} + +static inline uint32_t usb_dwc_ll_hfnum_get_frame_num(usb_dwc_dev_t *hw) +{ + return hw->hfnum_reg.frnum; +} + +// ---------------------------- HPTXSTS Register ------------------------------- + +static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_top(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxqtop); +} + +static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_space_avail(usb_dwc_dev_t *hw) +{ + return hw->hptxsts_reg.ptxqspcavail; +} + +static inline uint32_t usb_dwc_ll_ptxsts_get_ptxf_space_avail(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxfspcavail); +} + +// ----------------------------- HAINT Register -------------------------------- + +static inline uint32_t usb_dwc_ll_haint_get_chan_intrs(usb_dwc_dev_t *hw) +{ + return HAL_FORCE_READ_U32_REG_FIELD(hw->haint_reg, haint); +} + +// --------------------------- HAINTMSK Register ------------------------------- + +static inline void usb_dwc_ll_haintmsk_en_chan_intr(usb_dwc_dev_t *hw, uint32_t mask) +{ + + hw->haintmsk_reg.val |= mask; +} + +static inline void usb_dwc_ll_haintmsk_dis_chan_intr(usb_dwc_dev_t *hw, uint32_t mask) +{ + hw->haintmsk_reg.val &= ~mask; +} + +// --------------------------- HFLBAddr Register ------------------------------- + +/** + * @brief Set the base address of the scheduling frame list + * + * @note For some reason, this address must be 512 bytes aligned or else a bunch of frames will not be scheduled when + * the frame list rolls over. However, according to the databook, there is no mention of the HFLBAddr needing to + * be aligned. + * + * @param hw Start address of the DWC_OTG registers + * @param addr Base address of the scheduling frame list + */ +static inline void usb_dwc_ll_hflbaddr_set_base_addr(usb_dwc_dev_t *hw, uint32_t addr) +{ + hw->hflbaddr_reg.hflbaddr = addr; +} + +/** + * @brief Get the base address of the scheduling frame list + * + * @param hw Start address of the DWC_OTG registers + * @return uint32_t Base address of the scheduling frame list + */ +static inline uint32_t usb_dwc_ll_hflbaddr_get_base_addr(usb_dwc_dev_t *hw) +{ + return hw->hflbaddr_reg.hflbaddr; +} + +// ----------------------------- HPRT Register --------------------------------- + +static inline usb_dwc_speed_t usb_dwc_ll_hprt_get_speed(usb_dwc_dev_t *hw) +{ + return (usb_dwc_speed_t)hw->hprt_reg.prtspd; +} + +static inline uint32_t usb_dwc_ll_hprt_get_test_ctl(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prttstctl; +} + +static inline void usb_dwc_ll_hprt_set_test_ctl(usb_dwc_dev_t *hw, uint32_t test_mode) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prttstctl = test_mode; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline void usb_dwc_ll_hprt_en_pwr(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtpwr = 1; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline void usb_dwc_ll_hprt_dis_pwr(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtpwr = 0; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline uint32_t usb_dwc_ll_hprt_get_pwr_line_status(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtlnsts; +} + +static inline void usb_dwc_ll_hprt_set_port_reset(usb_dwc_dev_t *hw, bool reset) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtrst = reset; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_port_reset(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtrst; +} + +static inline void usb_dwc_ll_hprt_set_port_suspend(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtsusp = 1; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_port_suspend(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtsusp; +} + +static inline void usb_dwc_ll_hprt_set_port_resume(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtres = 1; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline void usb_dwc_ll_hprt_clr_port_resume(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtres = 0; + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_port_resume(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtres; +} + +static inline bool usb_dwc_ll_hprt_get_port_overcur(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtovrcurract; +} + +static inline bool usb_dwc_ll_hprt_get_port_en(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtena; +} + +static inline void usb_dwc_ll_hprt_port_dis(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hprt.prtena = 1; //W1C to disable + //we want to W1C ENA but not W1C the interrupt bits + hw->hprt_reg.val = hprt.val & ((~USB_DWC_LL_HPRT_W1C_MSK) | USB_DWC_LL_HPRT_ENA_MSK); +} + +static inline bool usb_dwc_ll_hprt_get_conn_status(usb_dwc_dev_t *hw) +{ + return hw->hprt_reg.prtconnsts; +} + +static inline uint32_t usb_dwc_ll_hprt_intr_read_and_clear(usb_dwc_dev_t *hw) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + //We want to W1C the interrupt bits but not that ENA + hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_ENA_MSK); + //Return only the interrupt bits + return (hprt.val & (USB_DWC_LL_HPRT_W1C_MSK & ~(USB_DWC_LL_HPRT_ENA_MSK))); +} + +static inline void usb_dwc_ll_hprt_intr_clear(usb_dwc_dev_t *hw, uint32_t intr_mask) +{ + usb_dwc_hprt_reg_t hprt; + hprt.val = hw->hprt_reg.val; + hw->hprt_reg.val = ((hprt.val & ~USB_DWC_LL_HPRT_ENA_MSK) & ~USB_DWC_LL_HPRT_W1C_MSK) | intr_mask; +} + +//Per Channel registers + +// --------------------------- HCCHARi Register -------------------------------- + +static inline void usb_dwc_ll_hcchar_enable_chan(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.chena = 1; +} + +static inline bool usb_dwc_ll_hcchar_chan_is_enabled(volatile usb_dwc_host_chan_regs_t *chan) +{ + return chan->hcchar_reg.chena; +} + +static inline void usb_dwc_ll_hcchar_disable_chan(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.chdis = 1; +} + +static inline void usb_dwc_ll_hcchar_set_odd_frame(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.oddfrm = 1; +} + +static inline void usb_dwc_ll_hcchar_set_even_frame(volatile usb_dwc_host_chan_regs_t *chan) +{ + chan->hcchar_reg.oddfrm = 0; +} + +static inline void usb_dwc_ll_hcchar_set_dev_addr(volatile usb_dwc_host_chan_regs_t *chan, uint32_t addr) +{ + chan->hcchar_reg.devaddr = addr; +} + +static inline void usb_dwc_ll_hcchar_set_ep_type(volatile usb_dwc_host_chan_regs_t *chan, usb_dwc_xfer_type_t type) +{ + chan->hcchar_reg.eptype = (uint32_t)type; +} + +//Indicates whether channel is commuunicating with a LS device connected via a FS hub. Setting this bit to 1 will cause +//each packet to be preceded by a PREamble packet +static inline void usb_dwc_ll_hcchar_set_lspddev(volatile usb_dwc_host_chan_regs_t *chan, bool is_ls) +{ + chan->hcchar_reg.lspddev = is_ls; +} + +static inline void usb_dwc_ll_hcchar_set_dir(volatile usb_dwc_host_chan_regs_t *chan, bool is_in) +{ + chan->hcchar_reg.epdir = is_in; +} + +static inline void usb_dwc_ll_hcchar_set_ep_num(volatile usb_dwc_host_chan_regs_t *chan, uint32_t num) +{ + chan->hcchar_reg.epnum = num; +} + +static inline void usb_dwc_ll_hcchar_set_mps(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mps) +{ + chan->hcchar_reg.mps = mps; +} + +static inline void usb_dwc_ll_hcchar_init(volatile usb_dwc_host_chan_regs_t *chan, int dev_addr, int ep_num, int mps, usb_dwc_xfer_type_t type, bool is_in, bool is_ls) +{ + //Sets all persistent fields of the channel over its lifetimez + usb_dwc_ll_hcchar_set_dev_addr(chan, dev_addr); + usb_dwc_ll_hcchar_set_ep_type(chan, type); + usb_dwc_ll_hcchar_set_lspddev(chan, is_ls); + usb_dwc_ll_hcchar_set_dir(chan, is_in); + usb_dwc_ll_hcchar_set_ep_num(chan, ep_num); + usb_dwc_ll_hcchar_set_mps(chan, mps); +} + +// ---------------------------- HCINTi Register -------------------------------- + +static inline uint32_t usb_dwc_ll_hcint_read_and_clear_intrs(volatile usb_dwc_host_chan_regs_t *chan) +{ + usb_dwc_hcint_reg_t hcint; + hcint.val = chan->hcint_reg.val; + chan->hcint_reg.val = hcint.val; + return hcint.val; +} + +// --------------------------- HCINTMSKi Register ------------------------------ + +static inline void usb_dwc_ll_hcintmsk_set_intr_mask(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mask) +{ + chan->hcintmsk_reg.val = mask; +} + +// ---------------------------- HCTSIZi Register ------------------------------- + +static inline void usb_dwc_ll_hctsiz_set_pid(volatile usb_dwc_host_chan_regs_t *chan, uint32_t data_pid) +{ + if (data_pid == 0) { + chan->hctsiz_reg.pid = 0; + } else { + chan->hctsiz_reg.pid = 2; + } +} + +static inline uint32_t usb_dwc_ll_hctsiz_get_pid(volatile usb_dwc_host_chan_regs_t *chan) +{ + if (chan->hctsiz_reg.pid == 0) { + return 0; //DATA0 + } else { + return 1; //DATA1 + } +} + +static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan_regs_t *chan, int qtd_list_len) +{ + usb_dwc_hctsiz_reg_t hctsiz; + hctsiz.val = chan->hctsiz_reg.val; + //Set the length of the descriptor list. NTD occupies xfersize[15:8] + hctsiz.xfersize &= ~(0xFF << 8); + hctsiz.xfersize |= ((qtd_list_len - 1) & 0xFF) << 8; + chan->hctsiz_reg.val = hctsiz.val; +} + +static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan) +{ + usb_dwc_hctsiz_reg_t hctsiz; + hctsiz.val = chan->hctsiz_reg.val; + hctsiz.dopng = 0; //Don't do ping + /* + Set SCHED_INFO which occupies xfersize[7:0] + It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels + */ + hctsiz.xfersize |= 0xFF; + chan->hctsiz_reg.val = hctsiz.val; +} + +static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset) +{ + // @see USB-OTG databook: Table 5-47 + // This function is relevant only for HS + usb_dwc_hctsiz_reg_t hctsiz; + hctsiz.val = chan->hctsiz_reg.val; + uint8_t sched_info_val; + switch (tokens_per_frame) { + case 1: + offset %= 8; // If the required offset > 8, we must wrap around to SCHED_INFO size = 8 + sched_info_val = 0b00000001; + break; + case 2: + offset %= 4; + sched_info_val = 0b00010001; + break; + case 4: + offset %= 2; + sched_info_val = 0b01010101; + break; + case 8: + offset = 0; + sched_info_val = 0b11111111; + break; + default: + abort(); + break; + } + sched_info_val <<= offset; + hctsiz.xfersize &= ~(0xFF); + hctsiz.xfersize |= sched_info_val; + chan->hctsiz_reg.val = hctsiz.val; +} + +// ---------------------------- HCDMAi Register -------------------------------- + +static inline void usb_dwc_ll_hcdma_set_qtd_list_addr(volatile usb_dwc_host_chan_regs_t *chan, void *dmaaddr, uint32_t qtd_idx) +{ + usb_dwc_hcdma_reg_t hcdma; + /* + Set the base address portion of the field which is dmaaddr[31:9]. This is + the based address of the QTD list and must be 512 bytes aligned + */ + hcdma.dmaaddr = ((uint32_t)dmaaddr) & 0xFFFFFE00; + //Set the current QTD index in the QTD list which is dmaaddr[8:3] + hcdma.dmaaddr |= (qtd_idx & 0x3F) << 3; + //dmaaddr[2:0] is reserved thus doesn't not need to be set + + chan->hcdma_reg.val = hcdma.val; +} + +static inline int usb_dwc_ll_hcdam_get_cur_qtd_idx(usb_dwc_host_chan_regs_t *chan) +{ + //The current QTD index is dmaaddr[8:3] + return (chan->hcdma_reg.dmaaddr >> 3) & 0x3F; +} + +// ---------------------------- HCDMABi Register ------------------------------- + +static inline void *usb_dwc_ll_hcdmab_get_buff_addr(volatile usb_dwc_host_chan_regs_t *chan) +{ + return (void *)chan->hcdmab_reg.hcdmab; +} + +/* ----------------------------------------------------------------------------- +---------------------------- Scatter/Gather DMA QTDs --------------------------- +----------------------------------------------------------------------------- */ + +// ---------------------------- Helper Functions ------------------------------- + +/** + * @brief Get the base address of a channel's register based on the channel's index + * + * @param dev Start address of the DWC_OTG registers + * @param chan_idx The channel's index + * @return usb_dwc_host_chan_regs_t* Pointer to channel's registers + */ +static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t *dev, int chan_idx) +{ + return &dev->host_chans[chan_idx]; +} + +// ------------------------------ QTD related ---------------------------------- + +#define USB_DWC_LL_QTD_STATUS_SUCCESS 0x0 //If QTD was processed, it indicates the data was transmitted/received successfully +#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data transmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK). +//Note: 0x2 is reserved +#define USB_DWC_LL_QTD_STATUS_BUFFER 0x3 //AHB error occurred. +#define USB_DWC_LL_QTD_STATUS_NOT_EXECUTED 0x4 //QTD as never processed + +/** + * @brief Set a QTD for a non isochronous IN transfer + * + * @param qtd Pointer to the QTD + * @param data_buff Pointer to buffer containing the data to transfer + * @param xfer_len Number of bytes in transfer. Setting 0 will do a zero length IN transfer. + * Non zero length must be multiple of the endpoint's MPS. + * @param hoc Halt on complete (will generate an interrupt and halt the channel) + */ +static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc) +{ + qtd->buffer = data_buff; //Set pointer to data buffer + qtd->buffer_status_val = 0; //Reset all flags to zero + qtd->in_non_iso.xfer_size = xfer_len; + if (hoc) { + qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD + qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd + } + qtd->in_non_iso.active = 1; +} + +/** + * @brief Set a QTD for a non isochronous OUT transfer + * + * @param qtd Pointer to the QTD + * @param data_buff Pointer to buffer containing the data to transfer + * @param xfer_len Number of bytes to transfer. Setting 0 will do a zero length transfer. + * For ctrl setup packets, this should be set to 8. + * @param hoc Halt on complete (will generate an interrupt) + * @param is_setup Indicates whether this is a control transfer setup packet or a normal OUT Data transfer. + * (As per the USB protocol, setup packets cannot be STALLd or NAKd by the device) + */ +static inline void usb_dwc_ll_qtd_set_out(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc, bool is_setup) +{ + qtd->buffer = data_buff; //Set pointer to data buffer + qtd->buffer_status_val = 0; //Reset all flags to zero + qtd->out_non_iso.xfer_size = xfer_len; + if (is_setup) { + qtd->out_non_iso.is_setup = 1; + } + if (hoc) { + qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD + qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd + } + qtd->out_non_iso.active = 1; +} + +/** + * @brief Set a QTD as NULL + * + * This sets the QTD to a value of 0. This is only useful when you need to insert + * blank QTDs into a list of QTDs + * + * @param qtd Pointer to the QTD + */ +static inline void usb_dwc_ll_qtd_set_null(usb_dwc_ll_dma_qtd_t *qtd) +{ + qtd->buffer = NULL; + qtd->buffer_status_val = 0; //Disable qtd by clearing it to zero. Used by interrupt/isoc as an unscheudled frame +} + +/** + * @brief Get the status of a QTD + * + * When a channel gets halted, call this to check whether each QTD was executed successfully + * + * @param qtd Pointer to the QTD + * @param[out] rem_len Number of bytes ramining in the QTD + * @param[out] status Status of the QTD + */ +static inline void usb_dwc_ll_qtd_get_status(usb_dwc_ll_dma_qtd_t *qtd, int *rem_len, int *status) +{ + //Status is the same regardless of IN or OUT + if (qtd->in_non_iso.active) { + //QTD was never processed + *status = USB_DWC_LL_QTD_STATUS_NOT_EXECUTED; + } else { + *status = qtd->in_non_iso.rx_status; + } + *rem_len = qtd->in_non_iso.xfer_size; + //Clear the QTD just for safety + qtd->buffer_status_val = 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/usb_utmi_hal.h b/components/hal/include/hal/usb_utmi_hal.h new file mode 100644 index 0000000000..dcb859a21c --- /dev/null +++ b/components/hal/include/hal/usb_utmi_hal.h @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "soc/soc_caps.h" +#if (SOC_USB_UTMI_PHY_NUM > 0) +#include "soc/usb_utmi_struct.h" +#include "hal/usb_utmi_ll.h" +#endif // (SOC_USB_UTMI_PHY_NUM > 0) + +#ifdef __cplusplus +extern "C" { +#endif + +#if (SOC_USB_UTMI_PHY_NUM > 0) + +/** + * @brief HAL context type of USB UTMI driver + */ +typedef struct { + usb_utmi_dev_t *dev; +} usb_utmi_hal_context_t; + +/** + * @brief Sets UTMI defaults + * + * Enable clock, reset the peripheral, sets default options (LS support, disconnection detection) + * + * @param[in] hal USB UTMI HAL context + */ +void _usb_utmi_hal_init(usb_utmi_hal_context_t *hal); + +#if SOC_RCC_IS_INDEPENDENT +#define usb_utmi_hal_init(...) _usb_utmi_hal_init(__VA_ARGS__) +#else +// Use a macro to wrap the function, force the caller to use it in a critical section +// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define usb_utmi_hal_init(...) do {(void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_hal_init(__VA_ARGS__);} while(0) +#endif + +/** + * @brief Disable UTMI + * + * Disable clock to the peripheral + */ +void _usb_utmi_hal_disable(void); + +#if SOC_RCC_IS_INDEPENDENT +#define usb_utmi_hal_disable(...) _usb_utmi_hal_disable(__VA_ARGS__) +#else +// Use a macro to wrap the function, force the caller to use it in a critical section +// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance +#define usb_utmi_hal_disable(...) do {(void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_hal_disable(__VA_ARGS__);} while(0) +#endif + +#endif // (SOC_USB_UTMI_PHY_NUM > 0) + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/usb_wrap_hal.h b/components/hal/include/hal/usb_wrap_hal.h index ceb69f3652..8477b6927d 100644 --- a/components/hal/include/hal/usb_wrap_hal.h +++ b/components/hal/include/hal/usb_wrap_hal.h @@ -8,17 +8,17 @@ #include #include "soc/soc_caps.h" -#if SOC_USB_OTG_SUPPORTED +#if (SOC_USB_OTG_PERIPH_NUM > 0) #include "soc/usb_wrap_struct.h" #include "hal/usb_wrap_ll.h" -#endif +#endif // (SOC_USB_OTG_PERIPH_NUM > 0) #include "hal/usb_wrap_types.h" #ifdef __cplusplus extern "C" { #endif -#if SOC_USB_OTG_SUPPORTED +#if (SOC_USB_OTG_PERIPH_NUM > 0) /** * @brief HAL context type of USB WRAP driver @@ -89,7 +89,7 @@ static inline void usb_wrap_hal_phy_test_mode_set_signals(usb_wrap_hal_context_t usb_wrap_ll_phy_test_mode_set_signals(hal->dev, vals); } -#endif // SOC_USB_OTG_SUPPORTED +#endif // (SOC_USB_OTG_PERIPH_NUM > 0) #ifdef __cplusplus } diff --git a/components/hal/include/hal/usb_wrap_types.h b/components/hal/include/hal/usb_wrap_types.h index 9d3310fb06..a8727f0ef8 100644 --- a/components/hal/include/hal/usb_wrap_types.h +++ b/components/hal/include/hal/usb_wrap_types.h @@ -13,7 +13,7 @@ extern "C" { #endif -#if SOC_USB_OTG_SUPPORTED +#if (SOC_USB_OTG_PERIPH_NUM > 0) /** * @brief USB WRAP pull up/down resistor override values @@ -46,7 +46,7 @@ typedef struct { bool rx_rcv; /**< Differential receive data from D+ and D- lines */ } usb_wrap_test_mode_vals_t; -#endif // SOC_USB_OTG_SUPPORTED +#endif // (SOC_USB_OTG_PERIPH_NUM > 0) #ifdef __cplusplus } diff --git a/components/hal/usb_dwc_hal.c b/components/hal/usb_dwc_hal.c index bcc8870ed4..b6f3201b71 100644 --- a/components/hal/usb_dwc_hal.c +++ b/components/hal/usb_dwc_hal.c @@ -11,6 +11,7 @@ #include "sdkconfig.h" #include "soc/chip_revision.h" #include "soc/usb_dwc_cfg.h" +#include "soc/usb_dwc_periph.h" #include "hal/usb_dwc_hal.h" #include "hal/usb_dwc_ll.h" #include "hal/efuse_hal.h" @@ -18,14 +19,6 @@ // ------------------------------------------------ Macros and Types --------------------------------------------------- -// TODO: Remove target specific section after support for multiple USB peripherals is implemented -#include "sdkconfig.h" -#if (CONFIG_IDF_TARGET_ESP32P4) -#define USB_BASE USB_DWC_HS -#else -#define USB_BASE USB_DWC -#endif - // ---------------------- Constants ------------------------ #define BENDPOINTADDRESS_NUM_MSK 0x0F //Endpoint number mask of the bEndpointAddress field of an endpoint descriptor @@ -118,10 +111,14 @@ static void set_defaults(usb_dwc_hal_context_t *hal) //GUSBCFG register usb_dwc_ll_gusbcfg_dis_hnp_cap(hal->dev); //Disable HNP usb_dwc_ll_gusbcfg_dis_srp_cap(hal->dev); //Disable SRP -#if (OTG_HSPHY_INTERFACE != 0) - usb_dwc_ll_gusbcfg_set_timeout_cal(hal->dev, 5); // 5 PHY clocks for our HS PHY - usb_dwc_ll_gusbcfg_set_utmi_phy(hal->dev); -#endif // (OTG_HSPHY_INTERFACE != 0) + + // Check if this USB-DWC supports HS PHY, if yes, use it + uint32_t ghwcfg[4]; + usb_dwc_ll_ghwcfg_get_hw_config(hal->dev, &ghwcfg[0], &ghwcfg[1], &ghwcfg[2], &ghwcfg[3]); + if (((usb_dwc_ghwcfg2_reg_t)ghwcfg[1]).hsphytype != 0) { + usb_dwc_ll_gusbcfg_set_timeout_cal(hal->dev, 5); // 5 PHY clocks for our HS PHY + usb_dwc_ll_gusbcfg_set_utmi_phy(hal->dev); + } //Enable interruts usb_dwc_ll_gintmsk_dis_intrs(hal->dev, 0xFFFFFFFF); //Mask all interrupts first usb_dwc_ll_gintmsk_en_intrs(hal->dev, CORE_INTRS_EN_MSK); //Unmask global interrupts @@ -134,7 +131,7 @@ static void set_defaults(usb_dwc_hal_context_t *hal) void usb_dwc_hal_init(usb_dwc_hal_context_t *hal) { //Check if a peripheral is alive by reading the core ID registers - usb_dwc_dev_t *dev = &USB_BASE; + usb_dwc_dev_t *dev = USB_DWC_LL_GET_HW(0); uint32_t core_id = usb_dwc_ll_gsnpsid_get_id(dev); HAL_ASSERT(core_id == CORE_REG_GSNPSID); (void) core_id; //Suppress unused variable warning if asserts are disabled @@ -270,7 +267,7 @@ void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal) bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx) { - HAL_ASSERT(hal->flags.fifo_sizes_set); //FIFO sizes should be set befor attempting to allocate a channel + HAL_ASSERT(hal->flags.fifo_sizes_set); //FIFO sizes should be set before attempting to allocate a channel //Attempt to allocate channel if (hal->channels.num_allocd == OTG_NUM_HOST_CHAN) { return false; //Out of free channels diff --git a/components/hal/usb_utmi_hal.c b/components/hal/usb_utmi_hal.c new file mode 100644 index 0000000000..cb7ceb49ba --- /dev/null +++ b/components/hal/usb_utmi_hal.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "hal/usb_utmi_ll.h" +#include "hal/usb_utmi_hal.h" + +void _usb_utmi_hal_init(usb_utmi_hal_context_t *hal) +{ + hal->dev = &USB_UTMI; + _usb_utmi_ll_enable_bus_clock(true); + _usb_utmi_ll_reset_register(); + + /* + Additional setting to solve missing DCONN event on ESP32P4 (IDF-9953). + + Note: On ESP32P4, the HP_SYSTEM_OTG_SUSPENDM is not connected to 1 by hardware. + For correct detection of the device detaching, internal signal should be set to 1 by the software. + */ + usb_utmi_ll_enable_precise_detection(true); + usb_utmi_ll_configure_ls(hal->dev, true); +} + +void _usb_utmi_hal_disable(void) +{ + _usb_utmi_ll_enable_bus_clock(false); +} diff --git a/components/usb/usb_phy_p4.c b/components/usb/usb_phy_p4.c index 8365cded17..43611c393c 100644 --- a/components/usb/usb_phy_p4.c +++ b/components/usb/usb_phy_p4.c @@ -4,37 +4,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO: Refactor during the IDF-9198 -#include "sdkconfig.h" -#include "soc/usb_dwc_cfg.h" -#include "hal/usb_utmi_ll.h" // We don't have usb_utmi_hal yet -#include "esp_private/periph_ctrl.h" -// TODO: Remove this file when proper support of P4 PHYs is implemented IDF-7323 +// 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) { -#if (OTG_HSPHY_INTERFACE != 0) -#if CONFIG_IDF_TARGET_ESP32P4 - PERIPH_RCC_ATOMIC() { - usb_utmi_ll_enable_bus_clock(true); - usb_utmi_ll_reset_register(); + USB_UTMI_BUS_CLK_ATOMIC() { + usb_utmi_hal_init(&s_utmi_hal_context); } - /* - Additional setting to solve missing DCONN event on ESP32P4 (IDF-9953). - - Note: On ESP32P4, the HP_SYSTEM_OTG_SUSPENDM is not connected to 1 by hardware. - For correct detection of the device detaching, internal signal should be set to 1 by the software. - */ - usb_utmi_ll_enable_precise_detection(true); - usb_utmi_ll_configure_ls(&USB_UTMI, true); -#endif // CONFIG_IDF_TARGET_ESP32P4 -#endif // (OTG_HSPHY_INTERFACE != 0) 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; } From 177679b74e5d92a52643a853937e114933a988c4 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Tue, 8 Oct 2024 12:28:46 +0200 Subject: [PATCH 3/3] feat(hal/usb): Make USB-DWC HAL&LL configuration independent Previously, we included symbols from soc/usb_dwc_cfg.h and configured the HAL and LL according to it. Now we get the configuration in runtime from USB-DWC registers. Added missing definition for USB FS peripheral on ESP32-P4. --- .codespellrc | 2 +- .../hal/esp32p4/include/hal/usb_dwc_ll.h | 72 +++--- .../hal/esp32p4/include/hal/usb_utmi_ll.h | 4 +- .../hal/esp32s2/include/hal/usb_dwc_ll.h | 68 +++--- .../hal/esp32s3/include/hal/usb_dwc_ll.h | 68 +++--- components/hal/include/hal/usb_dwc_hal.h | 45 ++-- components/hal/usb_dwc_hal.c | 103 +++++---- .../soc/esp32p4/include/soc/usb_dwc_cfg.h | 210 ++++++++++++------ .../soc/esp32p4/include/soc/usb_dwc_struct.h | 8 +- .../soc/esp32s2/include/soc/usb_dwc_struct.h | 4 +- .../soc/esp32s3/include/soc/usb_dwc_struct.h | 4 +- components/usb/hcd_dwc.c | 9 +- .../usb/test_apps/hcd/main/test_hcd_isoc.c | 10 +- 13 files changed, 347 insertions(+), 260 deletions(-) diff --git a/.codespellrc b/.codespellrc index 2724a4e3c2..21d23be365 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,4 +1,4 @@ [codespell] skip = build,*.yuv,components/fatfs/src/*,alice.txt,*.rgb,components/wpa_supplicant/*,components/esp_wifi/* -ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,ot,wel,parms +ignore-words-list = ser,dout,rsource,fram,inout,shs,ans,aci,unstall,unstalling,hart,wheight,ot,wel,parms,ehen write-changes = true diff --git a/components/hal/esp32p4/include/hal/usb_dwc_ll.h b/components/hal/esp32p4/include/hal/usb_dwc_ll.h index f93db6623c..fd0685a50d 100644 --- a/components/hal/esp32p4/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32p4/include/hal/usb_dwc_ll.h @@ -9,7 +9,6 @@ #include #include #include "soc/usb_dwc_struct.h" -#include "soc/usb_dwc_cfg.h" #include "hal/usb_dwc_types.h" #include "hal/misc.h" @@ -20,8 +19,7 @@ extern "C" { /* ----------------------------- Helper Macros ------------------------------ */ // Get USB hardware instance -// TODO: extend this macros when we have support for both FS and HS hardware on P4 -#define USB_DWC_LL_GET_HW(num) (&USB_DWC_HS) +#define USB_DWC_LL_GET_HW(num) (((num) == 1) ? &USB_DWC_FS : &USB_DWC_HS) /* ----------------------------------------------------------------------------- --------------------------------- DWC Constants -------------------------------- @@ -221,11 +219,9 @@ static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw) { -#if (OTG_HSPHY_INTERFACE != 0) hw->gusbcfg_reg.phyif = 1; // 16 bits interface hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+ hw->gusbcfg_reg.physel = 0; // HS PHY -#endif // (OTG_HSPHY_INTERFACE != 0) } // --------------------------- GRSTCTL Register -------------------------------- @@ -352,24 +348,19 @@ static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw) // --------------------------- GHWCFGx Register -------------------------------- -/** - * @brief Get the hardware configuration registers of the DWC_OTG controller - * - * The hardware configuration regitsers are read only and indicate the various - * features of the DWC_OTG core. - * - * @param hw Start address of the DWC_OTG registers - * @param[out] ghwcfg1 Hardware configuration registesr 1 - * @param[out] ghwcfg2 Hardware configuration registesr 2 - * @param[out] ghwcfg3 Hardware configuration registesr 3 - * @param[out] ghwcfg4 Hardware configuration registesr 4 - */ -static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4) +static inline unsigned usb_dwc_ll_ghwcfg_get_fifo_depth(usb_dwc_dev_t *hw) { - *ghwcfg1 = hw->ghwcfg1_reg.val; - *ghwcfg2 = hw->ghwcfg2_reg.val; - *ghwcfg3 = hw->ghwcfg3_reg.val; - *ghwcfg4 = hw->ghwcfg4_reg.val; + return hw->ghwcfg3_reg.dfifodepth; +} + +static inline unsigned usb_dwc_ll_ghwcfg_get_hsphy_type(usb_dwc_dev_t *hw) +{ + return hw->ghwcfg2_reg.hsphytype; +} + +static inline unsigned usb_dwc_ll_ghwcfg_get_channel_num(usb_dwc_dev_t *hw) +{ + return hw->ghwcfg2_reg.numhstchnl; } // --------------------------- HPTXFSIZ Register ------------------------------- @@ -434,47 +425,44 @@ static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw) hw->hcfg_reg.fslssupp = 1; } -static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw) -{ - hw->hcfg_reg.fslspclksel = 1; -} - /** - * @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA + * @brief Set FSLS PHY clock * + * @attention This function should only be called if FSLS PHY is selected * @param[in] hw Start address of the DWC_OTG registers - * @param[in] speed Speed to initialize the host port at */ -static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +static inline void usb_dwc_ll_hcfg_set_fsls_phy_clock(usb_dwc_dev_t *hw) { - hw->hcfg_reg.descdma = 1; //Enable scatt/gatt -#if (OTG_HSPHY_INTERFACE == 0) /* Indicate to the OTG core what speed the PHY clock is at - Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode, + Note: FSLS PHY has an implicit 8 divider applied when in LS mode, so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. */ - hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only -#endif // (OTG_HSPHY_INTERFACE == 0) - hw->hcfg_reg.perschedena = 0; //Disable perio sched + usb_dwc_speed_t speed = (usb_dwc_speed_t)hw->hprt_reg.prtspd; + hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; } // ----------------------------- HFIR Register --------------------------------- -static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +/** + * @brief Set Frame Interval + * + * @attention This function should only be called if FSLS PHY is selected + * @param[in] hw Start address of the DWC_OTG registers + */ +static inline void usb_dwc_ll_hfir_set_frame_interval(usb_dwc_dev_t *hw) { -#if (OTG_HSPHY_INTERFACE == 0) usb_dwc_hfir_reg_t hfir; hfir.val = hw->hfir_reg.val; - hfir.hfirrldctrl = 0; //Disable dynamic loading + hfir.hfirrldctrl = 0; // Disable dynamic loading /* Set frame interval to be equal to 1ms - Note: It seems like our PHY has an implicit 8 divider applied when in LS mode, + Note: FSLS PHY has an implicit 8 divider applied when in LS mode, so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. */ - hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS + usb_dwc_speed_t speed = (usb_dwc_speed_t)hw->hprt_reg.prtspd; + hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; hw->hfir_reg.val = hfir.val; -#endif // (OTG_HSPHY_INTERFACE == 0) } // ----------------------------- HFNUM Register -------------------------------- diff --git a/components/hal/esp32p4/include/hal/usb_utmi_ll.h b/components/hal/esp32p4/include/hal/usb_utmi_ll.h index adebd1cdec..f2ea99d8b6 100644 --- a/components/hal/esp32p4/include/hal/usb_utmi_ll.h +++ b/components/hal/esp32p4/include/hal/usb_utmi_ll.h @@ -39,7 +39,7 @@ FORCE_INLINE_ATTR void usb_utmi_ll_configure_ls(usb_utmi_dev_t *hw, bool paralle * * @param[in] clk_en True to enable, false to disable */ -FORCE_INLINE_ATTR void usb_utmi_ll_enable_bus_clock(bool clk_en) +FORCE_INLINE_ATTR void _usb_utmi_ll_enable_bus_clock(bool clk_en) { // Enable/disable system clock for USB_UTMI and USB_DWC_HS HP_SYS_CLKRST.soc_clk_ctrl1.reg_usb_otg20_sys_clk_en = clk_en; @@ -48,7 +48,7 @@ FORCE_INLINE_ATTR void usb_utmi_ll_enable_bus_clock(bool clk_en) } // HP_SYS_CLKRST.soc_clk_ctrlx and LP_AON_CLKRST.hp_usb_clkrst_ctrlx are shared registers, so this function must be used in an atomic way -#define usb_utmi_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; usb_utmi_ll_enable_bus_clock(__VA_ARGS__) +#define usb_utmi_ll_enable_bus_clock(...) (void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_ll_enable_bus_clock(__VA_ARGS__) /** * @brief Reset the USB UTMI PHY and USB_DWC_HS controller diff --git a/components/hal/esp32s2/include/hal/usb_dwc_ll.h b/components/hal/esp32s2/include/hal/usb_dwc_ll.h index 954a54bf80..7426be2efa 100644 --- a/components/hal/esp32s2/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32s2/include/hal/usb_dwc_ll.h @@ -220,11 +220,9 @@ static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw) { -#if (OTG_HSPHY_INTERFACE != 0) hw->gusbcfg_reg.phyif = 1; // 16 bits interface hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+ hw->gusbcfg_reg.physel = 0; // HS PHY -#endif // (OTG_HSPHY_INTERFACE != 0) } // --------------------------- GRSTCTL Register -------------------------------- @@ -351,24 +349,19 @@ static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw) // --------------------------- GHWCFGx Register -------------------------------- -/** - * @brief Get the hardware configuration registers of the DWC_OTG controller - * - * The hardware configuration regitsers are read only and indicate the various - * features of the DWC_OTG core. - * - * @param hw Start address of the DWC_OTG registers - * @param[out] ghwcfg1 Hardware configuration registesr 1 - * @param[out] ghwcfg2 Hardware configuration registesr 2 - * @param[out] ghwcfg3 Hardware configuration registesr 3 - * @param[out] ghwcfg4 Hardware configuration registesr 4 - */ -static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4) +static inline unsigned usb_dwc_ll_ghwcfg_get_fifo_depth(usb_dwc_dev_t *hw) { - *ghwcfg1 = hw->ghwcfg1_reg.val; - *ghwcfg2 = hw->ghwcfg2_reg.val; - *ghwcfg3 = hw->ghwcfg3_reg.val; - *ghwcfg4 = hw->ghwcfg4_reg.val; + return hw->ghwcfg3_reg.dfifodepth; +} + +static inline unsigned usb_dwc_ll_ghwcfg_get_hsphy_type(usb_dwc_dev_t *hw) +{ + return hw->ghwcfg2_reg.hsphytype; +} + +static inline unsigned usb_dwc_ll_ghwcfg_get_channel_num(usb_dwc_dev_t *hw) +{ + return hw->ghwcfg2_reg.numhstchnl; } // --------------------------- HPTXFSIZ Register ------------------------------- @@ -433,47 +426,44 @@ static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw) hw->hcfg_reg.fslssupp = 1; } -static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw) -{ - hw->hcfg_reg.fslspclksel = 1; -} - /** - * @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA + * @brief Set FSLS PHY clock * + * @attention This function should only be called if FSLS PHY is selected * @param[in] hw Start address of the DWC_OTG registers - * @param[in] speed Speed to initialize the host port at */ -static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +static inline void usb_dwc_ll_hcfg_set_fsls_phy_clock(usb_dwc_dev_t *hw) { - hw->hcfg_reg.descdma = 1; //Enable scatt/gatt -#if (OTG_HSPHY_INTERFACE == 0) /* Indicate to the OTG core what speed the PHY clock is at - Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode, + Note: FSLS PHY has an implicit 8 divider applied when in LS mode, so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. */ - hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only -#endif // (OTG_HSPHY_INTERFACE == 0) - hw->hcfg_reg.perschedena = 0; //Disable perio sched + usb_dwc_speed_t speed = (usb_dwc_speed_t)hw->hprt_reg.prtspd; + hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; } // ----------------------------- HFIR Register --------------------------------- -static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +/** + * @brief Set Frame Interval + * + * @attention This function should only be called if FSLS PHY is selected + * @param[in] hw Start address of the DWC_OTG registers + */ +static inline void usb_dwc_ll_hfir_set_frame_interval(usb_dwc_dev_t *hw) { -#if (OTG_HSPHY_INTERFACE == 0) usb_dwc_hfir_reg_t hfir; hfir.val = hw->hfir_reg.val; - hfir.hfirrldctrl = 0; //Disable dynamic loading + hfir.hfirrldctrl = 0; // Disable dynamic loading /* Set frame interval to be equal to 1ms - Note: It seems like our PHY has an implicit 8 divider applied when in LS mode, + Note: FSLS PHY has an implicit 8 divider applied when in LS mode, so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. */ - hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS + usb_dwc_speed_t speed = (usb_dwc_speed_t)hw->hprt_reg.prtspd; + hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; hw->hfir_reg.val = hfir.val; -#endif // (OTG_HSPHY_INTERFACE == 0) } // ----------------------------- HFNUM Register -------------------------------- diff --git a/components/hal/esp32s3/include/hal/usb_dwc_ll.h b/components/hal/esp32s3/include/hal/usb_dwc_ll.h index 954a54bf80..7426be2efa 100644 --- a/components/hal/esp32s3/include/hal/usb_dwc_ll.h +++ b/components/hal/esp32s3/include/hal/usb_dwc_ll.h @@ -220,11 +220,9 @@ static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw) { -#if (OTG_HSPHY_INTERFACE != 0) hw->gusbcfg_reg.phyif = 1; // 16 bits interface hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+ hw->gusbcfg_reg.physel = 0; // HS PHY -#endif // (OTG_HSPHY_INTERFACE != 0) } // --------------------------- GRSTCTL Register -------------------------------- @@ -351,24 +349,19 @@ static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw) // --------------------------- GHWCFGx Register -------------------------------- -/** - * @brief Get the hardware configuration registers of the DWC_OTG controller - * - * The hardware configuration regitsers are read only and indicate the various - * features of the DWC_OTG core. - * - * @param hw Start address of the DWC_OTG registers - * @param[out] ghwcfg1 Hardware configuration registesr 1 - * @param[out] ghwcfg2 Hardware configuration registesr 2 - * @param[out] ghwcfg3 Hardware configuration registesr 3 - * @param[out] ghwcfg4 Hardware configuration registesr 4 - */ -static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4) +static inline unsigned usb_dwc_ll_ghwcfg_get_fifo_depth(usb_dwc_dev_t *hw) { - *ghwcfg1 = hw->ghwcfg1_reg.val; - *ghwcfg2 = hw->ghwcfg2_reg.val; - *ghwcfg3 = hw->ghwcfg3_reg.val; - *ghwcfg4 = hw->ghwcfg4_reg.val; + return hw->ghwcfg3_reg.dfifodepth; +} + +static inline unsigned usb_dwc_ll_ghwcfg_get_hsphy_type(usb_dwc_dev_t *hw) +{ + return hw->ghwcfg2_reg.hsphytype; +} + +static inline unsigned usb_dwc_ll_ghwcfg_get_channel_num(usb_dwc_dev_t *hw) +{ + return hw->ghwcfg2_reg.numhstchnl; } // --------------------------- HPTXFSIZ Register ------------------------------- @@ -433,47 +426,44 @@ static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw) hw->hcfg_reg.fslssupp = 1; } -static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw) -{ - hw->hcfg_reg.fslspclksel = 1; -} - /** - * @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA + * @brief Set FSLS PHY clock * + * @attention This function should only be called if FSLS PHY is selected * @param[in] hw Start address of the DWC_OTG registers - * @param[in] speed Speed to initialize the host port at */ -static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +static inline void usb_dwc_ll_hcfg_set_fsls_phy_clock(usb_dwc_dev_t *hw) { - hw->hcfg_reg.descdma = 1; //Enable scatt/gatt -#if (OTG_HSPHY_INTERFACE == 0) /* Indicate to the OTG core what speed the PHY clock is at - Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode, + Note: FSLS PHY has an implicit 8 divider applied when in LS mode, so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. */ - hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only -#endif // (OTG_HSPHY_INTERFACE == 0) - hw->hcfg_reg.perschedena = 0; //Disable perio sched + usb_dwc_speed_t speed = (usb_dwc_speed_t)hw->hprt_reg.prtspd; + hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; } // ----------------------------- HFIR Register --------------------------------- -static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed) +/** + * @brief Set Frame Interval + * + * @attention This function should only be called if FSLS PHY is selected + * @param[in] hw Start address of the DWC_OTG registers + */ +static inline void usb_dwc_ll_hfir_set_frame_interval(usb_dwc_dev_t *hw) { -#if (OTG_HSPHY_INTERFACE == 0) usb_dwc_hfir_reg_t hfir; hfir.val = hw->hfir_reg.val; - hfir.hfirrldctrl = 0; //Disable dynamic loading + hfir.hfirrldctrl = 0; // Disable dynamic loading /* Set frame interval to be equal to 1ms - Note: It seems like our PHY has an implicit 8 divider applied when in LS mode, + Note: FSLS PHY has an implicit 8 divider applied when in LS mode, so the values of FSLSPclkSel and FrInt have to be adjusted accordingly. */ - hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS + usb_dwc_speed_t speed = (usb_dwc_speed_t)hw->hprt_reg.prtspd; + hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; hw->hfir_reg.val = hfir.val; -#endif // (OTG_HSPHY_INTERFACE == 0) } // ----------------------------- HFNUM Register -------------------------------- diff --git a/components/hal/include/hal/usb_dwc_hal.h b/components/hal/include/hal/usb_dwc_hal.h index 33ac57be75..d4c880571f 100644 --- a/components/hal/include/hal/usb_dwc_hal.h +++ b/components/hal/include/hal/usb_dwc_hal.h @@ -171,13 +171,23 @@ typedef struct { * @brief HAL context structure */ typedef struct { - //Context - usb_dwc_dev_t *dev; /**< Pointer to base address of DWC_OTG registers */ - //Host Port related - uint32_t *periodic_frame_list; /**< Pointer to scheduling frame list */ - usb_hal_frame_list_len_t frame_list_len; /**< Length of the periodic scheduling frame list */ - //FIFO related - usb_dwc_hal_fifo_config_t fifo_config; /**< FIFO sizes configuration */ + // HW context + usb_dwc_dev_t *dev; /**< Pointer to base address of DWC_OTG registers */ + + // Host Port related + uint32_t *periodic_frame_list; /**< Pointer to scheduling frame list */ + usb_hal_frame_list_len_t frame_list_len; /**< Length of the periodic scheduling frame list */ + + // FIFO related + usb_dwc_hal_fifo_config_t fifo_config; /**< FIFO sizes configuration */ + + // Configuration of the USB-DWC core. Read from read-only HW registers + struct { + unsigned chan_num_total; /**< Total number of channels for this configuration */ + unsigned hsphy_type; /**< HS PHY type of this configuration */ + unsigned fifo_size; /**< Total FIFO size [in lines] in this configuration */ + } constant_config; + union { struct { uint32_t dbnc_lock_enabled: 1; /**< Debounce lock enabled */ @@ -188,11 +198,12 @@ typedef struct { }; uint32_t val; } flags; - //Channel related + + // Channel related struct { - int num_allocd; /**< Number of channels currently allocated */ - uint32_t chan_pend_intrs_msk; /**< Bit mask of channels with pending interrupts */ - usb_dwc_hal_chan_t *hdls[OTG_NUM_HOST_CHAN]; /**< Handles of each channel. Set to NULL if channel has not been allocated */ + int num_allocated; /**< Number of channels currently allocated */ + uint32_t chan_pend_intrs_msk; /**< Bit mask of channels with pending interrupts */ + usb_dwc_hal_chan_t **hdls; /**< Handles of each channel. Set to NULL if channel has not been allocated */ } channels; } usb_dwc_hal_context_t; @@ -208,16 +219,20 @@ typedef struct { * - Interrupt allocated but DISABLED (in case of an unknown interrupt state) * Exit: * - Checks to see if DWC_OTG is alive, and if HW version/config is correct - * - HAl context initialized + * - HAL context initialized + * - Read and save relevant USB-DWC configuration parameters * - Sets default values to some global and OTG registers (GAHBCFG and GUSBCFG) * - Umask global interrupt signal * - Put DWC_OTG into host mode. Require 25ms delay before this takes effect. * - State -> USB_DWC_HAL_PORT_STATE_OTG * - Interrupts cleared. Users can now enable their ISR * - * @param[inout] hal Context of the HAL layer + * @attention The user must allocate memory for channel handlers with + * `hal->channels.hdls = malloc(hal->constant_config.chan_num_total * sizeof(usb_dwc_hal_chan_t*))` + * @param[inout] hal Context of the HAL layer + * @param[in] port_id USB port ID */ -void usb_dwc_hal_init(usb_dwc_hal_context_t *hal); +void usb_dwc_hal_init(usb_dwc_hal_context_t *hal, int port_id); /** * @brief Deinitialize the HAL context @@ -241,7 +256,7 @@ void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal); * * @note This has nothing to do with a USB bus reset. It simply resets the peripheral * - * @param hal Context of the HAL layer + * @param[in] hal Context of the HAL layer */ void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal); diff --git a/components/hal/usb_dwc_hal.c b/components/hal/usb_dwc_hal.c index b6f3201b71..3f43762637 100644 --- a/components/hal/usb_dwc_hal.c +++ b/components/hal/usb_dwc_hal.c @@ -10,7 +10,6 @@ #include // For abort() #include "sdkconfig.h" #include "soc/chip_revision.h" -#include "soc/usb_dwc_cfg.h" #include "soc/usb_dwc_periph.h" #include "hal/usb_dwc_hal.h" #include "hal/usb_dwc_ll.h" @@ -112,10 +111,8 @@ static void set_defaults(usb_dwc_hal_context_t *hal) usb_dwc_ll_gusbcfg_dis_hnp_cap(hal->dev); //Disable HNP usb_dwc_ll_gusbcfg_dis_srp_cap(hal->dev); //Disable SRP - // Check if this USB-DWC supports HS PHY, if yes, use it - uint32_t ghwcfg[4]; - usb_dwc_ll_ghwcfg_get_hw_config(hal->dev, &ghwcfg[0], &ghwcfg[1], &ghwcfg[2], &ghwcfg[3]); - if (((usb_dwc_ghwcfg2_reg_t)ghwcfg[1]).hsphytype != 0) { + // If this USB-DWC supports HS PHY, use it + if (hal->constant_config.hsphy_type != 0) { usb_dwc_ll_gusbcfg_set_timeout_cal(hal->dev, 5); // 5 PHY clocks for our HS PHY usb_dwc_ll_gusbcfg_set_utmi_phy(hal->dev); } @@ -128,16 +125,29 @@ static void set_defaults(usb_dwc_hal_context_t *hal) usb_dwc_ll_gusbcfg_force_host_mode(hal->dev); } -void usb_dwc_hal_init(usb_dwc_hal_context_t *hal) +void usb_dwc_hal_init(usb_dwc_hal_context_t *hal, int port_id) { - //Check if a peripheral is alive by reading the core ID registers - usb_dwc_dev_t *dev = USB_DWC_LL_GET_HW(0); + // Check if a peripheral is alive by reading the core ID registers + HAL_ASSERT(port_id < SOC_USB_OTG_PERIPH_NUM); + usb_dwc_dev_t *dev = USB_DWC_LL_GET_HW(port_id); uint32_t core_id = usb_dwc_ll_gsnpsid_get_id(dev); HAL_ASSERT(core_id == CORE_REG_GSNPSID); (void) core_id; //Suppress unused variable warning if asserts are disabled - //Initialize HAL context + + // Initialize HAL context memset(hal, 0, sizeof(usb_dwc_hal_context_t)); hal->dev = dev; + + // Save constant configuration of this USB-DWC instance + /* + * EPINFO_CTL is located at the end of FIFO, its size is fixed in HW. + * The reserved size is always the worst-case, which is device mode that requires 4 locations per EP direction (including EP0). + * Here we just read the FIFO size from HW register, to avoid any ambivalence + */ + hal->constant_config.fifo_size = usb_dwc_ll_ghwcfg_get_fifo_depth(dev); + hal->constant_config.hsphy_type = usb_dwc_ll_ghwcfg_get_hsphy_type(dev); + hal->constant_config.chan_num_total = usb_dwc_ll_ghwcfg_get_channel_num(dev); + set_defaults(hal); } @@ -154,32 +164,31 @@ void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal) { usb_dwc_ll_grstctl_core_soft_reset(hal->dev); while (usb_dwc_ll_grstctl_is_core_soft_reset_in_progress(hal->dev)) { - ; //Wait until core reset is done + ; // Wait until core reset is done } while (!usb_dwc_ll_grstctl_is_ahb_idle(hal->dev)) { - ; //Wait until AHB Master bus is idle before doing any other operations + ; // Wait until AHB Master bus is idle before doing any other operations } - //Set the default bits + + // Set the default bits in USB-DWC registers set_defaults(hal); - //Clear all the flags and channels + + // Clear all the flags and channels hal->periodic_frame_list = NULL; hal->flags.val = 0; - hal->channels.num_allocd = 0; + hal->channels.num_allocated = 0; hal->channels.chan_pend_intrs_msk = 0; - memset(hal->channels.hdls, 0, sizeof(usb_dwc_hal_chan_t *) * OTG_NUM_HOST_CHAN); + if (hal->channels.hdls) { + for (int i = 0; i < hal->constant_config.chan_num_total; i++) { + hal->channels.hdls[i] = NULL; + } + } } void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias) { - /* - * EPINFO_CTL is located at the end of FIFO, its size is fixed in HW. - * The reserved size is always the worst-case, which is device mode that requires 4 locations per EP direction (including EP0). - * Here we just read the FIFO size from HW register, to avoid any ambivalence - */ - uint32_t ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4; - usb_dwc_ll_ghwcfg_get_hw_config(hal->dev, &ghwcfg1, &ghwcfg2, &ghwcfg3, &ghwcfg4); - const uint16_t fifo_size_lines = ((usb_dwc_ghwcfg3_reg_t)ghwcfg3).dfifodepth; - + HAL_ASSERT(hal->channels.hdls); + const uint16_t fifo_size_lines = hal->constant_config.fifo_size; /* * Recommended FIFO sizes (see 2.1.2.4 for programming guide) * @@ -190,23 +199,28 @@ void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bi * Recommended sizes fit 2 packets of each type. For S2 and S3 we can't fit even one MPS ISOC packet (1023 FS and 1024 HS). * So the calculations below are compromises between the available FIFO size and optimal performance. */ + + // Information for maintainers: this calculation is here for backward compatibility + // It should be removed when we allow HAL users to configure the FIFO sizes IDF-9042 + const int otg_dfifo_depth = hal->constant_config.hsphy_type ? 1024 : 256; + usb_dwc_hal_fifo_config_t fifo_config; switch (fifo_bias) { // Define minimum viable (fits at least 1 MPS) FIFO sizes for non-biased FIFO types // Allocate the remaining size to the biased FIFO type case USB_HAL_FIFO_BIAS_DEFAULT: - fifo_config.nptx_fifo_lines = OTG_DFIFO_DEPTH / 4; - fifo_config.ptx_fifo_lines = OTG_DFIFO_DEPTH / 8; + fifo_config.nptx_fifo_lines = otg_dfifo_depth / 4; + fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8; fifo_config.rx_fifo_lines = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines; break; case USB_HAL_FIFO_BIAS_RX: - fifo_config.nptx_fifo_lines = OTG_DFIFO_DEPTH / 16; - fifo_config.ptx_fifo_lines = OTG_DFIFO_DEPTH / 8; + fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16; + fifo_config.ptx_fifo_lines = otg_dfifo_depth / 8; fifo_config.rx_fifo_lines = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines; break; case USB_HAL_FIFO_BIAS_PTX: - fifo_config.rx_fifo_lines = OTG_DFIFO_DEPTH / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1 - fifo_config.nptx_fifo_lines = OTG_DFIFO_DEPTH / 16; + fifo_config.rx_fifo_lines = otg_dfifo_depth / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1 + fifo_config.nptx_fifo_lines = otg_dfifo_depth / 16; fifo_config.ptx_fifo_lines = fifo_size_lines - fifo_config.nptx_fifo_lines - fifo_config.rx_fifo_lines; break; default: @@ -215,7 +229,7 @@ void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bi HAL_ASSERT((fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines + fifo_config.ptx_fifo_lines) <= fifo_size_lines); //Check that none of the channels are active - for (int i = 0; i < OTG_NUM_HOST_CHAN; i++) { + for (int i = 0; i < hal->constant_config.chan_num_total; i++) { if (hal->channels.hdls[i] != NULL) { HAL_ASSERT(!hal->channels.hdls[i]->flags.active); } @@ -254,11 +268,15 @@ static inline void debounce_lock_enable(usb_dwc_hal_context_t *hal) void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal) { - usb_dwc_speed_t speed = usb_dwc_ll_hprt_get_speed(hal->dev); - //Host Configuration - usb_dwc_ll_hcfg_set_defaults(hal->dev, speed); - //Configure HFIR - usb_dwc_ll_hfir_set_defaults(hal->dev, speed); + // Host Configuration + usb_dwc_ll_hcfg_en_scatt_gatt_dma(hal->dev); // Enable Scatther-Gather DMA mode + usb_dwc_ll_hcfg_dis_perio_sched(hal->dev); // Disable Periodic Scheduler (for now) + + // Configure PHY clock: Only for USB-DWC with FSLS PHY + if (hal->constant_config.hsphy_type == 0) { + usb_dwc_ll_hcfg_set_fsls_phy_clock(hal->dev); + usb_dwc_ll_hfir_set_frame_interval(hal->dev); + } } // ----------------------------------------------------- Channel ------------------------------------------------------- @@ -267,17 +285,18 @@ void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal) bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx) { + HAL_ASSERT(hal->channels.hdls); HAL_ASSERT(hal->flags.fifo_sizes_set); //FIFO sizes should be set before attempting to allocate a channel //Attempt to allocate channel - if (hal->channels.num_allocd == OTG_NUM_HOST_CHAN) { + if (hal->channels.num_allocated == hal->constant_config.chan_num_total) { return false; //Out of free channels } int chan_idx = -1; - for (int i = 0; i < OTG_NUM_HOST_CHAN; i++) { + for (int i = 0; i < hal->constant_config.chan_num_total; i++) { if (hal->channels.hdls[i] == NULL) { hal->channels.hdls[i] = chan_obj; chan_idx = i; - hal->channels.num_allocd++; + hal->channels.num_allocated++; break; } } @@ -299,6 +318,7 @@ bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan void usb_dwc_hal_chan_free(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj) { + HAL_ASSERT(hal->channels.hdls); if (chan_obj->type == USB_DWC_XFER_TYPE_INTR || chan_obj->type == USB_DWC_XFER_TYPE_ISOCHRONOUS) { //Unschedule this channel for (int i = 0; i < hal->frame_list_len; i++) { @@ -311,8 +331,8 @@ void usb_dwc_hal_chan_free(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_ usb_dwc_ll_haintmsk_dis_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx); //Deallocate channel hal->channels.hdls[chan_obj->flags.chan_idx] = NULL; - hal->channels.num_allocd--; - HAL_ASSERT(hal->channels.num_allocd >= 0); + hal->channels.num_allocated--; + HAL_ASSERT(hal->channels.num_allocated >= 0); } // ---------------- Channel Configuration ------------------ @@ -464,6 +484,7 @@ usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr(usb_dwc_hal_context_t *hal) usb_dwc_hal_chan_t *usb_dwc_hal_get_chan_pending_intr(usb_dwc_hal_context_t *hal) { + HAL_ASSERT(hal->channels.hdls); int chan_num = __builtin_ffs(hal->channels.chan_pend_intrs_msk); if (chan_num) { hal->channels.chan_pend_intrs_msk &= ~(1 << (chan_num - 1)); //Clear the pending bit for that channel diff --git a/components/soc/esp32p4/include/soc/usb_dwc_cfg.h b/components/soc/esp32p4/include/soc/usb_dwc_cfg.h index 3b5b887568..816cff4285 100644 --- a/components/soc/esp32p4/include/soc/usb_dwc_cfg.h +++ b/components/soc/esp32p4/include/soc/usb_dwc_cfg.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,94 +11,170 @@ extern "C" { #endif /* +HS Instance: Configuration Set ID: 11 */ /* 3.1 Basic Config Parameters */ -#define OTG_MODE 0 -#define OTG_ARCHITECTURE 2 -#define OTG_SINGLE_POINT 1 -#define OTG_ENABLE_LPM 0 -#define OTG_EN_DED_TX_FIFO 1 -#define OTG_EN_DESC_DMA 1 -#define OTG_MULTI_PROC_INTRPT 1 +#define OTG20_MODE 0 +#define OTG20_ARCHITECTURE 2 +#define OTG20_SINGLE_POINT 1 +#define OTG20_ENABLE_LPM 0 +#define OTG20_EN_DED_TX_FIFO 1 +#define OTG20_EN_DESC_DMA 1 +#define OTG20_MULTI_PROC_INTRPT 1 /* 3.2 USB Physical Layer Interface Parameters */ -#define OTG_HSPHY_INTERFACE 3 -#define OTG_HSPHY_DWIDTH 2 -#define OTG_FSPHY_INTERFACE 2 -#define OTG_ENABLE_IC_USB 0 -#define OTG_ENABLE_HSIC 0 -#define OTG_I2C_INTERFACE 0 -#define OTG_ULPI_CARKIT 1 -#define OTG_ADP_SUPPORT 1 -#define OTG_BC_SUPPORT 0 -#define OTG_VENDOR_CTL_INTERFACE 1 +#define OTG20_HSPHY_INTERFACE 3 +#define OTG20_HSPHY_DWIDTH 2 +#define OTG20_FSPHY_INTERFACE 2 +#define OTG20_ENABLE_IC_USB 0 +#define OTG20_ENABLE_HSIC 0 +#define OTG20_I2C_INTERFACE 0 +#define OTG20_ULPI_CARKIT 1 +#define OTG20_ADP_SUPPORT 1 +#define OTG20_BC_SUPPORT 0 +#define OTG20_VENDOR_CTL_INTERFACE 1 /* 3.3 Device Endpoint Configuration Parameters */ -#define OTG_NUM_EPS 15 -#define OTG_NUM_IN_EPS 8 -#define OTG_NUM_CRL_EPS 1 +#define OTG20_NUM_EPS 15 +#define OTG20_NUM_IN_EPS 8 +#define OTG20_NUM_CRL_EPS 1 /* 3.4 Host Endpoint Configuration Parameters */ -#define OTG_NUM_HOST_CHAN 16 -#define OTG_EN_PERIO_HOST 1 +#define OTG20_NUM_HOST_CHAN 16 +#define OTG20_EN_PERIO_HOST 1 /* 3.5 Endpoint Channel FIFO Configuration Parameters */ -#define OTG_DFIFO_DEPTH 1024 -#define OTG_DFIFO_DYNAMIC 1 -#define OTG_RX_DFIFO_DEPTH 1024 -#define OTG_TX_HNPERIO_DFIFO_DEPTH 1024 -#define OTG_TX_HPERIO_DFIFO_DEPTH 1024 -#define OTG_NPERIO_TX_QUEUE_DEPTH 4 -#define OTG_PERIO_TX_QUEUE_DEPTH 4 +#define OTG20_DFIFO_DEPTH 1024 +#define OTG20_DFIFO_DYNAMIC 1 +#define OTG20_RX_DFIFO_DEPTH 1024 +#define OTG20_TX_HNPERIO_DFIFO_DEPTH 1024 +#define OTG20_TX_HPERIO_DFIFO_DEPTH 1024 +#define OTG20_NPERIO_TX_QUEUE_DEPTH 4 +#define OTG20_PERIO_TX_QUEUE_DEPTH 4 /* 3.6 Additional Configuration Options Parameters */ -#define OTG_TRANS_COUNT_WIDTH 17 -#define OTG_PACKET_COUNT_WIDTH 8 -#define OTG_RM_OPT_FEATURES 1 -#define OTG_EN_PWROPT 1 -#define OTG_SYNC_RESET_TYPE 0 -#define OTG_EN_IDDIG_FILTER 1 -#define OTG_EN_VBUSVALID_FILTER 1 -#define OTG_EN_A_VALID_FILTER 1 -#define OTG_EN_B_VALID_FILTER 1 -#define OTG_EN_SESSIONEND_FILTER 1 -#define OTG_EXCP_CNTL_XFER_FLOW 1 -#define OTG_PWR_CLAMP 0 -#define OTG_PWR_SWITCH_POLARITY 0 +#define OTG20_TRANS_COUNT_WIDTH 17 +#define OTG20_PACKET_COUNT_WIDTH 8 +#define OTG20_RM_OPT_FEATURES 1 +#define OTG20_EN_PWROPT 1 +#define OTG20_SYNC_RESET_TYPE 0 +#define OTG20_EN_IDDIG_FILTER 1 +#define OTG20_EN_VBUSVALID_FILTER 1 +#define OTG20_EN_A_VALID_FILTER 1 +#define OTG20_EN_B_VALID_FILTER 1 +#define OTG20_EN_SESSIONEND_FILTER 1 +#define OTG20_EXCP_CNTL_XFER_FLOW 1 +#define OTG20_PWR_CLAMP 0 +#define OTG20_PWR_SWITCH_POLARITY 0 /* 3.7 Endpoint Direction Parameters */ -#define OTG_EP_DIR_1 0 -#define OTG_EP_DIR_2 0 -#define OTG_EP_DIR_3 0 -#define OTG_EP_DIR_4 0 -#define OTG_EP_DIR_5 0 -#define OTG_EP_DIR_6 0 -#define OTG_EP_DIR_7 0 -#define OTG_EP_DIR_8 0 -#define OTG_EP_DIR_9 0 -#define OTG_EP_DIR_10 0 -#define OTG_EP_DIR_11 0 -#define OTG_EP_DIR_12 0 -#define OTG_EP_DIR_13 0 -#define OTG_EP_DIR_14 0 -#define OTG_EP_DIR_15 0 +#define OTG20_EP_DIR_1 0 +#define OTG20_EP_DIR_2 0 +#define OTG20_EP_DIR_3 0 +#define OTG20_EP_DIR_4 0 +#define OTG20_EP_DIR_5 0 +#define OTG20_EP_DIR_6 0 +#define OTG20_EP_DIR_7 0 +#define OTG20_EP_DIR_8 0 +#define OTG20_EP_DIR_9 0 +#define OTG20_EP_DIR_10 0 +#define OTG20_EP_DIR_11 0 +#define OTG20_EP_DIR_12 0 +#define OTG20_EP_DIR_13 0 +#define OTG20_EP_DIR_14 0 +#define OTG20_EP_DIR_15 0 /* 3.8 Device Periodic FIFO Depth Parameters */ /* 3.9 Device IN Endpoint FIFO Depth Parameters */ -#define OTG_TX_DINEP_DFIFO_DEPTH_0 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_1 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_2 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_3 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_4 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_5 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_6 512 -#define OTG_TX_DINEP_DFIFO_DEPTH_7 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_0 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_1 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_2 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_3 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_4 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_5 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_6 512 +#define OTG20_TX_DINEP_DFIFO_DEPTH_7 512 /* 3.10 UTMI-To-UTMI Bridge Component Parameters */ -#define DWC_U2UB_EN 0 +#define OTG20_U2UB_EN 0 + +/* +FS Instance: +Configuration Set ID: 1 +*/ + +/* 3.1 Basic Config Parameters */ +#define OTG11_MODE 0 +#define OTG11_ARCHITECTURE 2 +#define OTG11_SINGLE_POINT 1 +#define OTG11_ENABLE_LPM 0 +#define OTG11_EN_DED_TX_FIFO 1 +#define OTG11_EN_DESC_DMA 1 +#define OTG11_MULTI_PROC_INTRPT 0 + +/* 3.2 USB Physical Layer Interface Parameters */ +#define OTG11_HSPHY_INTERFACE 0 +#define OTG11_FSPHY_INTERFACE 1 +#define OTG11_ENABLE_IC_USB 0 +#define OTG11_I2C_INTERFACE 0 +#define OTG11_ADP_SUPPORT 0 +#define OTG11_BC_SUPPORT 0 + +/* 3.3 Device Endpoint Configuration Parameters */ +#define OTG11_NUM_EPS 6 +#define OTG11_NUM_IN_EPS 5 +#define OTG11_NUM_CRL_EPS 0 + +/* 3.4 Host Endpoint Configuration Parameters */ +#define OTG11_NUM_HOST_CHAN 8 +#define OTG11_EN_PERIO_HOST 1 + +/* 3.5 Endpoint Channel FIFO Configuration Parameters */ +#define OTG11_DFIFO_DEPTH 256 +#define OTG11_DFIFO_DYNAMIC 1 +#define OTG11_RX_DFIFO_DEPTH 256 +#define OTG11_TX_HNPERIO_DFIFO_DEPTH 256 +#define OTG11_TX_NPERIO_DFIFO_DEPTH 256 +#define OTG11_TX_HPERIO_DFIFO_DEPTH 256 +#define OTG11_NPERIO_TX_QUEUE_DEPTH 4 +#define OTG11_PERIO_TX_QUEUE_DEPTH 8 + +/* 3.6 Additional Configuration Options Parameters */ +#define OTG11_TRANS_COUNT_WIDTH 16 +#define OTG11_PACKET_COUNT_WIDTH 7 +#define OTG11_RM_OPT_FEATURES 1 +#define OTG11_EN_PWROPT 1 +#define OTG11_SYNC_RESET_TYPE 0 +#define OTG11_EN_IDDIG_FILTER 1 +#define OTG11_EN_VBUSVALID_FILTER 1 +#define OTG11_EN_A_VALID_FILTER 1 +#define OTG11_EN_B_VALID_FILTER 1 +#define OTG11_EN_SESSIONEND_FILTER 1 +#define OTG11_EXCP_CNTL_XFER_FLOW 1 +#define OTG11_PWR_CLAMP 0 +#define OTG11_PWR_SWITCH_POLARITY 0 + +/* 3.7 Endpoint Direction Parameters */ +#define OTG11_EP_DIR_1 0 +#define OTG11_EP_DIR_2 0 +#define OTG11_EP_DIR_3 0 +#define OTG11_EP_DIR_4 0 +#define OTG11_EP_DIR_5 0 +#define OTG11_EP_DIR_6 0 + +/* 3.8 Device Periodic FIFO Depth Parameters */ + +/* 3.9 Device IN Endpoint FIFO Depth Parameters */ +#define OTG11_TX_DINEP_DFIFO_DEPTH_1 256 +#define OTG11_TX_DINEP_DFIFO_DEPTH_2 256 +#define OTG11_TX_DINEP_DFIFO_DEPTH_3 256 +#define OTG11_TX_DINEP_DFIFO_DEPTH_4 256 + +/* 3.10 UTMI-To-UTMI Bridge Component Parameters */ +#define OTG11_U2UB_EN 0 #ifdef __cplusplus } diff --git a/components/soc/esp32p4/include/soc/usb_dwc_struct.h b/components/soc/esp32p4/include/soc/usb_dwc_struct.h index 728be16b0f..60b44f5dac 100644 --- a/components/soc/esp32p4/include/soc/usb_dwc_struct.h +++ b/components/soc/esp32p4/include/soc/usb_dwc_struct.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,7 +13,10 @@ extern "C" { #endif /* -Registers and fields were generated based on a set of configuration options. +Registers and fields were generated based on a set of USB-DWC configuration options. +ESP32-P4 contains 2 instances of USB-DWC with different configurations, the structure below corresponds to the HS instance. +The FS instance contains a subset of registers from HS instance, the user (HAL) is responsible for accessing only existing fields. + See ESP32-P4 "usb_dwc_cfg.h" for more details. */ @@ -1368,6 +1371,7 @@ _Static_assert(sizeof(usb_dwc_dev_t) == 0xe08, "Invalid size of usb_dwc_dev_t st #endif extern usb_dwc_dev_t USB_DWC_HS; +extern usb_dwc_dev_t USB_DWC_FS; #ifdef __cplusplus } diff --git a/components/soc/esp32s2/include/soc/usb_dwc_struct.h b/components/soc/esp32s2/include/soc/usb_dwc_struct.h index 7c9ca381c3..45e2610237 100644 --- a/components/soc/esp32s2/include/soc/usb_dwc_struct.h +++ b/components/soc/esp32s2/include/soc/usb_dwc_struct.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -88,7 +88,7 @@ typedef union { struct { uint32_t toutcal: 3; uint32_t phyif: 1; - uint32_t reserved_4: 1; + uint32_t ulpiutmisel: 1; uint32_t fsintf: 1; uint32_t physel: 1; uint32_t reserved_7: 1; diff --git a/components/soc/esp32s3/include/soc/usb_dwc_struct.h b/components/soc/esp32s3/include/soc/usb_dwc_struct.h index 5fd5800715..404c5194c6 100644 --- a/components/soc/esp32s3/include/soc/usb_dwc_struct.h +++ b/components/soc/esp32s3/include/soc/usb_dwc_struct.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -88,7 +88,7 @@ typedef union { struct { uint32_t toutcal: 3; uint32_t phyif: 1; - uint32_t reserved_4: 1; + uint32_t ulpiutmisel: 1; uint32_t fsintf: 1; uint32_t physel: 1; uint32_t reserved_7: 1; diff --git a/components/usb/hcd_dwc.c b/components/usb/hcd_dwc.c index 2005a963a0..8b9860548a 100644 --- a/components/usb/hcd_dwc.c +++ b/components/usb/hcd_dwc.c @@ -1274,7 +1274,9 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h port_obj->callback = port_config->callback; port_obj->callback_arg = port_config->callback_arg; port_obj->context = port_config->context; - usb_dwc_hal_init(port_obj->hal); + usb_dwc_hal_init(port_obj->hal, 0); + port_obj->hal->channels.hdls = calloc(port_obj->hal->constant_config.chan_num_total, sizeof(usb_dwc_hal_chan_t*)); + HCD_CHECK_FROM_CRIT(port_obj->hal->channels.hdls != NULL, ESP_ERR_NO_MEM); port_obj->initialized = true; // Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset memset(port_obj->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t)); @@ -1298,6 +1300,7 @@ esp_err_t hcd_port_deinit(hcd_port_handle_t port_hdl) ESP_ERR_INVALID_STATE); port->initialized = false; esp_intr_disable(s_hcd_obj->isr_hdl); + free(port->hal->channels.hdls); usb_dwc_hal_deinit(port->hal); HCD_EXIT_CRITICAL(); @@ -1410,14 +1413,14 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl) && port->num_pipes_idle == 0 && port->num_pipes_queued == 0 && port->flags.val == 0 && port->task_waiting_port_notif == NULL, ESP_ERR_INVALID_STATE); + // We are about to do a soft reset on the peripheral. Disable the peripheral throughout esp_intr_disable(s_hcd_obj->isr_hdl); usb_dwc_hal_core_soft_reset(port->hal); port->state = HCD_PORT_STATE_NOT_POWERED; port->last_event = HCD_PORT_EVENT_NONE; port->flags.val = 0; - // Soft reset wipes all registers so we need to reinitialize the HAL - usb_dwc_hal_init(port->hal); + // Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset memset(port->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t)); esp_intr_enable(s_hcd_obj->isr_hdl); diff --git a/components/usb/test_apps/hcd/main/test_hcd_isoc.c b/components/usb/test_apps/hcd/main/test_hcd_isoc.c index 1f6420bf81..2f5f837af4 100644 --- a/components/usb/test_apps/hcd/main/test_hcd_isoc.c +++ b/components/usb/test_apps/hcd/main/test_hcd_isoc.c @@ -6,7 +6,7 @@ #include #include -#include "soc/usb_dwc_cfg.h" +#include "hal/usb_dwc_ll.h" // For USB-DWC configuration #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "unity.h" @@ -17,7 +17,7 @@ #define NUM_URBS 3 #define NUM_PACKETS_PER_URB 3 #define POST_ENQUEUE_DELAY_US 20 -#define ENQUEUE_DELAY (OTG_HSPHY_INTERFACE ? 100 : 500) // With this delay we want to enqueue the URBs at different times +#define ENQUEUE_DELAY (usb_dwc_ll_ghwcfg_get_hsphy_type(USB_DWC_LL_GET_HW(0)) ? 100 : 500) // With this delay we want to enqueue the URBs at different times /* Test HCD ISOC pipe URBs @@ -126,12 +126,12 @@ TEST_CASE("Test HCD isochronous pipe URBs all", "[isoc][full_speed][high_speed]" uint8_t dev_addr = test_hcd_enum_device(default_pipe); urb_t *urb_list[NUM_URBS]; - hcd_pipe_handle_t unused_pipes[OTG_NUM_HOST_CHAN]; + hcd_pipe_handle_t unused_pipes[16]; const usb_ep_desc_t *out_ep_desc = dev_isoc_get_out_ep_desc(port_speed); const int isoc_packet_size = USB_EP_DESC_GET_MPS(out_ep_desc); - // For all channels - for (int channel = 0; channel < OTG_NUM_HOST_CHAN - 1; channel++) { + // For all channels (except channel allocated for EP0) + for (int channel = 0; channel < usb_dwc_ll_ghwcfg_get_channel_num(USB_DWC_LL_GET_HW(0)) - 1; channel++) { // Allocate unused pipes, so the active isoc_out_pipe uses different channel index for (int ch = 0; ch < channel; ch++) { unused_pipes[ch] = test_hcd_pipe_alloc(port_hdl, out_ep_desc, dev_addr + 1, port_speed);