Merge branch 'feat/dynamic_usb_hal_backport_v5.3' into 'release/v5.3'

feat(hal/usb): Make USB-DWC HAL&LL configuration independent backport v5.3

See merge request espressif/esp-idf!34811
This commit is contained in:
morris
2024-12-07 23:19:56 +08:00
28 changed files with 2687 additions and 320 deletions

View File

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

View File

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

View File

@@ -0,0 +1,982 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "soc/usb_dwc_struct.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) (((num) == 1) ? &USB_DWC_FS : &USB_DWC_HS)
/* -----------------------------------------------------------------------------
--------------------------------- 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)
{
hw->gusbcfg_reg.phyif = 1; // 16 bits interface
hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+
hw->gusbcfg_reg.physel = 0; // HS PHY
}
// --------------------------- 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 --------------------------------
static inline unsigned usb_dwc_ll_ghwcfg_get_fifo_depth(usb_dwc_dev_t *hw)
{
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 -------------------------------
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;
}
/**
* @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
*/
static inline void usb_dwc_ll_hcfg_set_fsls_phy_clock(usb_dwc_dev_t *hw)
{
/*
Indicate to the OTG core what speed the PHY clock is at
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.
*/
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 ---------------------------------
/**
* @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)
{
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: FSLS PHY has an implicit 8 divider applied when in LS mode,
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
*/
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;
}
// ----------------------------- 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

View File

@@ -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,12 +48,12 @@ 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
*/
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

View File

@@ -6,25 +6,21 @@
#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 <stdint.h>
#include <stdbool.h>
#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
#define USB_DWC_LL_GET_HW(num) (&USB_DWC)
/* -----------------------------------------------------------------------------
--------------------------------- DWC Constants --------------------------------
@@ -222,14 +218,12 @@ 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)
{
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 --------------------------------
@@ -355,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 regiters of the DWC_OTG controller
*
* The hardware configuraiton 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 -------------------------------
@@ -405,7 +394,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)
{
@@ -437,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 --------------------------------
@@ -903,7 +889,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 +900,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 +918,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 +958,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 +978,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

View File

@@ -0,0 +1,983 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#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)
{
hw->gusbcfg_reg.phyif = 1; // 16 bits interface
hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+
hw->gusbcfg_reg.physel = 0; // HS PHY
}
// --------------------------- 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 --------------------------------
static inline unsigned usb_dwc_ll_ghwcfg_get_fifo_depth(usb_dwc_dev_t *hw)
{
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 -------------------------------
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;
}
/**
* @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
*/
static inline void usb_dwc_ll_hcfg_set_fsls_phy_clock(usb_dwc_dev_t *hw)
{
/*
Indicate to the OTG core what speed the PHY clock is at
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.
*/
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 ---------------------------------
/**
* @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)
{
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: FSLS PHY has an implicit 8 divider applied when in LS mode,
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
*/
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;
}
// ----------------------------- 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

View File

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

View File

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

View File

@@ -8,17 +8,17 @@
#include <stdbool.h>
#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
}

View File

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

View File

@@ -10,7 +10,7 @@
#include <stdlib.h> // 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"
#include "hal/efuse_hal.h"
@@ -18,14 +18,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 +110,12 @@ 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)
// 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);
}
//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
@@ -131,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_BASE;
// 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);
}
@@ -157,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)
*
@@ -193,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:
@@ -218,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);
}
@@ -257,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 -------------------------------------------------------
@@ -270,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->flags.fifo_sizes_set); //FIFO sizes should be set befor attempting to allocate a channel
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;
}
}
@@ -302,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++) {
@@ -314,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 ------------------
@@ -467,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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,8 +4,79 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/usb_dwc_periph.h"
#include <stddef.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

View File

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

View File

@@ -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 <stdint.h>
#include <stdbool.h>
#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

View File

@@ -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,
@@ -1286,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));
@@ -1310,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();
@@ -1422,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);

View File

@@ -6,7 +6,7 @@
#include <stdio.h>
#include <string.h>
#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);

View File

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