mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
feat(hal/usb): Add USB UTMI PHY HAL
* Add a bare-bones HAL API for the USB UTMI PHY * Split USB-DWC LL per target
This commit is contained in:
parent
b02ad37a55
commit
c1f5d3067f
@ -271,6 +271,10 @@ if(NOT BOOTLOADER_BUILD)
|
||||
list(APPEND srcs "usb_serial_jtag_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_USB_UTMI_PHY_NUM GREATER 0)
|
||||
list(APPEND srcs "usb_utmi_hal.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_USB_OTG_SUPPORTED)
|
||||
list(APPEND srcs
|
||||
"usb_dwc_hal.c"
|
||||
|
@ -6,25 +6,22 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
/*
|
||||
This header is shared across all targets. Resolve to an empty header for targets
|
||||
that don't support USB OTG.
|
||||
*/
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/usb_dwc_struct.h"
|
||||
#include "soc/usb_dwc_cfg.h"
|
||||
#include "hal/usb_dwc_types.h"
|
||||
#include "hal/misc.h"
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
/* ----------------------------- Helper Macros ------------------------------ */
|
||||
|
||||
// Get USB hardware instance
|
||||
// TODO: extend this macros when we have support for both FS and HS hardware on P4
|
||||
#define USB_DWC_LL_GET_HW(num) (&USB_DWC_HS)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
--------------------------------- DWC Constants --------------------------------
|
||||
@ -222,14 +219,14 @@ static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t
|
||||
hw->gusbcfg_reg.toutcal = tout_cal;
|
||||
}
|
||||
|
||||
#if (OTG_HSPHY_INTERFACE != 0)
|
||||
static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw)
|
||||
{
|
||||
#if (OTG_HSPHY_INTERFACE != 0)
|
||||
hw->gusbcfg_reg.phyif = 1; // 16 bits interface
|
||||
hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+
|
||||
hw->gusbcfg_reg.physel = 0; // HS PHY
|
||||
}
|
||||
#endif // (OTG_HSPHY_INTERFACE != 0)
|
||||
}
|
||||
|
||||
// --------------------------- GRSTCTL Register --------------------------------
|
||||
|
||||
@ -356,9 +353,9 @@ static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw)
|
||||
// --------------------------- GHWCFGx Register --------------------------------
|
||||
|
||||
/**
|
||||
* @brief Get the hardware configuration regiters of the DWC_OTG controller
|
||||
* @brief Get the hardware configuration registers of the DWC_OTG controller
|
||||
*
|
||||
* The hardware configuraiton regitsers are read only and indicate the various
|
||||
* The hardware configuration regitsers are read only and indicate the various
|
||||
* features of the DWC_OTG core.
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
@ -405,7 +402,7 @@ static inline void usb_dwc_ll_hcfg_dis_perio_sched(usb_dwc_dev_t *hw)
|
||||
/**
|
||||
* Sets the length of the frame list
|
||||
*
|
||||
* @param num_entires Number of entires in the frame list
|
||||
* @param num_entires Number of entries in the frame list
|
||||
*/
|
||||
static inline void usb_dwc_ll_hcfg_set_num_frame_list_entries(usb_dwc_dev_t *hw, usb_hal_frame_list_len_t num_entries)
|
||||
{
|
||||
@ -903,7 +900,7 @@ static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t *
|
||||
// ------------------------------ QTD related ----------------------------------
|
||||
|
||||
#define USB_DWC_LL_QTD_STATUS_SUCCESS 0x0 //If QTD was processed, it indicates the data was transmitted/received successfully
|
||||
#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data trasnmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK).
|
||||
#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data transmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK).
|
||||
//Note: 0x2 is reserved
|
||||
#define USB_DWC_LL_QTD_STATUS_BUFFER 0x3 //AHB error occurred.
|
||||
#define USB_DWC_LL_QTD_STATUS_NOT_EXECUTED 0x4 //QTD as never processed
|
||||
@ -914,7 +911,7 @@ static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t *
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param data_buff Pointer to buffer containing the data to transfer
|
||||
* @param xfer_len Number of bytes in transfer. Setting 0 will do a zero length IN transfer.
|
||||
* Non zero length must be mulitple of the endpoint's MPS.
|
||||
* Non zero length must be multiple of the endpoint's MPS.
|
||||
* @param hoc Halt on complete (will generate an interrupt and halt the channel)
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc)
|
||||
@ -932,7 +929,7 @@ static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *dat
|
||||
/**
|
||||
* @brief Set a QTD for a non isochronous OUT transfer
|
||||
*
|
||||
* @param qtd Poitner to the QTD
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param data_buff Pointer to buffer containing the data to transfer
|
||||
* @param xfer_len Number of bytes to transfer. Setting 0 will do a zero length transfer.
|
||||
* For ctrl setup packets, this should be set to 8.
|
||||
@ -972,9 +969,9 @@ static inline void usb_dwc_ll_qtd_set_null(usb_dwc_ll_dma_qtd_t *qtd)
|
||||
/**
|
||||
* @brief Get the status of a QTD
|
||||
*
|
||||
* When a channel get's halted, call this to check whether each QTD was executed successfully
|
||||
* When a channel gets halted, call this to check whether each QTD was executed successfully
|
||||
*
|
||||
* @param qtd Poitner to the QTD
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param[out] rem_len Number of bytes ramining in the QTD
|
||||
* @param[out] status Status of the QTD
|
||||
*/
|
||||
@ -992,8 +989,6 @@ static inline void usb_dwc_ll_qtd_get_status(usb_dwc_ll_dma_qtd_t *qtd, int *rem
|
||||
qtd->buffer_status_val = 0;
|
||||
}
|
||||
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -53,7 +53,7 @@ FORCE_INLINE_ATTR void _usb_utmi_ll_enable_bus_clock(bool clk_en)
|
||||
/**
|
||||
* @brief Reset the USB UTMI PHY and USB_DWC_HS controller
|
||||
*/
|
||||
FORCE_INLINE_ATTR void usb_utmi_ll_reset_register(void)
|
||||
FORCE_INLINE_ATTR void _usb_utmi_ll_reset_register(void)
|
||||
{
|
||||
// Reset the USB_UTMI and USB_DWC_HS
|
||||
LP_AON_CLKRST.hp_usb_clkrst_ctrl1.rst_en_usb_otg20 = 1;
|
||||
@ -63,7 +63,7 @@ FORCE_INLINE_ATTR void usb_utmi_ll_reset_register(void)
|
||||
}
|
||||
|
||||
// P_AON_CLKRST.hp_usb_clkrst_ctrlx is shared register, so this function must be used in an atomic way
|
||||
#define usb_utmi_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; usb_utmi_ll_reset_register(__VA_ARGS__)
|
||||
#define usb_utmi_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_ll_reset_register(__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* @brief Enable precise detection of VBUS
|
||||
|
993
components/hal/esp32s2/include/hal/usb_dwc_ll.h
Normal file
993
components/hal/esp32s2/include/hal/usb_dwc_ll.h
Normal file
@ -0,0 +1,993 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/usb_dwc_struct.h"
|
||||
#include "soc/usb_dwc_cfg.h"
|
||||
#include "hal/usb_dwc_types.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ----------------------------- Helper Macros ------------------------------ */
|
||||
|
||||
// Get USB hardware instance
|
||||
#define USB_DWC_LL_GET_HW(num) (&USB_DWC)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
--------------------------------- DWC Constants --------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
#define USB_DWC_QTD_LIST_MEM_ALIGN 512
|
||||
#define USB_DWC_FRAME_LIST_MEM_ALIGN 512 // The frame list needs to be 512 bytes aligned (contrary to the databook)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
------------------------------- Global Registers -------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Interrupt bit masks of the GINTSTS and GINTMSK registers
|
||||
*/
|
||||
#define USB_DWC_LL_INTR_CORE_WKUPINT (1 << 31)
|
||||
#define USB_DWC_LL_INTR_CORE_SESSREQINT (1 << 30)
|
||||
#define USB_DWC_LL_INTR_CORE_DISCONNINT (1 << 29)
|
||||
#define USB_DWC_LL_INTR_CORE_CONIDSTSCHNG (1 << 28)
|
||||
#define USB_DWC_LL_INTR_CORE_PTXFEMP (1 << 26)
|
||||
#define USB_DWC_LL_INTR_CORE_HCHINT (1 << 25)
|
||||
#define USB_DWC_LL_INTR_CORE_PRTINT (1 << 24)
|
||||
#define USB_DWC_LL_INTR_CORE_RESETDET (1 << 23)
|
||||
#define USB_DWC_LL_INTR_CORE_FETSUSP (1 << 22)
|
||||
#define USB_DWC_LL_INTR_CORE_INCOMPIP (1 << 21)
|
||||
#define USB_DWC_LL_INTR_CORE_INCOMPISOIN (1 << 20)
|
||||
#define USB_DWC_LL_INTR_CORE_OEPINT (1 << 19)
|
||||
#define USB_DWC_LL_INTR_CORE_IEPINT (1 << 18)
|
||||
#define USB_DWC_LL_INTR_CORE_EPMIS (1 << 17)
|
||||
#define USB_DWC_LL_INTR_CORE_EOPF (1 << 15)
|
||||
#define USB_DWC_LL_INTR_CORE_ISOOUTDROP (1 << 14)
|
||||
#define USB_DWC_LL_INTR_CORE_ENUMDONE (1 << 13)
|
||||
#define USB_DWC_LL_INTR_CORE_USBRST (1 << 12)
|
||||
#define USB_DWC_LL_INTR_CORE_USBSUSP (1 << 11)
|
||||
#define USB_DWC_LL_INTR_CORE_ERLYSUSP (1 << 10)
|
||||
#define USB_DWC_LL_INTR_CORE_GOUTNAKEFF (1 << 7)
|
||||
#define USB_DWC_LL_INTR_CORE_GINNAKEFF (1 << 6)
|
||||
#define USB_DWC_LL_INTR_CORE_NPTXFEMP (1 << 5)
|
||||
#define USB_DWC_LL_INTR_CORE_RXFLVL (1 << 4)
|
||||
#define USB_DWC_LL_INTR_CORE_SOF (1 << 3)
|
||||
#define USB_DWC_LL_INTR_CORE_OTGINT (1 << 2)
|
||||
#define USB_DWC_LL_INTR_CORE_MODEMIS (1 << 1)
|
||||
#define USB_DWC_LL_INTR_CORE_CURMOD (1 << 0)
|
||||
|
||||
/*
|
||||
* Bit mask of interrupt generating bits of the the HPRT register. These bits
|
||||
* are ORd into the USB_DWC_LL_INTR_CORE_PRTINT interrupt.
|
||||
*
|
||||
* Note: Some fields of the HPRT are W1C (write 1 clear), this we cannot do a
|
||||
* simple read and write-back to clear the HPRT interrupt bits. Instead we need
|
||||
* a W1C mask the non-interrupt related bits
|
||||
*/
|
||||
#define USB_DWC_LL_HPRT_W1C_MSK (0x2E)
|
||||
#define USB_DWC_LL_HPRT_ENA_MSK (0x04)
|
||||
#define USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG (1 << 5)
|
||||
#define USB_DWC_LL_INTR_HPRT_PRTENCHNG (1 << 3)
|
||||
#define USB_DWC_LL_INTR_HPRT_PRTCONNDET (1 << 1)
|
||||
|
||||
/*
|
||||
* Bit mask of channel interrupts (HCINTi and HCINTMSKi registers)
|
||||
*
|
||||
* Note: Under Scatter/Gather DMA mode, only the following interrupts can be unmasked
|
||||
* - DESC_LS_ROLL
|
||||
* - XCS_XACT_ERR (always unmasked)
|
||||
* - BNAINTR
|
||||
* - CHHLTD
|
||||
* - XFERCOMPL
|
||||
* The remaining interrupt bits will still be set (when the corresponding event occurs)
|
||||
* but will not generate an interrupt. Therefore we must proxy through the
|
||||
* USB_DWC_LL_INTR_CHAN_CHHLTD interrupt to check the other interrupt bits.
|
||||
*/
|
||||
#define USB_DWC_LL_INTR_CHAN_DESC_LS_ROLL (1 << 13)
|
||||
#define USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR (1 << 12)
|
||||
#define USB_DWC_LL_INTR_CHAN_BNAINTR (1 << 11)
|
||||
#define USB_DWC_LL_INTR_CHAN_DATATGLERR (1 << 10)
|
||||
#define USB_DWC_LL_INTR_CHAN_FRMOVRUN (1 << 9)
|
||||
#define USB_DWC_LL_INTR_CHAN_BBLEER (1 << 8)
|
||||
#define USB_DWC_LL_INTR_CHAN_XACTERR (1 << 7)
|
||||
#define USB_DWC_LL_INTR_CHAN_NYET (1 << 6)
|
||||
#define USB_DWC_LL_INTR_CHAN_ACK (1 << 5)
|
||||
#define USB_DWC_LL_INTR_CHAN_NAK (1 << 4)
|
||||
#define USB_DWC_LL_INTR_CHAN_STALL (1 << 3)
|
||||
#define USB_DWC_LL_INTR_CHAN_AHBERR (1 << 2)
|
||||
#define USB_DWC_LL_INTR_CHAN_CHHLTD (1 << 1)
|
||||
#define USB_DWC_LL_INTR_CHAN_XFERCOMPL (1 << 0)
|
||||
|
||||
/*
|
||||
* QTD (Queue Transfer Descriptor) structure used in Scatter/Gather DMA mode.
|
||||
* Each QTD describes one transfer. Scatter gather mode will automatically split
|
||||
* a transfer into multiple MPS packets. Each QTD is 64bits in size
|
||||
*
|
||||
* Note: The status information part of the QTD is interpreted differently depending
|
||||
* on IN or OUT, and ISO or non-ISO
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t xfer_size: 17;
|
||||
uint32_t aqtd_offset: 6;
|
||||
uint32_t aqtd_valid: 1;
|
||||
uint32_t reserved_24: 1;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t eol: 1;
|
||||
uint32_t reserved_27: 1;
|
||||
uint32_t rx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} in_non_iso;
|
||||
struct {
|
||||
uint32_t xfer_size: 12;
|
||||
uint32_t reserved_12_24: 13;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t reserved_26_27: 2;
|
||||
uint32_t rx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} in_iso;
|
||||
struct {
|
||||
uint32_t xfer_size: 17;
|
||||
uint32_t reserved_17_23: 7;
|
||||
uint32_t is_setup: 1;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t eol: 1;
|
||||
uint32_t reserved_27: 1;
|
||||
uint32_t tx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} out_non_iso;
|
||||
struct {
|
||||
uint32_t xfer_size: 12;
|
||||
uint32_t reserved_12_24: 13;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t eol: 1;
|
||||
uint32_t reserved_27: 1;
|
||||
uint32_t tx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} out_iso;
|
||||
uint32_t buffer_status_val;
|
||||
};
|
||||
uint8_t *buffer;
|
||||
} usb_dwc_ll_dma_qtd_t;
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
------------------------------- Global Registers -------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
// --------------------------- GAHBCFG Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_en_dma_mode(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.dmaen = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_en_slave_mode(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.dmaen = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_set_hbstlen(usb_dwc_dev_t *hw, uint32_t burst_len)
|
||||
{
|
||||
hw->gahbcfg_reg.hbstlen = burst_len;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_en_global_intr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.glbllntrmsk = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_dis_global_intr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.glbllntrmsk = 0;
|
||||
}
|
||||
|
||||
// --------------------------- GUSBCFG Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_force_host_mode(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gusbcfg_reg.forcehstmode = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_dis_hnp_cap(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gusbcfg_reg.hnpcap = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_dis_srp_cap(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gusbcfg_reg.srpcap = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t tout_cal)
|
||||
{
|
||||
hw->gusbcfg_reg.toutcal = tout_cal;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw)
|
||||
{
|
||||
#if (OTG_HSPHY_INTERFACE != 0)
|
||||
hw->gusbcfg_reg.phyif = 1; // 16 bits interface
|
||||
hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+
|
||||
hw->gusbcfg_reg.physel = 0; // HS PHY
|
||||
#endif // (OTG_HSPHY_INTERFACE != 0)
|
||||
}
|
||||
|
||||
// --------------------------- GRSTCTL Register --------------------------------
|
||||
|
||||
static inline bool usb_dwc_ll_grstctl_is_ahb_idle(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->grstctl_reg.ahbidle;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_grstctl_is_dma_req_in_progress(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->grstctl_reg.dmareq;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_flush_nptx_fifo(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.txfnum = 0; //Set the TX FIFO number to 0 to select the non-periodic TX FIFO
|
||||
hw->grstctl_reg.txfflsh = 1; //Flush the selected TX FIFO
|
||||
//Wait for the flushing to complete
|
||||
while (hw->grstctl_reg.txfflsh) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_flush_ptx_fifo(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.txfnum = 1; //Set the TX FIFO number to 1 to select the periodic TX FIFO
|
||||
hw->grstctl_reg.txfflsh = 1; //FLush the select TX FIFO
|
||||
//Wait for the flushing to complete
|
||||
while (hw->grstctl_reg.txfflsh) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_flush_rx_fifo(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.rxfflsh = 1;
|
||||
//Wait for the flushing to complete
|
||||
while (hw->grstctl_reg.rxfflsh) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_reset_frame_counter(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.frmcntrrst = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_core_soft_reset(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.csftrst = 1;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_grstctl_is_core_soft_reset_in_progress(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->grstctl_reg.csftrst;
|
||||
}
|
||||
|
||||
// --------------------------- GINTSTS Register --------------------------------
|
||||
|
||||
/**
|
||||
* @brief Reads and clears the global interrupt register
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @return uint32_t Mask of interrupts
|
||||
*/
|
||||
static inline uint32_t usb_dwc_ll_gintsts_read_and_clear_intrs(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_gintsts_reg_t gintsts;
|
||||
gintsts.val = hw->gintsts_reg.val;
|
||||
hw->gintsts_reg.val = gintsts.val; //Write back to clear
|
||||
return gintsts.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear specific interrupts
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param intr_msk Mask of interrupts to clear
|
||||
*/
|
||||
static inline void usb_dwc_ll_gintsts_clear_intrs(usb_dwc_dev_t *hw, uint32_t intr_msk)
|
||||
{
|
||||
//All GINTSTS fields are either W1C or read only. So safe to write directly
|
||||
hw->gintsts_reg.val = intr_msk;
|
||||
}
|
||||
|
||||
// --------------------------- GINTMSK Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gintmsk_en_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
hw->gintmsk_reg.val |= intr_mask;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gintmsk_dis_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
hw->gintmsk_reg.val &= ~intr_mask;
|
||||
}
|
||||
|
||||
// --------------------------- GRXFSIZ Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_grxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t num_lines)
|
||||
{
|
||||
//Set size in words
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->grxfsiz_reg, rxfdep, num_lines);
|
||||
}
|
||||
|
||||
// -------------------------- GNPTXFSIZ Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gnptxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines)
|
||||
{
|
||||
usb_dwc_gnptxfsiz_reg_t gnptxfsiz;
|
||||
gnptxfsiz.val = hw->gnptxfsiz_reg.val;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfstaddr, addr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfdep, num_lines);
|
||||
hw->gnptxfsiz_reg.val = gnptxfsiz.val;
|
||||
}
|
||||
|
||||
// --------------------------- GSNPSID Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->gsnpsid_reg.val;
|
||||
}
|
||||
|
||||
// --------------------------- GHWCFGx Register --------------------------------
|
||||
|
||||
/**
|
||||
* @brief Get the hardware configuration registers of the DWC_OTG controller
|
||||
*
|
||||
* The hardware configuration regitsers are read only and indicate the various
|
||||
* features of the DWC_OTG core.
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param[out] ghwcfg1 Hardware configuration registesr 1
|
||||
* @param[out] ghwcfg2 Hardware configuration registesr 2
|
||||
* @param[out] ghwcfg3 Hardware configuration registesr 3
|
||||
* @param[out] ghwcfg4 Hardware configuration registesr 4
|
||||
*/
|
||||
static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4)
|
||||
{
|
||||
*ghwcfg1 = hw->ghwcfg1_reg.val;
|
||||
*ghwcfg2 = hw->ghwcfg2_reg.val;
|
||||
*ghwcfg3 = hw->ghwcfg3_reg.val;
|
||||
*ghwcfg4 = hw->ghwcfg4_reg.val;
|
||||
}
|
||||
|
||||
// --------------------------- HPTXFSIZ Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines)
|
||||
{
|
||||
usb_dwc_hptxfsiz_reg_t hptxfsiz;
|
||||
hptxfsiz.val = hw->hptxfsiz_reg.val;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfstaddr, addr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfsize, num_lines);
|
||||
hw->hptxfsiz_reg.val = hptxfsiz.val;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
-------------------------------- Host Registers --------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
// ----------------------------- HCFG Register ---------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_en_perio_sched(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.perschedena = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_dis_perio_sched(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.perschedena = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the frame list
|
||||
*
|
||||
* @param num_entires Number of entries in the frame list
|
||||
*/
|
||||
static inline void usb_dwc_ll_hcfg_set_num_frame_list_entries(usb_dwc_dev_t *hw, usb_hal_frame_list_len_t num_entries)
|
||||
{
|
||||
uint32_t frlisten;
|
||||
switch (num_entries) {
|
||||
case USB_HAL_FRAME_LIST_LEN_8:
|
||||
frlisten = 0;
|
||||
break;
|
||||
case USB_HAL_FRAME_LIST_LEN_16:
|
||||
frlisten = 1;
|
||||
break;
|
||||
case USB_HAL_FRAME_LIST_LEN_32:
|
||||
frlisten = 2;
|
||||
break;
|
||||
default: //USB_HAL_FRAME_LIST_LEN_64
|
||||
frlisten = 3;
|
||||
break;
|
||||
}
|
||||
hw->hcfg_reg.frlisten = frlisten;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_en_scatt_gatt_dma(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.descdma = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.fslssupp = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.fslspclksel = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA
|
||||
*
|
||||
* @param[in] hw Start address of the DWC_OTG registers
|
||||
* @param[in] speed Speed to initialize the host port at
|
||||
*/
|
||||
static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed)
|
||||
{
|
||||
hw->hcfg_reg.descdma = 1; //Enable scatt/gatt
|
||||
#if (OTG_HSPHY_INTERFACE == 0)
|
||||
/*
|
||||
Indicate to the OTG core what speed the PHY clock is at
|
||||
Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode,
|
||||
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
|
||||
*/
|
||||
hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only
|
||||
#endif // (OTG_HSPHY_INTERFACE == 0)
|
||||
hw->hcfg_reg.perschedena = 0; //Disable perio sched
|
||||
}
|
||||
|
||||
// ----------------------------- HFIR Register ---------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed)
|
||||
{
|
||||
#if (OTG_HSPHY_INTERFACE == 0)
|
||||
usb_dwc_hfir_reg_t hfir;
|
||||
hfir.val = hw->hfir_reg.val;
|
||||
hfir.hfirrldctrl = 0; //Disable dynamic loading
|
||||
/*
|
||||
Set frame interval to be equal to 1ms
|
||||
Note: It seems like our PHY has an implicit 8 divider applied when in LS mode,
|
||||
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
|
||||
*/
|
||||
hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS
|
||||
hw->hfir_reg.val = hfir.val;
|
||||
#endif // (OTG_HSPHY_INTERFACE == 0)
|
||||
}
|
||||
|
||||
// ----------------------------- HFNUM Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hfnum_get_frame_time_rem(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->hfnum_reg, frrem);
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hfnum_get_frame_num(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hfnum_reg.frnum;
|
||||
}
|
||||
|
||||
// ---------------------------- HPTXSTS Register -------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_top(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxqtop);
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_space_avail(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hptxsts_reg.ptxqspcavail;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_ptxsts_get_ptxf_space_avail(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxfspcavail);
|
||||
}
|
||||
|
||||
// ----------------------------- HAINT Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_haint_get_chan_intrs(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->haint_reg, haint);
|
||||
}
|
||||
|
||||
// --------------------------- HAINTMSK Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_haintmsk_en_chan_intr(usb_dwc_dev_t *hw, uint32_t mask)
|
||||
{
|
||||
|
||||
hw->haintmsk_reg.val |= mask;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_haintmsk_dis_chan_intr(usb_dwc_dev_t *hw, uint32_t mask)
|
||||
{
|
||||
hw->haintmsk_reg.val &= ~mask;
|
||||
}
|
||||
|
||||
// --------------------------- HFLBAddr Register -------------------------------
|
||||
|
||||
/**
|
||||
* @brief Set the base address of the scheduling frame list
|
||||
*
|
||||
* @note For some reason, this address must be 512 bytes aligned or else a bunch of frames will not be scheduled when
|
||||
* the frame list rolls over. However, according to the databook, there is no mention of the HFLBAddr needing to
|
||||
* be aligned.
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param addr Base address of the scheduling frame list
|
||||
*/
|
||||
static inline void usb_dwc_ll_hflbaddr_set_base_addr(usb_dwc_dev_t *hw, uint32_t addr)
|
||||
{
|
||||
hw->hflbaddr_reg.hflbaddr = addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the base address of the scheduling frame list
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @return uint32_t Base address of the scheduling frame list
|
||||
*/
|
||||
static inline uint32_t usb_dwc_ll_hflbaddr_get_base_addr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hflbaddr_reg.hflbaddr;
|
||||
}
|
||||
|
||||
// ----------------------------- HPRT Register ---------------------------------
|
||||
|
||||
static inline usb_dwc_speed_t usb_dwc_ll_hprt_get_speed(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return (usb_dwc_speed_t)hw->hprt_reg.prtspd;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hprt_get_test_ctl(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prttstctl;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_test_ctl(usb_dwc_dev_t *hw, uint32_t test_mode)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prttstctl = test_mode;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_en_pwr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtpwr = 1;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_dis_pwr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtpwr = 0;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hprt_get_pwr_line_status(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtlnsts;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_port_reset(usb_dwc_dev_t *hw, bool reset)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtrst = reset;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_reset(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtrst;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_port_suspend(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtsusp = 1;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_suspend(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtsusp;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_port_resume(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtres = 1;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_clr_port_resume(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtres = 0;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_resume(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtres;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_overcur(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtovrcurract;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_en(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtena;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_port_dis(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtena = 1; //W1C to disable
|
||||
//we want to W1C ENA but not W1C the interrupt bits
|
||||
hw->hprt_reg.val = hprt.val & ((~USB_DWC_LL_HPRT_W1C_MSK) | USB_DWC_LL_HPRT_ENA_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_conn_status(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtconnsts;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hprt_intr_read_and_clear(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
//We want to W1C the interrupt bits but not that ENA
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_ENA_MSK);
|
||||
//Return only the interrupt bits
|
||||
return (hprt.val & (USB_DWC_LL_HPRT_W1C_MSK & ~(USB_DWC_LL_HPRT_ENA_MSK)));
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_intr_clear(usb_dwc_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hw->hprt_reg.val = ((hprt.val & ~USB_DWC_LL_HPRT_ENA_MSK) & ~USB_DWC_LL_HPRT_W1C_MSK) | intr_mask;
|
||||
}
|
||||
|
||||
//Per Channel registers
|
||||
|
||||
// --------------------------- HCCHARi Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_enable_chan(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.chena = 1;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hcchar_chan_is_enabled(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
return chan->hcchar_reg.chena;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_disable_chan(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.chdis = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_odd_frame(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.oddfrm = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_even_frame(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.oddfrm = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_dev_addr(volatile usb_dwc_host_chan_regs_t *chan, uint32_t addr)
|
||||
{
|
||||
chan->hcchar_reg.devaddr = addr;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_ep_type(volatile usb_dwc_host_chan_regs_t *chan, usb_dwc_xfer_type_t type)
|
||||
{
|
||||
chan->hcchar_reg.eptype = (uint32_t)type;
|
||||
}
|
||||
|
||||
//Indicates whether channel is commuunicating with a LS device connected via a FS hub. Setting this bit to 1 will cause
|
||||
//each packet to be preceded by a PREamble packet
|
||||
static inline void usb_dwc_ll_hcchar_set_lspddev(volatile usb_dwc_host_chan_regs_t *chan, bool is_ls)
|
||||
{
|
||||
chan->hcchar_reg.lspddev = is_ls;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_dir(volatile usb_dwc_host_chan_regs_t *chan, bool is_in)
|
||||
{
|
||||
chan->hcchar_reg.epdir = is_in;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_ep_num(volatile usb_dwc_host_chan_regs_t *chan, uint32_t num)
|
||||
{
|
||||
chan->hcchar_reg.epnum = num;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_mps(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mps)
|
||||
{
|
||||
chan->hcchar_reg.mps = mps;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_init(volatile usb_dwc_host_chan_regs_t *chan, int dev_addr, int ep_num, int mps, usb_dwc_xfer_type_t type, bool is_in, bool is_ls)
|
||||
{
|
||||
//Sets all persistent fields of the channel over its lifetimez
|
||||
usb_dwc_ll_hcchar_set_dev_addr(chan, dev_addr);
|
||||
usb_dwc_ll_hcchar_set_ep_type(chan, type);
|
||||
usb_dwc_ll_hcchar_set_lspddev(chan, is_ls);
|
||||
usb_dwc_ll_hcchar_set_dir(chan, is_in);
|
||||
usb_dwc_ll_hcchar_set_ep_num(chan, ep_num);
|
||||
usb_dwc_ll_hcchar_set_mps(chan, mps);
|
||||
}
|
||||
|
||||
// ---------------------------- HCINTi Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hcint_read_and_clear_intrs(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
usb_dwc_hcint_reg_t hcint;
|
||||
hcint.val = chan->hcint_reg.val;
|
||||
chan->hcint_reg.val = hcint.val;
|
||||
return hcint.val;
|
||||
}
|
||||
|
||||
// --------------------------- HCINTMSKi Register ------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcintmsk_set_intr_mask(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mask)
|
||||
{
|
||||
chan->hcintmsk_reg.val = mask;
|
||||
}
|
||||
|
||||
// ---------------------------- HCTSIZi Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_set_pid(volatile usb_dwc_host_chan_regs_t *chan, uint32_t data_pid)
|
||||
{
|
||||
if (data_pid == 0) {
|
||||
chan->hctsiz_reg.pid = 0;
|
||||
} else {
|
||||
chan->hctsiz_reg.pid = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hctsiz_get_pid(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
if (chan->hctsiz_reg.pid == 0) {
|
||||
return 0; //DATA0
|
||||
} else {
|
||||
return 1; //DATA1
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan_regs_t *chan, int qtd_list_len)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
//Set the length of the descriptor list. NTD occupies xfersize[15:8]
|
||||
hctsiz.xfersize &= ~(0xFF << 8);
|
||||
hctsiz.xfersize |= ((qtd_list_len - 1) & 0xFF) << 8;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
hctsiz.dopng = 0; //Don't do ping
|
||||
/*
|
||||
Set SCHED_INFO which occupies xfersize[7:0]
|
||||
It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels
|
||||
*/
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset)
|
||||
{
|
||||
// @see USB-OTG databook: Table 5-47
|
||||
// This function is relevant only for HS
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
uint8_t sched_info_val;
|
||||
switch (tokens_per_frame) {
|
||||
case 1:
|
||||
offset %= 8; // If the required offset > 8, we must wrap around to SCHED_INFO size = 8
|
||||
sched_info_val = 0b00000001;
|
||||
break;
|
||||
case 2:
|
||||
offset %= 4;
|
||||
sched_info_val = 0b00010001;
|
||||
break;
|
||||
case 4:
|
||||
offset %= 2;
|
||||
sched_info_val = 0b01010101;
|
||||
break;
|
||||
case 8:
|
||||
offset = 0;
|
||||
sched_info_val = 0b11111111;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
sched_info_val <<= offset;
|
||||
hctsiz.xfersize &= ~(0xFF);
|
||||
hctsiz.xfersize |= sched_info_val;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
// ---------------------------- HCDMAi Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcdma_set_qtd_list_addr(volatile usb_dwc_host_chan_regs_t *chan, void *dmaaddr, uint32_t qtd_idx)
|
||||
{
|
||||
usb_dwc_hcdma_reg_t hcdma;
|
||||
/*
|
||||
Set the base address portion of the field which is dmaaddr[31:9]. This is
|
||||
the based address of the QTD list and must be 512 bytes aligned
|
||||
*/
|
||||
hcdma.dmaaddr = ((uint32_t)dmaaddr) & 0xFFFFFE00;
|
||||
//Set the current QTD index in the QTD list which is dmaaddr[8:3]
|
||||
hcdma.dmaaddr |= (qtd_idx & 0x3F) << 3;
|
||||
//dmaaddr[2:0] is reserved thus doesn't not need to be set
|
||||
|
||||
chan->hcdma_reg.val = hcdma.val;
|
||||
}
|
||||
|
||||
static inline int usb_dwc_ll_hcdam_get_cur_qtd_idx(usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
//The current QTD index is dmaaddr[8:3]
|
||||
return (chan->hcdma_reg.dmaaddr >> 3) & 0x3F;
|
||||
}
|
||||
|
||||
// ---------------------------- HCDMABi Register -------------------------------
|
||||
|
||||
static inline void *usb_dwc_ll_hcdmab_get_buff_addr(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
return (void *)chan->hcdmab_reg.hcdmab;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
---------------------------- Scatter/Gather DMA QTDs ---------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
// ---------------------------- Helper Functions -------------------------------
|
||||
|
||||
/**
|
||||
* @brief Get the base address of a channel's register based on the channel's index
|
||||
*
|
||||
* @param dev Start address of the DWC_OTG registers
|
||||
* @param chan_idx The channel's index
|
||||
* @return usb_dwc_host_chan_regs_t* Pointer to channel's registers
|
||||
*/
|
||||
static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t *dev, int chan_idx)
|
||||
{
|
||||
return &dev->host_chans[chan_idx];
|
||||
}
|
||||
|
||||
// ------------------------------ QTD related ----------------------------------
|
||||
|
||||
#define USB_DWC_LL_QTD_STATUS_SUCCESS 0x0 //If QTD was processed, it indicates the data was transmitted/received successfully
|
||||
#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data transmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK).
|
||||
//Note: 0x2 is reserved
|
||||
#define USB_DWC_LL_QTD_STATUS_BUFFER 0x3 //AHB error occurred.
|
||||
#define USB_DWC_LL_QTD_STATUS_NOT_EXECUTED 0x4 //QTD as never processed
|
||||
|
||||
/**
|
||||
* @brief Set a QTD for a non isochronous IN transfer
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param data_buff Pointer to buffer containing the data to transfer
|
||||
* @param xfer_len Number of bytes in transfer. Setting 0 will do a zero length IN transfer.
|
||||
* Non zero length must be multiple of the endpoint's MPS.
|
||||
* @param hoc Halt on complete (will generate an interrupt and halt the channel)
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc)
|
||||
{
|
||||
qtd->buffer = data_buff; //Set pointer to data buffer
|
||||
qtd->buffer_status_val = 0; //Reset all flags to zero
|
||||
qtd->in_non_iso.xfer_size = xfer_len;
|
||||
if (hoc) {
|
||||
qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD
|
||||
qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd
|
||||
}
|
||||
qtd->in_non_iso.active = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a QTD for a non isochronous OUT transfer
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param data_buff Pointer to buffer containing the data to transfer
|
||||
* @param xfer_len Number of bytes to transfer. Setting 0 will do a zero length transfer.
|
||||
* For ctrl setup packets, this should be set to 8.
|
||||
* @param hoc Halt on complete (will generate an interrupt)
|
||||
* @param is_setup Indicates whether this is a control transfer setup packet or a normal OUT Data transfer.
|
||||
* (As per the USB protocol, setup packets cannot be STALLd or NAKd by the device)
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_out(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc, bool is_setup)
|
||||
{
|
||||
qtd->buffer = data_buff; //Set pointer to data buffer
|
||||
qtd->buffer_status_val = 0; //Reset all flags to zero
|
||||
qtd->out_non_iso.xfer_size = xfer_len;
|
||||
if (is_setup) {
|
||||
qtd->out_non_iso.is_setup = 1;
|
||||
}
|
||||
if (hoc) {
|
||||
qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD
|
||||
qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd
|
||||
}
|
||||
qtd->out_non_iso.active = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a QTD as NULL
|
||||
*
|
||||
* This sets the QTD to a value of 0. This is only useful when you need to insert
|
||||
* blank QTDs into a list of QTDs
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_null(usb_dwc_ll_dma_qtd_t *qtd)
|
||||
{
|
||||
qtd->buffer = NULL;
|
||||
qtd->buffer_status_val = 0; //Disable qtd by clearing it to zero. Used by interrupt/isoc as an unscheudled frame
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the status of a QTD
|
||||
*
|
||||
* When a channel gets halted, call this to check whether each QTD was executed successfully
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param[out] rem_len Number of bytes ramining in the QTD
|
||||
* @param[out] status Status of the QTD
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_get_status(usb_dwc_ll_dma_qtd_t *qtd, int *rem_len, int *status)
|
||||
{
|
||||
//Status is the same regardless of IN or OUT
|
||||
if (qtd->in_non_iso.active) {
|
||||
//QTD was never processed
|
||||
*status = USB_DWC_LL_QTD_STATUS_NOT_EXECUTED;
|
||||
} else {
|
||||
*status = qtd->in_non_iso.rx_status;
|
||||
}
|
||||
*rem_len = qtd->in_non_iso.xfer_size;
|
||||
//Clear the QTD just for safety
|
||||
qtd->buffer_status_val = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
993
components/hal/esp32s3/include/hal/usb_dwc_ll.h
Normal file
993
components/hal/esp32s3/include/hal/usb_dwc_ll.h
Normal file
@ -0,0 +1,993 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/usb_dwc_struct.h"
|
||||
#include "soc/usb_dwc_cfg.h"
|
||||
#include "hal/usb_dwc_types.h"
|
||||
#include "hal/misc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ----------------------------- Helper Macros ------------------------------ */
|
||||
|
||||
// Get USB hardware instance
|
||||
#define USB_DWC_LL_GET_HW(num) (&USB_DWC)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
--------------------------------- DWC Constants --------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
#define USB_DWC_QTD_LIST_MEM_ALIGN 512
|
||||
#define USB_DWC_FRAME_LIST_MEM_ALIGN 512 // The frame list needs to be 512 bytes aligned (contrary to the databook)
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
------------------------------- Global Registers -------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Interrupt bit masks of the GINTSTS and GINTMSK registers
|
||||
*/
|
||||
#define USB_DWC_LL_INTR_CORE_WKUPINT (1 << 31)
|
||||
#define USB_DWC_LL_INTR_CORE_SESSREQINT (1 << 30)
|
||||
#define USB_DWC_LL_INTR_CORE_DISCONNINT (1 << 29)
|
||||
#define USB_DWC_LL_INTR_CORE_CONIDSTSCHNG (1 << 28)
|
||||
#define USB_DWC_LL_INTR_CORE_PTXFEMP (1 << 26)
|
||||
#define USB_DWC_LL_INTR_CORE_HCHINT (1 << 25)
|
||||
#define USB_DWC_LL_INTR_CORE_PRTINT (1 << 24)
|
||||
#define USB_DWC_LL_INTR_CORE_RESETDET (1 << 23)
|
||||
#define USB_DWC_LL_INTR_CORE_FETSUSP (1 << 22)
|
||||
#define USB_DWC_LL_INTR_CORE_INCOMPIP (1 << 21)
|
||||
#define USB_DWC_LL_INTR_CORE_INCOMPISOIN (1 << 20)
|
||||
#define USB_DWC_LL_INTR_CORE_OEPINT (1 << 19)
|
||||
#define USB_DWC_LL_INTR_CORE_IEPINT (1 << 18)
|
||||
#define USB_DWC_LL_INTR_CORE_EPMIS (1 << 17)
|
||||
#define USB_DWC_LL_INTR_CORE_EOPF (1 << 15)
|
||||
#define USB_DWC_LL_INTR_CORE_ISOOUTDROP (1 << 14)
|
||||
#define USB_DWC_LL_INTR_CORE_ENUMDONE (1 << 13)
|
||||
#define USB_DWC_LL_INTR_CORE_USBRST (1 << 12)
|
||||
#define USB_DWC_LL_INTR_CORE_USBSUSP (1 << 11)
|
||||
#define USB_DWC_LL_INTR_CORE_ERLYSUSP (1 << 10)
|
||||
#define USB_DWC_LL_INTR_CORE_GOUTNAKEFF (1 << 7)
|
||||
#define USB_DWC_LL_INTR_CORE_GINNAKEFF (1 << 6)
|
||||
#define USB_DWC_LL_INTR_CORE_NPTXFEMP (1 << 5)
|
||||
#define USB_DWC_LL_INTR_CORE_RXFLVL (1 << 4)
|
||||
#define USB_DWC_LL_INTR_CORE_SOF (1 << 3)
|
||||
#define USB_DWC_LL_INTR_CORE_OTGINT (1 << 2)
|
||||
#define USB_DWC_LL_INTR_CORE_MODEMIS (1 << 1)
|
||||
#define USB_DWC_LL_INTR_CORE_CURMOD (1 << 0)
|
||||
|
||||
/*
|
||||
* Bit mask of interrupt generating bits of the the HPRT register. These bits
|
||||
* are ORd into the USB_DWC_LL_INTR_CORE_PRTINT interrupt.
|
||||
*
|
||||
* Note: Some fields of the HPRT are W1C (write 1 clear), this we cannot do a
|
||||
* simple read and write-back to clear the HPRT interrupt bits. Instead we need
|
||||
* a W1C mask the non-interrupt related bits
|
||||
*/
|
||||
#define USB_DWC_LL_HPRT_W1C_MSK (0x2E)
|
||||
#define USB_DWC_LL_HPRT_ENA_MSK (0x04)
|
||||
#define USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG (1 << 5)
|
||||
#define USB_DWC_LL_INTR_HPRT_PRTENCHNG (1 << 3)
|
||||
#define USB_DWC_LL_INTR_HPRT_PRTCONNDET (1 << 1)
|
||||
|
||||
/*
|
||||
* Bit mask of channel interrupts (HCINTi and HCINTMSKi registers)
|
||||
*
|
||||
* Note: Under Scatter/Gather DMA mode, only the following interrupts can be unmasked
|
||||
* - DESC_LS_ROLL
|
||||
* - XCS_XACT_ERR (always unmasked)
|
||||
* - BNAINTR
|
||||
* - CHHLTD
|
||||
* - XFERCOMPL
|
||||
* The remaining interrupt bits will still be set (when the corresponding event occurs)
|
||||
* but will not generate an interrupt. Therefore we must proxy through the
|
||||
* USB_DWC_LL_INTR_CHAN_CHHLTD interrupt to check the other interrupt bits.
|
||||
*/
|
||||
#define USB_DWC_LL_INTR_CHAN_DESC_LS_ROLL (1 << 13)
|
||||
#define USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR (1 << 12)
|
||||
#define USB_DWC_LL_INTR_CHAN_BNAINTR (1 << 11)
|
||||
#define USB_DWC_LL_INTR_CHAN_DATATGLERR (1 << 10)
|
||||
#define USB_DWC_LL_INTR_CHAN_FRMOVRUN (1 << 9)
|
||||
#define USB_DWC_LL_INTR_CHAN_BBLEER (1 << 8)
|
||||
#define USB_DWC_LL_INTR_CHAN_XACTERR (1 << 7)
|
||||
#define USB_DWC_LL_INTR_CHAN_NYET (1 << 6)
|
||||
#define USB_DWC_LL_INTR_CHAN_ACK (1 << 5)
|
||||
#define USB_DWC_LL_INTR_CHAN_NAK (1 << 4)
|
||||
#define USB_DWC_LL_INTR_CHAN_STALL (1 << 3)
|
||||
#define USB_DWC_LL_INTR_CHAN_AHBERR (1 << 2)
|
||||
#define USB_DWC_LL_INTR_CHAN_CHHLTD (1 << 1)
|
||||
#define USB_DWC_LL_INTR_CHAN_XFERCOMPL (1 << 0)
|
||||
|
||||
/*
|
||||
* QTD (Queue Transfer Descriptor) structure used in Scatter/Gather DMA mode.
|
||||
* Each QTD describes one transfer. Scatter gather mode will automatically split
|
||||
* a transfer into multiple MPS packets. Each QTD is 64bits in size
|
||||
*
|
||||
* Note: The status information part of the QTD is interpreted differently depending
|
||||
* on IN or OUT, and ISO or non-ISO
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t xfer_size: 17;
|
||||
uint32_t aqtd_offset: 6;
|
||||
uint32_t aqtd_valid: 1;
|
||||
uint32_t reserved_24: 1;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t eol: 1;
|
||||
uint32_t reserved_27: 1;
|
||||
uint32_t rx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} in_non_iso;
|
||||
struct {
|
||||
uint32_t xfer_size: 12;
|
||||
uint32_t reserved_12_24: 13;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t reserved_26_27: 2;
|
||||
uint32_t rx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} in_iso;
|
||||
struct {
|
||||
uint32_t xfer_size: 17;
|
||||
uint32_t reserved_17_23: 7;
|
||||
uint32_t is_setup: 1;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t eol: 1;
|
||||
uint32_t reserved_27: 1;
|
||||
uint32_t tx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} out_non_iso;
|
||||
struct {
|
||||
uint32_t xfer_size: 12;
|
||||
uint32_t reserved_12_24: 13;
|
||||
uint32_t intr_cplt: 1;
|
||||
uint32_t eol: 1;
|
||||
uint32_t reserved_27: 1;
|
||||
uint32_t tx_status: 2;
|
||||
uint32_t reserved_30: 1;
|
||||
uint32_t active: 1;
|
||||
} out_iso;
|
||||
uint32_t buffer_status_val;
|
||||
};
|
||||
uint8_t *buffer;
|
||||
} usb_dwc_ll_dma_qtd_t;
|
||||
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
------------------------------- Global Registers -------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
// --------------------------- GAHBCFG Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_en_dma_mode(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.dmaen = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_en_slave_mode(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.dmaen = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_set_hbstlen(usb_dwc_dev_t *hw, uint32_t burst_len)
|
||||
{
|
||||
hw->gahbcfg_reg.hbstlen = burst_len;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_en_global_intr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.glbllntrmsk = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gahbcfg_dis_global_intr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gahbcfg_reg.glbllntrmsk = 0;
|
||||
}
|
||||
|
||||
// --------------------------- GUSBCFG Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_force_host_mode(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gusbcfg_reg.forcehstmode = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_dis_hnp_cap(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gusbcfg_reg.hnpcap = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_dis_srp_cap(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->gusbcfg_reg.srpcap = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_set_timeout_cal(usb_dwc_dev_t *hw, uint8_t tout_cal)
|
||||
{
|
||||
hw->gusbcfg_reg.toutcal = tout_cal;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gusbcfg_set_utmi_phy(usb_dwc_dev_t *hw)
|
||||
{
|
||||
#if (OTG_HSPHY_INTERFACE != 0)
|
||||
hw->gusbcfg_reg.phyif = 1; // 16 bits interface
|
||||
hw->gusbcfg_reg.ulpiutmisel = 0; // UTMI+
|
||||
hw->gusbcfg_reg.physel = 0; // HS PHY
|
||||
#endif // (OTG_HSPHY_INTERFACE != 0)
|
||||
}
|
||||
|
||||
// --------------------------- GRSTCTL Register --------------------------------
|
||||
|
||||
static inline bool usb_dwc_ll_grstctl_is_ahb_idle(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->grstctl_reg.ahbidle;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_grstctl_is_dma_req_in_progress(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->grstctl_reg.dmareq;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_flush_nptx_fifo(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.txfnum = 0; //Set the TX FIFO number to 0 to select the non-periodic TX FIFO
|
||||
hw->grstctl_reg.txfflsh = 1; //Flush the selected TX FIFO
|
||||
//Wait for the flushing to complete
|
||||
while (hw->grstctl_reg.txfflsh) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_flush_ptx_fifo(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.txfnum = 1; //Set the TX FIFO number to 1 to select the periodic TX FIFO
|
||||
hw->grstctl_reg.txfflsh = 1; //FLush the select TX FIFO
|
||||
//Wait for the flushing to complete
|
||||
while (hw->grstctl_reg.txfflsh) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_flush_rx_fifo(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.rxfflsh = 1;
|
||||
//Wait for the flushing to complete
|
||||
while (hw->grstctl_reg.rxfflsh) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_reset_frame_counter(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.frmcntrrst = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_grstctl_core_soft_reset(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->grstctl_reg.csftrst = 1;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_grstctl_is_core_soft_reset_in_progress(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->grstctl_reg.csftrst;
|
||||
}
|
||||
|
||||
// --------------------------- GINTSTS Register --------------------------------
|
||||
|
||||
/**
|
||||
* @brief Reads and clears the global interrupt register
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @return uint32_t Mask of interrupts
|
||||
*/
|
||||
static inline uint32_t usb_dwc_ll_gintsts_read_and_clear_intrs(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_gintsts_reg_t gintsts;
|
||||
gintsts.val = hw->gintsts_reg.val;
|
||||
hw->gintsts_reg.val = gintsts.val; //Write back to clear
|
||||
return gintsts.val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clear specific interrupts
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param intr_msk Mask of interrupts to clear
|
||||
*/
|
||||
static inline void usb_dwc_ll_gintsts_clear_intrs(usb_dwc_dev_t *hw, uint32_t intr_msk)
|
||||
{
|
||||
//All GINTSTS fields are either W1C or read only. So safe to write directly
|
||||
hw->gintsts_reg.val = intr_msk;
|
||||
}
|
||||
|
||||
// --------------------------- GINTMSK Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gintmsk_en_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
hw->gintmsk_reg.val |= intr_mask;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_gintmsk_dis_intrs(usb_dwc_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
hw->gintmsk_reg.val &= ~intr_mask;
|
||||
}
|
||||
|
||||
// --------------------------- GRXFSIZ Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_grxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t num_lines)
|
||||
{
|
||||
//Set size in words
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->grxfsiz_reg, rxfdep, num_lines);
|
||||
}
|
||||
|
||||
// -------------------------- GNPTXFSIZ Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_gnptxfsiz_set_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines)
|
||||
{
|
||||
usb_dwc_gnptxfsiz_reg_t gnptxfsiz;
|
||||
gnptxfsiz.val = hw->gnptxfsiz_reg.val;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfstaddr, addr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(gnptxfsiz, nptxfdep, num_lines);
|
||||
hw->gnptxfsiz_reg.val = gnptxfsiz.val;
|
||||
}
|
||||
|
||||
// --------------------------- GSNPSID Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_gsnpsid_get_id(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->gsnpsid_reg.val;
|
||||
}
|
||||
|
||||
// --------------------------- GHWCFGx Register --------------------------------
|
||||
|
||||
/**
|
||||
* @brief Get the hardware configuration registers of the DWC_OTG controller
|
||||
*
|
||||
* The hardware configuration regitsers are read only and indicate the various
|
||||
* features of the DWC_OTG core.
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param[out] ghwcfg1 Hardware configuration registesr 1
|
||||
* @param[out] ghwcfg2 Hardware configuration registesr 2
|
||||
* @param[out] ghwcfg3 Hardware configuration registesr 3
|
||||
* @param[out] ghwcfg4 Hardware configuration registesr 4
|
||||
*/
|
||||
static inline void usb_dwc_ll_ghwcfg_get_hw_config(usb_dwc_dev_t *hw, uint32_t *ghwcfg1, uint32_t *ghwcfg2, uint32_t *ghwcfg3, uint32_t *ghwcfg4)
|
||||
{
|
||||
*ghwcfg1 = hw->ghwcfg1_reg.val;
|
||||
*ghwcfg2 = hw->ghwcfg2_reg.val;
|
||||
*ghwcfg3 = hw->ghwcfg3_reg.val;
|
||||
*ghwcfg4 = hw->ghwcfg4_reg.val;
|
||||
}
|
||||
|
||||
// --------------------------- HPTXFSIZ Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(usb_dwc_dev_t *hw, uint32_t addr, uint32_t num_lines)
|
||||
{
|
||||
usb_dwc_hptxfsiz_reg_t hptxfsiz;
|
||||
hptxfsiz.val = hw->hptxfsiz_reg.val;
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfstaddr, addr);
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(hptxfsiz, ptxfsize, num_lines);
|
||||
hw->hptxfsiz_reg.val = hptxfsiz.val;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
-------------------------------- Host Registers --------------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
// ----------------------------- HCFG Register ---------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_en_perio_sched(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.perschedena = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_dis_perio_sched(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.perschedena = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the length of the frame list
|
||||
*
|
||||
* @param num_entires Number of entries in the frame list
|
||||
*/
|
||||
static inline void usb_dwc_ll_hcfg_set_num_frame_list_entries(usb_dwc_dev_t *hw, usb_hal_frame_list_len_t num_entries)
|
||||
{
|
||||
uint32_t frlisten;
|
||||
switch (num_entries) {
|
||||
case USB_HAL_FRAME_LIST_LEN_8:
|
||||
frlisten = 0;
|
||||
break;
|
||||
case USB_HAL_FRAME_LIST_LEN_16:
|
||||
frlisten = 1;
|
||||
break;
|
||||
case USB_HAL_FRAME_LIST_LEN_32:
|
||||
frlisten = 2;
|
||||
break;
|
||||
default: //USB_HAL_FRAME_LIST_LEN_64
|
||||
frlisten = 3;
|
||||
break;
|
||||
}
|
||||
hw->hcfg_reg.frlisten = frlisten;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_en_scatt_gatt_dma(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.descdma = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_set_fsls_supp_only(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.fslssupp = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcfg_set_fsls_pclk_sel(usb_dwc_dev_t *hw)
|
||||
{
|
||||
hw->hcfg_reg.fslspclksel = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets some default values to HCFG to operate in Host mode with scatter/gather DMA
|
||||
*
|
||||
* @param[in] hw Start address of the DWC_OTG registers
|
||||
* @param[in] speed Speed to initialize the host port at
|
||||
*/
|
||||
static inline void usb_dwc_ll_hcfg_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed)
|
||||
{
|
||||
hw->hcfg_reg.descdma = 1; //Enable scatt/gatt
|
||||
#if (OTG_HSPHY_INTERFACE == 0)
|
||||
/*
|
||||
Indicate to the OTG core what speed the PHY clock is at
|
||||
Note: It seems like S2/S3 PHY has an implicit 8 divider applied when in LS mode,
|
||||
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
|
||||
*/
|
||||
hw->hcfg_reg.fslspclksel = (speed == USB_DWC_SPEED_FULL) ? 1 : 2; //PHY clock on esp32-sx for FS/LS-only
|
||||
#endif // (OTG_HSPHY_INTERFACE == 0)
|
||||
hw->hcfg_reg.perschedena = 0; //Disable perio sched
|
||||
}
|
||||
|
||||
// ----------------------------- HFIR Register ---------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hfir_set_defaults(usb_dwc_dev_t *hw, usb_dwc_speed_t speed)
|
||||
{
|
||||
#if (OTG_HSPHY_INTERFACE == 0)
|
||||
usb_dwc_hfir_reg_t hfir;
|
||||
hfir.val = hw->hfir_reg.val;
|
||||
hfir.hfirrldctrl = 0; //Disable dynamic loading
|
||||
/*
|
||||
Set frame interval to be equal to 1ms
|
||||
Note: It seems like our PHY has an implicit 8 divider applied when in LS mode,
|
||||
so the values of FSLSPclkSel and FrInt have to be adjusted accordingly.
|
||||
*/
|
||||
hfir.frint = (speed == USB_DWC_SPEED_FULL) ? 48000 : 6000; //esp32-sx targets only support FS or LS
|
||||
hw->hfir_reg.val = hfir.val;
|
||||
#endif // (OTG_HSPHY_INTERFACE == 0)
|
||||
}
|
||||
|
||||
// ----------------------------- HFNUM Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hfnum_get_frame_time_rem(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->hfnum_reg, frrem);
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hfnum_get_frame_num(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hfnum_reg.frnum;
|
||||
}
|
||||
|
||||
// ---------------------------- HPTXSTS Register -------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_top(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxqtop);
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hptxsts_get_ptxq_space_avail(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hptxsts_reg.ptxqspcavail;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_ptxsts_get_ptxf_space_avail(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->hptxsts_reg, ptxfspcavail);
|
||||
}
|
||||
|
||||
// ----------------------------- HAINT Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_haint_get_chan_intrs(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return HAL_FORCE_READ_U32_REG_FIELD(hw->haint_reg, haint);
|
||||
}
|
||||
|
||||
// --------------------------- HAINTMSK Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_haintmsk_en_chan_intr(usb_dwc_dev_t *hw, uint32_t mask)
|
||||
{
|
||||
|
||||
hw->haintmsk_reg.val |= mask;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_haintmsk_dis_chan_intr(usb_dwc_dev_t *hw, uint32_t mask)
|
||||
{
|
||||
hw->haintmsk_reg.val &= ~mask;
|
||||
}
|
||||
|
||||
// --------------------------- HFLBAddr Register -------------------------------
|
||||
|
||||
/**
|
||||
* @brief Set the base address of the scheduling frame list
|
||||
*
|
||||
* @note For some reason, this address must be 512 bytes aligned or else a bunch of frames will not be scheduled when
|
||||
* the frame list rolls over. However, according to the databook, there is no mention of the HFLBAddr needing to
|
||||
* be aligned.
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @param addr Base address of the scheduling frame list
|
||||
*/
|
||||
static inline void usb_dwc_ll_hflbaddr_set_base_addr(usb_dwc_dev_t *hw, uint32_t addr)
|
||||
{
|
||||
hw->hflbaddr_reg.hflbaddr = addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the base address of the scheduling frame list
|
||||
*
|
||||
* @param hw Start address of the DWC_OTG registers
|
||||
* @return uint32_t Base address of the scheduling frame list
|
||||
*/
|
||||
static inline uint32_t usb_dwc_ll_hflbaddr_get_base_addr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hflbaddr_reg.hflbaddr;
|
||||
}
|
||||
|
||||
// ----------------------------- HPRT Register ---------------------------------
|
||||
|
||||
static inline usb_dwc_speed_t usb_dwc_ll_hprt_get_speed(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return (usb_dwc_speed_t)hw->hprt_reg.prtspd;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hprt_get_test_ctl(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prttstctl;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_test_ctl(usb_dwc_dev_t *hw, uint32_t test_mode)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prttstctl = test_mode;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_en_pwr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtpwr = 1;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_dis_pwr(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtpwr = 0;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hprt_get_pwr_line_status(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtlnsts;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_port_reset(usb_dwc_dev_t *hw, bool reset)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtrst = reset;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_reset(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtrst;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_port_suspend(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtsusp = 1;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_suspend(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtsusp;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_set_port_resume(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtres = 1;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_clr_port_resume(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtres = 0;
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_W1C_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_resume(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtres;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_overcur(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtovrcurract;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_port_en(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtena;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_port_dis(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hprt.prtena = 1; //W1C to disable
|
||||
//we want to W1C ENA but not W1C the interrupt bits
|
||||
hw->hprt_reg.val = hprt.val & ((~USB_DWC_LL_HPRT_W1C_MSK) | USB_DWC_LL_HPRT_ENA_MSK);
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hprt_get_conn_status(usb_dwc_dev_t *hw)
|
||||
{
|
||||
return hw->hprt_reg.prtconnsts;
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hprt_intr_read_and_clear(usb_dwc_dev_t *hw)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
//We want to W1C the interrupt bits but not that ENA
|
||||
hw->hprt_reg.val = hprt.val & (~USB_DWC_LL_HPRT_ENA_MSK);
|
||||
//Return only the interrupt bits
|
||||
return (hprt.val & (USB_DWC_LL_HPRT_W1C_MSK & ~(USB_DWC_LL_HPRT_ENA_MSK)));
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hprt_intr_clear(usb_dwc_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
usb_dwc_hprt_reg_t hprt;
|
||||
hprt.val = hw->hprt_reg.val;
|
||||
hw->hprt_reg.val = ((hprt.val & ~USB_DWC_LL_HPRT_ENA_MSK) & ~USB_DWC_LL_HPRT_W1C_MSK) | intr_mask;
|
||||
}
|
||||
|
||||
//Per Channel registers
|
||||
|
||||
// --------------------------- HCCHARi Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_enable_chan(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.chena = 1;
|
||||
}
|
||||
|
||||
static inline bool usb_dwc_ll_hcchar_chan_is_enabled(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
return chan->hcchar_reg.chena;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_disable_chan(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.chdis = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_odd_frame(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.oddfrm = 1;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_even_frame(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
chan->hcchar_reg.oddfrm = 0;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_dev_addr(volatile usb_dwc_host_chan_regs_t *chan, uint32_t addr)
|
||||
{
|
||||
chan->hcchar_reg.devaddr = addr;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_ep_type(volatile usb_dwc_host_chan_regs_t *chan, usb_dwc_xfer_type_t type)
|
||||
{
|
||||
chan->hcchar_reg.eptype = (uint32_t)type;
|
||||
}
|
||||
|
||||
//Indicates whether channel is commuunicating with a LS device connected via a FS hub. Setting this bit to 1 will cause
|
||||
//each packet to be preceded by a PREamble packet
|
||||
static inline void usb_dwc_ll_hcchar_set_lspddev(volatile usb_dwc_host_chan_regs_t *chan, bool is_ls)
|
||||
{
|
||||
chan->hcchar_reg.lspddev = is_ls;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_dir(volatile usb_dwc_host_chan_regs_t *chan, bool is_in)
|
||||
{
|
||||
chan->hcchar_reg.epdir = is_in;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_ep_num(volatile usb_dwc_host_chan_regs_t *chan, uint32_t num)
|
||||
{
|
||||
chan->hcchar_reg.epnum = num;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_set_mps(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mps)
|
||||
{
|
||||
chan->hcchar_reg.mps = mps;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hcchar_init(volatile usb_dwc_host_chan_regs_t *chan, int dev_addr, int ep_num, int mps, usb_dwc_xfer_type_t type, bool is_in, bool is_ls)
|
||||
{
|
||||
//Sets all persistent fields of the channel over its lifetimez
|
||||
usb_dwc_ll_hcchar_set_dev_addr(chan, dev_addr);
|
||||
usb_dwc_ll_hcchar_set_ep_type(chan, type);
|
||||
usb_dwc_ll_hcchar_set_lspddev(chan, is_ls);
|
||||
usb_dwc_ll_hcchar_set_dir(chan, is_in);
|
||||
usb_dwc_ll_hcchar_set_ep_num(chan, ep_num);
|
||||
usb_dwc_ll_hcchar_set_mps(chan, mps);
|
||||
}
|
||||
|
||||
// ---------------------------- HCINTi Register --------------------------------
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hcint_read_and_clear_intrs(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
usb_dwc_hcint_reg_t hcint;
|
||||
hcint.val = chan->hcint_reg.val;
|
||||
chan->hcint_reg.val = hcint.val;
|
||||
return hcint.val;
|
||||
}
|
||||
|
||||
// --------------------------- HCINTMSKi Register ------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcintmsk_set_intr_mask(volatile usb_dwc_host_chan_regs_t *chan, uint32_t mask)
|
||||
{
|
||||
chan->hcintmsk_reg.val = mask;
|
||||
}
|
||||
|
||||
// ---------------------------- HCTSIZi Register -------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_set_pid(volatile usb_dwc_host_chan_regs_t *chan, uint32_t data_pid)
|
||||
{
|
||||
if (data_pid == 0) {
|
||||
chan->hctsiz_reg.pid = 0;
|
||||
} else {
|
||||
chan->hctsiz_reg.pid = 2;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t usb_dwc_ll_hctsiz_get_pid(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
if (chan->hctsiz_reg.pid == 0) {
|
||||
return 0; //DATA0
|
||||
} else {
|
||||
return 1; //DATA1
|
||||
}
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_set_qtd_list_len(volatile usb_dwc_host_chan_regs_t *chan, int qtd_list_len)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
//Set the length of the descriptor list. NTD occupies xfersize[15:8]
|
||||
hctsiz.xfersize &= ~(0xFF << 8);
|
||||
hctsiz.xfersize |= ((qtd_list_len - 1) & 0xFF) << 8;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_init(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
hctsiz.dopng = 0; //Don't do ping
|
||||
/*
|
||||
Set SCHED_INFO which occupies xfersize[7:0]
|
||||
It is always set to 0xFF for full speed and not used in Bulk/Ctrl channels
|
||||
*/
|
||||
hctsiz.xfersize |= 0xFF;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
static inline void usb_dwc_ll_hctsiz_set_sched_info(volatile usb_dwc_host_chan_regs_t *chan, int tokens_per_frame, int offset)
|
||||
{
|
||||
// @see USB-OTG databook: Table 5-47
|
||||
// This function is relevant only for HS
|
||||
usb_dwc_hctsiz_reg_t hctsiz;
|
||||
hctsiz.val = chan->hctsiz_reg.val;
|
||||
uint8_t sched_info_val;
|
||||
switch (tokens_per_frame) {
|
||||
case 1:
|
||||
offset %= 8; // If the required offset > 8, we must wrap around to SCHED_INFO size = 8
|
||||
sched_info_val = 0b00000001;
|
||||
break;
|
||||
case 2:
|
||||
offset %= 4;
|
||||
sched_info_val = 0b00010001;
|
||||
break;
|
||||
case 4:
|
||||
offset %= 2;
|
||||
sched_info_val = 0b01010101;
|
||||
break;
|
||||
case 8:
|
||||
offset = 0;
|
||||
sched_info_val = 0b11111111;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
sched_info_val <<= offset;
|
||||
hctsiz.xfersize &= ~(0xFF);
|
||||
hctsiz.xfersize |= sched_info_val;
|
||||
chan->hctsiz_reg.val = hctsiz.val;
|
||||
}
|
||||
|
||||
// ---------------------------- HCDMAi Register --------------------------------
|
||||
|
||||
static inline void usb_dwc_ll_hcdma_set_qtd_list_addr(volatile usb_dwc_host_chan_regs_t *chan, void *dmaaddr, uint32_t qtd_idx)
|
||||
{
|
||||
usb_dwc_hcdma_reg_t hcdma;
|
||||
/*
|
||||
Set the base address portion of the field which is dmaaddr[31:9]. This is
|
||||
the based address of the QTD list and must be 512 bytes aligned
|
||||
*/
|
||||
hcdma.dmaaddr = ((uint32_t)dmaaddr) & 0xFFFFFE00;
|
||||
//Set the current QTD index in the QTD list which is dmaaddr[8:3]
|
||||
hcdma.dmaaddr |= (qtd_idx & 0x3F) << 3;
|
||||
//dmaaddr[2:0] is reserved thus doesn't not need to be set
|
||||
|
||||
chan->hcdma_reg.val = hcdma.val;
|
||||
}
|
||||
|
||||
static inline int usb_dwc_ll_hcdam_get_cur_qtd_idx(usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
//The current QTD index is dmaaddr[8:3]
|
||||
return (chan->hcdma_reg.dmaaddr >> 3) & 0x3F;
|
||||
}
|
||||
|
||||
// ---------------------------- HCDMABi Register -------------------------------
|
||||
|
||||
static inline void *usb_dwc_ll_hcdmab_get_buff_addr(volatile usb_dwc_host_chan_regs_t *chan)
|
||||
{
|
||||
return (void *)chan->hcdmab_reg.hcdmab;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
---------------------------- Scatter/Gather DMA QTDs ---------------------------
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
// ---------------------------- Helper Functions -------------------------------
|
||||
|
||||
/**
|
||||
* @brief Get the base address of a channel's register based on the channel's index
|
||||
*
|
||||
* @param dev Start address of the DWC_OTG registers
|
||||
* @param chan_idx The channel's index
|
||||
* @return usb_dwc_host_chan_regs_t* Pointer to channel's registers
|
||||
*/
|
||||
static inline usb_dwc_host_chan_regs_t *usb_dwc_ll_chan_get_regs(usb_dwc_dev_t *dev, int chan_idx)
|
||||
{
|
||||
return &dev->host_chans[chan_idx];
|
||||
}
|
||||
|
||||
// ------------------------------ QTD related ----------------------------------
|
||||
|
||||
#define USB_DWC_LL_QTD_STATUS_SUCCESS 0x0 //If QTD was processed, it indicates the data was transmitted/received successfully
|
||||
#define USB_DWC_LL_QTD_STATUS_PKTERR 0x1 //Data transmitted/received with errors (CRC/Timeout/Stuff/False EOP/Excessive NAK).
|
||||
//Note: 0x2 is reserved
|
||||
#define USB_DWC_LL_QTD_STATUS_BUFFER 0x3 //AHB error occurred.
|
||||
#define USB_DWC_LL_QTD_STATUS_NOT_EXECUTED 0x4 //QTD as never processed
|
||||
|
||||
/**
|
||||
* @brief Set a QTD for a non isochronous IN transfer
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param data_buff Pointer to buffer containing the data to transfer
|
||||
* @param xfer_len Number of bytes in transfer. Setting 0 will do a zero length IN transfer.
|
||||
* Non zero length must be multiple of the endpoint's MPS.
|
||||
* @param hoc Halt on complete (will generate an interrupt and halt the channel)
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_in(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc)
|
||||
{
|
||||
qtd->buffer = data_buff; //Set pointer to data buffer
|
||||
qtd->buffer_status_val = 0; //Reset all flags to zero
|
||||
qtd->in_non_iso.xfer_size = xfer_len;
|
||||
if (hoc) {
|
||||
qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD
|
||||
qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd
|
||||
}
|
||||
qtd->in_non_iso.active = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a QTD for a non isochronous OUT transfer
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param data_buff Pointer to buffer containing the data to transfer
|
||||
* @param xfer_len Number of bytes to transfer. Setting 0 will do a zero length transfer.
|
||||
* For ctrl setup packets, this should be set to 8.
|
||||
* @param hoc Halt on complete (will generate an interrupt)
|
||||
* @param is_setup Indicates whether this is a control transfer setup packet or a normal OUT Data transfer.
|
||||
* (As per the USB protocol, setup packets cannot be STALLd or NAKd by the device)
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_out(usb_dwc_ll_dma_qtd_t *qtd, uint8_t *data_buff, int xfer_len, bool hoc, bool is_setup)
|
||||
{
|
||||
qtd->buffer = data_buff; //Set pointer to data buffer
|
||||
qtd->buffer_status_val = 0; //Reset all flags to zero
|
||||
qtd->out_non_iso.xfer_size = xfer_len;
|
||||
if (is_setup) {
|
||||
qtd->out_non_iso.is_setup = 1;
|
||||
}
|
||||
if (hoc) {
|
||||
qtd->in_non_iso.intr_cplt = 1; //We need to set this to distinguish between a halt due to a QTD
|
||||
qtd->in_non_iso.eol = 1; //Used to halt the channel at this qtd
|
||||
}
|
||||
qtd->out_non_iso.active = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a QTD as NULL
|
||||
*
|
||||
* This sets the QTD to a value of 0. This is only useful when you need to insert
|
||||
* blank QTDs into a list of QTDs
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_set_null(usb_dwc_ll_dma_qtd_t *qtd)
|
||||
{
|
||||
qtd->buffer = NULL;
|
||||
qtd->buffer_status_val = 0; //Disable qtd by clearing it to zero. Used by interrupt/isoc as an unscheudled frame
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the status of a QTD
|
||||
*
|
||||
* When a channel gets halted, call this to check whether each QTD was executed successfully
|
||||
*
|
||||
* @param qtd Pointer to the QTD
|
||||
* @param[out] rem_len Number of bytes ramining in the QTD
|
||||
* @param[out] status Status of the QTD
|
||||
*/
|
||||
static inline void usb_dwc_ll_qtd_get_status(usb_dwc_ll_dma_qtd_t *qtd, int *rem_len, int *status)
|
||||
{
|
||||
//Status is the same regardless of IN or OUT
|
||||
if (qtd->in_non_iso.active) {
|
||||
//QTD was never processed
|
||||
*status = USB_DWC_LL_QTD_STATUS_NOT_EXECUTED;
|
||||
} else {
|
||||
*status = qtd->in_non_iso.rx_status;
|
||||
}
|
||||
*rem_len = qtd->in_non_iso.xfer_size;
|
||||
//Clear the QTD just for safety
|
||||
qtd->buffer_status_val = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
64
components/hal/include/hal/usb_utmi_hal.h
Normal file
64
components/hal/include/hal/usb_utmi_hal.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if (SOC_USB_UTMI_PHY_NUM > 0)
|
||||
#include "soc/usb_utmi_struct.h"
|
||||
#include "hal/usb_utmi_ll.h"
|
||||
#endif // (SOC_USB_UTMI_PHY_NUM > 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if (SOC_USB_UTMI_PHY_NUM > 0)
|
||||
|
||||
/**
|
||||
* @brief HAL context type of USB UTMI driver
|
||||
*/
|
||||
typedef struct {
|
||||
usb_utmi_dev_t *dev;
|
||||
} usb_utmi_hal_context_t;
|
||||
|
||||
/**
|
||||
* @brief Sets UTMI defaults
|
||||
*
|
||||
* Enable clock, reset the peripheral, sets default options (LS support, disconnection detection)
|
||||
*
|
||||
* @param[in] hal USB UTMI HAL context
|
||||
*/
|
||||
void _usb_utmi_hal_init(usb_utmi_hal_context_t *hal);
|
||||
|
||||
#if SOC_RCC_IS_INDEPENDENT
|
||||
#define usb_utmi_hal_init(...) _usb_utmi_hal_init(__VA_ARGS__)
|
||||
#else
|
||||
// Use a macro to wrap the function, force the caller to use it in a critical section
|
||||
// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define usb_utmi_hal_init(...) do {(void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_hal_init(__VA_ARGS__);} while(0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Disable UTMI
|
||||
*
|
||||
* Disable clock to the peripheral
|
||||
*/
|
||||
void _usb_utmi_hal_disable(void);
|
||||
|
||||
#if SOC_RCC_IS_INDEPENDENT
|
||||
#define usb_utmi_hal_disable(...) _usb_utmi_hal_disable(__VA_ARGS__)
|
||||
#else
|
||||
// Use a macro to wrap the function, force the caller to use it in a critical section
|
||||
// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define usb_utmi_hal_disable(...) do {(void)__DECLARE_RCC_ATOMIC_ENV; _usb_utmi_hal_disable(__VA_ARGS__);} while(0)
|
||||
#endif
|
||||
|
||||
#endif // (SOC_USB_UTMI_PHY_NUM > 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -8,17 +8,17 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#if (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
#include "soc/usb_wrap_struct.h"
|
||||
#include "hal/usb_wrap_ll.h"
|
||||
#endif
|
||||
#endif // (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
#include "hal/usb_wrap_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#if (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
/**
|
||||
* @brief HAL context type of USB WRAP driver
|
||||
@ -89,7 +89,7 @@ static inline void usb_wrap_hal_phy_test_mode_set_signals(usb_wrap_hal_context_t
|
||||
usb_wrap_ll_phy_test_mode_set_signals(hal->dev, vals);
|
||||
}
|
||||
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
#endif // (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
#if (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
/**
|
||||
* @brief USB WRAP pull up/down resistor override values
|
||||
@ -46,7 +46,7 @@ typedef struct {
|
||||
bool rx_rcv; /**< Differential receive data from D+ and D- lines */
|
||||
} usb_wrap_test_mode_vals_t;
|
||||
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
#endif // (SOC_USB_OTG_PERIPH_NUM > 0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/chip_revision.h"
|
||||
#include "soc/usb_dwc_cfg.h"
|
||||
#include "soc/usb_dwc_periph.h"
|
||||
#include "hal/usb_dwc_hal.h"
|
||||
#include "hal/usb_dwc_ll.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
@ -18,14 +19,6 @@
|
||||
|
||||
// ------------------------------------------------ Macros and Types ---------------------------------------------------
|
||||
|
||||
// TODO: Remove target specific section after support for multiple USB peripherals is implemented
|
||||
#include "sdkconfig.h"
|
||||
#if (CONFIG_IDF_TARGET_ESP32P4)
|
||||
#define USB_BASE USB_DWC_HS
|
||||
#else
|
||||
#define USB_BASE USB_DWC
|
||||
#endif
|
||||
|
||||
// ---------------------- Constants ------------------------
|
||||
|
||||
#define BENDPOINTADDRESS_NUM_MSK 0x0F //Endpoint number mask of the bEndpointAddress field of an endpoint descriptor
|
||||
@ -118,10 +111,14 @@ static void set_defaults(usb_dwc_hal_context_t *hal)
|
||||
//GUSBCFG register
|
||||
usb_dwc_ll_gusbcfg_dis_hnp_cap(hal->dev); //Disable HNP
|
||||
usb_dwc_ll_gusbcfg_dis_srp_cap(hal->dev); //Disable SRP
|
||||
#if (OTG_HSPHY_INTERFACE != 0)
|
||||
usb_dwc_ll_gusbcfg_set_timeout_cal(hal->dev, 5); // 5 PHY clocks for our HS PHY
|
||||
usb_dwc_ll_gusbcfg_set_utmi_phy(hal->dev);
|
||||
#endif // (OTG_HSPHY_INTERFACE != 0)
|
||||
|
||||
// Check if this USB-DWC supports HS PHY, if yes, use it
|
||||
uint32_t ghwcfg[4];
|
||||
usb_dwc_ll_ghwcfg_get_hw_config(hal->dev, &ghwcfg[0], &ghwcfg[1], &ghwcfg[2], &ghwcfg[3]);
|
||||
if (((usb_dwc_ghwcfg2_reg_t)ghwcfg[1]).hsphytype != 0) {
|
||||
usb_dwc_ll_gusbcfg_set_timeout_cal(hal->dev, 5); // 5 PHY clocks for our HS PHY
|
||||
usb_dwc_ll_gusbcfg_set_utmi_phy(hal->dev);
|
||||
}
|
||||
//Enable interruts
|
||||
usb_dwc_ll_gintmsk_dis_intrs(hal->dev, 0xFFFFFFFF); //Mask all interrupts first
|
||||
usb_dwc_ll_gintmsk_en_intrs(hal->dev, CORE_INTRS_EN_MSK); //Unmask global interrupts
|
||||
@ -134,7 +131,7 @@ static void set_defaults(usb_dwc_hal_context_t *hal)
|
||||
void usb_dwc_hal_init(usb_dwc_hal_context_t *hal)
|
||||
{
|
||||
//Check if a peripheral is alive by reading the core ID registers
|
||||
usb_dwc_dev_t *dev = &USB_BASE;
|
||||
usb_dwc_dev_t *dev = USB_DWC_LL_GET_HW(0);
|
||||
uint32_t core_id = usb_dwc_ll_gsnpsid_get_id(dev);
|
||||
HAL_ASSERT(core_id == CORE_REG_GSNPSID);
|
||||
(void) core_id; //Suppress unused variable warning if asserts are disabled
|
||||
@ -270,7 +267,7 @@ void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal)
|
||||
|
||||
bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx)
|
||||
{
|
||||
HAL_ASSERT(hal->flags.fifo_sizes_set); //FIFO sizes should be set befor attempting to allocate a channel
|
||||
HAL_ASSERT(hal->flags.fifo_sizes_set); //FIFO sizes should be set before attempting to allocate a channel
|
||||
//Attempt to allocate channel
|
||||
if (hal->channels.num_allocd == OTG_NUM_HOST_CHAN) {
|
||||
return false; //Out of free channels
|
||||
|
29
components/hal/usb_utmi_hal.c
Normal file
29
components/hal/usb_utmi_hal.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "hal/usb_utmi_ll.h"
|
||||
#include "hal/usb_utmi_hal.h"
|
||||
|
||||
void _usb_utmi_hal_init(usb_utmi_hal_context_t *hal)
|
||||
{
|
||||
hal->dev = &USB_UTMI;
|
||||
_usb_utmi_ll_enable_bus_clock(true);
|
||||
_usb_utmi_ll_reset_register();
|
||||
|
||||
/*
|
||||
Additional setting to solve missing DCONN event on ESP32P4 (IDF-9953).
|
||||
|
||||
Note: On ESP32P4, the HP_SYSTEM_OTG_SUSPENDM is not connected to 1 by hardware.
|
||||
For correct detection of the device detaching, internal signal should be set to 1 by the software.
|
||||
*/
|
||||
usb_utmi_ll_enable_precise_detection(true);
|
||||
usb_utmi_ll_configure_ls(hal->dev, true);
|
||||
}
|
||||
|
||||
void _usb_utmi_hal_disable(void)
|
||||
{
|
||||
_usb_utmi_ll_enable_bus_clock(false);
|
||||
}
|
@ -4,47 +4,33 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// TODO: Refactor during the IDF-9198
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/usb_dwc_cfg.h"
|
||||
#include "hal/usb_utmi_ll.h" // We don't have usb_utmi_hal yet
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
// TODO: Remove this file when proper support of P4 PHYs is implemented IDF-7323
|
||||
// TODO: Remove this file when proper support of P4 PHYs is implemented IDF-11144
|
||||
#include "hal/usb_utmi_hal.h"
|
||||
#include "esp_private/usb_phy.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_RCC_IS_INDEPENDENT
|
||||
#define USB_UTMI_BUS_CLK_ATOMIC()
|
||||
#else
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#define USB_UTMI_BUS_CLK_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#endif
|
||||
|
||||
static usb_utmi_hal_context_t s_utmi_hal_context;
|
||||
|
||||
esp_err_t usb_new_phy(const usb_phy_config_t *config, usb_phy_handle_t *handle_ret)
|
||||
{
|
||||
#if (OTG_HSPHY_INTERFACE != 0)
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
USB_UTMI_BUS_CLK_ATOMIC() {
|
||||
usb_utmi_ll_enable_bus_clock(true);
|
||||
usb_utmi_ll_reset_register();
|
||||
usb_utmi_hal_init(&s_utmi_hal_context);
|
||||
}
|
||||
/*
|
||||
Additional setting to solve missing DCONN event on ESP32P4 (IDF-9953).
|
||||
|
||||
Note: On ESP32P4, the HP_SYSTEM_OTG_SUSPENDM is not connected to 1 by hardware.
|
||||
For correct detection of the device detaching, internal signal should be set to 1 by the software.
|
||||
*/
|
||||
usb_utmi_ll_enable_precise_detection(true);
|
||||
usb_utmi_ll_configure_ls(&USB_UTMI, true);
|
||||
#endif // CONFIG_IDF_TARGET_ESP32P4
|
||||
#endif // (OTG_HSPHY_INTERFACE != 0)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t usb_del_phy(usb_phy_handle_t handle)
|
||||
{
|
||||
// Note: handle argument is not checked, because we don't have phy_handle for P4 yet
|
||||
USB_UTMI_BUS_CLK_ATOMIC() {
|
||||
usb_utmi_ll_enable_bus_clock(false);
|
||||
usb_utmi_hal_disable();
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user