2021-10-25 09:42:09 -04:00
/*
2024-01-10 04:06:20 -05:00
* SPDX - FileCopyrightText : 2020 - 2024 Espressif Systems ( Shanghai ) CO LTD
2021-10-25 09:42:09 -04:00
*
* SPDX - License - Identifier : Apache - 2.0
*/
2020-12-28 05:42:49 -05:00
# include <stddef.h>
# include <stdint.h>
2023-12-07 04:11:41 -05:00
# include <string.h> // For memset()
# include <stdlib.h> // For abort()
2021-06-02 05:57:50 -04:00
# include "sdkconfig.h"
2023-05-22 08:45:02 -04:00
# include "soc/chip_revision.h"
2024-01-22 06:14:11 -05:00
# include "soc/usb_dwc_cfg.h"
2022-09-15 08:43:18 -04:00
# include "hal/usb_dwc_hal.h"
# include "hal/usb_dwc_ll.h"
2023-12-14 15:34:02 -05:00
# include "hal/efuse_hal.h"
2021-05-18 22:53:21 -04:00
# include "hal/assert.h"
2020-12-28 05:42:49 -05:00
2021-03-11 07:50:05 -05:00
// ------------------------------------------------ Macros and Types ---------------------------------------------------
2020-12-28 05:42:49 -05:00
2024-01-22 06:14:11 -05:00
// 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
2021-03-11 07:50:05 -05:00
// ---------------------- Constants ------------------------
2020-12-28 05:42:49 -05:00
2021-03-05 07:51:25 -05:00
# 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
2024-01-10 04:06:20 -05:00
# define CORE_REG_GSNPSID 0x4F54400A //Release number of USB_DWC used in Espressif's SoCs
2023-11-29 07:11:28 -05:00
2021-03-11 07:50:05 -05:00
// -------------------- Configurable -----------------------
2020-12-28 05:42:49 -05:00
/**
* The following core interrupts will be enabled ( listed LSB to MSB ) . Some of these
* interrupts are enabled later than others .
2022-09-15 09:26:00 -04:00
* - USB_DWC_LL_INTR_CORE_PRTINT
* - USB_DWC_LL_INTR_CORE_HCHINT
* - USB_DWC_LL_INTR_CORE_DISCONNINT
2020-12-28 05:42:49 -05:00
* The following PORT interrupts cannot be masked , listed LSB to MSB
2022-09-15 09:26:00 -04:00
* - USB_DWC_LL_INTR_HPRT_PRTCONNDET
* - USB_DWC_LL_INTR_HPRT_PRTENCHNG
* - USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG
2020-12-28 05:42:49 -05:00
*/
2022-09-15 09:26:00 -04:00
# define CORE_INTRS_EN_MSK (USB_DWC_LL_INTR_CORE_DISCONNINT)
2020-12-28 05:42:49 -05:00
//Interrupts that pertain to core events
2022-09-15 09:26:00 -04:00
# define CORE_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_CORE_DISCONNINT | \
USB_DWC_LL_INTR_CORE_HCHINT )
2020-12-28 05:42:49 -05:00
//Interrupt that pertain to host port events
2022-09-15 09:26:00 -04:00
# define PORT_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_HPRT_PRTCONNDET | \
USB_DWC_LL_INTR_HPRT_PRTENCHNG | \
USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG )
2020-12-28 05:42:49 -05:00
/**
* The following channel interrupt bits are currently checked ( in order LSB to MSB )
2022-09-15 09:26:00 -04:00
* - 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
2020-12-28 05:42:49 -05:00
*
2021-02-08 21:29:01 -05:00
* Note the following points about channel interrupts :
2020-12-28 05:42:49 -05:00
* - Not all bits are unmaskable under scatter / gather
2022-09-15 09:26:00 -04:00
* - 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 ) :
2021-02-08 21:29:01 -05:00
* - 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
2022-09-15 09:26:00 -04:00
* return a USB_DWC_LL_INTR_CHAN_BNAINTR instead .
2021-02-08 21:29:01 -05:00
* - 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 .
2020-12-28 05:42:49 -05:00
*/
2022-09-15 09:26:00 -04:00
# define CHAN_INTRS_EN_MSK (USB_DWC_LL_INTR_CHAN_XFERCOMPL | \
USB_DWC_LL_INTR_CHAN_CHHLTD | \
USB_DWC_LL_INTR_CHAN_BNAINTR )
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
# 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 )
2020-12-28 05:42:49 -05:00
2021-03-11 07:50:05 -05:00
// -------------------------------------------------- Core (Global) ----------------------------------------------------
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
static void set_defaults ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
//GAHBCFG register
2022-09-15 09:26:00 -04:00
usb_dwc_ll_gahbcfg_en_dma_mode ( hal - > dev ) ;
2023-05-22 08:45:02 -04:00
int hbstlen = 0 ; //Use AHB burst SINGLE by default
# if CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP32S2_REV_MIN_FULL < 100
/*
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 //CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP32S2_REV_MIN_FULL < 100
usb_dwc_ll_gahbcfg_set_hbstlen ( hal - > dev , hbstlen ) ; //Set AHB burst mode
2020-12-28 05:42:49 -05:00
//GUSBCFG register
2022-09-15 09:26:00 -04:00
usb_dwc_ll_gusbcfg_dis_hnp_cap ( hal - > dev ) ; //Disable HNP
usb_dwc_ll_gusbcfg_dis_srp_cap ( hal - > dev ) ; //Disable SRP
2024-01-22 06:14:11 -05:00
# 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)
2020-12-28 05:42:49 -05:00
//Enable interruts
2022-09-15 09:26:00 -04:00
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
2020-12-28 05:42:49 -05:00
//Enable host mode
2022-09-15 09:26:00 -04:00
usb_dwc_ll_gusbcfg_force_host_mode ( hal - > dev ) ;
2020-12-28 05:42:49 -05:00
}
2022-09-15 09:26:00 -04:00
void usb_dwc_hal_init ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
//Check if a peripheral is alive by reading the core ID registers
2024-01-22 06:14:11 -05:00
usb_dwc_dev_t * dev = & USB_BASE ;
2022-09-15 09:26:00 -04:00
uint32_t core_id = usb_dwc_ll_gsnpsid_get_id ( dev ) ;
2021-05-18 22:53:21 -04:00
HAL_ASSERT ( core_id = = CORE_REG_GSNPSID ) ;
2021-03-11 07:50:05 -05:00
( void ) core_id ; //Suppress unused variable warning if asserts are disabled
2020-12-28 05:42:49 -05:00
//Initialize HAL context
2022-09-15 09:26:00 -04:00
memset ( hal , 0 , sizeof ( usb_dwc_hal_context_t ) ) ;
2020-12-28 05:42:49 -05:00
hal - > dev = dev ;
set_defaults ( hal ) ;
}
2022-09-15 09:26:00 -04:00
void usb_dwc_hal_deinit ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
//Disable and clear global interrupt
2022-09-15 09:26:00 -04:00
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
2020-12-28 05:42:49 -05:00
hal - > dev = NULL ;
}
2022-09-15 09:26:00 -04:00
void usb_dwc_hal_core_soft_reset ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
2022-09-15 09:26:00 -04:00
usb_dwc_ll_grstctl_core_soft_reset ( hal - > dev ) ;
while ( usb_dwc_ll_grstctl_is_core_soft_reset_in_progress ( hal - > dev ) ) {
2020-12-28 05:42:49 -05:00
; //Wait until core reset is done
}
2022-09-15 09:26:00 -04:00
while ( ! usb_dwc_ll_grstctl_is_ahb_idle ( hal - > dev ) ) {
2020-12-28 05:42:49 -05:00
; //Wait until AHB Master bus is idle before doing any other operations
}
//Set the default bits
set_defaults ( hal ) ;
2021-02-08 21:29:01 -05:00
//Clear all the flags and channels
2021-03-11 07:50:05 -05:00
hal - > periodic_frame_list = NULL ;
2020-12-28 05:42:49 -05:00
hal - > flags . val = 0 ;
2021-02-08 21:29:01 -05:00
hal - > channels . num_allocd = 0 ;
hal - > channels . chan_pend_intrs_msk = 0 ;
2023-12-19 03:36:55 -05:00
memset ( hal - > channels . hdls , 0 , sizeof ( usb_dwc_hal_chan_t * ) * OTG_NUM_HOST_CHAN ) ;
2020-12-28 05:42:49 -05:00
}
2023-11-29 07:11:28 -05:00
void usb_dwc_hal_set_fifo_bias ( usb_dwc_hal_context_t * hal , const usb_hal_fifo_bias_t fifo_bias )
2021-03-11 07:50:05 -05:00
{
2024-01-10 04:06:20 -05:00
/*
* EPINFO_CTL is located at the end of FIFO , its size is fixed in HW .
* The reserved size is always the worst - case , which is device mode that requires 4 locations per EP direction ( including EP0 ) .
* Here we just read the FIFO size from HW register , to avoid any ambivalence
*/
uint32_t ghwcfg1 , ghwcfg2 , ghwcfg3 , ghwcfg4 ;
usb_dwc_ll_ghwcfg_get_hw_config ( hal - > dev , & ghwcfg1 , & ghwcfg2 , & ghwcfg3 , & ghwcfg4 ) ;
const uint16_t fifo_size_lines = ( ( usb_dwc_ghwcfg3_reg_t ) ghwcfg3 ) . dfifodepth ;
/*
* Recommended FIFO sizes ( see 2.1 .2 .4 for programming guide )
*
* RXFIFO : ( ( LPS / 4 ) * 2 ) + 2
* NPTXFIFO : ( LPS / 4 ) * 2
* PTXFIFO : ( LPS / 4 ) * 2
*
* Recommended sizes fit 2 packets of each type . For S2 and S3 we can ' t fit even one MPS ISOC packet ( 1023 FS and 1024 HS ) .
* So the calculations below are compromises between the available FIFO size and optimal performance .
*/
usb_dwc_hal_fifo_config_t fifo_config ;
2023-11-29 07:11:28 -05:00
switch ( fifo_bias ) {
2024-01-10 04:06:20 -05:00
// Define minimum viable (fits at least 1 MPS) FIFO sizes for non-biased FIFO types
// Allocate the remaining size to the biased FIFO type
2023-11-29 07:11:28 -05:00
case USB_HAL_FIFO_BIAS_DEFAULT :
2024-01-10 04:06:20 -05:00
fifo_config . nptx_fifo_lines = OTG_DFIFO_DEPTH / 4 ;
fifo_config . ptx_fifo_lines = OTG_DFIFO_DEPTH / 8 ;
fifo_config . rx_fifo_lines = fifo_size_lines - fifo_config . ptx_fifo_lines - fifo_config . nptx_fifo_lines ;
2023-11-29 07:11:28 -05:00
break ;
case USB_HAL_FIFO_BIAS_RX :
2024-01-10 04:06:20 -05:00
fifo_config . nptx_fifo_lines = OTG_DFIFO_DEPTH / 16 ;
fifo_config . ptx_fifo_lines = OTG_DFIFO_DEPTH / 8 ;
fifo_config . rx_fifo_lines = fifo_size_lines - fifo_config . ptx_fifo_lines - fifo_config . nptx_fifo_lines ;
2023-11-29 07:11:28 -05:00
break ;
case USB_HAL_FIFO_BIAS_PTX :
2024-01-10 04:06:20 -05:00
fifo_config . rx_fifo_lines = OTG_DFIFO_DEPTH / 8 + 2 ; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1
fifo_config . nptx_fifo_lines = OTG_DFIFO_DEPTH / 16 ;
fifo_config . ptx_fifo_lines = fifo_size_lines - fifo_config . nptx_fifo_lines - fifo_config . rx_fifo_lines ;
2023-11-29 07:11:28 -05:00
break ;
default :
abort ( ) ;
}
2024-01-10 04:06:20 -05:00
HAL_ASSERT ( ( fifo_config . rx_fifo_lines + fifo_config . nptx_fifo_lines + fifo_config . ptx_fifo_lines ) < = fifo_size_lines ) ;
2021-03-11 07:50:05 -05:00
//Check that none of the channels are active
2023-12-19 03:36:55 -05:00
for ( int i = 0 ; i < OTG_NUM_HOST_CHAN ; i + + ) {
2021-03-11 07:50:05 -05:00
if ( hal - > channels . hdls [ i ] ! = NULL ) {
2021-05-18 22:53:21 -04:00
HAL_ASSERT ( ! hal - > channels . hdls [ i ] - > flags . active ) ;
2021-03-11 07:50:05 -05:00
}
}
//Set the new FIFO lengths
2024-01-10 04:06:20 -05:00
usb_dwc_ll_grxfsiz_set_fifo_size ( hal - > dev , fifo_config . rx_fifo_lines ) ;
usb_dwc_ll_gnptxfsiz_set_fifo_size ( hal - > dev , fifo_config . rx_fifo_lines , fifo_config . nptx_fifo_lines ) ;
usb_dwc_ll_hptxfsiz_set_ptx_fifo_size ( hal - > dev , fifo_config . rx_fifo_lines + fifo_config . nptx_fifo_lines , fifo_config . ptx_fifo_lines ) ;
2021-03-11 07:50:05 -05:00
//Flush the FIFOs
2022-09-15 09:26:00 -04:00
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 ) ;
2024-01-10 04:06:20 -05:00
hal - > fifo_config = fifo_config ; // Implicit struct copy
2021-03-11 07:50:05 -05:00
hal - > flags . fifo_sizes_set = 1 ;
}
2023-11-29 07:11:28 -05:00
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 ) ;
2024-01-10 04:06:20 -05:00
const usb_dwc_hal_fifo_config_t * fifo_config = & ( hal - > fifo_config ) ;
2023-11-29 07:11:28 -05:00
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 ;
}
2021-03-11 07:50:05 -05:00
// ---------------------------------------------------- Host Port ------------------------------------------------------
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
static inline void debounce_lock_enable ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
//Disable the hprt (connection) and disconnection interrupts to prevent repeated triggerings
2022-09-15 09:26:00 -04:00
usb_dwc_ll_gintmsk_dis_intrs ( hal - > dev , USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT ) ;
2020-12-28 05:42:49 -05:00
hal - > flags . dbnc_lock_enabled = 1 ;
}
2022-09-15 09:26:00 -04:00
void usb_dwc_hal_port_enable ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
2023-11-29 03:30:04 -05:00
usb_dwc_speed_t speed = usb_dwc_ll_hprt_get_speed ( hal - > dev ) ;
2020-12-28 05:42:49 -05:00
//Host Configuration
2022-09-15 09:26:00 -04:00
usb_dwc_ll_hcfg_set_defaults ( hal - > dev , speed ) ;
2020-12-28 05:42:49 -05:00
//Configure HFIR
2022-09-15 09:26:00 -04:00
usb_dwc_ll_hfir_set_defaults ( hal - > dev , speed ) ;
2020-12-28 05:42:49 -05:00
}
2021-03-11 07:50:05 -05:00
// ----------------------------------------------------- Channel -------------------------------------------------------
2020-12-28 05:42:49 -05:00
2021-03-11 07:50:05 -05:00
// ----------------- Channel Allocation --------------------
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
bool usb_dwc_hal_chan_alloc ( usb_dwc_hal_context_t * hal , usb_dwc_hal_chan_t * chan_obj , void * chan_ctx )
2020-12-28 05:42:49 -05:00
{
2021-05-18 22:53:21 -04:00
HAL_ASSERT ( hal - > flags . fifo_sizes_set ) ; //FIFO sizes should be set befor attempting to allocate a channel
2020-12-28 05:42:49 -05:00
//Attempt to allocate channel
2023-12-19 03:36:55 -05:00
if ( hal - > channels . num_allocd = = OTG_NUM_HOST_CHAN ) {
2020-12-28 05:42:49 -05:00
return false ; //Out of free channels
}
int chan_idx = - 1 ;
2023-12-19 03:36:55 -05:00
for ( int i = 0 ; i < OTG_NUM_HOST_CHAN ; i + + ) {
2020-12-28 05:42:49 -05:00
if ( hal - > channels . hdls [ i ] = = NULL ) {
hal - > channels . hdls [ i ] = chan_obj ;
chan_idx = i ;
hal - > channels . num_allocd + + ;
break ;
}
}
2021-05-18 22:53:21 -04:00
HAL_ASSERT ( chan_idx ! = - 1 ) ;
2020-12-28 05:42:49 -05:00
//Initialize channel object
2022-09-15 09:26:00 -04:00
memset ( chan_obj , 0 , sizeof ( usb_dwc_hal_chan_t ) ) ;
2020-12-28 05:42:49 -05:00
chan_obj - > flags . chan_idx = chan_idx ;
2022-09-15 09:26:00 -04:00
chan_obj - > regs = usb_dwc_ll_chan_get_regs ( hal - > dev , chan_idx ) ;
2020-12-28 05:42:49 -05:00
chan_obj - > chan_ctx = chan_ctx ;
//Note: EP characteristics configured separately
//Clean and unmask the channel's interrupt
2022-09-15 09:26:00 -04:00
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_set_pid ( chan_obj - > regs , 0 ) ; //Set the initial PID to zero
usb_dwc_ll_hctsiz_init ( chan_obj - > regs ) ; //Set the non changing parts of the HCTSIZ registers (e.g., do_ping and sched info)
2020-12-28 05:42:49 -05:00
return true ;
}
2022-09-15 09:26:00 -04:00
void usb_dwc_hal_chan_free ( usb_dwc_hal_context_t * hal , usb_dwc_hal_chan_t * chan_obj )
2020-12-28 05:42:49 -05:00
{
2023-11-29 03:30:04 -05:00
if ( chan_obj - > type = = USB_DWC_XFER_TYPE_INTR | | chan_obj - > type = = USB_DWC_XFER_TYPE_ISOCHRONOUS ) {
2021-03-11 07:50:05 -05:00
//Unschedule this channel
for ( int i = 0 ; i < hal - > frame_list_len ; i + + ) {
hal - > periodic_frame_list [ i ] & = ~ ( 1 < < chan_obj - > flags . chan_idx ) ;
}
}
2020-12-28 05:42:49 -05:00
//Can only free a channel when in the disabled state and descriptor list released
2021-10-25 09:42:09 -04:00
HAL_ASSERT ( ! chan_obj - > flags . active ) ;
2021-06-09 11:04:19 -04:00
//Disable channel's interrupt
2022-09-15 09:26:00 -04:00
usb_dwc_ll_haintmsk_dis_chan_intr ( hal - > dev , 1 < < chan_obj - > flags . chan_idx ) ;
2020-12-28 05:42:49 -05:00
//Deallocate channel
hal - > channels . hdls [ chan_obj - > flags . chan_idx ] = NULL ;
hal - > channels . num_allocd - - ;
2021-05-18 22:53:21 -04:00
HAL_ASSERT ( hal - > channels . num_allocd > = 0 ) ;
2020-12-28 05:42:49 -05:00
}
2021-03-11 07:50:05 -05:00
// ---------------- Channel Configuration ------------------
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
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 )
2020-12-28 05:42:49 -05:00
{
//Cannot change ep_char whilst channel is still active or in error
2021-10-25 09:42:09 -04:00
HAL_ASSERT ( ! chan_obj - > flags . active ) ;
2020-12-28 05:42:49 -05:00
//Set the endpoint characteristics of the pipe
2022-09-15 09:26:00 -04:00
usb_dwc_ll_hcchar_init ( chan_obj - > regs ,
2020-12-28 05:42:49 -05:00
ep_char - > dev_addr ,
2021-03-05 07:51:25 -05:00
ep_char - > bEndpointAddress & BENDPOINTADDRESS_NUM_MSK ,
2020-12-28 05:42:49 -05:00
ep_char - > mps ,
ep_char - > type ,
2021-03-05 07:51:25 -05:00
ep_char - > bEndpointAddress & BENDPOINTADDRESS_DIR_MSK ,
2021-02-08 21:29:01 -05:00
ep_char - > ls_via_fs_hub ) ;
2021-03-11 07:50:05 -05:00
//Save channel type
chan_obj - > type = ep_char - > type ;
//If this is a periodic endpoint/channel, set its schedule in the frame list
2023-11-29 03:30:04 -05:00
if ( ep_char - > type = = USB_DWC_XFER_TYPE_ISOCHRONOUS | | ep_char - > type = = USB_DWC_XFER_TYPE_INTR ) {
2023-12-07 04:11:41 -05:00
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
if ( ep_char - > periodic . is_hs ) {
unsigned int tokens_per_frame ;
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
}
usb_dwc_ll_hctsiz_set_sched_info ( chan_obj - > regs , tokens_per_frame , ep_char - > periodic . offset ) ;
2021-03-11 07:50:05 -05:00
}
}
2020-12-28 05:42:49 -05:00
}
2021-03-11 07:50:05 -05:00
// ------------------- Channel Control ---------------------
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
void usb_dwc_hal_chan_activate ( usb_dwc_hal_chan_t * chan_obj , void * xfer_desc_list , int desc_list_len , int start_idx )
2020-12-28 05:42:49 -05:00
{
2021-03-11 07:50:05 -05:00
//Cannot activate a channel that has already been enabled or is pending error handling
2021-10-25 09:42:09 -04:00
HAL_ASSERT ( ! chan_obj - > flags . active ) ;
2020-12-28 05:42:49 -05:00
//Set start address of the QTD list and starting QTD index
2022-09-15 09:26:00 -04:00
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
2021-03-11 07:50:05 -05:00
chan_obj - > flags . active = 1 ;
2020-12-28 05:42:49 -05:00
}
2022-09-15 09:26:00 -04:00
bool usb_dwc_hal_chan_request_halt ( usb_dwc_hal_chan_t * chan_obj )
2020-12-28 05:42:49 -05:00
{
2022-12-05 10:22:25 -05:00
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 ) .
*/
2022-09-15 09:26:00 -04:00
usb_dwc_ll_hcchar_disable_chan ( chan_obj - > regs ) ;
2020-12-28 05:42:49 -05:00
chan_obj - > flags . halt_requested = 1 ;
return false ;
2021-08-24 11:20:50 -04:00
} else {
2022-12-05 10:22:25 -05:00
//Channel was never active to begin with, simply return true
2021-08-24 11:20:50 -04:00
return true ;
2020-12-28 05:42:49 -05:00
}
}
2021-03-11 07:50:05 -05:00
// ------------------------------------------------- Event Handling ----------------------------------------------------
2020-12-28 05:42:49 -05:00
2022-09-15 09:26:00 -04:00
usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
2022-09-15 09:26:00 -04:00
uint32_t intrs_core = usb_dwc_ll_gintsts_read_and_clear_intrs ( hal - > dev ) ; //Read and clear core interrupts
2020-12-28 05:42:49 -05:00
uint32_t intrs_port = 0 ;
2022-09-15 09:26:00 -04:00
if ( intrs_core & USB_DWC_LL_INTR_CORE_PRTINT ) {
2020-12-28 05:42:49 -05:00
//There are host port interrupts. Read and clear those as well.
2022-09-15 09:26:00 -04:00
intrs_port = usb_dwc_ll_hprt_intr_read_and_clear ( hal - > dev ) ;
2020-12-28 05:42:49 -05:00
}
//Note: Do not change order of checks. Regressing events (e.g. enable -> disabled, connected -> connected)
2021-08-24 11:20:50 -04:00
//always take precedence. ENABLED < DISABLED < CONN < DISCONN < OVRCUR
2022-09-15 09:26:00 -04:00
usb_dwc_hal_port_event_t event = USB_DWC_HAL_PORT_EVENT_NONE ;
2020-12-28 05:42:49 -05:00
//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
2022-09-15 09:26:00 -04:00
if ( intrs_core & USB_DWC_LL_INTR_CORE_DISCONNINT ) {
event = USB_DWC_HAL_PORT_EVENT_DISCONN ;
2020-12-28 05:42:49 -05:00
debounce_lock_enable ( hal ) ;
//Mask the port connection and disconnection interrupts to prevent repeated triggering
2022-09-15 09:26:00 -04:00
} else if ( intrs_port & USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG ) {
2020-12-28 05:42:49 -05:00
//Check if this is an overcurrent or an overcurrent cleared
2022-09-15 09:26:00 -04:00
if ( usb_dwc_ll_hprt_get_port_overcur ( hal - > dev ) ) {
event = USB_DWC_HAL_PORT_EVENT_OVRCUR ;
2020-12-28 05:42:49 -05:00
} else {
2022-09-15 09:26:00 -04:00
event = USB_DWC_HAL_PORT_EVENT_OVRCUR_CLR ;
2020-12-28 05:42:49 -05:00
}
2022-09-15 09:26:00 -04:00
} 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 ;
2020-12-28 05:42:49 -05:00
} else { //Host port has been disabled
2022-09-15 09:26:00 -04:00
event = USB_DWC_HAL_PORT_EVENT_DISABLED ;
2020-12-28 05:42:49 -05:00
}
2022-09-15 09:26:00 -04:00
} else if ( intrs_port & USB_DWC_LL_INTR_HPRT_PRTCONNDET & & ! hal - > flags . dbnc_lock_enabled ) {
event = USB_DWC_HAL_PORT_EVENT_CONN ;
2020-12-28 05:42:49 -05:00
debounce_lock_enable ( hal ) ;
}
}
2021-08-24 11:20:50 -04:00
//Port events always take precedence over channel events
2022-09-15 09:26:00 -04:00
if ( event = = USB_DWC_HAL_PORT_EVENT_NONE & & ( intrs_core & USB_DWC_LL_INTR_CORE_HCHINT ) ) {
2020-12-28 05:42:49 -05:00
//One or more channels have pending interrupts. Store the mask of those channels
2022-09-15 09:26:00 -04:00
hal - > channels . chan_pend_intrs_msk = usb_dwc_ll_haint_get_chan_intrs ( hal - > dev ) ;
event = USB_DWC_HAL_PORT_EVENT_CHAN ;
2020-12-28 05:42:49 -05:00
}
return event ;
}
2022-09-15 09:26:00 -04:00
usb_dwc_hal_chan_t * usb_dwc_hal_get_chan_pending_intr ( usb_dwc_hal_context_t * hal )
2020-12-28 05:42:49 -05:00
{
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 ;
}
}
2022-09-15 09:26:00 -04:00
usb_dwc_hal_chan_event_t usb_dwc_hal_chan_decode_intr ( usb_dwc_hal_chan_t * chan_obj )
2020-12-28 05:42:49 -05:00
{
2022-09-15 09:26:00 -04:00
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()
2021-03-11 07:50:05 -05:00
2022-12-05 10:22:25 -05:00
/*
Note : Do not change order of checks as some events take precedence over others .
Errors > Channel Halt Request > Transfer completed
*/
2021-03-11 07:50:05 -05:00
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
2022-09-15 09:26:00 -04:00
HAL_ASSERT ( chan_intrs & USB_DWC_LL_INTR_CHAN_CHHLTD ) ; //An error should have halted the channel
2020-12-28 05:42:49 -05:00
//Store the error in hal context
2022-09-15 09:26:00 -04:00
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 ;
2020-12-28 05:42:49 -05:00
}
//Update flags
chan_obj - > error = error ;
2021-03-11 07:50:05 -05:00
chan_obj - > flags . active = 0 ;
2020-12-28 05:42:49 -05:00
//Save the error to be handled later
2022-09-15 09:26:00 -04:00
chan_event = USB_DWC_HAL_CHAN_EVENT_ERROR ;
} else if ( chan_intrs & USB_DWC_LL_INTR_CHAN_CHHLTD ) {
2021-03-11 07:50:05 -05:00
if ( chan_obj - > flags . halt_requested ) {
chan_obj - > flags . halt_requested = 0 ;
2022-09-15 09:26:00 -04:00
chan_event = USB_DWC_HAL_CHAN_EVENT_HALT_REQ ;
2020-12-28 05:42:49 -05:00
} else {
2021-03-11 07:50:05 -05:00
//Must have been halted due to QTD HOC
2022-09-15 09:26:00 -04:00
chan_event = USB_DWC_HAL_CHAN_EVENT_CPLT ;
2020-12-28 05:42:49 -05:00
}
2021-03-11 07:50:05 -05:00
chan_obj - > flags . active = 0 ;
2022-09-15 09:26:00 -04:00
} else if ( chan_intrs & USB_DWC_LL_INTR_CHAN_XFERCOMPL ) {
2021-03-11 07:50:05 -05:00
/*
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
2022-09-15 09:26:00 -04:00
usb_dwc_ll_hcchar_disable_chan ( chan_obj - > regs ) ;
2021-03-11 07:50:05 -05:00
/*
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 .
*/
2022-09-15 09:26:00 -04:00
chan_event = USB_DWC_HAL_CHAN_EVENT_NONE ;
2020-12-28 05:42:49 -05:00
} else {
2023-12-07 04:11:41 -05:00
abort ( ) ;
2020-12-28 05:42:49 -05:00
}
return chan_event ;
}