mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'refactor/make_usb_hal_independent_backport_v6.0' into 'release/v6.0'
refactor(usb): Make usb hal layer independent (backport v6.0) See merge request espressif/esp-idf!43249
This commit is contained in:
@@ -235,16 +235,6 @@ elseif(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"
|
||||
"usb_wrap_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
|
||||
# Source files for the legacy touch hal driver
|
||||
if(CONFIG_SOC_TOUCH_SENSOR_VERSION LESS 3)
|
||||
|
||||
@@ -1,983 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 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 + 1;
|
||||
}
|
||||
|
||||
// --------------------------- 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_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
|
||||
hctsiz.pid = 0; // Set PID to DATA0
|
||||
/*
|
||||
* Set SCHED_INFO which occupies xfersize[7:0]
|
||||
*
|
||||
* Although the hardware documentation suggests that SCHED_INFO is only used for periodic channels,
|
||||
* empirical evidence shows that omitting this configuration on non-periodic channels can cause them to freeze.
|
||||
* Therefore, we set this field for all channels to ensure reliable operation.
|
||||
*/
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform PING protocol
|
||||
*
|
||||
* @note This function is here only for compatibility reasons. PING is not relevant on FS only targets
|
||||
* @param[in] chan Channel registers
|
||||
* @param[in] enable true: Enable PING, false: Disable PING
|
||||
*/
|
||||
static inline void usb_dwc_ll_hctsiz_set_dopng(volatile usb_dwc_host_chan_regs_t *chan, bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set scheduling info for Periodic channel
|
||||
*
|
||||
* @note ESP32-H4 is Full-Speed only, so SCHED_INFO is always set to 0xFF
|
||||
* @attention This function must be called for each periodic channel!
|
||||
* @see USB-OTG databook: Table 5-47
|
||||
*
|
||||
* @param[in] chan Channel registers
|
||||
* @param[in] tokens_per_frame Ignored
|
||||
* @param[in] offset Ignored
|
||||
*/
|
||||
static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
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
|
||||
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc.h"
|
||||
#include "register/soc/pcr_struct.h"
|
||||
#include "register/soc/usb_wrap_struct.h"
|
||||
#include "hal/usb_wrap_types.h"
|
||||
|
||||
/* ----------------------------- Macros & Types ----------------------------- */
|
||||
|
||||
#define USB_WRAP_LL_EXT_PHY_SUPPORTED 0 // Cannot route to an external FSLS PHY
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------------------------- USB PHY Control ---------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Sets default
|
||||
*
|
||||
* Some register fields and features of the USB WRAP are redundant on the ESP32-H4.
|
||||
* This function sets those fields to their appropriate default values.
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_defaults(usb_wrap_dev_t *hw)
|
||||
{
|
||||
// Always select internal PHY for H4
|
||||
hw->wrap_otg_conf.wrap_phy_sel = 0;
|
||||
hw->wrap_otg_conf.wrap_usb_pad_enable = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables and sets the override value for the session end signal
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param sessend Session end override value. True means VBus < 0.2V, false means VBus > 0.8V
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_srp_sessend_override(usb_wrap_dev_t *hw, bool sessend)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_srp_sessend_value = sessend;
|
||||
hw->wrap_otg_conf.wrap_srp_sessend_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable session end override
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_srp_sessend_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_srp_sessend_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables/disables exchanging of the D+/D- pins USB PHY
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Enables pin exchange, disabled otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pin_exchg(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
hw->wrap_otg_conf.wrap_exchg_pins = 1;
|
||||
hw->wrap_otg_conf.wrap_exchg_pins_override = 1;
|
||||
} else {
|
||||
hw->wrap_otg_conf.wrap_exchg_pins_override = 0;
|
||||
hw->wrap_otg_conf.wrap_exchg_pins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables and sets voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vrefh_step High voltage threshold. 0 to 3 indicating 80mV steps from 1.76V to 2V.
|
||||
* @param vrefl_step Low voltage threshold. 0 to 3 indicating 80mV steps from 0.8V to 1.04V.
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_vref_override(usb_wrap_dev_t *hw, unsigned int vrefh_step, unsigned int vrefl_step)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_vrefh = vrefh_step;
|
||||
hw->wrap_otg_conf.wrap_vrefl = vrefl_step;
|
||||
hw->wrap_otg_conf.wrap_vref_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_vref_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_vref_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable override of USB FSLS PHY's pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Override values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pull_override(usb_wrap_dev_t *hw, const usb_wrap_pull_override_vals_t *vals)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_dp_pullup = vals->dp_pu;
|
||||
hw->wrap_otg_conf.wrap_dp_pulldown = vals->dp_pd;
|
||||
hw->wrap_otg_conf.wrap_dm_pullup = vals->dm_pu;
|
||||
hw->wrap_otg_conf.wrap_dm_pulldown = vals->dm_pd;
|
||||
hw->wrap_otg_conf.wrap_pad_pull_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable override of USB FSLS PHY pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_pull_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_pad_pull_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the strength of the pullup resistor
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param strong True is a ~1.4K pullup, false is a ~2.4K pullup
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_pullup_strength(usb_wrap_dev_t *hw, bool strong)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_pullup_value = strong;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if USB FSLS PHY pads are enabled
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @return True if enabled, false otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR bool usb_wrap_ll_phy_is_pad_enabled(usb_wrap_dev_t *hw)
|
||||
{
|
||||
return hw->wrap_otg_conf.wrap_usb_pad_enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY pads
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY pads
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pad(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->wrap_otg_conf.wrap_usb_pad_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set USB FSLS PHY TX output clock edge
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param clk_neg_edge True if TX output at negedge, posedge otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_tx_edge(usb_wrap_dev_t *hw, bool clk_neg_edge)
|
||||
{
|
||||
// Not supported on ESP32-H4: no wrap_phy_tx_edge_sel field
|
||||
(void)hw;
|
||||
(void)clk_neg_edge;
|
||||
}
|
||||
|
||||
/* ------------------------------ USB PHY Test ------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY's test mode
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY's test mode
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_test_mode(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
/* Not supported on H4: test_conf not present in usb_wrap_dev_t */
|
||||
(void)hw; (void)enable; // Corrected to indicate test_conf is not present
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the USB FSLS PHY's signal test values
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Test values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_test_mode_set_signals(usb_wrap_dev_t *hw, const usb_wrap_test_mode_vals_t *vals)
|
||||
{
|
||||
/* Not supported on H4: test_conf not present in usb_wrap_dev_t */
|
||||
(void)hw; (void)vals; // Corrected to indicate test_conf is not present
|
||||
}
|
||||
|
||||
/* ----------------------------- RCC Functions ----------------------------- */
|
||||
|
||||
/**
|
||||
* Enable the bus clock for USB Wrap module
|
||||
* @param clk_en True if enable the clock of USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_enable_bus_clock(bool clk_en)
|
||||
{
|
||||
PCR.usb_device_conf.usb_device_clk_en = clk_en;
|
||||
}
|
||||
|
||||
// SYSTEM.perip_clk_enx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_reset_register(void)
|
||||
{
|
||||
PCR.usb_device_conf.usb_device_rst_en = 1;
|
||||
PCR.usb_device_conf.usb_device_rst_en = 0;
|
||||
}
|
||||
|
||||
// SYSTEM.perip_rst_enx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "soc/lp_clkrst_struct.h"
|
||||
#include "soc/hp_sys_clkrst_struct.h"
|
||||
#include "soc/hp_system_struct.h"
|
||||
#include "soc/usb_utmi_struct.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------------------------- USB PHY Control ---------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Configure Low-Speed mode
|
||||
*
|
||||
* @param[in] hw Beginning address of the peripheral registers
|
||||
* @param[in] parallel Parallel or serial LS mode
|
||||
* @return FORCE_INLINE_ATTR
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_utmi_ll_configure_ls(usb_utmi_dev_t *hw, bool parallel)
|
||||
{
|
||||
hw->fc_06.ls_par_en = parallel;
|
||||
hw->fc_06.ls_kpalv_en = 1;
|
||||
}
|
||||
|
||||
/* ----------------------------- RCC Functions ----------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Enable the bus clock for the USB UTMI PHY and USB_DWC_HS controller
|
||||
*
|
||||
* @param[in] clk_en True to enable, false to disable
|
||||
*/
|
||||
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;
|
||||
// Enable PHY ref clock (48MHz) for USB UTMI PHY
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.usb_otg20_phyref_clk_en = 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(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_utmi_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* Get the enable status of the USB UTMI PHY bus clock
|
||||
*
|
||||
* @return Return true if USB UTMI PHY bus clock is enabled
|
||||
*/
|
||||
FORCE_INLINE_ATTR bool _usb_utmi_ll_bus_clock_is_enabled(void)
|
||||
{
|
||||
return (HP_SYS_CLKRST.soc_clk_ctrl1.reg_usb_otg20_sys_clk_en && LP_AON_CLKRST.hp_usb_clkrst_ctrl1.usb_otg20_phyref_clk_en);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset the USB UTMI PHY and USB_DWC_HS controller
|
||||
*/
|
||||
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;
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg20_phy = 1;
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg20_phy = 0;
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg20 = 0;
|
||||
}
|
||||
|
||||
// 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(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_utmi_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Enable precise detection of VBUS
|
||||
*
|
||||
* @param[in] enable Enable/Disable precise detection
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_utmi_ll_enable_precise_detection(bool enable)
|
||||
{
|
||||
// Enable VBUS precise detection
|
||||
HP_SYSTEM.sys_usbotg20_ctrl.sys_otg_suspendm = enable;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,273 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/lp_system_struct.h"
|
||||
#include "soc/lp_clkrst_struct.h"
|
||||
#include "soc/hp_sys_clkrst_struct.h"
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "hal/usb_wrap_types.h"
|
||||
|
||||
/* ----------------------------- Macros & Types ----------------------------- */
|
||||
|
||||
#define USB_WRAP_LL_SELECT_PHY_SUPPORTED 1 // Can swap to another internal FSLS PHY
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------------------------- USB PHY Control ---------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Sets default
|
||||
*
|
||||
* Some register fields/features of the USB WRAP are redundant on the ESP32-P4.
|
||||
* This function those fields are set to the appropriate default values.
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_defaults(usb_wrap_dev_t *hw)
|
||||
{
|
||||
// External FSLS PHY is not supported
|
||||
hw->otg_conf.phy_sel = 0;
|
||||
hw->otg_conf.usb_pad_enable = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select the internal USB FSLS PHY for the USB WRAP
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param phy_idx Selected PHY's index
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_select(usb_wrap_dev_t *hw, unsigned int phy_idx)
|
||||
{
|
||||
// Enable SW control mapping USB_WRAP and USJ to USB FSLS PHY 0 and 1
|
||||
LP_SYS.usb_ctrl.sw_hw_usb_phy_sel = 1;
|
||||
/*
|
||||
For 'sw_usb_phy_sel':
|
||||
False - USJ mapped to USB FSLS PHY 0, USB_WRAP mapped to USB FSLS PHY 1 (default)
|
||||
True - USJ mapped to USB FSLS PHY 1, USB_WRAP mapped to USB FSLS PHY 0
|
||||
*/
|
||||
switch (phy_idx) {
|
||||
case 0:
|
||||
LP_SYS.usb_ctrl.sw_usb_phy_sel = true;
|
||||
case 1:
|
||||
LP_SYS.usb_ctrl.sw_usb_phy_sel = false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables and sets the override value for the session end signal
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param sessend Session end override value. True means VBus < 0.2V, false means VBus > 0.8V
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_srp_sessend_override(usb_wrap_dev_t *hw, bool sessend)
|
||||
{
|
||||
hw->otg_conf.srp_sessend_value = sessend;
|
||||
hw->otg_conf.srp_sessend_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable session end override
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_srp_sessend_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.srp_sessend_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables/disables exchanging of the D+/D- pins USB PHY
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Enables pin exchange, disabled otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pin_exchg(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
hw->otg_conf.exchg_pins = 1;
|
||||
hw->otg_conf.exchg_pins_override = 1;
|
||||
} else {
|
||||
hw->otg_conf.exchg_pins_override = 0;
|
||||
hw->otg_conf.exchg_pins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables and sets voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vrefh_step High voltage threshold. 0 to 3 indicating 80mV steps from 1.76V to 2V.
|
||||
* @param vrefl_step Low voltage threshold. 0 to 3 indicating 80mV steps from 0.8V to 1.04V.
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_vref_override(usb_wrap_dev_t *hw, unsigned int vrefh_step, unsigned int vrefl_step)
|
||||
{
|
||||
hw->otg_conf.vrefh = vrefh_step;
|
||||
hw->otg_conf.vrefl = vrefl_step;
|
||||
hw->otg_conf.vref_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_vref_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.vref_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable override of USB FSLS PHY's pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Override values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pull_override(usb_wrap_dev_t *hw, const usb_wrap_pull_override_vals_t *vals)
|
||||
{
|
||||
hw->otg_conf.dp_pullup = vals->dp_pu;
|
||||
hw->otg_conf.dp_pulldown = vals->dp_pd;
|
||||
hw->otg_conf.dm_pullup = vals->dm_pu;
|
||||
hw->otg_conf.dm_pulldown = vals->dm_pd;
|
||||
hw->otg_conf.pad_pull_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable override of USB FSLS PHY pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_pull_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.pad_pull_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the strength of the pullup resistor
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param strong True is a ~1.4K pullup, false is a ~2.4K pullup
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_pullup_strength(usb_wrap_dev_t *hw, bool strong)
|
||||
{
|
||||
hw->otg_conf.pullup_value = strong;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if USB FSLS PHY pads are enabled
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @return True if enabled, false otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR bool usb_wrap_ll_phy_is_pad_enabled(usb_wrap_dev_t *hw)
|
||||
{
|
||||
return hw->otg_conf.usb_pad_enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY pads
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY pads
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pad(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->otg_conf.usb_pad_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set USB FSLS PHY TX output clock edge
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param clk_neg_edge True if TX output at negedge, posedge otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_tx_edge(usb_wrap_dev_t *hw, bool clk_neg_edge)
|
||||
{
|
||||
hw->otg_conf.phy_tx_edge_sel = clk_neg_edge;
|
||||
}
|
||||
|
||||
/* ------------------------------ USB PHY Test ------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY's test mode
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY's test mode
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_test_mode(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->test_conf.test_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the USB FSLS PHY's signal test values
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Test values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_test_mode_set_signals(usb_wrap_dev_t *hw, const usb_wrap_test_mode_vals_t *vals)
|
||||
{
|
||||
usb_wrap_test_conf_reg_t test_conf;
|
||||
test_conf.val = hw->test_conf.val;
|
||||
|
||||
test_conf.test_usb_wrap_oe = vals->tx_enable_n;
|
||||
test_conf.test_tx_dp = vals->tx_dp;
|
||||
test_conf.test_tx_dm = vals->tx_dm;
|
||||
test_conf.test_rx_rcv = vals->rx_rcv;
|
||||
test_conf.test_rx_dp = vals->rx_dp;
|
||||
test_conf.test_rx_dm = vals->rx_dm;
|
||||
|
||||
hw->test_conf.val = test_conf.val;
|
||||
}
|
||||
|
||||
/* ----------------------------- RCC Functions ----------------------------- */
|
||||
|
||||
/**
|
||||
* Enable the bus clock for USB Wrap module and USB_DWC_FS controller
|
||||
* @param clk_en True if enable the clock of USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_enable_bus_clock(bool clk_en)
|
||||
{
|
||||
// Enable/disable system clock for USB_WRAP and USB_DWC_FS
|
||||
HP_SYS_CLKRST.soc_clk_ctrl1.reg_usb_otg11_sys_clk_en = clk_en;
|
||||
// Enable PHY clock (48MHz) for USB FSLS PHY 1
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl0.usb_otg11_48m_clk_en = 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_wrap_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the USB Wrap module and USB_DWC_FS controller
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_reset_register(void)
|
||||
{
|
||||
// Reset the USB_WRAP and USB_DWC_FS
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg11 = 1;
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg11 = 0;
|
||||
}
|
||||
|
||||
// P_AON_CLKRST.hp_usb_clkrst_ctrlx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,981 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 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;
|
||||
while (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 + 1;
|
||||
}
|
||||
|
||||
// --------------------------- 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_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
|
||||
hctsiz.pid = 0; // Set PID to DATA0
|
||||
/*
|
||||
* Set SCHED_INFO which occupies xfersize[7:0]
|
||||
*
|
||||
* Although the hardware documentation suggests that SCHED_INFO is only used for periodic channels,
|
||||
* empirical evidence shows that omitting this configuration on non-periodic channels can cause them to freeze.
|
||||
* Therefore, we set this field for all channels to ensure reliable operation.
|
||||
*/
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform PING protocol
|
||||
*
|
||||
* @note This function is here only for compatibility reasons. PING is not relevant on FS only targets
|
||||
* @param[in] chan Channel registers
|
||||
* @param[in] enable true: Enable PING, false: Disable PING
|
||||
*/
|
||||
static inline void usb_dwc_ll_hctsiz_set_dopng(volatile usb_dwc_host_chan_regs_t *chan, bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set scheduling info for Periodic channel
|
||||
*
|
||||
* @note ESP32-S2 is Full-Speed only, so SCHED_INFO is always set to 0xFF
|
||||
* @attention This function must be called for each periodic channel!
|
||||
* @see USB-OTG databook: Table 5-47
|
||||
*
|
||||
* @param[in] chan Channel registers
|
||||
* @param[in] tokens_per_frame Ignored
|
||||
* @param[in] offset Ignored
|
||||
*/
|
||||
static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
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
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/system_reg.h"
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "hal/usb_wrap_types.h"
|
||||
|
||||
/* ----------------------------- Macros & Types ----------------------------- */
|
||||
|
||||
#define USB_WRAP_LL_EXT_PHY_SUPPORTED 1 // Can route to an external FSLS PHY
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------------------------- USB PHY Control ---------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Enables and sets the override value for the session end signal
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param sessend Session end override value. True means VBus < 0.2V, false means VBus > 0.8V
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_srp_sessend_override(usb_wrap_dev_t *hw, bool sessend)
|
||||
{
|
||||
hw->otg_conf.srp_sessend_value = sessend;
|
||||
hw->otg_conf.srp_sessend_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable session end override
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_srp_sessend_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.srp_sessend_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets whether the USB Wrap's FSLS PHY interface routes to an internal or external PHY
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Enables external PHY, internal otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_external(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->otg_conf.phy_sel = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables/disables exchanging of the D+/D- pins USB PHY
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Enables pin exchange, disabled otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pin_exchg(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
hw->otg_conf.exchg_pins = 1;
|
||||
hw->otg_conf.exchg_pins_override = 1;
|
||||
} else {
|
||||
hw->otg_conf.exchg_pins_override = 0;
|
||||
hw->otg_conf.exchg_pins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables and sets voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vrefh_step High voltage threshold. 0 to 3 indicating 80mV steps from 1.76V to 2V.
|
||||
* @param vrefl_step Low voltage threshold. 0 to 3 indicating 80mV steps from 0.8V to 1.04V.
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_vref_override(usb_wrap_dev_t *hw, unsigned int vrefh_step, unsigned int vrefl_step)
|
||||
{
|
||||
hw->otg_conf.vrefh = vrefh_step;
|
||||
hw->otg_conf.vrefl = vrefl_step;
|
||||
hw->otg_conf.vref_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_vref_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.vref_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable override of USB FSLS PHY's pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Override values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pull_override(usb_wrap_dev_t *hw, const usb_wrap_pull_override_vals_t *vals)
|
||||
{
|
||||
hw->otg_conf.dp_pullup = vals->dp_pu;
|
||||
hw->otg_conf.dp_pulldown = vals->dp_pd;
|
||||
hw->otg_conf.dm_pullup = vals->dm_pu;
|
||||
hw->otg_conf.dm_pulldown = vals->dm_pd;
|
||||
hw->otg_conf.pad_pull_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable override of USB FSLS PHY pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_pull_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.pad_pull_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the strength of the pullup resistor
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param strong True is a ~1.4K pullup, false is a ~2.4K pullup
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_pullup_strength(usb_wrap_dev_t *hw, bool strong)
|
||||
{
|
||||
hw->otg_conf.pullup_value = strong;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if USB FSLS PHY pads are enabled
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @return True if enabled, false otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR bool usb_wrap_ll_phy_is_pad_enabled(usb_wrap_dev_t *hw)
|
||||
{
|
||||
return hw->otg_conf.pad_enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY pads
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY pads
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pad(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->otg_conf.pad_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set USB FSLS PHY TX output clock edge
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param clk_neg_edge True if TX output at negedge, posedge otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_tx_edge(usb_wrap_dev_t *hw, bool clk_neg_edge)
|
||||
{
|
||||
hw->otg_conf.phy_tx_edge_sel = clk_neg_edge;
|
||||
}
|
||||
|
||||
/* ------------------------------ USB PHY Test ------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY's test mode
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY's test mode
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_test_mode(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->test_conf.test_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the USB FSLS PHY's signal test values
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Test values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_test_mode_set_signals(usb_wrap_dev_t *hw, const usb_wrap_test_mode_vals_t *vals)
|
||||
{
|
||||
usb_wrap_test_conf_reg_t test_conf;
|
||||
test_conf.val = hw->test_conf.val;
|
||||
|
||||
test_conf.test_usb_wrap_oe = vals->tx_enable_n;
|
||||
test_conf.test_tx_dp = vals->tx_dp;
|
||||
test_conf.test_tx_dm = vals->tx_dm;
|
||||
test_conf.test_rx_rcv = vals->rx_rcv;
|
||||
test_conf.test_rx_dp = vals->rx_dp;
|
||||
test_conf.test_rx_dm = vals->rx_dm;
|
||||
|
||||
hw->test_conf.val = test_conf.val;
|
||||
}
|
||||
|
||||
/* ----------------------------- RCC Functions ----------------------------- */
|
||||
|
||||
/**
|
||||
* Enable the bus clock for USB Wrap module
|
||||
* @param clk_en True if enable the clock of USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_enable_bus_clock(bool clk_en)
|
||||
{
|
||||
REG_SET_FIELD(DPORT_PERIP_CLK_EN0_REG, DPORT_USB_CLK_EN, clk_en);
|
||||
}
|
||||
|
||||
// SYSTEM.perip_clk_enx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_reset_register(void)
|
||||
{
|
||||
REG_SET_FIELD(DPORT_PERIP_RST_EN0_REG, DPORT_USB_RST, 1);
|
||||
REG_SET_FIELD(DPORT_PERIP_RST_EN0_REG, DPORT_USB_RST, 0);
|
||||
}
|
||||
|
||||
// SYSTEM.perip_rst_enx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,981 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 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;
|
||||
while (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 + 1;
|
||||
}
|
||||
|
||||
// --------------------------- 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_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
|
||||
hctsiz.pid = 0; // Set PID to DATA0
|
||||
/*
|
||||
* Set SCHED_INFO which occupies xfersize[7:0]
|
||||
*
|
||||
* Although the hardware documentation suggests that SCHED_INFO is only used for periodic channels,
|
||||
* empirical evidence shows that omitting this configuration on non-periodic channels can cause them to freeze.
|
||||
* Therefore, we set this field for all channels to ensure reliable operation.
|
||||
*/
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Perform PING protocol
|
||||
*
|
||||
* @note This function is here only for compatibility reasons. PING is not relevant on FS only targets
|
||||
* @param[in] chan Channel registers
|
||||
* @param[in] enable true: Enable PING, false: Disable PING
|
||||
*/
|
||||
static inline void usb_dwc_ll_hctsiz_set_dopng(volatile usb_dwc_host_chan_regs_t *chan, bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set scheduling info for Periodic channel
|
||||
*
|
||||
* @note ESP32-S3 is Full-Speed only, so SCHED_INFO is always set to 0xFF
|
||||
* @attention This function must be called for each periodic channel!
|
||||
* @see USB-OTG databook: Table 5-47
|
||||
*
|
||||
* @param[in] chan Channel registers
|
||||
* @param[in] tokens_per_frame Ignored
|
||||
* @param[in] offset Ignored
|
||||
*/
|
||||
static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
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
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/system_struct.h"
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "soc/rtc_cntl_struct.h"
|
||||
#include "hal/usb_wrap_types.h"
|
||||
|
||||
/* ----------------------------- Macros & Types ----------------------------- */
|
||||
|
||||
#define USB_WRAP_LL_EXT_PHY_SUPPORTED 1 // Can route to an external FSLS PHY
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ---------------------------- USB PHY Control ---------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Enables and sets the override value for the session end signal
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param sessend Session end override value. True means VBus < 0.2V, false means VBus > 0.8V
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_srp_sessend_override(usb_wrap_dev_t *hw, bool sessend)
|
||||
{
|
||||
hw->otg_conf.srp_sessend_value = sessend;
|
||||
hw->otg_conf.srp_sessend_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable session end override
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_srp_sessend_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.srp_sessend_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets whether the USB Wrap's FSLS PHY interface routes to an internal or external PHY
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Enables external PHY, internal otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_external(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->otg_conf.phy_sel = enable;
|
||||
// Enable SW control of muxing USB OTG vs USJ to the internal USB FSLS PHY
|
||||
RTCCNTL.usb_conf.sw_hw_usb_phy_sel = 1;
|
||||
/*
|
||||
For 'sw_usb_phy_sel':
|
||||
0 - Internal USB FSLS PHY is mapped to the USJ. USB Wrap mapped to external PHY
|
||||
1 - Internal USB FSLS PHY is mapped to the USB Wrap. USJ mapped to external PHY
|
||||
*/
|
||||
RTCCNTL.usb_conf.sw_usb_phy_sel = !enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables/disables exchanging of the D+/D- pins USB PHY
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Enables pin exchange, disabled otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pin_exchg(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
hw->otg_conf.exchg_pins = 1;
|
||||
hw->otg_conf.exchg_pins_override = 1;
|
||||
} else {
|
||||
hw->otg_conf.exchg_pins_override = 0;
|
||||
hw->otg_conf.exchg_pins = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables and sets voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vrefh_step High voltage threshold. 0 to 3 indicating 80mV steps from 1.76V to 2V.
|
||||
* @param vrefl_step Low voltage threshold. 0 to 3 indicating 80mV steps from 0.8V to 1.04V.
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_vref_override(usb_wrap_dev_t *hw, unsigned int vrefh_step, unsigned int vrefl_step)
|
||||
{
|
||||
hw->otg_conf.vrefh = vrefh_step;
|
||||
hw->otg_conf.vrefl = vrefl_step;
|
||||
hw->otg_conf.vref_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables voltage threshold overrides for USB FSLS PHY single-ended inputs
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_vref_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.vref_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable override of USB FSLS PHY's pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Override values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pull_override(usb_wrap_dev_t *hw, const usb_wrap_pull_override_vals_t *vals)
|
||||
{
|
||||
hw->otg_conf.dp_pullup = vals->dp_pu;
|
||||
hw->otg_conf.dp_pulldown = vals->dp_pd;
|
||||
hw->otg_conf.dm_pullup = vals->dm_pu;
|
||||
hw->otg_conf.dm_pulldown = vals->dm_pd;
|
||||
hw->otg_conf.pad_pull_override = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable override of USB FSLS PHY pull up/down resistors
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_disable_pull_override(usb_wrap_dev_t *hw)
|
||||
{
|
||||
hw->otg_conf.pad_pull_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the strength of the pullup resistor
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param strong True is a ~1.4K pullup, false is a ~2.4K pullup
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_pullup_strength(usb_wrap_dev_t *hw, bool strong)
|
||||
{
|
||||
hw->otg_conf.pullup_value = strong;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if USB FSLS PHY pads are enabled
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @return True if enabled, false otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR bool usb_wrap_ll_phy_is_pad_enabled(usb_wrap_dev_t *hw)
|
||||
{
|
||||
return hw->otg_conf.pad_enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY pads
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY pads
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_pad(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->otg_conf.pad_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set USB FSLS PHY TX output clock edge
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param clk_neg_edge True if TX output at negedge, posedge otherwise
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_set_tx_edge(usb_wrap_dev_t *hw, bool clk_neg_edge)
|
||||
{
|
||||
hw->otg_conf.phy_tx_edge_sel = clk_neg_edge;
|
||||
}
|
||||
|
||||
/* ------------------------------ USB PHY Test ------------------------------ */
|
||||
|
||||
/**
|
||||
* @brief Enable the USB FSLS PHY's test mode
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param enable Whether to enable the USB FSLS PHY's test mode
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_enable_test_mode(usb_wrap_dev_t *hw, bool enable)
|
||||
{
|
||||
hw->test_conf.test_enable = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the USB FSLS PHY's signal test values
|
||||
*
|
||||
* @param hw Start address of the USB Wrap registers
|
||||
* @param vals Test values to set
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_wrap_ll_phy_test_mode_set_signals(usb_wrap_dev_t *hw, const usb_wrap_test_mode_vals_t *vals)
|
||||
{
|
||||
usb_wrap_test_conf_reg_t test_conf;
|
||||
test_conf.val = hw->test_conf.val;
|
||||
|
||||
test_conf.test_usb_wrap_oe = vals->tx_enable_n;
|
||||
test_conf.test_tx_dp = vals->tx_dp;
|
||||
test_conf.test_tx_dm = vals->tx_dm;
|
||||
test_conf.test_rx_rcv = vals->rx_rcv;
|
||||
test_conf.test_rx_dp = vals->rx_dp;
|
||||
test_conf.test_rx_dm = vals->rx_dm;
|
||||
|
||||
hw->test_conf.val = test_conf.val;
|
||||
}
|
||||
|
||||
/* ----------------------------- RCC Functions ----------------------------- */
|
||||
|
||||
/**
|
||||
* Enable the bus clock for USB Wrap module
|
||||
* @param clk_en True if enable the clock of USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_enable_bus_clock(bool clk_en)
|
||||
{
|
||||
SYSTEM.perip_clk_en0.usb_clk_en = clk_en;
|
||||
}
|
||||
|
||||
// SYSTEM.perip_clk_enx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_enable_bus_clock(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_enable_bus_clock(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
/**
|
||||
* @brief Reset the USB Wrap module
|
||||
*/
|
||||
FORCE_INLINE_ATTR void _usb_wrap_ll_reset_register(void)
|
||||
{
|
||||
SYSTEM.perip_rst_en0.usb_rst = 1;
|
||||
SYSTEM.perip_rst_en0.usb_rst = 0;
|
||||
}
|
||||
|
||||
// SYSTEM.perip_rst_enx are shared registers, so this function must be used in an atomic way
|
||||
#define usb_wrap_ll_reset_register(...) do { \
|
||||
(void)__DECLARE_RCC_ATOMIC_ENV; \
|
||||
_usb_wrap_ll_reset_register(__VA_ARGS__); \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,846 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#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 "hal/usb_dwc_ll.h"
|
||||
#include "hal/usb_dwc_types.h"
|
||||
#include "hal/assert.h"
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
// ------------------------------------------------ Macros and Types ---------------------------------------------------
|
||||
|
||||
// ----------------------- Configs -------------------------
|
||||
|
||||
/**
|
||||
* @brief MPS limits based on FIFO configuration
|
||||
*
|
||||
* In bytes
|
||||
*
|
||||
* The resulting values depend on
|
||||
* 1. FIFO total size (chip specific)
|
||||
* 2. Set FIFO bias
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int in_mps; /**< Maximum packet size of IN packet */
|
||||
unsigned int non_periodic_out_mps; /**< Maximum packet size of BULK and CTRL OUT packets */
|
||||
unsigned int periodic_out_mps; /**< Maximum packet size of INTR and ISOC OUT packets */
|
||||
} usb_hal_fifo_mps_limits_t;
|
||||
|
||||
/**
|
||||
* @brief FIFO size configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t rx_fifo_lines; /**< Size of the RX FIFO in terms the number of FIFO lines */
|
||||
uint32_t nptx_fifo_lines; /**< Size of the Non-periodic FIFO in terms the number of FIFO lines */
|
||||
uint32_t ptx_fifo_lines; /**< Size of the Periodic FIFO in terms the number of FIFO lines */
|
||||
} usb_dwc_hal_fifo_config_t;
|
||||
|
||||
// --------------------- HAL Events ------------------------
|
||||
|
||||
/**
|
||||
* @brief Host port HAL events
|
||||
*/
|
||||
typedef enum {
|
||||
USB_DWC_HAL_PORT_EVENT_NONE, /**< No event occurred, or could not decode interrupt */
|
||||
USB_DWC_HAL_PORT_EVENT_CHAN, /**< A channel event has occurred. Call the the channel event handler instead */
|
||||
USB_DWC_HAL_PORT_EVENT_CONN, /**< The host port has detected a connection */
|
||||
USB_DWC_HAL_PORT_EVENT_DISCONN, /**< The host port has been disconnected */
|
||||
USB_DWC_HAL_PORT_EVENT_ENABLED, /**< The host port has been enabled (i.e., connected to a device that has been reset. Started sending SOFs) */
|
||||
USB_DWC_HAL_PORT_EVENT_DISABLED, /**< The host port has been disabled (no more SOFs). Could be due to disable/reset request, or a port error (e.g. port babble condition. See 11.8.1 of USB2.0 spec) */
|
||||
USB_DWC_HAL_PORT_EVENT_OVRCUR, /**< The host port has encountered an overcurrent condition */
|
||||
USB_DWC_HAL_PORT_EVENT_OVRCUR_CLR, /**< The host port has been cleared of the overcurrent condition */
|
||||
} usb_dwc_hal_port_event_t;
|
||||
|
||||
/**
|
||||
* @brief Channel events
|
||||
*/
|
||||
typedef enum {
|
||||
USB_DWC_HAL_CHAN_EVENT_CPLT, /**< The channel has completed execution of a transfer descriptor that had the USB_DWC_HAL_XFER_DESC_FLAG_HOC flag set. Channel is now halted */
|
||||
USB_DWC_HAL_CHAN_EVENT_ERROR, /**< The channel has encountered an error. Channel is now halted. */
|
||||
USB_DWC_HAL_CHAN_EVENT_HALT_REQ, /**< The channel has been successfully halted as requested */
|
||||
USB_DWC_HAL_CHAN_EVENT_NONE, /**< No event (interrupt ran for internal processing) */
|
||||
} usb_dwc_hal_chan_event_t;
|
||||
|
||||
// --------------------- HAL Errors ------------------------
|
||||
|
||||
/**
|
||||
* @brief Channel errors
|
||||
*/
|
||||
typedef enum {
|
||||
USB_DWC_HAL_CHAN_ERROR_XCS_XACT = 0, /**< Excessive (three consecutive) transaction errors (e.g., no response, bad CRC etc */
|
||||
USB_DWC_HAL_CHAN_ERROR_BNA, /**< Buffer Not Available error (i.e., An inactive transfer descriptor was fetched by the channel) */
|
||||
USB_DWC_HAL_CHAN_ERROR_PKT_BBL, /**< Packet babbler error (packet exceeded MPS) */
|
||||
USB_DWC_HAL_CHAN_ERROR_STALL, /**< STALL response received */
|
||||
} usb_dwc_hal_chan_error_t;
|
||||
|
||||
// ------------- Transfer Descriptor Related ---------------
|
||||
|
||||
/**
|
||||
* @brief Flags used to describe the type of transfer descriptor to fill
|
||||
*/
|
||||
#define USB_DWC_HAL_XFER_DESC_FLAG_IN 0x01 /**< Indicates this transfer descriptor is of the IN direction */
|
||||
#define USB_DWC_HAL_XFER_DESC_FLAG_SETUP 0x02 /**< Indicates this transfer descriptor is an OUT setup */
|
||||
#define USB_DWC_HAL_XFER_DESC_FLAG_HOC 0x04 /**< Indicates that the channel will be halted after this transfer descriptor completes */
|
||||
|
||||
/**
|
||||
* @brief Status value of a transfer descriptor
|
||||
*
|
||||
* A transfer descriptor's status remains unexecuted until the entire transfer descriptor completes (either successfully
|
||||
* or an error). Therefore, if a channel halt is requested before a transfer descriptor completes, the transfer
|
||||
* descriptor remains unexecuted.
|
||||
*/
|
||||
#define USB_DWC_HAL_XFER_DESC_STS_SUCCESS USB_DWC_LL_QTD_STATUS_SUCCESS
|
||||
#define USB_DWC_HAL_XFER_DESC_STS_PKTERR USB_DWC_LL_QTD_STATUS_PKTERR
|
||||
#define USB_DWC_HAL_XFER_DESC_STS_BUFFER_ERR USB_DWC_LL_QTD_STATUS_BUFFER
|
||||
#define USB_DWC_HAL_XFER_DESC_STS_NOT_EXECUTED USB_DWC_LL_QTD_STATUS_NOT_EXECUTED
|
||||
|
||||
// -------------------- Object Types -----------------------
|
||||
|
||||
/**
|
||||
* @brief Endpoint characteristics structure
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
usb_dwc_xfer_type_t type: 2; /**< The type of endpoint */
|
||||
uint32_t bEndpointAddress: 8; /**< Endpoint address (containing endpoint number and direction) */
|
||||
uint32_t mps: 11; /**< Maximum Packet Size */
|
||||
uint32_t dev_addr: 8; /**< Device Address */
|
||||
uint32_t ls_via_fs_hub: 1; /**< The endpoint is on a LS device that is routed through an FS hub.
|
||||
Setting this bit will lead to the addition of the PREamble packet */
|
||||
uint32_t reserved2: 2;
|
||||
};
|
||||
uint32_t val;
|
||||
};
|
||||
struct {
|
||||
unsigned int interval; /**< The interval of the endpoint in frames (FS) or microframes (HS) */
|
||||
uint32_t offset; /**< Offset of this channel in the periodic scheduler */
|
||||
bool is_hs; /**< This endpoint is HighSpeed. Needed for Periodic Frame List (HAL layer) scheduling */
|
||||
} periodic; /**< Characteristic for periodic (interrupt/isochronous) endpoints only */
|
||||
} usb_dwc_hal_ep_char_t;
|
||||
|
||||
/**
|
||||
* @brief Channel object
|
||||
*/
|
||||
typedef struct {
|
||||
//Channel control, status, and information
|
||||
union {
|
||||
struct {
|
||||
uint32_t active: 1; /**< Debugging bit to indicate whether channel is enabled */
|
||||
uint32_t halt_requested: 1; /**< A halt has been requested */
|
||||
uint32_t reserved: 2;
|
||||
uint32_t chan_idx: 4; /**< The index number of the channel */
|
||||
uint32_t reserved24: 24;
|
||||
};
|
||||
uint32_t val;
|
||||
} flags; /**< Flags regarding channel's status and information */
|
||||
usb_dwc_host_chan_regs_t *regs; /**< Pointer to the channel's register set */
|
||||
usb_dwc_hal_chan_error_t error; /**< The last error that occurred on the channel */
|
||||
usb_dwc_xfer_type_t type; /**< The transfer type of the channel */
|
||||
void *chan_ctx; /**< Context variable for the owner of the channel */
|
||||
} usb_dwc_hal_chan_t;
|
||||
|
||||
/**
|
||||
* @brief HAL context structure
|
||||
*/
|
||||
typedef struct {
|
||||
// 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 */
|
||||
uint32_t fifo_sizes_set: 1; /**< Whether the FIFO sizes have been set or not */
|
||||
uint32_t periodic_sched_enabled: 1; /**< Periodic scheduling (for interrupt and isochronous transfers) is enabled */
|
||||
uint32_t reserved: 5;
|
||||
uint32_t reserved24: 24;
|
||||
};
|
||||
uint32_t val;
|
||||
} flags;
|
||||
|
||||
// Channel related
|
||||
struct {
|
||||
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;
|
||||
|
||||
// -------------------------------------------------- Core (Global) ----------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Initialize the HAL context and check if DWC_OTG is alive
|
||||
*
|
||||
* Entry:
|
||||
* - The peripheral must have been reset and clock un-gated
|
||||
* - The USB PHY (internal or external) and associated GPIOs must already be configured
|
||||
* - GPIO pins configured
|
||||
* - 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
|
||||
* - 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
|
||||
*
|
||||
* @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, int port_id);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the HAL context
|
||||
*
|
||||
* Entry:
|
||||
* - All channels must be properly disabled, and any pending events handled
|
||||
* Exit:
|
||||
* - DWC_OTG global interrupt disabled
|
||||
* - HAL context deinitialized
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal);
|
||||
|
||||
/**
|
||||
* @brief Issue a soft reset to the controller
|
||||
*
|
||||
* This should be called when the host port encounters an error event or has been disconnected. Before calling this,
|
||||
* users are responsible for safely freeing all channels as a soft reset will wipe all host port and channel registers.
|
||||
* This function will result in the host port being put back into same state as after calling usb_dwc_hal_init().
|
||||
*
|
||||
* @note This has nothing to do with a USB bus reset. It simply resets the peripheral
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
*/
|
||||
void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal);
|
||||
|
||||
/**
|
||||
* @brief Check if FIFO configuration is valid
|
||||
*
|
||||
* This function checks that the sum of FIFO sizes does not exceed available space.
|
||||
* It does not modify hardware state and is safe to call from HAL or upper layers.
|
||||
*
|
||||
* @param[in] hal Pointer to HAL context (must be initialized)
|
||||
* @param[in] config Pointer to FIFO config to validate
|
||||
* @return true if config is valid, false otherwise
|
||||
*/
|
||||
bool usb_dwc_hal_fifo_config_is_valid(const usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set the FIFO sizes of the USB-DWC core
|
||||
*
|
||||
* This function programs the FIFO sizing registers (RX FIFO, Non-Periodic TX FIFO,
|
||||
* and Periodic TX FIFO) based on the provided configuration. It must be called
|
||||
* during USB initialization, before any channels are allocated or transfers started.
|
||||
*
|
||||
* The sum of all FIFO sizes must not exceed the hardware-defined limit
|
||||
* (see HWCFG3.DfifoDepth and EPINFO_CTL).
|
||||
*
|
||||
* @note This function must be called exactly once during initialization and after
|
||||
* each USB port reset. It is typically used internally by the USB Host stack.
|
||||
*
|
||||
* @param[inout] hal Pointer to the HAL context
|
||||
* @param[in] config Pointer to the FIFO configuration to apply (must be valid)
|
||||
*/
|
||||
void usb_dwc_hal_set_fifo_config(usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Get MPS limits
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[out] mps_limits MPS limits
|
||||
*/
|
||||
void usb_dwc_hal_get_mps_limits(usb_dwc_hal_context_t *hal, usb_hal_fifo_mps_limits_t *mps_limits);
|
||||
|
||||
// ---------------------------------------------------- Host Port ------------------------------------------------------
|
||||
|
||||
// ------------------ Host Port Control --------------------
|
||||
|
||||
/**
|
||||
* @brief Initialize the host port
|
||||
*
|
||||
* - Will enable the host port's interrupts allowing port and channel events to occur
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_init(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
//Configure Host related interrupts
|
||||
usb_dwc_ll_haintmsk_dis_chan_intr(hal->dev, 0xFFFFFFFF); //Disable interrupts for all channels
|
||||
usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinitialize the host port
|
||||
*
|
||||
* - Will disable the host port's interrupts preventing further port aand channel events from occurring
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_deinit(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
//Disable Host port and channel interrupts
|
||||
usb_dwc_ll_gintmsk_dis_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_HCHINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggle the host port's power
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param power_on Whether to power ON or OFF the port
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_toggle_power(usb_dwc_hal_context_t *hal, bool power_on)
|
||||
{
|
||||
if (power_on) {
|
||||
usb_dwc_ll_hprt_en_pwr(hal->dev);
|
||||
} else {
|
||||
usb_dwc_ll_hprt_dis_pwr(hal->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggle reset signal on the bus
|
||||
*
|
||||
* The reset signal should be held for at least 10ms
|
||||
* Entry:
|
||||
* - Host port detects a device connection or Host port is already enabled
|
||||
* Exit:
|
||||
* - On release of the reset signal, a USB_DWC_HAL_PORT_EVENT_ENABLED will be generated
|
||||
*
|
||||
* @note If the host port is already enabled, then issuing a reset will cause it be disabled and generate a
|
||||
* USB_DWC_HAL_PORT_EVENT_DISABLED event. The host port will not be enabled until the reset signal is released (thus
|
||||
* generating the USB_DWC_HAL_PORT_EVENT_ENABLED event)
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param enable Enable/disable reset signal
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_toggle_reset(usb_dwc_hal_context_t *hal, bool enable)
|
||||
{
|
||||
usb_dwc_ll_hprt_set_port_reset(hal->dev, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the host port
|
||||
*
|
||||
* Entry:
|
||||
* - Host port enabled event triggered following a reset
|
||||
* Exit:
|
||||
* - Host port enabled to operate in scatter/gather DMA mode
|
||||
* - DMA fifo sizes configured
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal);
|
||||
|
||||
/**
|
||||
* @brief Disable the host port
|
||||
*
|
||||
* Exit:
|
||||
* - Host port disabled event triggered
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_disable(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
usb_dwc_ll_hprt_port_dis(hal->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Suspend the host port
|
||||
*
|
||||
* @param hal Context of the HAL layers
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_suspend(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
usb_dwc_ll_hprt_set_port_suspend(hal->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Toggle resume signal on the bus
|
||||
*
|
||||
* Hosts should hold the resume signal for at least 20ms
|
||||
*
|
||||
* @note If a remote wakeup event occurs, the resume signal is driven and cleared automatically.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param enable Enable/disable resume signal
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_toggle_resume(usb_dwc_hal_context_t *hal, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
usb_dwc_ll_hprt_set_port_resume(hal->dev);
|
||||
} else {
|
||||
usb_dwc_ll_hprt_clr_port_resume(hal->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether the resume signal is being driven
|
||||
*
|
||||
* If a remote wakeup event occurs, the core will automatically drive and clear the resume signal for the required
|
||||
* amount of time. Call this function to check whether the resume signal has completed.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @return true Resume signal is still being driven
|
||||
* @return false Resume signal is no longer driven
|
||||
*/
|
||||
static inline bool usb_dwc_hal_port_check_resume(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
return usb_dwc_ll_hprt_get_port_resume(hal->dev);
|
||||
}
|
||||
|
||||
// ---------------- Host Port Scheduling -------------------
|
||||
|
||||
/**
|
||||
* @brief Sets the periodic scheduling frame list
|
||||
*
|
||||
* @note This function must be called before attempting configuring any channels to be period via
|
||||
* usb_dwc_hal_chan_set_ep_char()
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param frame_list Base address of the frame list
|
||||
* @param frame_list_len Number of entries in the frame list (can only be 8, 16, 32, 64)
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_set_frame_list(usb_dwc_hal_context_t *hal, uint32_t *frame_list, usb_hal_frame_list_len_t len)
|
||||
{
|
||||
//Clear and save frame list
|
||||
hal->periodic_frame_list = frame_list;
|
||||
hal->frame_list_len = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable periodic scheduling
|
||||
*
|
||||
* @note The periodic frame list must be set via usb_dwc_hal_port_set_frame_list() should be set before calling this
|
||||
* function
|
||||
* @note This function must be called before activating any periodic channels
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_periodic_enable(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
HAL_ASSERT(hal->periodic_frame_list != NULL);
|
||||
usb_dwc_ll_hflbaddr_set_base_addr(hal->dev, (uint32_t)hal->periodic_frame_list);
|
||||
usb_dwc_ll_hcfg_set_num_frame_list_entries(hal->dev, hal->frame_list_len);
|
||||
usb_dwc_ll_hcfg_en_perio_sched(hal->dev);
|
||||
hal->flags.periodic_sched_enabled = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable periodic scheduling
|
||||
*
|
||||
* Disabling periodic scheduling will save a bit of DMA bandwidth (as the controller will no longer fetch the schedule
|
||||
* from the frame list).
|
||||
*
|
||||
* @note Before disabling periodic scheduling, it is the user's responsibility to ensure that all periodic channels have
|
||||
* halted safely.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_port_periodic_disable(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
HAL_ASSERT(hal->flags.periodic_sched_enabled);
|
||||
usb_dwc_ll_hcfg_dis_perio_sched(hal->dev);
|
||||
hal->flags.periodic_sched_enabled = 0;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_hal_port_get_cur_frame_num(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
return usb_dwc_ll_hfnum_get_frame_num(hal->dev);
|
||||
}
|
||||
|
||||
// --------------- Host Port Status/State ------------------
|
||||
|
||||
/**
|
||||
* @brief Check if a device is currently connected to the host port
|
||||
*
|
||||
* This function is intended to be called after one of the following events followed by an adequate debounce delay
|
||||
* - USB_DWC_HAL_PORT_EVENT_CONN
|
||||
* - USB_DWC_HAL_PORT_EVENT_DISCONN
|
||||
*
|
||||
* @note No other connection/disconnection event will occur again until the debounce lock is disabled via
|
||||
* usb_dwc_hal_disable_debounce_lock()
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @return true A device is connected to the host port
|
||||
* @return false A device is not connected to the host port
|
||||
*/
|
||||
static inline bool usb_dwc_hal_port_check_if_connected(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
return usb_dwc_ll_hprt_get_conn_status(hal->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check the speed of the device connected to the host port
|
||||
*
|
||||
* @note This function should only be called after confirming that a device is connected to the host port
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @return usb_dwc_speed_t Speed of the connected device
|
||||
*/
|
||||
static inline usb_dwc_speed_t usb_dwc_hal_port_get_conn_speed(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
return usb_dwc_ll_hprt_get_speed(hal->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the debounce lock
|
||||
*
|
||||
* This function must be called after calling usb_dwc_hal_port_check_if_connected() and will allow connection/disconnection
|
||||
* events to occur again. Any pending connection or disconnection interrupts are cleared.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
*/
|
||||
static inline void usb_dwc_hal_disable_debounce_lock(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
hal->flags.dbnc_lock_enabled = 0;
|
||||
//Clear Connection and disconnection interrupt in case it triggered again
|
||||
usb_dwc_ll_gintsts_clear_intrs(hal->dev, USB_DWC_LL_INTR_CORE_DISCONNINT);
|
||||
usb_dwc_ll_hprt_intr_clear(hal->dev, USB_DWC_LL_INTR_HPRT_PRTCONNDET);
|
||||
//Re-enable the hprt (connection) and disconnection interrupts
|
||||
usb_dwc_ll_gintmsk_en_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the root port is suspended
|
||||
*
|
||||
* This function checks if the root port entered suspended state, after calling usb_dwc_hal_port_suspend()
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @return true The root port is suspended
|
||||
* @return false The root port is not suspended
|
||||
*/
|
||||
static inline bool usb_dwc_hal_port_check_if_suspended(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
return usb_dwc_ll_hprt_get_port_suspend(hal->dev);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------- Channel -------------------------------------------------------
|
||||
|
||||
// ----------------- Channel Allocation --------------------
|
||||
|
||||
/**
|
||||
* @brief Allocate a channel
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[inout] chan_obj Empty channel object
|
||||
* @param[in] chan_ctx Context variable for the allocator of the channel
|
||||
* @return true Channel successfully allocated
|
||||
* @return false Failed to allocate channel
|
||||
*/
|
||||
bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx);
|
||||
|
||||
/**
|
||||
* @brief Free a channel
|
||||
*
|
||||
* @param[in] hal Context of the HAL layer
|
||||
* @param[in] chan_obj Channel object
|
||||
*/
|
||||
void usb_dwc_hal_chan_free(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj);
|
||||
|
||||
// ---------------- Channel Configuration ------------------
|
||||
|
||||
/**
|
||||
* @brief Get the context variable of the channel
|
||||
*
|
||||
* @param[in] chan_obj Channel object
|
||||
* @return void* The context variable of the channel
|
||||
*/
|
||||
static inline void *usb_dwc_hal_chan_get_context(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
return chan_obj->chan_ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the endpoint information for a particular channel
|
||||
*
|
||||
* This should be called when a channel switches target from one EP to another
|
||||
*
|
||||
* @note the channel must be in the disabled state in order to change its EP
|
||||
* information
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param chan_obj Channel object
|
||||
* @param ep_char Endpoint characteristics
|
||||
*/
|
||||
void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, usb_dwc_hal_ep_char_t *ep_char);
|
||||
|
||||
/**
|
||||
* @brief Set the direction of the channel
|
||||
*
|
||||
* This is a convenience function to flip the direction of a channel without
|
||||
* needing to reconfigure all of the channel's EP info. This is used primarily
|
||||
* for control transfers.
|
||||
*
|
||||
* @note This function should only be called when the channel is halted
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @param is_in Whether the direction is IN
|
||||
*/
|
||||
static inline void usb_dwc_hal_chan_set_dir(usb_dwc_hal_chan_t *chan_obj, bool is_in)
|
||||
{
|
||||
//Cannot change direction whilst channel is still active or in error
|
||||
HAL_ASSERT(!chan_obj->flags.active);
|
||||
usb_dwc_ll_hcchar_set_dir(chan_obj->regs, is_in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the next Packet ID of the channel (e.g., DATA0/DATA1)
|
||||
*
|
||||
* This should be called when a channel switches target from one EP to another
|
||||
* or when change stages for a control transfer
|
||||
*
|
||||
* @note The channel should only be called when the channel is in the
|
||||
* halted state.
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @param pid PID of the next DATA packet (DATA0 or DATA1)
|
||||
*/
|
||||
static inline void usb_dwc_hal_chan_set_pid(usb_dwc_hal_chan_t *chan_obj, int pid)
|
||||
{
|
||||
//Cannot change pid whilst channel is still active or in error
|
||||
HAL_ASSERT(!chan_obj->flags.active);
|
||||
//Update channel object and set the register
|
||||
usb_dwc_ll_hctsiz_set_pid(chan_obj->regs, pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next PID of a channel
|
||||
*
|
||||
* Returns the next PID (DATA0 or DATA1) of the channel. This function should be
|
||||
* used when the next PID of a pipe needs to be saved (e.g., when switching pipes
|
||||
* on a channel)
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @return uint32_t Starting PID of the next transfer (DATA0 or DATA1)
|
||||
*/
|
||||
static inline uint32_t usb_dwc_hal_chan_get_pid(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
HAL_ASSERT(!chan_obj->flags.active);
|
||||
return usb_dwc_ll_hctsiz_get_pid(chan_obj->regs);
|
||||
}
|
||||
|
||||
// ------------------- Channel Control ---------------------
|
||||
|
||||
/**
|
||||
* @brief Activate a channel
|
||||
*
|
||||
* Activating a channel will cause the channel to start executing transfer descriptors.
|
||||
*
|
||||
* @note This function should only be called on channels that were previously halted
|
||||
* @note An event will be generated when the channel is halted
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @param xfer_desc_list A filled transfer descriptor list
|
||||
* @param desc_list_len Transfer descriptor list length
|
||||
* @param start_idx Index of the starting transfer descriptor in the list
|
||||
*/
|
||||
void usb_dwc_hal_chan_activate(usb_dwc_hal_chan_t *chan_obj, void *xfer_desc_list, int desc_list_len, int start_idx);
|
||||
|
||||
/**
|
||||
* @brief Get the index of the current transfer descriptor
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @return int Descriptor index
|
||||
*/
|
||||
static inline int usb_dwc_hal_chan_get_qtd_idx(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
return usb_dwc_ll_hcdam_get_cur_qtd_idx(chan_obj->regs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request to halt a channel
|
||||
*
|
||||
* This function should be called in order to halt a channel. If the channel is already halted, this function will
|
||||
* return true. If the channel is still active, this function will return false and users must wait for the
|
||||
* USB_DWC_HAL_CHAN_EVENT_HALT_REQ event before treating the channel as halted.
|
||||
*
|
||||
* @note When a transfer is in progress (i.e., the channel is active) and a halt is requested, the channel will halt
|
||||
* after the next USB packet is completed. If the transfer has more pending packets, the transfer will just be
|
||||
* marked as USB_DWC_HAL_XFER_DESC_STS_NOT_EXECUTED.
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @return true The channel is already halted
|
||||
* @return false The halt was requested, wait for USB_DWC_HAL_CHAN_EVENT_HALT_REQ
|
||||
*/
|
||||
bool usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t *chan_obj);
|
||||
|
||||
/**
|
||||
* @brief Indicate that a channel is halted after a port error
|
||||
*
|
||||
* When a port error occurs (e.g., disconnect, overcurrent):
|
||||
* - Any previously active channels will remain active (i.e., they will not receive a channel interrupt)
|
||||
* - Attempting to disable them using usb_dwc_hal_chan_request_halt() will NOT generate an interrupt for ISOC channels
|
||||
* (probably something to do with the periodic scheduling)
|
||||
*
|
||||
* However, the channel's enable bit can be left as 1 since after a port error, a soft reset will be done anyways.
|
||||
* This function simply updates the channels internal state variable to indicate it is halted (thus allowing it to be
|
||||
* freed).
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
*/
|
||||
static inline void usb_dwc_hal_chan_mark_halted(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
chan_obj->flags.active = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a channel's error
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @return usb_dwc_hal_chan_error_t The type of error the channel has encountered
|
||||
*/
|
||||
static inline usb_dwc_hal_chan_error_t usb_dwc_hal_chan_get_error(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
return chan_obj->error;
|
||||
}
|
||||
|
||||
// -------------------------------------------- Transfer Descriptor List -----------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Fill a single entry in a transfer descriptor list
|
||||
*
|
||||
* - Depending on the transfer type, a single transfer descriptor may corresponds
|
||||
* - A stage of a transfer (for control transfers)
|
||||
* - A frame of a transfer interval (for interrupt and isoc)
|
||||
* - An entire transfer (for bulk transfers)
|
||||
* - Check the various USB_DWC_HAL_XFER_DESC_FLAG_ flags for filling a specific type of descriptor
|
||||
* - For IN transfer entries, set the USB_DWC_HAL_XFER_DESC_FLAG_IN. The transfer size must also be an integer multiple of
|
||||
* the endpoint's MPS
|
||||
*
|
||||
* @note Critical section is not required for this function
|
||||
*
|
||||
* @param desc_list Transfer descriptor list
|
||||
* @param desc_idx Transfer descriptor index
|
||||
* @param xfer_data_buff Transfer data buffer
|
||||
* @param xfer_len Transfer length
|
||||
* @param flags Transfer flags
|
||||
*/
|
||||
static inline void usb_dwc_hal_xfer_desc_fill(void *desc_list, uint32_t desc_idx, uint8_t *xfer_data_buff, int xfer_len, uint32_t flags)
|
||||
{
|
||||
usb_dwc_ll_dma_qtd_t *qtd_list = (usb_dwc_ll_dma_qtd_t *)desc_list;
|
||||
if (flags & USB_DWC_HAL_XFER_DESC_FLAG_IN) {
|
||||
usb_dwc_ll_qtd_set_in(&qtd_list[desc_idx],
|
||||
xfer_data_buff, xfer_len,
|
||||
flags & USB_DWC_HAL_XFER_DESC_FLAG_HOC);
|
||||
} else {
|
||||
usb_dwc_ll_qtd_set_out(&qtd_list[desc_idx],
|
||||
xfer_data_buff,
|
||||
xfer_len,
|
||||
flags & USB_DWC_HAL_XFER_DESC_FLAG_HOC,
|
||||
flags & USB_DWC_HAL_XFER_DESC_FLAG_SETUP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear a transfer descriptor (sets all its fields to NULL)
|
||||
*
|
||||
* @param desc_list Transfer descriptor list
|
||||
* @param desc_idx Transfer descriptor index
|
||||
*/
|
||||
static inline void usb_dwc_hal_xfer_desc_clear(void *desc_list, uint32_t desc_idx)
|
||||
{
|
||||
usb_dwc_ll_dma_qtd_t *qtd_list = (usb_dwc_ll_dma_qtd_t *)desc_list;
|
||||
usb_dwc_ll_qtd_set_null(&qtd_list[desc_idx]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse a transfer decriptor's results
|
||||
*
|
||||
* @param desc_list Transfer descriptor list
|
||||
* @param desc_idx Transfer descriptor index
|
||||
* @param[out] xfer_rem_len Remaining length of the transfer in bytes
|
||||
* @param[out] xfer_status Status of the transfer
|
||||
*
|
||||
* @note Critical section is not required for this function
|
||||
*/
|
||||
static inline void usb_dwc_hal_xfer_desc_parse(void *desc_list, uint32_t desc_idx, int *xfer_rem_len, int *xfer_status)
|
||||
{
|
||||
usb_dwc_ll_dma_qtd_t *qtd_list = (usb_dwc_ll_dma_qtd_t *)desc_list;
|
||||
usb_dwc_ll_qtd_get_status(&qtd_list[desc_idx], xfer_rem_len, xfer_status);
|
||||
//Clear the QTD to prevent it from being read again
|
||||
usb_dwc_ll_qtd_set_null(&qtd_list[desc_idx]);
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Event Handling ----------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Decode global and host port interrupts
|
||||
*
|
||||
* - Reads and clears global and host port interrupt registers
|
||||
* - Decodes the interrupt bits to determine what host port event occurred
|
||||
*
|
||||
* @note This should be the first interrupt decode function to be run
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @return usb_dwc_hal_port_event_t Host port event
|
||||
*/
|
||||
usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr(usb_dwc_hal_context_t *hal);
|
||||
|
||||
/**
|
||||
* @brief Gets the next channel with a pending interrupt
|
||||
*
|
||||
* If no channel is pending an interrupt, this function will return NULL. If one or more channels are pending an
|
||||
* interrupt, this function returns one of the channel's objects. Call this function repeatedly until it returns NULL.
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @return usb_dwc_hal_chan_t* Channel object. NULL if no channel are pending an interrupt.
|
||||
*/
|
||||
usb_dwc_hal_chan_t *usb_dwc_hal_get_chan_pending_intr(usb_dwc_hal_context_t *hal);
|
||||
|
||||
/**
|
||||
* @brief Decode a particular channel's interrupt
|
||||
*
|
||||
* - Reads and clears the interrupt register of the channel
|
||||
* - Returns the corresponding event for that channel
|
||||
*
|
||||
* @param chan_obj Channel object
|
||||
* @note If the host port has an error (e.g., a sudden disconnect or an port error), any active channels will not
|
||||
* receive an interrupt. Each active channel must be manually halted.
|
||||
* @return usb_dwc_hal_chan_event_t Channel event
|
||||
*/
|
||||
usb_dwc_hal_chan_event_t usb_dwc_hal_chan_decode_intr(usb_dwc_hal_chan_t *chan_obj);
|
||||
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: This header file contains USB2.0 related types and macros that can be used by code specific to the DWC_OTG
|
||||
controller (i.e., the HW specific layers of the USB host stack). Thus, this header is only meant to be used below (and
|
||||
including) the HAL layer. For types and macros that are HW implementation agnostic (i.e., HCD layer and above), add them
|
||||
to the "usb/usb_types_ch9.h" header instead.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB speeds supported by the DWC OTG controller
|
||||
*
|
||||
* @note usb_dwc_speed_t enum values must match the values of the DWC_OTG prtspd register field
|
||||
*/
|
||||
typedef enum {
|
||||
USB_DWC_SPEED_HIGH = 0,
|
||||
USB_DWC_SPEED_FULL = 1,
|
||||
USB_DWC_SPEED_LOW = 2,
|
||||
} usb_dwc_speed_t;
|
||||
|
||||
/**
|
||||
* @brief USB transfer types supported by the DWC OTG controller
|
||||
*
|
||||
* @note usb_dwc_xfer_type_t enum values must match the values of the DWC_OTG hcchar register field
|
||||
*/
|
||||
typedef enum {
|
||||
USB_DWC_XFER_TYPE_CTRL = 0,
|
||||
USB_DWC_XFER_TYPE_ISOCHRONOUS = 1,
|
||||
USB_DWC_XFER_TYPE_BULK = 2,
|
||||
USB_DWC_XFER_TYPE_INTR = 3,
|
||||
} usb_dwc_xfer_type_t;
|
||||
|
||||
/**
|
||||
* @brief Enumeration of different possible lengths of the periodic frame list
|
||||
*/
|
||||
typedef enum {
|
||||
USB_HAL_FRAME_LIST_LEN_8 = 8,
|
||||
USB_HAL_FRAME_LIST_LEN_16 = 16,
|
||||
USB_HAL_FRAME_LIST_LEN_32 = 32,
|
||||
USB_HAL_FRAME_LIST_LEN_64 = 64,
|
||||
} usb_hal_frame_list_len_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
Note: This header file contains USB2.0 related types and macros that can be used by code specific to the DWC_OTG
|
||||
controller (i.e., the HW specific layers of the USB host stack). Thus, this header is only meant to be used below (and
|
||||
including) the HAL layer. For types and macros that are HW implementation agnostic (i.e., HCD layer and above), add them
|
||||
to the "usb/usb_types_ch9.h" header instead.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB PHY target
|
||||
*/
|
||||
typedef enum {
|
||||
USB_PHY_TARGET_INT, /**< USB target is internal FSLS PHY */
|
||||
USB_PHY_TARGET_UTMI, /**< USB target is internal UTMI PHY */
|
||||
USB_PHY_TARGET_EXT, /**< USB target is external PHY */
|
||||
USB_PHY_TARGET_MAX,
|
||||
} usb_phy_target_t;
|
||||
|
||||
/**
|
||||
* @brief USB PHY source
|
||||
*/
|
||||
typedef enum {
|
||||
USB_PHY_CTRL_OTG, /**< PHY controller is USB OTG */
|
||||
#if SOC_USB_SERIAL_JTAG_SUPPORTED
|
||||
USB_PHY_CTRL_SERIAL_JTAG, /**< PHY controller is USB Serial JTAG */
|
||||
#endif
|
||||
USB_PHY_CTRL_MAX,
|
||||
} usb_phy_controller_t;
|
||||
|
||||
/**
|
||||
* @brief USB OTG mode
|
||||
*/
|
||||
typedef enum {
|
||||
USB_PHY_MODE_DEFAULT, /**< USB OTG default mode */
|
||||
USB_OTG_MODE_HOST, /**< USB OTG host mode */
|
||||
USB_OTG_MODE_DEVICE, /**< USB OTG device mode */
|
||||
USB_OTG_MODE_MAX,
|
||||
} usb_otg_mode_t;
|
||||
|
||||
/**
|
||||
* @brief USB speed
|
||||
*/
|
||||
typedef enum {
|
||||
USB_PHY_SPEED_UNDEFINED,
|
||||
USB_PHY_SPEED_LOW, /**< USB Low Speed (1.5 Mbit/s) */
|
||||
USB_PHY_SPEED_FULL, /**< USB Full Speed (12 Mbit/s) */
|
||||
USB_PHY_SPEED_HIGH, /**< USB High Speed (480 Mbit/s) */
|
||||
USB_PHY_SPEED_MAX,
|
||||
} usb_phy_speed_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#if (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "hal/usb_wrap_ll.h"
|
||||
#endif // (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
#include "hal/usb_wrap_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
/**
|
||||
* @brief HAL context type of USB WRAP driver
|
||||
*/
|
||||
typedef struct {
|
||||
usb_wrap_dev_t *dev;
|
||||
} usb_wrap_hal_context_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the USB WRAP HAL driver
|
||||
*
|
||||
* @param hal USB WRAP HAL context
|
||||
*/
|
||||
void _usb_wrap_hal_init(usb_wrap_hal_context_t *hal);
|
||||
|
||||
#if SOC_RCC_IS_INDEPENDENT
|
||||
#define usb_wrap_hal_init(...) _usb_wrap_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_wrap_hal_init(...) do {(void)__DECLARE_RCC_ATOMIC_ENV; _usb_wrap_hal_init(__VA_ARGS__);} while(0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Disable USB WRAP
|
||||
*
|
||||
* Disable clock to the peripheral
|
||||
*/
|
||||
void _usb_wrap_hal_disable(void);
|
||||
|
||||
#if SOC_RCC_IS_INDEPENDENT
|
||||
#define usb_wrap_hal_disable(...) _usb_wrap_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_wrap_hal_disable(...) do {(void)__DECLARE_RCC_ATOMIC_ENV; _usb_wrap_hal_disable(__VA_ARGS__);} while(0)
|
||||
#endif
|
||||
|
||||
/* ---------------------------- USB PHY Control ---------------------------- */
|
||||
|
||||
#if USB_WRAP_LL_EXT_PHY_SUPPORTED
|
||||
/**
|
||||
* @brief Configure whether USB WRAP is routed to internal/external FSLS PHY
|
||||
*
|
||||
* @param hal USB WRAP HAL context
|
||||
* @param external True if external, False if internal
|
||||
*/
|
||||
void usb_wrap_hal_phy_set_external(usb_wrap_hal_context_t *hal, bool external);
|
||||
#endif // USB_WRAP_LL_EXT_PHY_SUPPORTED
|
||||
|
||||
/**
|
||||
* @brief Enables and sets override of pull up/down resistors
|
||||
*
|
||||
* @param hal USB WRAP HAL context
|
||||
* @param vals Override values
|
||||
*/
|
||||
static inline void usb_wrap_hal_phy_enable_pull_override(usb_wrap_hal_context_t *hal, const usb_wrap_pull_override_vals_t *vals)
|
||||
{
|
||||
usb_wrap_ll_phy_enable_pull_override(hal->dev, vals);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disables pull up/down resistor override
|
||||
*
|
||||
* @param hal USB WRAP HAL context
|
||||
*/
|
||||
static inline void usb_wrap_hal_phy_disable_pull_override(usb_wrap_hal_context_t *hal)
|
||||
{
|
||||
usb_wrap_ll_phy_disable_pull_override(hal->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables/disables the USB FSLS PHY's test mode
|
||||
*
|
||||
* @param hal USB WRAP HAL context
|
||||
* @param enable Whether to enable test mode
|
||||
*/
|
||||
static inline void usb_wrap_hal_phy_enable_test_mode(usb_wrap_hal_context_t *hal, bool enable)
|
||||
{
|
||||
usb_wrap_ll_phy_enable_test_mode(hal->dev, enable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the USB FSLS PHY's signal test values
|
||||
*
|
||||
* @param hal USB WRAP HAL context
|
||||
* @param vals Test values
|
||||
*/
|
||||
static inline void usb_wrap_hal_phy_test_mode_set_signals(usb_wrap_hal_context_t *hal, const usb_wrap_test_mode_vals_t *vals)
|
||||
{
|
||||
usb_wrap_ll_phy_test_mode_set_signals(hal->dev, vals);
|
||||
}
|
||||
|
||||
#endif // (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
/**
|
||||
* @brief USB WRAP pull up/down resistor override values
|
||||
*
|
||||
* Specifies whether each pull up/down resistor should be enabled/disabled when
|
||||
* overriding connected USB PHY's pull resistors.
|
||||
*/
|
||||
typedef struct {
|
||||
bool dp_pu; /**< D+ pull-up resistor enable/disable */
|
||||
bool dm_pu; /**< D- pull-up resistor enable/disable */
|
||||
bool dp_pd; /**< D+ pull-down resistor enable/disable */
|
||||
bool dm_pd; /**< D- pull-down resistor enable/disable */
|
||||
} usb_wrap_pull_override_vals_t;
|
||||
|
||||
/**
|
||||
* @brief USB WRAP test mode values
|
||||
*
|
||||
* Specifies the logic values of each of the USB FSLS Serial PHY interface
|
||||
* signals when in test mode.
|
||||
*
|
||||
* @note See section "2.2.1.13 FsLsSerialMode" of UTMI+ specification for more
|
||||
* details of each signal.
|
||||
*/
|
||||
typedef struct {
|
||||
bool tx_enable_n; /**< Active low output enable signal */
|
||||
bool tx_dp; /**< Single-ended D+ line driver */
|
||||
bool tx_dm; /**< Single-ended D- line driver */
|
||||
bool rx_dp; /**< Single-ended D+ signal from the transceiver */
|
||||
bool rx_dm; /**< Single-ended D- signal from the transceiver */
|
||||
bool rx_rcv; /**< Differential receive data from D+ and D- lines */
|
||||
} usb_wrap_test_mode_vals_t;
|
||||
|
||||
#endif // (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,544 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h> // For memset()
|
||||
#include <stdlib.h> // For abort()
|
||||
#include "soc/soc_caps_full.h"
|
||||
#include "soc/chip_revision.h"
|
||||
#include "soc/usb_periph.h"
|
||||
#include "hal/usb_dwc_hal.h"
|
||||
#include "hal/usb_dwc_ll.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
#include "hal/assert.h"
|
||||
|
||||
// ------------------------------------------------ Macros and Types ---------------------------------------------------
|
||||
|
||||
// ---------------------- Constants ------------------------
|
||||
|
||||
#define BENDPOINTADDRESS_NUM_MSK 0x0F //Endpoint number mask of the bEndpointAddress field of an endpoint descriptor
|
||||
#define BENDPOINTADDRESS_DIR_MSK 0x80 //Endpoint direction mask of the bEndpointAddress field of an endpoint descriptor
|
||||
|
||||
// Core register IDs supported by this driver: v4.00a and v4.30a
|
||||
#define CORE_REG_GSNPSID_4_00a 0x4F54400A
|
||||
#define CORE_REG_GSNPSID_4_30a 0x4F54430A
|
||||
|
||||
// -------------------- Configurable -----------------------
|
||||
|
||||
/**
|
||||
* The following core interrupts will be enabled (listed LSB to MSB). Some of these
|
||||
* interrupts are enabled later than others.
|
||||
* - USB_DWC_LL_INTR_CORE_PRTINT
|
||||
* - USB_DWC_LL_INTR_CORE_HCHINT
|
||||
* - USB_DWC_LL_INTR_CORE_DISCONNINT
|
||||
* The following PORT interrupts cannot be masked, listed LSB to MSB
|
||||
* - USB_DWC_LL_INTR_HPRT_PRTCONNDET
|
||||
* - USB_DWC_LL_INTR_HPRT_PRTENCHNG
|
||||
* - USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG
|
||||
*/
|
||||
#define CORE_INTRS_EN_MSK (USB_DWC_LL_INTR_CORE_DISCONNINT)
|
||||
|
||||
//Interrupts that pertain to core events
|
||||
#define CORE_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_CORE_DISCONNINT | \
|
||||
USB_DWC_LL_INTR_CORE_HCHINT)
|
||||
|
||||
//Interrupt that pertain to host port events
|
||||
#define PORT_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_HPRT_PRTCONNDET | \
|
||||
USB_DWC_LL_INTR_HPRT_PRTENCHNG | \
|
||||
USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG)
|
||||
|
||||
/**
|
||||
* The following channel interrupt bits are currently checked (in order LSB to MSB)
|
||||
* - USB_DWC_LL_INTR_CHAN_XFERCOMPL
|
||||
* - USB_DWC_LL_INTR_CHAN_CHHLTD
|
||||
* - USB_DWC_LL_INTR_CHAN_STALL
|
||||
* - USB_DWC_LL_INTR_CHAN_BBLEER
|
||||
* - USB_DWC_LL_INTR_CHAN_BNAINTR
|
||||
* - USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR
|
||||
*
|
||||
* Note the following points about channel interrupts:
|
||||
* - Not all bits are unmaskable under scatter/gather
|
||||
* - Those bits proxy their interrupt through the USB_DWC_LL_INTR_CHAN_CHHLTD bit
|
||||
* - USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR is always unmasked
|
||||
* - When USB_DWC_LL_INTR_CHAN_BNAINTR occurs, USB_DWC_LL_INTR_CHAN_CHHLTD will NOT.
|
||||
* - USB_DWC_LL_INTR_CHAN_AHBERR doesn't actually ever happen on our system (i.e., ESP32-S2, ESP32-S3):
|
||||
* - If the QTD list's starting address is an invalid address (e.g., NULL), the core will attempt to fetch that
|
||||
* address for a transfer descriptor and probably gets all zeroes. It will interpret the zero as a bad QTD and
|
||||
* return a USB_DWC_LL_INTR_CHAN_BNAINTR instead.
|
||||
* - If the QTD's buffer pointer is an invalid address, the core will attempt to read/write data to/from that
|
||||
* invalid buffer address with NO INDICATION OF ERROR. The transfer will be acknowledged and treated as
|
||||
* successful. Bad buffer pointers MUST BE CHECKED FROM HIGHER LAYERS INSTEAD.
|
||||
*/
|
||||
#define CHAN_INTRS_EN_MSK (USB_DWC_LL_INTR_CHAN_XFERCOMPL | \
|
||||
USB_DWC_LL_INTR_CHAN_CHHLTD | \
|
||||
USB_DWC_LL_INTR_CHAN_BNAINTR)
|
||||
|
||||
#define CHAN_INTRS_ERROR_MSK (USB_DWC_LL_INTR_CHAN_STALL | \
|
||||
USB_DWC_LL_INTR_CHAN_BBLEER | \
|
||||
USB_DWC_LL_INTR_CHAN_BNAINTR | \
|
||||
USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR)
|
||||
|
||||
// -------------------------------------------------- Core (Global) ----------------------------------------------------
|
||||
|
||||
static void set_defaults(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
//GAHBCFG register
|
||||
usb_dwc_ll_gahbcfg_en_dma_mode(hal->dev);
|
||||
int hbstlen = 0; //Use AHB burst SINGLE by default
|
||||
#if SOC_IS(ESP32S2)
|
||||
/*
|
||||
Hardware errata workaround for the ESP32-S2 ECO0 (see ESP32-S2 Errata Document section 4.0 for full details).
|
||||
|
||||
ESP32-S2 ECO0 has a hardware errata where the AHB bus arbiter may generate incorrect arbitration signals leading to
|
||||
the DWC_OTG corrupting the DMA transfers of other peripherals (or vice versa) on the same bus. The peripherals that
|
||||
share the same bus with DWC_OTG include I2C and SPI (see ESP32-S2 Errata Document for more details). To workaround
|
||||
this, the DWC_OTG's AHB should use INCR mode to prevent change of arbitration during a burst operation, thus
|
||||
avoiding this errata.
|
||||
|
||||
Note: Setting AHB burst to INCR increases the likeliness of DMA underruns on other peripherals sharing the same bus
|
||||
arbiter as the DWC_OTG (e.g., I2C and SPI) as change of arbitration during the burst operation is not permitted.
|
||||
Users should keep this limitation in mind when the DWC_OTG transfers large data payloads (e.g., 512 MPS transfers)
|
||||
while this workaround is enabled.
|
||||
*/
|
||||
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 100)) {
|
||||
hbstlen = 1; //Set AHB burst to INCR to workaround hardware errata
|
||||
}
|
||||
#endif // SOC_IS(ESP32S2)
|
||||
usb_dwc_ll_gahbcfg_set_hbstlen(hal->dev, hbstlen); //Set AHB burst mode
|
||||
//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 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
|
||||
usb_dwc_ll_gintsts_read_and_clear_intrs(hal->dev); //Clear interrupts
|
||||
usb_dwc_ll_gahbcfg_en_global_intr(hal->dev); //Enable interrupt signal
|
||||
//Enable host mode
|
||||
usb_dwc_ll_gusbcfg_force_host_mode(hal->dev);
|
||||
}
|
||||
|
||||
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
|
||||
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_4_00a || core_id == CORE_REG_GSNPSID_4_30a);
|
||||
(void) core_id; //Suppress unused variable warning if asserts are disabled
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
//Disable and clear global interrupt
|
||||
usb_dwc_ll_gintmsk_dis_intrs(hal->dev, 0xFFFFFFFF); //Disable all interrupts
|
||||
usb_dwc_ll_gintsts_read_and_clear_intrs(hal->dev); //Clear interrupts
|
||||
usb_dwc_ll_gahbcfg_dis_global_intr(hal->dev); //Disable interrupt signal
|
||||
hal->dev = NULL;
|
||||
}
|
||||
|
||||
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_ahb_idle(hal->dev)) {
|
||||
; // Wait until AHB Master bus is idle before doing any other operations
|
||||
}
|
||||
|
||||
// Set the default bits in USB-DWC registers
|
||||
set_defaults(hal);
|
||||
|
||||
// Clear all the flags and channels
|
||||
hal->periodic_frame_list = NULL;
|
||||
hal->flags.val = 0;
|
||||
hal->channels.num_allocated = 0;
|
||||
hal->channels.chan_pend_intrs_msk = 0;
|
||||
if (hal->channels.hdls) {
|
||||
for (int i = 0; i < hal->constant_config.chan_num_total; i++) {
|
||||
hal->channels.hdls[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool usb_dwc_hal_fifo_config_is_valid(const usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config)
|
||||
{
|
||||
if (!hal || !config) {
|
||||
return false;
|
||||
}
|
||||
uint32_t used_lines = config->rx_fifo_lines + config->nptx_fifo_lines + config->ptx_fifo_lines;
|
||||
return (used_lines <= hal->constant_config.fifo_size);
|
||||
}
|
||||
|
||||
|
||||
void usb_dwc_hal_set_fifo_config(usb_dwc_hal_context_t *hal, const usb_dwc_hal_fifo_config_t *config)
|
||||
{
|
||||
// Check internal HAL state
|
||||
HAL_ASSERT(hal != NULL);
|
||||
HAL_ASSERT(hal->channels.hdls != NULL);
|
||||
|
||||
// Validate provided config
|
||||
HAL_ASSERT(config != NULL);
|
||||
|
||||
// Check if configuration exceeds available FIFO memory
|
||||
HAL_ASSERT(usb_dwc_hal_fifo_config_is_valid(hal, config));
|
||||
|
||||
// Ensure no active channels (must only be called before USB install completes)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Program FIFO size registers
|
||||
usb_dwc_ll_grxfsiz_set_fifo_size(hal->dev, config->rx_fifo_lines);// Set RX FIFO size (GRXFSIZ)
|
||||
// Set Non-Periodic TX FIFO (GNPTXFSIZ)
|
||||
// Offset = RX FIFO lines
|
||||
usb_dwc_ll_gnptxfsiz_set_fifo_size(hal->dev, config->rx_fifo_lines, config->nptx_fifo_lines);
|
||||
// Set Periodic TX FIFO (HPTXFSIZ)
|
||||
// Offset = RX + NPTX
|
||||
usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(hal->dev,
|
||||
config->rx_fifo_lines + config->nptx_fifo_lines,
|
||||
config->ptx_fifo_lines);
|
||||
|
||||
// Flush all FIFOs
|
||||
usb_dwc_ll_grstctl_flush_nptx_fifo(hal->dev);
|
||||
usb_dwc_ll_grstctl_flush_ptx_fifo(hal->dev);
|
||||
usb_dwc_ll_grstctl_flush_rx_fifo(hal->dev);
|
||||
|
||||
// Save configuration to HAL context
|
||||
hal->fifo_config = *config;
|
||||
hal->flags.fifo_sizes_set = 1;
|
||||
}
|
||||
|
||||
void usb_dwc_hal_get_mps_limits(usb_dwc_hal_context_t *hal, usb_hal_fifo_mps_limits_t *mps_limits)
|
||||
{
|
||||
HAL_ASSERT(hal && mps_limits);
|
||||
HAL_ASSERT(hal->flags.fifo_sizes_set);
|
||||
|
||||
const usb_dwc_hal_fifo_config_t *fifo_config = &(hal->fifo_config);
|
||||
mps_limits->in_mps = (fifo_config->rx_fifo_lines - 2) * 4; // Two lines are reserved for status quadlets internally by USB_DWC
|
||||
mps_limits->non_periodic_out_mps = fifo_config->nptx_fifo_lines * 4;
|
||||
mps_limits->periodic_out_mps = fifo_config->ptx_fifo_lines * 4;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------- Host Port ------------------------------------------------------
|
||||
|
||||
static inline void debounce_lock_enable(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
//Disable the hprt (connection) and disconnection interrupts to prevent repeated triggerings
|
||||
usb_dwc_ll_gintmsk_dis_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT);
|
||||
hal->flags.dbnc_lock_enabled = 1;
|
||||
}
|
||||
|
||||
void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
// 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 -------------------------------------------------------
|
||||
|
||||
// ----------------- Channel Allocation --------------------
|
||||
|
||||
bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx)
|
||||
{
|
||||
HAL_ASSERT(hal->channels.hdls);
|
||||
HAL_ASSERT(hal->flags.fifo_sizes_set); //FIFO sizes should be set before attempting to allocate a channel
|
||||
//Attempt to allocate channel
|
||||
if (hal->channels.num_allocated == hal->constant_config.chan_num_total) {
|
||||
return false; //Out of free channels
|
||||
}
|
||||
int chan_idx = -1;
|
||||
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_allocated++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
HAL_ASSERT(chan_idx != -1);
|
||||
//Initialize channel object
|
||||
memset(chan_obj, 0, sizeof(usb_dwc_hal_chan_t));
|
||||
chan_obj->flags.chan_idx = chan_idx;
|
||||
chan_obj->regs = usb_dwc_ll_chan_get_regs(hal->dev, chan_idx);
|
||||
chan_obj->chan_ctx = chan_ctx;
|
||||
//Note: EP characteristics configured separately
|
||||
//Clean and unmask the channel's interrupt
|
||||
usb_dwc_ll_hcint_read_and_clear_intrs(chan_obj->regs); //Clear the interrupt bits for that channel
|
||||
usb_dwc_ll_haintmsk_en_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx);
|
||||
usb_dwc_ll_hcintmsk_set_intr_mask(chan_obj->regs, CHAN_INTRS_EN_MSK); //Unmask interrupts for this channel
|
||||
usb_dwc_ll_hctsiz_init(chan_obj->regs);
|
||||
return true;
|
||||
}
|
||||
|
||||
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++) {
|
||||
hal->periodic_frame_list[i] &= ~(1 << chan_obj->flags.chan_idx);
|
||||
}
|
||||
}
|
||||
//Can only free a channel when in the disabled state and descriptor list released
|
||||
HAL_ASSERT(!chan_obj->flags.active);
|
||||
//Disable channel's interrupt
|
||||
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_allocated--;
|
||||
HAL_ASSERT(hal->channels.num_allocated >= 0);
|
||||
}
|
||||
|
||||
// ---------------- Channel Configuration ------------------
|
||||
|
||||
void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, usb_dwc_hal_ep_char_t *ep_char)
|
||||
{
|
||||
//Cannot change ep_char whilst channel is still active or in error
|
||||
HAL_ASSERT(!chan_obj->flags.active);
|
||||
//Set the endpoint characteristics of the pipe
|
||||
usb_dwc_ll_hcchar_init(chan_obj->regs,
|
||||
ep_char->dev_addr,
|
||||
ep_char->bEndpointAddress & BENDPOINTADDRESS_NUM_MSK,
|
||||
ep_char->mps,
|
||||
ep_char->type,
|
||||
ep_char->bEndpointAddress & BENDPOINTADDRESS_DIR_MSK,
|
||||
ep_char->ls_via_fs_hub);
|
||||
//Save channel type
|
||||
chan_obj->type = ep_char->type;
|
||||
//If this is a periodic endpoint/channel, set its schedule in the frame list
|
||||
if (ep_char->type == USB_DWC_XFER_TYPE_ISOCHRONOUS || ep_char->type == USB_DWC_XFER_TYPE_INTR) {
|
||||
unsigned int interval_frame_list = ep_char->periodic.interval;
|
||||
unsigned int offset_frame_list = ep_char->periodic.offset;
|
||||
// Periodic Frame List works with USB frames. For HS endpoints we must divide interval[microframes] by 8 to get interval[frames]
|
||||
if (ep_char->periodic.is_hs) {
|
||||
interval_frame_list /= 8;
|
||||
offset_frame_list /= 8;
|
||||
}
|
||||
// Interval in Periodic Frame List must be power of 2.
|
||||
// This is not a HW restriction. It is just a lot easier to schedule channels like this.
|
||||
if (interval_frame_list >= (int)hal->frame_list_len) { // Upper limits is Periodic Frame List length
|
||||
interval_frame_list = (int)hal->frame_list_len;
|
||||
} else if (interval_frame_list >= 32) {
|
||||
interval_frame_list = 32;
|
||||
} else if (interval_frame_list >= 16) {
|
||||
interval_frame_list = 16;
|
||||
} else if (interval_frame_list >= 8) {
|
||||
interval_frame_list = 8;
|
||||
} else if (interval_frame_list >= 4) {
|
||||
interval_frame_list = 4;
|
||||
} else if (interval_frame_list >= 2) {
|
||||
interval_frame_list = 2;
|
||||
} else { // Lower limit is 1
|
||||
interval_frame_list = 1;
|
||||
}
|
||||
// Schedule the channel in the frame list
|
||||
for (int i = 0; i < hal->frame_list_len; i+= interval_frame_list) {
|
||||
int index = (offset_frame_list + i) % hal->frame_list_len;
|
||||
hal->periodic_frame_list[index] |= 1 << chan_obj->flags.chan_idx;
|
||||
}
|
||||
// For HS endpoints we must write to sched_info field of HCTSIZ register to schedule microframes
|
||||
// For FS endpoints sched_info is always 0xFF
|
||||
// LS endpoints do not support periodic transfers
|
||||
unsigned int tokens_per_frame = 0;
|
||||
if (ep_char->periodic.is_hs) {
|
||||
if (ep_char->periodic.interval >= 8) {
|
||||
tokens_per_frame = 1; // 1 token every 8 microframes
|
||||
} else if (ep_char->periodic.interval >= 4) {
|
||||
tokens_per_frame = 2; // 1 token every 4 microframes
|
||||
} else if (ep_char->periodic.interval >= 2) {
|
||||
tokens_per_frame = 4; // 1 token every 2 microframes
|
||||
} else {
|
||||
tokens_per_frame = 8; // 1 token every microframe
|
||||
}
|
||||
} else {
|
||||
tokens_per_frame = 8;
|
||||
}
|
||||
usb_dwc_ll_hctsiz_set_sched_info(chan_obj->regs, tokens_per_frame, ep_char->periodic.offset);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- Channel Control ---------------------
|
||||
|
||||
void usb_dwc_hal_chan_activate(usb_dwc_hal_chan_t *chan_obj, void *xfer_desc_list, int desc_list_len, int start_idx)
|
||||
{
|
||||
// Cannot activate a channel that has already been enabled or is pending error handling
|
||||
HAL_ASSERT(!chan_obj->flags.active);
|
||||
// Make sure that PING is not enabled from previous transaction
|
||||
usb_dwc_ll_hctsiz_set_dopng(chan_obj->regs, false);
|
||||
// Set start address of the QTD list and starting QTD index
|
||||
usb_dwc_ll_hcdma_set_qtd_list_addr(chan_obj->regs, xfer_desc_list, start_idx);
|
||||
usb_dwc_ll_hctsiz_set_qtd_list_len(chan_obj->regs, desc_list_len);
|
||||
usb_dwc_ll_hcchar_enable_chan(chan_obj->regs); // Start the channel
|
||||
chan_obj->flags.active = 1;
|
||||
}
|
||||
|
||||
bool usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
if (chan_obj->flags.active) {
|
||||
/*
|
||||
Request a halt so long as the channel's active flag is set.
|
||||
- If the underlying hardware channel is already halted but the channel is pending interrupt handling,
|
||||
disabling the channel will have no effect (i.e., no channel interrupt is generated).
|
||||
- If the underlying channel is currently active, disabling the channel will trigger a channel interrupt.
|
||||
|
||||
Regardless, setting the "halt_requested" should cause "usb_dwc_hal_chan_decode_intr()" to report the
|
||||
USB_DWC_HAL_CHAN_EVENT_HALT_REQ event when channel interrupt is handled (pending or triggered).
|
||||
*/
|
||||
usb_dwc_ll_hcchar_disable_chan(chan_obj->regs);
|
||||
chan_obj->flags.halt_requested = 1;
|
||||
return false;
|
||||
} else {
|
||||
//Channel was never active to begin with, simply return true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------- Event Handling ----------------------------------------------------
|
||||
|
||||
usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
uint32_t intrs_core = usb_dwc_ll_gintsts_read_and_clear_intrs(hal->dev); //Read and clear core interrupts
|
||||
uint32_t intrs_port = 0;
|
||||
if (intrs_core & USB_DWC_LL_INTR_CORE_PRTINT) {
|
||||
//There are host port interrupts. Read and clear those as well.
|
||||
intrs_port = usb_dwc_ll_hprt_intr_read_and_clear(hal->dev);
|
||||
}
|
||||
//Note: Do not change order of checks. Regressing events (e.g. enable -> disabled, connected -> connected)
|
||||
//always take precedence. ENABLED < DISABLED < CONN < DISCONN < OVRCUR
|
||||
usb_dwc_hal_port_event_t event = USB_DWC_HAL_PORT_EVENT_NONE;
|
||||
|
||||
//Check if this is a core or port event
|
||||
if ((intrs_core & CORE_EVENTS_INTRS_MSK) || (intrs_port & PORT_EVENTS_INTRS_MSK)) {
|
||||
//Do not change the order of the following checks. Some events/interrupts take precedence over others
|
||||
if (intrs_core & USB_DWC_LL_INTR_CORE_DISCONNINT) {
|
||||
event = USB_DWC_HAL_PORT_EVENT_DISCONN;
|
||||
debounce_lock_enable(hal);
|
||||
//Mask the port connection and disconnection interrupts to prevent repeated triggering
|
||||
} else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG) {
|
||||
//Check if this is an overcurrent or an overcurrent cleared
|
||||
if (usb_dwc_ll_hprt_get_port_overcur(hal->dev)) {
|
||||
event = USB_DWC_HAL_PORT_EVENT_OVRCUR;
|
||||
} else {
|
||||
event = USB_DWC_HAL_PORT_EVENT_OVRCUR_CLR;
|
||||
}
|
||||
} else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTENCHNG) {
|
||||
if (usb_dwc_ll_hprt_get_port_en(hal->dev)) { //Host port was enabled
|
||||
event = USB_DWC_HAL_PORT_EVENT_ENABLED;
|
||||
} else { //Host port has been disabled
|
||||
event = USB_DWC_HAL_PORT_EVENT_DISABLED;
|
||||
}
|
||||
} else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTCONNDET && !hal->flags.dbnc_lock_enabled) {
|
||||
event = USB_DWC_HAL_PORT_EVENT_CONN;
|
||||
debounce_lock_enable(hal);
|
||||
}
|
||||
}
|
||||
//Port events always take precedence over channel events
|
||||
if (event == USB_DWC_HAL_PORT_EVENT_NONE && (intrs_core & USB_DWC_LL_INTR_CORE_HCHINT)) {
|
||||
//One or more channels have pending interrupts. Store the mask of those channels
|
||||
hal->channels.chan_pend_intrs_msk = usb_dwc_ll_haint_get_chan_intrs(hal->dev);
|
||||
event = USB_DWC_HAL_PORT_EVENT_CHAN;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
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
|
||||
return hal->channels.hdls[chan_num - 1];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
usb_dwc_hal_chan_event_t usb_dwc_hal_chan_decode_intr(usb_dwc_hal_chan_t *chan_obj)
|
||||
{
|
||||
uint32_t chan_intrs = usb_dwc_ll_hcint_read_and_clear_intrs(chan_obj->regs);
|
||||
usb_dwc_hal_chan_event_t chan_event;
|
||||
//Note: We don't assert on (chan_obj->flags.active) here as it could have been already cleared by usb_dwc_hal_chan_request_halt()
|
||||
|
||||
/*
|
||||
Note: Do not change order of checks as some events take precedence over others.
|
||||
Errors > Channel Halt Request > Transfer completed
|
||||
*/
|
||||
if (chan_intrs & CHAN_INTRS_ERROR_MSK) { //Note: Errors are uncommon, so we check against the entire interrupt mask to reduce frequency of entering this call path
|
||||
HAL_ASSERT(chan_intrs & USB_DWC_LL_INTR_CHAN_CHHLTD); //An error should have halted the channel
|
||||
//Store the error in hal context
|
||||
usb_dwc_hal_chan_error_t error;
|
||||
if (chan_intrs & USB_DWC_LL_INTR_CHAN_STALL) {
|
||||
error = USB_DWC_HAL_CHAN_ERROR_STALL;
|
||||
} else if (chan_intrs & USB_DWC_LL_INTR_CHAN_BBLEER) {
|
||||
error = USB_DWC_HAL_CHAN_ERROR_PKT_BBL;
|
||||
} else if (chan_intrs & USB_DWC_LL_INTR_CHAN_BNAINTR) {
|
||||
error = USB_DWC_HAL_CHAN_ERROR_BNA;
|
||||
} else { //USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR
|
||||
error = USB_DWC_HAL_CHAN_ERROR_XCS_XACT;
|
||||
}
|
||||
//Update flags
|
||||
chan_obj->error = error;
|
||||
chan_obj->flags.active = 0;
|
||||
//Save the error to be handled later
|
||||
chan_event = USB_DWC_HAL_CHAN_EVENT_ERROR;
|
||||
} else if (chan_intrs & USB_DWC_LL_INTR_CHAN_CHHLTD) {
|
||||
if (chan_obj->flags.halt_requested) {
|
||||
chan_obj->flags.halt_requested = 0;
|
||||
chan_event = USB_DWC_HAL_CHAN_EVENT_HALT_REQ;
|
||||
} else {
|
||||
//Must have been halted due to QTD HOC
|
||||
chan_event = USB_DWC_HAL_CHAN_EVENT_CPLT;
|
||||
}
|
||||
chan_obj->flags.active = 0;
|
||||
} else if (chan_intrs & USB_DWC_LL_INTR_CHAN_XFERCOMPL) {
|
||||
/*
|
||||
A transfer complete interrupt WITHOUT the channel halting only occurs when receiving a short interrupt IN packet
|
||||
and the underlying QTD does not have the HOC bit set. This signifies the last packet of the Interrupt transfer
|
||||
as all interrupt packets must MPS sized except the last.
|
||||
*/
|
||||
//The channel isn't halted yet, so we need to halt it manually to stop the execution of the next QTD/packet
|
||||
usb_dwc_ll_hcchar_disable_chan(chan_obj->regs);
|
||||
/*
|
||||
After setting the halt bit, this will generate another channel halted interrupt. We treat this interrupt as
|
||||
a NONE event, then cycle back with the channel halted interrupt to handle the CPLT event.
|
||||
*/
|
||||
chan_event = USB_DWC_HAL_CHAN_EVENT_NONE;
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
return chan_event;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/usb_wrap_ll.h"
|
||||
#include "hal/usb_wrap_hal.h"
|
||||
|
||||
void _usb_wrap_hal_init(usb_wrap_hal_context_t *hal)
|
||||
{
|
||||
hal->dev = &USB_WRAP;
|
||||
_usb_wrap_ll_enable_bus_clock(true);
|
||||
_usb_wrap_ll_reset_register();
|
||||
#if !USB_WRAP_LL_EXT_PHY_SUPPORTED
|
||||
usb_wrap_ll_phy_set_defaults(hal->dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void _usb_wrap_hal_disable(void)
|
||||
{
|
||||
_usb_wrap_ll_enable_bus_clock(false);
|
||||
}
|
||||
|
||||
#if USB_WRAP_LL_EXT_PHY_SUPPORTED
|
||||
void usb_wrap_hal_phy_set_external(usb_wrap_hal_context_t *hal, bool external)
|
||||
{
|
||||
if (external) {
|
||||
usb_wrap_ll_phy_enable_external(hal->dev, true);
|
||||
} else {
|
||||
usb_wrap_ll_phy_enable_external(hal->dev, false);
|
||||
usb_wrap_ll_phy_enable_pad(hal->dev, true);
|
||||
}
|
||||
}
|
||||
#endif // USB_WRAP_LL_EXT_PHY_SUPPORTED
|
||||
Reference in New Issue
Block a user