From 4012e150757b517620465d93a22b0787c6400c5d Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 15 Oct 2024 17:55:24 +0700 Subject: move core init code to dwc2 common. update/correct build for esppressif --- src/common/tusb_verify.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/common') diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h index 3e0f1f106..6d02d3572 100644 --- a/src/common/tusb_verify.h +++ b/src/common/tusb_verify.h @@ -83,7 +83,7 @@ if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */ \ } while(0) -#elif defined(__riscv) && !TUP_MCU_ESPRESSIF +#elif defined(__riscv) && !TUSB_MCU_VENDOR_ESPRESSIF #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0) #elif defined(_mips) -- cgit v1.3.1 From 61b33ca926065cd25a2045077ab96dd990ce2560 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 18 Oct 2024 22:16:21 +0700 Subject: testing hcd with f723 due to h743eval issue with mfx vbus drive. --- hw/bsp/stm32f7/boards/stm32f723disco/board.cmake | 2 +- hw/bsp/stm32f7/boards/stm32f723disco/board.h | 41 ++++++++++++++++++++++-- hw/bsp/stm32f7/family.c | 24 ++++++-------- src/common/tusb_types.h | 4 +-- 4 files changed, 50 insertions(+), 21 deletions(-) (limited to 'src/common') diff --git a/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake b/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake index dd3c35b00..8a7b4cb27 100644 --- a/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake +++ b/hw/bsp/stm32f7/boards/stm32f723disco/board.cmake @@ -1,6 +1,6 @@ set(MCU_VARIANT stm32f723xx) set(JLINK_DEVICE stm32f723ie) - +#set(JLINK_OPTION "-USB 000776606156") set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/STM32F723xE_FLASH.ld) function(update_board TARGET) diff --git a/hw/bsp/stm32f7/boards/stm32f723disco/board.h b/hw/bsp/stm32f7/boards/stm32f723disco/board.h index 258786b7f..178fd6e5e 100644 --- a/hw/bsp/stm32f7/boards/stm32f723disco/board.h +++ b/hw/bsp/stm32f7/boards/stm32f723disco/board.h @@ -56,8 +56,7 @@ //--------------------------------------------------------------------+ // RCC Clock //--------------------------------------------------------------------+ -static inline void board_clock_init(void) -{ +static inline void board_clock_init(void) { RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_OscInitTypeDef RCC_OscInitStruct; @@ -74,7 +73,7 @@ static inline void board_clock_init(void) RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; - RCC_OscInitStruct.PLL.PLLM = HSE_VALUE/1000000; + RCC_OscInitStruct.PLL.PLLM = HSE_VALUE / 1000000; RCC_OscInitStruct.PLL.PLLN = 432; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 9; @@ -98,6 +97,42 @@ static inline void board_clock_init(void) // //} +typedef struct { + GPIO_TypeDef* port; + GPIO_InitTypeDef pin_init; + bool active_state; +} board_pindef_t; + +static board_pindef_t vbus_pindef[] = { +{ + .port = GPIOG, + .pin_init = { + .Pin = GPIO_PIN_8, .Mode = GPIO_MODE_OUTPUT_OD, .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_HIGH, .Alternate = 0 + }, + .active_state = false + }, +{ + .port = GPIOH, + .pin_init = { + .Pin = GPIO_PIN_12, .Mode = GPIO_MODE_OUTPUT_PP, .Pull = GPIO_NOPULL, + .Speed = GPIO_SPEED_HIGH, .Alternate = 0 + }, + .active_state = true + }, +}; + +static inline void board_vbus_set(uint8_t rhport, bool state) { + static bool pin_inited[2] = { false, false }; + board_pindef_t* pindef = &vbus_pindef[rhport]; + if (!pin_inited[rhport]) { + HAL_GPIO_Init(pindef->port, &pindef->pin_init); + pin_inited[rhport] = true; + } + + HAL_GPIO_WritePin(pindef->port, pindef->pin_init.Pin, state == pindef->active_state ? GPIO_PIN_SET : GPIO_PIN_RESET); +} + #ifdef __cplusplus } #endif diff --git a/hw/bsp/stm32f7/family.c b/hw/bsp/stm32f7/family.c index 61f1d2a7f..1f49d7693 100644 --- a/hw/bsp/stm32f7/family.c +++ b/hw/bsp/stm32f7/family.c @@ -34,13 +34,13 @@ // Forward USB interrupt events to TinyUSB IRQ Handler //--------------------------------------------------------------------+ void OTG_FS_IRQHandler(void) { - tud_int_handler(0); + tusb_int_handler(0, true); } // Despite being call USB2_OTG // OTG_HS is marked as RHPort1 by TinyUSB to be consistent across stm32 port void OTG_HS_IRQHandler(void) { - tud_int_handler(1); + tusb_int_handler(1, true); } //--------------------------------------------------------------------+ @@ -65,7 +65,7 @@ void board_init(void) { __HAL_RCC_GPIOJ_CLK_ENABLE(); #endif - UART_CLK_EN(); + UART_CLK_EN(); #if CFG_TUSB_OS == OPT_OS_NONE // 1ms tick timer @@ -122,9 +122,7 @@ void board_init(void) { UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; HAL_UART_Init(&UartHandle); -#if BOARD_TUD_RHPORT == 0 - // OTG_FS - + //------------- rhport0: OTG_FS -------------// /* Configure DM DP Pins */ GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12); GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; @@ -171,9 +169,7 @@ void board_init(void) { USB_OTG_FS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; #endif // vbus sense -#else - // OTG_HS - + //------------- rhport1: OTG_HS -------------// #ifdef USB_HS_PHYC // MCU with built-in HS PHY such as F723, F733, F730 @@ -261,12 +257,10 @@ void board_init(void) { USB_OTG_HS->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL; #endif - // Force device mode - USB_OTG_HS->GUSBCFG &= ~USB_OTG_GUSBCFG_FHMOD; - USB_OTG_HS->GUSBCFG |= USB_OTG_GUSBCFG_FDMOD; - -#endif // BOARD_TUD_RHPORT - + // Turn on host vbus +#if CFG_TUH_ENABLED + board_vbus_set(BOARD_TUH_RHPORT, true); +#endif } //--------------------------------------------------------------------+ diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 53bb367cb..0faa6eee9 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -41,8 +41,8 @@ typedef enum { TUSB_ROLE_INVALID = 0, - TUSB_ROLE_DEVICE, - TUSB_ROLE_HOST, + TUSB_ROLE_DEVICE = 0x1, + TUSB_ROLE_HOST = 0x2, } tusb_role_t; /// defined base on EHCI specs value for Endpoint Speed -- cgit v1.3.1 From 063661e3a32429e2d5f74c097da72aa3679cc889 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 25 Oct 2024 00:20:34 +0700 Subject: more progress on dwc2 hcd, initial code for edpt xfer --- src/common/tusb_common.h | 8 +- src/common/tusb_types.h | 8 +- src/portable/synopsys/dwc2/dcd_dwc2.c | 61 ++++--- src/portable/synopsys/dwc2/dwc2_common.c | 14 +- src/portable/synopsys/dwc2/dwc2_common.h | 35 +--- src/portable/synopsys/dwc2/dwc2_info.py | 1 - src/portable/synopsys/dwc2/dwc2_type.h | 149 ++++++++------- src/portable/synopsys/dwc2/hcd_dwc2.c | 301 +++++++++++++++++++++++++++---- src/tusb_option.h | 2 +- 9 files changed, 407 insertions(+), 172 deletions(-) (limited to 'src/common') diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h index d3e0cf888..59cc0fb48 100644 --- a/src/common/tusb_common.h +++ b/src/common/tusb_common.h @@ -123,11 +123,15 @@ TU_ATTR_ALWAYS_INLINE static inline int tu_memcpy_s(void *dest, size_t destsz, c //------------- Bytes -------------// TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32(uint8_t b3, uint8_t b2, uint8_t b1, uint8_t b0) { - return ( ((uint32_t) b3) << 24) | ( ((uint32_t) b2) << 16) | ( ((uint32_t) b1) << 8) | b0; + return (((uint32_t)b3) << 24) | (((uint32_t)b2) << 16) | (((uint32_t)b1) << 8) | b0; +} + +TU_ATTR_ALWAYS_INLINE static inline uint32_t tu_u32_from_u16(uint16_t high, uint16_t low) { + return (((uint32_t)high) << 16) | low; } TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_u16(uint8_t high, uint8_t low) { - return (uint16_t) ((((uint16_t) high) << 8) | low); + return (uint16_t)((((uint16_t)high) << 8) | low); } TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_u32_byte3(uint32_t ui32) { return TU_U32_BYTE3(ui32); } diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 0faa6eee9..3e5aa6818 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -56,10 +56,10 @@ typedef enum { /// defined base on USB Specs Endpoint's bmAttributes typedef enum { - TUSB_XFER_CONTROL = 0 , - TUSB_XFER_ISOCHRONOUS , - TUSB_XFER_BULK , - TUSB_XFER_INTERRUPT + TUSB_XFER_CONTROL = 0, + TUSB_XFER_ISOCHRONOUS = 1, + TUSB_XFER_BULK = 2, + TUSB_XFER_INTERRUPT = 3 } tusb_xfer_type_t; typedef enum { diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index e7440529a..0731a88c1 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -56,7 +56,7 @@ static xfer_ctl_t xfer_status[DWC2_EP_MAX][2]; // EP0 transfers are limited to 1 packet - larger sizes has to be split static uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type -static uint16_t _dfifo_top; // top free location in FIFO RAM +static uint16_t _dfifo_top; // top free location in DFIFO in words // Number of IN endpoints active static uint8_t _allocated_ep_in_count; @@ -68,6 +68,12 @@ static bool _sof_en; // DMA //-------------------------------------------------------------------- +TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) { + (void) dwc2; + // Internal DMA only + return CFG_TUD_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; +} + static void dma_setup_prepare(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -103,11 +109,9 @@ static void dma_setup_prepare(uint8_t rhport) { possible since the free space is located between the RX and TX FIFOs. ---------------- ep_fifo_size - | EPInfo | - | for DMA | + | EPInfo DMA | |-------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) - | IN FIFO 0 | - | control | + | IN FIFO 0 | control EP |-------------| | IN FIFO 1 | |-------------| @@ -126,13 +130,13 @@ static void dma_setup_prepare(uint8_t rhport) { - All EP OUT shared a unique OUT FIFO which uses (for Slave or Buffer DMA, Scatt/Gather DMA use different formula): - 13 for setup packets + control words (up to 3 setup packets). - 1 for global NAK (not required/used here). - - Largest-EPsize / 4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4) + 1" + - Largest-EPsize/4 + 1. ( FS: 64 bytes, HS: 512 bytes). Recommended is "2 x (Largest-EPsize/4 + 1)" - 2 for each used OUT endpoint Therefore GRXFSIZ = 13 + 1 + 2 x (Largest-EPsize/4 + 1) + 2 x EPOUTnum */ -TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { +TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count; } @@ -148,7 +152,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { uint16_t fifo_size = tu_div_ceil(packet_size, 4); if (dir == TUSB_DIR_OUT) { // Calculate required size of RX FIFO - uint16_t const new_sz = calc_grxfsiz(4 * fifo_size, ep_count); + uint16_t const new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count); // If size_rx needs to be extended check if there is enough free space if (dwc2->grxfsiz < new_sz) { @@ -184,17 +188,18 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { return true; } -static void dfifo_init(uint8_t rhport) { +static void dfifo_device_init(uint8_t rhport) { const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; dwc2_regs_t* dwc2 = DWC2_REG(rhport); - dwc2->grxfsiz = calc_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); + dwc2->grxfsiz = calc_device_grxfsiz(CFG_TUD_ENDPOINT0_SIZE, dwc2_controller->ep_count); - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { - // DMA use last DFIFO to store metadata - _dfifo_top = dma_cal_epfifo_base(rhport); - }else { - _dfifo_top = dwc2_controller->ep_fifo_size / 4; + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction + const bool is_dma = dma_device_enabled(dwc2); + _dfifo_top = dwc2_controller->ep_fifo_size/4; + if (is_dma) { + _dfifo_top -= 2 * dwc2_controller->ep_count; } + dwc2->gdfifocfg = (_dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | _dfifo_top; // Allocate FIFO for EP0 IN dfifo_alloc(rhport, 0x80, CFG_TUD_ENDPOINT0_SIZE); @@ -357,7 +362,7 @@ static void bus_reset(uint8_t rhport) { dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; // 4. Set up DFIFO - dfifo_init(rhport); + dfifo_device_init(rhport); // 5. Reset device address dwc2->dcfg &= ~DCFG_DAD_Msk; @@ -369,7 +374,7 @@ static void bus_reset(uint8_t rhport) { xfer_status[0][TUSB_DIR_OUT].max_size = 64; xfer_status[0][TUSB_DIR_IN].max_size = 64; - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } else { dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); @@ -401,7 +406,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dep->diepdma = (uintptr_t)xfer->buffer; // For ISO endpoint set correct odd/even bit for next frame. @@ -439,7 +444,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c dep->doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); } - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dep->doepdma = (uintptr_t)xfer->buffer; } @@ -451,11 +456,12 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c // Controller API //-------------------------------------------------------------------- bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Core Initialization - const bool is_highspeed = dwc2_core_is_highspeed(dwc2, rh_init); - const bool is_dma = dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE); + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); + const bool is_dma = dma_device_enabled(dwc2); TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); // Device Initialization @@ -463,7 +469,7 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Set device max speed uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk; - if (dwc2_core_is_highspeed(dwc2, rh_init)) { + if (is_highspeed) { dcfg |= DCFG_DSPD_HS << DCFG_DSPD_Pos; // XCVRDLY: transceiver delay between xcvr_sel and txvalid during device chirp is required @@ -611,7 +617,7 @@ void dcd_edpt_close_all(uint8_t rhport) { dfifo_flush_tx(dwc2, 0x10); // all tx fifo dfifo_flush_rx(dwc2); - dfifo_init(rhport); // re-init dfifo + dfifo_device_init(rhport); // re-init dfifo } bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { @@ -690,8 +696,9 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { } void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); edpt_disable(rhport, ep_addr, true); - if((tu_edpt_number(ep_addr) == 0) && dwc2_dma_enabled(DWC2_REG(rhport), TUSB_ROLE_DEVICE)) { + if((tu_edpt_number(ep_addr) == 0) && dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } } @@ -808,7 +815,7 @@ static void handle_epout_irq(uint8_t rhport) { if (doepint & DOEPINT_SETUP) { epout->doepint = DOEPINT_SETUP; - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } @@ -824,7 +831,7 @@ static void handle_epout_irq(uint8_t rhport) { if (!(doepint & (DOEPINT_SETUP | DOEPINT_STPKTRX | DOEPINT_STSPHSRX))) { xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - if(dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if(dma_device_enabled(dwc2)) { if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) { // EP0 can only handle one packet Schedule another packet to be received. edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); @@ -874,7 +881,7 @@ static void handle_epin_irq(uint8_t rhport) { // Schedule another packet to be transmitted. edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); } else { - if((n == 0) && dwc2_dma_enabled(dwc2, TUSB_ROLE_DEVICE)) { + if((n == 0) && dma_device_enabled(dwc2)) { dma_setup_prepare(rhport); } dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index d0ad764a5..c5990c53f 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -168,16 +168,16 @@ static bool check_dwc2(dwc2_regs_t* dwc2) { //-------------------------------------------------------------------- // //-------------------------------------------------------------------- -bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init) { +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) { (void)dwc2; #if CFG_TUD_ENABLED - if (rh_init->role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { + if (role == TUSB_ROLE_DEVICE && !TUD_OPT_HIGH_SPEED) { return false; } #endif #if CFG_TUH_ENABLED - if (rh_init->role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { + if (role == TUSB_ROLE_HOST && !TUH_OPT_HIGH_SPEED) { return false; } #endif @@ -234,15 +234,7 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2->gintmsk = 0; - // TODO can be enabled with device as well but tested with host for now - // if (rh_init->role == TUSB_ROLE_HOST) { - // dwc2->gintmsk |= OTG_INT_COMMON; - // } - if (is_dma) { - const uint16_t epinfo_base = dma_cal_epfifo_base(rhport); - dwc2->gdfifocfg = (epinfo_base << GDFIFOCFG_EPINFOBASE_SHIFT) | epinfo_base; - // DMA seems to be only settable after a core reset, and not possible to switch on-the-fly dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2; } else { diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 7088ff3c3..fff21ca5c 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -61,7 +61,9 @@ enum { OTG_INT_COMMON = 0 // GINTSTS_DISCINT | GINTSTS_CONIDSTSCHNG }; -//------------- Core -------------// +//--------------------------------------------------------------------+ +// Core/Controller +//--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { if (rhport >= DWC2_CONTROLLER_COUNT) { // user mis-configured, ignore and use first controller @@ -70,11 +72,13 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { return (dwc2_regs_t*)_dwc2_controller[rhport].reg_base; } -bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, const tusb_rhport_init_t* rh_init); +bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role); bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma); void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr); -//------------- DFIFO -------------// +//--------------------------------------------------------------------+ +// DFIFO +//--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_tx(dwc2_regs_t* dwc2, uint8_t fnum) { // flush TX fifo and wait for it cleared dwc2->grstctl = GRSTCTL_TXFFLSH | (fnum << GRSTCTL_TXFNUM_Pos); @@ -86,27 +90,8 @@ TU_ATTR_ALWAYS_INLINE static inline void dfifo_flush_rx(dwc2_regs_t* dwc2) { while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {} } -//------------- DMA -------------// -TU_ATTR_ALWAYS_INLINE static inline bool dwc2_dma_enabled(const dwc2_regs_t* dwc2, tusb_role_t role) { - (void) dwc2; - - if (CFG_TUD_DWC2_DMA == 0 && role == TUSB_ROLE_DEVICE) { - return false; - } - - if (CFG_TUH_DWC2_DMA == 0 && role == TUSB_ROLE_HOST) { - return false; - } - - // Internal DMA only - return dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; -} - -TU_ATTR_ALWAYS_INLINE static inline uint16_t dma_cal_epfifo_base(uint8_t rhport) { - // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per endpoint direction - const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; - return dwc2_controller->ep_fifo_size/4 - 2*dwc2_controller->ep_count; -} - +//--------------------------------------------------------------------+ +// DMA +//--------------------------------------------------------------------+ #endif diff --git a/src/portable/synopsys/dwc2/dwc2_info.py b/src/portable/synopsys/dwc2/dwc2_info.py index d97a1192e..fe08ecce9 100755 --- a/src/portable/synopsys/dwc2/dwc2_info.py +++ b/src/portable/synopsys/dwc2/dwc2_info.py @@ -24,7 +24,6 @@ dwc2_reg_value = { 'ST U5A5 HS': [0x5000, 0x4F54411A, 0, 0x228FE052, 0x03B882E8, 0xE2103E30], 'GD32VF103': [0x1000, 0, 0, 0, 0, 0], 'XMC4500': [0xAEC000, 0x4F54292A, 0, 0x228F5930, 0x027A01E5, 0xDBF08030] - } # Combine dwc2_info with dwc2_reg_list diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index d2993bdd9..ed335f1a3 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -138,6 +138,13 @@ enum { GINTSTS_CMODE_HOST = 1, }; +enum { + HCTSIZ_PID_DATA0 = 0, + HCTSIZ_PID_DATA2 = 1, + HCTSIZ_PID_DATA1 = 2, + HCTSIZ_PID_SETUP = 3, +}; + //-------------------------------------------------------------------- // Register bitfield definitions //-------------------------------------------------------------------- @@ -334,17 +341,49 @@ typedef struct TU_ATTR_PACKED { uint32_t speed : 2; // 17..18 Port speed uint32_t rsv19_31 :13; // 19..31 Reserved }dwc2_hprt_t; +TU_VERIFY_STATIC(sizeof(dwc2_hprt_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t ep_size : 11; // 0..10 Maximum packet size + uint32_t ep_num : 4; // 11..14 Endpoint number + uint32_t ep_dir : 1; // 15 Endpoint direction + uint32_t rsv16 : 1; // 16 Reserved + uint32_t low_speed_dev : 1; // 17 Low-speed device + uint32_t ep_type : 2; // 18..19 Endpoint type + uint32_t err_multi_count : 2; // 20..21 Error (splitEn = 1) / Multi (SplitEn = 0) count + uint32_t dev_addr : 7; // 22..28 Device address + uint32_t odd_frame : 1; // 29 Odd frame + uint32_t disable : 1; // 30 Channel disable + uint32_t enable : 1; // 31 Channel enable +} dwc2_channel_char_t; +TU_VERIFY_STATIC(sizeof(dwc2_channel_char_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t hub_port : 7; // 0..6 Hub port number + uint32_t hub_addr : 7; // 7..13 Hub address + uint32_t xact_pos : 2; // 14..15 Transaction position + uint32_t split_compl : 1; // 16 Split completion + uint32_t rsv17_30 : 14; // 17..30 Reserved + uint32_t split_en : 1; // 31 Split enable +} dwc2_channel_split_t; +TU_VERIFY_STATIC(sizeof(dwc2_channel_split_t) == 4, "incorrect size"); // Host Channel typedef struct { - volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics - volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control - volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt - volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask - volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size - volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address - uint32_t reserved518; // 518 + 20*ch - volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address + union { + volatile uint32_t hcchar; // 500 + 20*ch Host Channel Characteristics + volatile dwc2_channel_char_t hcchar_bm; + }; + union { + volatile uint32_t hcsplt; // 504 + 20*ch Host Channel Split Control + volatile dwc2_channel_split_t hcsplt_bm; + }; + volatile uint32_t hcint; // 508 + 20*ch Host Channel Interrupt + volatile uint32_t hcintmsk; // 50C + 20*ch Host Channel Interrupt Mask + volatile uint32_t hctsiz; // 510 + 20*ch Host Channel Transfer Size + volatile uint32_t hcdma; // 514 + 20*ch Host Channel DMA Address + uint32_t reserved518; // 518 + 20*ch + volatile uint32_t hcdmab; // 51C + 20*ch Host Channel DMA Address } dwc2_channel_t; // Endpoint IN @@ -1720,15 +1759,15 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCSPLT_SPLITEN HCSPLT_SPLITEN_Msk // Split enable /******************** Bit definition for HCINT register ********************/ -#define HCINT_XFRC_Pos (0U) -#define HCINT_XFRC_Msk (0x1UL << HCINT_XFRC_Pos) // 0x00000001 -#define HCINT_XFRC HCINT_XFRC_Msk // Transfer completed -#define HCINT_CHH_Pos (1U) -#define HCINT_CHH_Msk (0x1UL << HCINT_CHH_Pos) // 0x00000002 -#define HCINT_CHH HCINT_CHH_Msk // Channel halted -#define HCINT_AHBERR_Pos (2U) -#define HCINT_AHBERR_Msk (0x1UL << HCINT_AHBERR_Pos) // 0x00000004 -#define HCINT_AHBERR HCINT_AHBERR_Msk // AHB error +#define HCINT_XFER_COMPLETE_Pos (0U) +#define HCINT_XFER_COMPLETE_Msk (0x1UL << HCINT_XFER_COMPLETE_Pos) // 0x00000001 +#define HCINT_XFER_COMPLETE HCINT_XFER_COMPLETE_Msk // Transfer completed +#define HCINT_CHANNEL_HALTED_Pos (1U) +#define HCINT_CHANNEL_HALTED_Msk (0x1UL << HCINT_CHANNEL_HALTED_Pos) // 0x00000002 +#define HCINT_CHANNEL_HALTED HCINT_CHANNEL_HALTED_Msk // Channel halted +#define HCINT_AHB_ERR_Pos (2U) +#define HCINT_AHB_ERR_Msk (0x1UL << HCINT_AHB_ERR_Pos) // 0x00000004 +#define HCINT_AHB_ERR HCINT_AHB_ERR_Msk // AHB error #define HCINT_STALL_Pos (3U) #define HCINT_STALL_Msk (0x1UL << HCINT_STALL_Pos) // 0x00000008 #define HCINT_STALL HCINT_STALL_Msk // STALL response received interrupt @@ -1741,18 +1780,27 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCINT_NYET_Pos (6U) #define HCINT_NYET_Msk (0x1UL << HCINT_NYET_Pos) // 0x00000040 #define HCINT_NYET HCINT_NYET_Msk // Response received interrupt -#define HCINT_TXERR_Pos (7U) -#define HCINT_TXERR_Msk (0x1UL << HCINT_TXERR_Pos) // 0x00000080 -#define HCINT_TXERR HCINT_TXERR_Msk // Transaction error -#define HCINT_BBERR_Pos (8U) -#define HCINT_BBERR_Msk (0x1UL << HCINT_BBERR_Pos) // 0x00000100 -#define HCINT_BBERR HCINT_BBERR_Msk // Babble error -#define HCINT_FRMOR_Pos (9U) -#define HCINT_FRMOR_Msk (0x1UL << HCINT_FRMOR_Pos) // 0x00000200 -#define HCINT_FRMOR HCINT_FRMOR_Msk // Frame overrun -#define HCINT_DTERR_Pos (10U) -#define HCINT_DTERR_Msk (0x1UL << HCINT_DTERR_Pos) // 0x00000400 -#define HCINT_DTERR HCINT_DTERR_Msk // Data toggle error +#define HCINT_XACT_ERR_Pos (7U) +#define HCINT_XACT_ERR_Msk (0x1UL << HCINT_XACT_ERR_Pos) // 0x00000080 +#define HCINT_XACT_ERR HCINT_XACT_ERR_Msk // Transaction error +#define HCINT_BABBLE_ERR_Pos (8U) +#define HCINT_BABBLE_ERR_Msk (0x1UL << HCINT_BABBLE_ERR_Pos) // 0x00000100 +#define HCINT_BABBLE_ERR HCINT_BABBLE_ERR_Msk // Babble error +#define HCINT_FARME_OVERRUN_Pos (9U) +#define HCINT_FARME_OVERRUN_Msk (0x1UL << HCINT_FARME_OVERRUN_Pos) // 0x00000200 +#define HCINT_FARME_OVERRUN HCINT_FARME_OVERRUN_Msk // Frame overrun +#define HCINT_DATATOGGLE_ERR_Pos (10U) +#define HCINT_DATATOGGLE_ERR_Msk (0x1UL << HCINT_DATATOGGLE_ERR_Pos) // 0x00000400 +#define HCINT_DATATOGGLE_ERR HCINT_DATATOGGLE_ERR_Msk // Data toggle error +#define HCINT_BUFFER_NAK_Pos (11U) +#define HCINT_BUFFER_NAK_Msk (0x1UL << HCINT_BUFFER_NAK_Pos) // 0x00000800 +#define HCINT_BUFFER_NAK HCINT_BUFFER_NAK_Msk // Buffer not available interrupt +#define HCINT_XCS_XACT_ERR_Pos (12U) +#define HCINT_XCS_XACT_ERR_Msk (0x1UL << HCINT_XCS_XACT_ERR_Pos) // 0x00001000 +#define HCINT_XCS_XACT_ERR HCINT_XCS_XACT_ERR_Msk // Excessive transaction error +#define HCINT_DESC_ROLLOVER_Pos (13U) +#define HCINT_DESC_ROLLOVER_Msk (0x1UL << HCINT_DESC_ROLLOVER_Pos) // 0x00002000 +#define HCINT_DESC_ROLLOVER HCINT_DESC_ROLLOVER_Msk // Descriptor rollover /******************** Bit definition for DIEPINT register ********************/ #define DIEPINT_XFRC_Pos (0U) @@ -1795,41 +1843,6 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define DIEPINT_NAK_Msk (0x1UL << DIEPINT_NAK_Pos) // 0x00002000 #define DIEPINT_NAK DIEPINT_NAK_Msk // NAK interrupt -/******************** Bit definition for HCINTMSK register ********************/ -#define HCINTMSK_XFRCM_Pos (0U) -#define HCINTMSK_XFRCM_Msk (0x1UL << HCINTMSK_XFRCM_Pos) // 0x00000001 -#define HCINTMSK_XFRCM HCINTMSK_XFRCM_Msk // Transfer completed mask -#define HCINTMSK_CHHM_Pos (1U) -#define HCINTMSK_CHHM_Msk (0x1UL << HCINTMSK_CHHM_Pos) // 0x00000002 -#define HCINTMSK_CHHM HCINTMSK_CHHM_Msk // Channel halted mask -#define HCINTMSK_AHBERR_Pos (2U) -#define HCINTMSK_AHBERR_Msk (0x1UL << HCINTMSK_AHBERR_Pos) // 0x00000004 -#define HCINTMSK_AHBERR HCINTMSK_AHBERR_Msk // AHB error -#define HCINTMSK_STALLM_Pos (3U) -#define HCINTMSK_STALLM_Msk (0x1UL << HCINTMSK_STALLM_Pos) // 0x00000008 -#define HCINTMSK_STALLM HCINTMSK_STALLM_Msk // STALL response received interrupt mask -#define HCINTMSK_NAKM_Pos (4U) -#define HCINTMSK_NAKM_Msk (0x1UL << HCINTMSK_NAKM_Pos) // 0x00000010 -#define HCINTMSK_NAKM HCINTMSK_NAKM_Msk // NAK response received interrupt mask -#define HCINTMSK_ACKM_Pos (5U) -#define HCINTMSK_ACKM_Msk (0x1UL << HCINTMSK_ACKM_Pos) // 0x00000020 -#define HCINTMSK_ACKM HCINTMSK_ACKM_Msk // ACK response received/transmitted interrupt mask -#define HCINTMSK_NYET_Pos (6U) -#define HCINTMSK_NYET_Msk (0x1UL << HCINTMSK_NYET_Pos) // 0x00000040 -#define HCINTMSK_NYET HCINTMSK_NYET_Msk // response received interrupt mask -#define HCINTMSK_TXERRM_Pos (7U) -#define HCINTMSK_TXERRM_Msk (0x1UL << HCINTMSK_TXERRM_Pos) // 0x00000080 -#define HCINTMSK_TXERRM HCINTMSK_TXERRM_Msk // Transaction error mask -#define HCINTMSK_BBERRM_Pos (8U) -#define HCINTMSK_BBERRM_Msk (0x1UL << HCINTMSK_BBERRM_Pos) // 0x00000100 -#define HCINTMSK_BBERRM HCINTMSK_BBERRM_Msk // Babble error mask -#define HCINTMSK_FRMORM_Pos (9U) -#define HCINTMSK_FRMORM_Msk (0x1UL << HCINTMSK_FRMORM_Pos) // 0x00000200 -#define HCINTMSK_FRMORM HCINTMSK_FRMORM_Msk // Frame overrun mask -#define HCINTMSK_DTERRM_Pos (10U) -#define HCINTMSK_DTERRM_Msk (0x1UL << HCINTMSK_DTERRM_Pos) // 0x00000400 -#define HCINTMSK_DTERRM HCINTMSK_DTERRM_Msk // Data toggle error mask - /******************** Bit definition for DIEPTSIZ register ********************/ #define DIEPTSIZ_XFRSIZ_Pos (0U) @@ -1851,11 +1864,9 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define HCTSIZ_DOPING_Pos (31U) #define HCTSIZ_DOPING_Msk (0x1UL << HCTSIZ_DOPING_Pos) // 0x80000000 #define HCTSIZ_DOPING HCTSIZ_DOPING_Msk // Do PING -#define HCTSIZ_DPID_Pos (29U) -#define HCTSIZ_DPID_Msk (0x3UL << HCTSIZ_DPID_Pos) // 0x60000000 -#define HCTSIZ_DPID HCTSIZ_DPID_Msk // Data PID -#define HCTSIZ_DPID_0 (0x1UL << HCTSIZ_DPID_Pos) // 0x20000000 -#define HCTSIZ_DPID_1 (0x2UL << HCTSIZ_DPID_Pos) // 0x40000000 +#define HCTSIZ_PID_Pos (29U) +#define HCTSIZ_PID_Msk (0x3UL << HCTSIZ_PID_Pos) // 0x60000000 +#define HCTSIZ_PID HCTSIZ_PID_Msk // Data PID /******************** Bit definition for DIEPDMA register ********************/ #define DIEPDMA_DMAADDR_Pos (0U) diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index bb5086631..f37bf717d 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -34,10 +34,37 @@ #include "host/hcd.h" #include "dwc2_common.h" +// DWC2 has limit number of channel, in order to support all endpoints we can store channel char/split to swap later on +#ifndef CFG_TUH_DWC2_CHANNEL_MAX +#define CFG_TUH_DWC2_CHANNEL_MAX (CFG_TUH_DEVICE_MAX*CFG_TUH_ENDPOINT_MAX + CFG_TUH_HUB) +#endif + enum { HPRT_W1C_MASK = HPRT_CONN_DETECT | HPRT_ENABLE | HPRT_ENABLE_CHANGE | HPRT_OVER_CURRENT_CHANGE }; +typedef struct { + union { + uint32_t hcchar; + dwc2_channel_char_t hcchar_bm; + }; + union { + uint32_t hcsplt; + dwc2_channel_split_t hcsplt_bm; + }; + + uint8_t next_data_toggle; +} hcd_pipe_t; + +typedef struct { + hcd_pipe_t pipe[CFG_TUH_DWC2_CHANNEL_MAX]; +} dwc2_hcd_t; + +dwc2_hcd_t _hcd_data; + +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hprt_speed) { tusb_speed_t speed; switch(hprt_speed) { @@ -49,6 +76,85 @@ TU_ATTR_ALWAYS_INLINE static inline tusb_speed_t convert_hprt_speed(uint32_t hpr return speed; } +TU_ATTR_ALWAYS_INLINE static inline bool dma_host_enabled(const dwc2_regs_t* dwc2) { + (void) dwc2; + // Internal DMA only + return CFG_TUH_DWC2_DMA && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; +} + +/* USB Data FIFO Layout + + The FIFO is split up into + - EPInfo: for storing DMA metadata (check dcd_dwc2.c for more details) + - 1 RX FIFO: for receiving data + - 1 TX FIFO for non-periodic (NPTX) + - 1 TX FIFO for periodic (PTX) + + We allocated TX FIFO from top to bottom (using top pointer), this to allow the RX FIFO to grow dynamically which is + possible since the free space is located between the RX and TX FIFOs. + + ----------------- ep_fifo_size + | EPInfo DMA | + |--------------|-- gdfifocfg.EPINFOBASE (max is ghwcfg3.dfifo_depth) + | Non-Periodic | + | TX FIFO | + |--------------|--- GNPTXFSIZ.addr (fixed size) + | Periodic | + | TX FIFO | + |--------------|--- HPTXFSIZ.addr (expandable downward) + | FREE | + | | + |--------------|-- GRXFSIZ (expandable upward) + | RX FIFO | + ---------------- 0 +*/ + +/* Programming Guide 2.1.2 FIFO RAM allocation + * RX + * - Largest-EPsize/4 + 2 (status info). recommended x2 if high bandwidth or multiple ISO are used. + * - 2 for transfer complete and channel halted status + * - 1 for each Control/Bulk out endpoint to Handle NAK/NYET (i.e max is number of host channel) + * + * TX non-periodic (NPTX) + * - At least largest-EPsize/4, recommended x2 + * + * TX periodic (PTX) + * - At least largest-EPsize*MulCount/4 (MulCount up to 3 for high-bandwidth ISO/interrupt) +*/ +static void dfifo_host_init(uint8_t rhport) { + const dwc2_controller_t* dwc2_controller = &_dwc2_controller[rhport]; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + // Scatter/Gather DMA mode is not yet supported. Buffer DMA only need 1 words per channel + const bool is_dma = dma_host_enabled(dwc2); + uint16_t dfifo_top = dwc2_controller->ep_fifo_size/4; + if (is_dma) { + dfifo_top -= dwc2->ghwcfg2_bm.num_host_ch; + } + + // fixed allocation for now, improve later: + // - ptx_largest is limited to 256 for FS since most FS core only has 1024 bytes total + bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST); + uint32_t nptx_largest = is_highspeed ? TUSB_EPSIZE_BULK_HS/4 : TUSB_EPSIZE_BULK_FS/4; + uint32_t ptx_largest = is_highspeed ? TUSB_EPSIZE_ISO_HS_MAX/4 : 256/4; + + uint16_t nptxfsiz = 2 * nptx_largest; + uint16_t rxfsiz = 2 * (ptx_largest + 2) + dwc2->ghwcfg2_bm.num_host_ch; + TU_ASSERT(dfifo_top >= (nptxfsiz + rxfsiz),); + uint16_t ptxfsiz = dfifo_top - (nptxfsiz + rxfsiz); + + dwc2->gdfifocfg = (dfifo_top << GDFIFOCFG_EPINFOBASE_SHIFT) | dfifo_top; + + dfifo_top -= rxfsiz; + dwc2->grxfsiz = rxfsiz; + + dfifo_top -= nptxfsiz; + dwc2->gnptxfsiz = tu_u32_from_u16(nptxfsiz, dfifo_top); + + dfifo_top -= ptxfsiz; + dwc2->hptxfsiz = tu_u32_from_u16(ptxfsiz, dfifo_top); +} + //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ @@ -64,11 +170,14 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { // Initialize controller to host mode bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; dwc2_regs_t* dwc2 = DWC2_REG(rhport); + tu_memclr(&_hcd_data, sizeof(_hcd_data)); + // Core Initialization - const bool is_highspeed = dwc2_core_is_highspeed(dwc2, rh_init); - const bool is_dma = dwc2_dma_enabled(dwc2, TUSB_ROLE_HOST); + const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST); + const bool is_dma = dma_host_enabled(dwc2); TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); //------------- 3.1 Host Initialization -------------// @@ -98,6 +207,9 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FDMOD) | GUSBCFG_FHMOD; while( (dwc2->gintsts & GINTSTS_CMOD) != GINTSTS_CMODE_HOST) {} + // configure fixed-allocated fifo scheme + dfifo_host_init(rhport); + dwc2->hprt = HPRT_W1C_MASK; // clear all write-1-clear bits dwc2->hprt = HPRT_POWER; // turn on VBUS @@ -169,23 +281,108 @@ void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { //--------------------------------------------------------------------+ // Open an endpoint -bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * ep_desc) { +// channel0 is reserved for dev0 control endpoint +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep) { (void) rhport; - (void) dev_addr; - (void) ep_desc; + //dwc2_regs_t* dwc2 = DWC2_REG(rhport); + + hcd_devtree_info_t devtree_info; + hcd_devtree_get_info(dev_addr, &devtree_info); + + // find a free pipe + for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) { + hcd_pipe_t* pipe = &_hcd_data.pipe[i]; + dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; + dwc2_channel_split_t* hcsplt_bm = &pipe->hcsplt_bm; + + if (hcchar_bm->enable == 0) { + hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); + hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); + hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); + hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; + hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; + hcchar_bm->err_multi_count = 0; + hcchar_bm->dev_addr = dev_addr; + hcchar_bm->odd_frame = 0; + hcchar_bm->disable = 0; + hcchar_bm->enable = 1; + + hcsplt_bm->hub_port = devtree_info.hub_port; + hcsplt_bm->hub_addr = devtree_info.hub_addr; + // TODO not support split transaction yet + hcsplt_bm->xact_pos = 0; + hcsplt_bm->split_compl = 0; + hcsplt_bm->split_en = 0; + + pipe->next_data_toggle = HCTSIZ_PID_DATA0; + + return true; + } + } return false; } +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_free_channel(dwc2_regs_t* dwc2) { + const uint8_t max_channel = tu_min8(dwc2->ghwcfg2_bm.num_host_ch, 16); + for (uint8_t i=0; ihaintmsk, i)) { + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + +TU_ATTR_ALWAYS_INLINE static inline uint8_t find_opened_pipe(uint8_t dev_addr, uint8_t ep_addr) { + for (uint32_t i = 0; i < CFG_TUH_DWC2_CHANNEL_MAX; i++) { + const dwc2_channel_char_t* hcchar_bm = &_hcd_data.pipe[i].hcchar_bm; + if (hcchar_bm->enable && hcchar_bm->dev_addr == dev_addr && + ep_addr == tu_edpt_addr(hcchar_bm->ep_num, hcchar_bm->ep_dir)) { + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) { - (void) rhport; - (void) dev_addr; - (void) ep_addr; - (void) buffer; - (void) buflen; + dwc2_regs_t* dwc2 = DWC2_REG(rhport); - return false; + uint8_t pipe_id = find_opened_pipe(dev_addr, ep_addr); + TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + const dwc2_channel_char_t* hcchar_bm = &pipe->hcchar_bm; + + uint8_t ch_id = find_free_channel(dwc2); + TU_ASSERT(ch_id < 16); // all channel are in use + dwc2->haintmsk |= TU_BIT(ch_id); + + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + channel->hcintmsk = HCINT_XFER_COMPLETE | HCINT_CHANNEL_HALTED | HCINT_STALL | + HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + + const uint16_t packet_count = tu_div_ceil(buflen, hcchar_bm->ep_size); + channel->hctsiz = (pipe->next_data_toggle << HCTSIZ_PID_Pos) | (packet_count << HCTSIZ_PKTCNT_Pos) | buflen; + + // Control transfer always start with DATA1 for data and status stage. May has issue with ZLP + if (pipe->next_data_toggle == HCTSIZ_PID_DATA0 || tu_edpt_number(ep_addr) == 0) { + pipe->next_data_toggle = HCTSIZ_PID_DATA1; + } else { + pipe->next_data_toggle = HCTSIZ_PID_DATA0; + } + + if (dma_host_enabled(dwc2)) { + channel->hcdma = (uint32_t) buffer; + } else { + TU_ASSERT(false); // not yet support + } + + // TODO support split transaction + channel->hcsplt = pipe->hcsplt; + channel->hcchar = pipe->hcchar; // kick-off transfer + + return true; } // Abort a queued transfer. Note: it can only abort transfer that has not been started @@ -199,12 +396,13 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { } // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked -bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { - (void) rhport; - (void) dev_addr; - (void) setup_packet; +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, const uint8_t setup_packet[8]) { + uint8_t pipe_id = find_opened_pipe(dev_addr, 0); + TU_ASSERT(pipe_id < CFG_TUH_DWC2_CHANNEL_MAX); // no opened pipe + hcd_pipe_t* pipe = &_hcd_data.pipe[pipe_id]; + pipe->next_data_toggle = HCTSIZ_PID_SETUP; - return false; + return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8); } // clear stall, data toggle is also reset to DATA0 @@ -300,23 +498,55 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } -/* Interrupt Hierarchy +void handle_channel_irq(uint8_t rhport, bool in_isr) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + for(uint8_t ch_id=0; ch_id<32; ch_id++) { + if (tu_bit_test(dwc2->haint, ch_id)) { + dwc2_channel_t* channel = &dwc2->channel[ch_id]; + uint32_t hcint = channel->hcint; + hcint &= channel->hcintmsk; + + xfer_result_t result = XFER_RESULT_FAILED; + if (hcint & HCINT_XFER_COMPLETE) { + result = XFER_RESULT_SUCCESS; + } + if (hcint & HCINT_STALL) { + result = XFER_RESULT_STALLED; + } + if (hcint & (HCINT_CHANNEL_HALTED | HCINT_AHB_ERR | HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR | + HCINT_BUFFER_NAK | HCINT_XCS_XACT_ERR | HCINT_DESC_ROLLOVER)) { + result = XFER_RESULT_FAILED; + } + + const uint8_t ep_addr = tu_edpt_addr(channel->hcchar_bm.ep_num, channel->hcchar_bm.ep_dir); + hcd_event_xfer_complete(channel->hcchar_bm.dev_addr, ep_addr, 0, result, in_isr); - HCINTn.XferCompl HCINTMSKn.XferComplMsk - | | - +---------- AND --------+ - | - HAINT.CHn HAINTMSK.CHn - | | - +---------- AND --------+ - | - GINTSTS.PrtInt GINTMSK.PrtInt - | | - +---------- AND --------+ - | - GAHBCFG.GblIntrMsk - | - IRQn + channel->hcint = hcint; // clear all interrupt flags + + // de-allocate channel by clearing haintmsk + dwc2->haintmsk &= ~TU_BIT(ch_id); + } + } +} + +/* Interrupt Hierarchy + HCINTn.XferCompl HCINTMSKn.XferComplMsk HPRT ConnDetect PrtEnChng OverCurChng + | | | | | + +---------- AND --------+ +------------ OR -----------+ + | | + HAINT.CHn HAINTMSK.CHn | + | | | + +---------- AND --------+ | + | | + GINTSTS.HCInt GINTMSK.HCInt GINTSTS.PrtInt GINTMSK.PrtInt + | | | | + +---------- AND ---------+ +---------- AND ---------+ + | | + +-------------------- OR ---------------------------+ + | + GAHBCFG.GblIntrMsk + | + IRQn */ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); @@ -336,10 +566,17 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { } if (int_status & GINTSTS_HPRTINT) { + // Host port interrupt: source is cleared in HPRT register TU_LOG1_HEX(dwc2->hprt); handle_hprt_irq(rhport, in_isr); } + if (int_status & GINTSTS_HCINT) { + // Host Channel interrupt: source is cleared in HCINT register + TU_LOG1_HEX(dwc2->hprt); + handle_channel_irq(rhport, in_isr); + } + // RxFIFO non-empty interrupt handling. if (int_status & GINTSTS_RXFLVL) { // RXFLVL bit is read-only diff --git a/src/tusb_option.h b/src/tusb_option.h index 3607a8c1a..f3a8bb724 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -255,7 +255,7 @@ #endif #ifndef CFG_TUH_DWC2_DMA - #define CFG_TUH_DWC2_DMA 0 + #define CFG_TUH_DWC2_DMA 1 #endif // Enable PIO-USB software host controller -- cgit v1.3.1 From df55d587dfe8b24d72eb20cd027cd54821d0bf09 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 29 Oct 2024 18:23:56 +0700 Subject: update handle in/out, separate allocated and xfer result to make it easier to manage. Fix channel disable/deallocated. --- src/common/tusb_types.h | 8 +- src/portable/synopsys/dwc2/hcd_dwc2.c | 191 ++++++++++++++++++---------------- 2 files changed, 103 insertions(+), 96 deletions(-) (limited to 'src/common') diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 3e5aa6818..92cebc42b 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -224,10 +224,10 @@ enum { // USB 2.0 Spec Table 9-7: Test Mode Selectors typedef enum { TUSB_FEATURE_TEST_J = 1, - TUSB_FEATURE_TEST_K, - TUSB_FEATURE_TEST_SE0_NAK, - TUSB_FEATURE_TEST_PACKET, - TUSB_FEATURE_TEST_FORCE_ENABLE, + TUSB_FEATURE_TEST_K = 2, + TUSB_FEATURE_TEST_SE0_NAK = 3, + TUSB_FEATURE_TEST_PACKET = 4, + TUSB_FEATURE_TEST_FORCE_ENABLE = 5, } tusb_feature_test_mode_t; //--------------------------------------------------------------------+ diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 568ce0c1f..5d843537b 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -52,12 +52,6 @@ enum { HCD_XFER_ERROR_MAX = 3 }; -enum { - HCD_XFER_STATE_UNALLOCATED = 0, - HCD_XFER_STATE_ACTIVE = 1, - HCD_XFER_STATE_DONE = 2, -}; - //-------------------------------------------------------------------- // //-------------------------------------------------------------------- @@ -79,7 +73,8 @@ typedef struct { // Additional info for each channel when it is active typedef struct { - volatile uint8_t state; + volatile bool allocated; + uint8_t result; uint8_t err_count; uint8_t* buffer; uint16_t total_bytes; @@ -128,9 +123,9 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - if (HCD_XFER_STATE_UNALLOCATED == xfer->state) { + if (!xfer->allocated) { tu_memclr(xfer, sizeof(hcd_xfer_t)); - xfer->state = HCD_XFER_STATE_ACTIVE; + xfer->allocated = true; return ch_id; } } @@ -138,23 +133,20 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_alloc(dwc2_regs_t* dwc2) { } TU_ATTR_ALWAYS_INLINE static inline void channel_dealloc(dwc2_regs_t* dwc2, uint8_t ch_id) { - _hcd_data.xfer[ch_id].state = HCD_XFER_STATE_UNALLOCATED; + _hcd_data.xfer[ch_id].allocated = false; dwc2->haintmsk &= ~TU_BIT(ch_id); } TU_ATTR_ALWAYS_INLINE static inline void channel_disable(dwc2_channel_t* channel) { channel->hcintmsk |= HCINT_HALTED; - - uint32_t hcchar = channel->hcchar & ~HCCHAR_CHENA; // skip w1s enabled bit - hcchar |= HCCHAR_CHDIS; - channel->hcchar = hcchar; + channel->hcchar |= HCCHAR_CHDIS | HCCHAR_CHENA; // must set both CHDIS and CHENA } // Find currently enabled channel. Note: EP0 is bidirectional TU_ATTR_ALWAYS_INLINE static inline uint8_t channel_find_enabled(dwc2_regs_t* dwc2, uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { - if (_hcd_data.xfer[ch_id].state != HCD_XFER_STATE_UNALLOCATED) { + if (_hcd_data.xfer[ch_id].allocated) { const dwc2_channel_char_t hcchar_bm = dwc2->channel[ch_id].hcchar_bm; if (hcchar_bm.dev_addr == dev_addr && hcchar_bm.ep_num == ep_num && (ep_num == 0 || hcchar_bm.ep_dir == ep_dir)) { return ch_id; @@ -169,6 +161,18 @@ TU_ATTR_ALWAYS_INLINE static inline bool edpt_is_periodic(uint8_t ep_type) { return ep_type == HCCHAR_EPTYPE_INTERRUPT || ep_type == HCCHAR_EPTYPE_ISOCHRONOUS; } +// Allocate a new endpoint +TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_alloc(void) { + for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + if (edpt->hcchar_bm.enable == 0) { + edpt->hcchar_bm.enable = 1; + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + // Find a endpoint that is opened previously with hcd_edpt_open() // Note: EP0 is bidirectional TU_ATTR_ALWAYS_INLINE static inline uint8_t edpt_find_opened(uint8_t dev_addr, uint8_t ep_num, uint8_t ep_dir) { @@ -402,38 +406,32 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, const tusb_desc_endpoint_t* hcd_devtree_get_info(dev_addr, &devtree_info); // find a free endpoint - for (uint32_t i = 0; i < CFG_TUH_DWC2_ENDPOINT_MAX; i++) { - hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; - dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; - dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm; - - if (hcchar_bm->enable == 0) { - hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); - hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); - hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); - hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; - hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* - hcchar_bm->err_multi_count = 0; - hcchar_bm->dev_addr = dev_addr; - hcchar_bm->odd_frame = 0; - hcchar_bm->disable = 0; - hcchar_bm->enable = 1; - - hcsplt_bm->hub_port = devtree_info.hub_port; - hcsplt_bm->hub_addr = devtree_info.hub_addr; - hcsplt_bm->xact_pos = 0; - hcsplt_bm->split_compl = 0; - hcsplt_bm->split_en = 0; - - edpt->next_data_toggle = HCTSIZ_PID_DATA0; - - // TODO not support split transaction yet - - return true; - } - } + const uint8_t ep_id = edpt_alloc(); + TU_ASSERT(ep_id < CFG_TUH_DWC2_ENDPOINT_MAX); + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; - return false; + dwc2_channel_char_t* hcchar_bm = &edpt->hcchar_bm; + hcchar_bm->ep_size = tu_edpt_packet_size(desc_ep); + hcchar_bm->ep_num = tu_edpt_number(desc_ep->bEndpointAddress); + hcchar_bm->ep_dir = tu_edpt_dir(desc_ep->bEndpointAddress); + hcchar_bm->low_speed_dev = (devtree_info.speed == TUSB_SPEED_LOW) ? 1 : 0; + hcchar_bm->ep_type = desc_ep->bmAttributes.xfer; // ep_type matches TUSB_XFER_* + hcchar_bm->err_multi_count = 0; + hcchar_bm->dev_addr = dev_addr; + hcchar_bm->odd_frame = 0; + hcchar_bm->disable = 0; + hcchar_bm->enable = 1; + + dwc2_channel_split_t* hcsplt_bm = &edpt->hcsplt_bm; + hcsplt_bm->hub_port = devtree_info.hub_port; + hcsplt_bm->hub_addr = devtree_info.hub_addr; + hcsplt_bm->xact_pos = 0; + hcsplt_bm->split_compl = 0; + hcsplt_bm->split_en = 0; + + edpt->next_data_toggle = HCTSIZ_PID_DATA0; + + return true; } // Submit a transfer, when complete hcd_event_xfer_complete() must be invoked @@ -473,12 +471,21 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * xfer->buffer = buffer; xfer->total_bytes = buflen; + xfer->result = XFER_RESULT_INVALID; - uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; if (dma_host_enabled(dwc2)) { channel->hcdma = (uint32_t) buffer; TU_ASSERT(false); // not yet supported } else { + uint32_t hcintmsk = HCINT_NAK | HCINT_XACT_ERR | HCINT_STALL | HCINT_XFER_COMPLETE | HCINT_DATATOGGLE_ERR; + if (ep_dir == TUSB_DIR_IN) { + hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; + } else { + hcintmsk |= HCINT_NYET; + } + channel->hcintmsk = hcintmsk; + dwc2->haintmsk |= TU_BIT(ch_id); + bool const is_period = edpt_is_periodic(hcchar_bm->ep_type); // enable channel for slave mode: @@ -489,8 +496,6 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * if (ep_dir == TUSB_DIR_IN) { TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; - - hcintmsk |= HCINT_BABBLE_ERR | HCINT_DATATOGGLE_ERR; } else { channel->hcchar |= HCCHAR_CHENA; if (buflen > 0) { @@ -498,14 +503,9 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t * // And write packet in the interrupt handler dwc2->gintmsk |= (is_period ? GINTSTS_PTX_FIFO_EMPTY : GINTSTS_NPTX_FIFO_EMPTY); } - - hcintmsk |= HCINT_NYET; } } - channel->hcintmsk = hcintmsk; - dwc2->haintmsk |= TU_BIT(ch_id); - return true; } @@ -527,7 +527,7 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; // disable also require request queue if (request_queue_avail(dwc2, edpt_is_periodic(edpt->hcchar_bm.ep_type))) { - channel->hcchar |= HCCHAR_CHDIS; + channel_disable(channel); } else { TU_BREAKPOINT(); } @@ -669,13 +669,14 @@ TU_ATTR_ALWAYS_INLINE static inline void handle_hprt_irq(uint8_t rhport, bool in dwc2->hprt = hprt; // clear interrupt } -xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +bool handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - xfer_result_t result = XFER_RESULT_INVALID; + + bool is_done = false; if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; + xfer->result = XFER_RESULT_SUCCESS; channel_disable(channel); channel->hcintmsk &= ~HCINT_ACK; } else if (hcint & (HCINT_XACT_ERR | HCINT_BABBLE_ERR | HCINT_STALL)) { @@ -686,8 +687,11 @@ xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_ } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; - if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { - channel_dealloc(dwc2, ch_id); + if (xfer->result != XFER_RESULT_INVALID) { + is_done = true; + } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + xfer->result = XFER_RESULT_FAILED; + is_done = true; } else { // Re-initialize Channel } @@ -698,25 +702,26 @@ xfer_result_t handle_channel_slave_in(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_ xfer->err_count = 0; } else if (hcint & HCINT_NAK) { // NAK received, re-enable channel if request queue is available - TU_ASSERT(request_queue_avail(dwc2, is_period), XFER_RESULT_INVALID); + TU_ASSERT(request_queue_avail(dwc2, is_period)); channel->hcchar |= HCCHAR_CHENA; } - return result; + return is_done; } -xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { +bool handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is_period, uint32_t hcint) { (void) is_period; hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; dwc2_channel_t* channel = &dwc2->channel[ch_id]; - xfer_result_t result = XFER_RESULT_INVALID; + + bool is_notify = false; if (hcint & HCINT_XFER_COMPLETE) { - result = XFER_RESULT_SUCCESS; + is_notify = true; + xfer->result = XFER_RESULT_SUCCESS; channel->hcintmsk &= ~HCINT_ACK; - channel_dealloc(dwc2, ch_id); } else if (hcint & HCINT_STALL) { - xfer->state = HCD_XFER_STATE_DONE; + xfer->result = XFER_RESULT_STALLED; channel_disable(channel); } else if (hcint & (HCINT_NAK | HCINT_XACT_ERR | HCINT_NYET)) { channel_disable(channel); @@ -728,9 +733,11 @@ xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is } } else if (hcint & HCINT_HALTED) { channel->hcintmsk &= ~HCINT_HALTED; - if (xfer->state == HCD_XFER_STATE_DONE || channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { - result = (xfer->state == HCD_XFER_STATE_DONE ? XFER_RESULT_STALLED : XFER_RESULT_FAILED); - channel_dealloc(dwc2, ch_id); + if (xfer->result != XFER_RESULT_INVALID) { + is_notify = true; + } else if (channel->hcchar_bm.err_multi_count == HCD_XFER_ERROR_MAX) { + xfer->result = XFER_RESULT_FAILED; + is_notify = true; } else { // Re-initialize Channel (Do ping protocol for HS) } @@ -739,7 +746,7 @@ xfer_result_t handle_channel_slave_out(dwc2_regs_t* dwc2, uint8_t ch_id, bool is channel->hcintmsk &= ~HCINT_ACK; } - return result; + return is_notify; } void handle_channel_irq(uint8_t rhport, bool in_isr) { @@ -760,19 +767,20 @@ void handle_channel_irq(uint8_t rhport, bool in_isr) { if (is_dma) { } else { - xfer_result_t result; + bool is_done; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { - result = handle_channel_slave_out(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_slave_out(dwc2, ch_id, is_period, hcint); } else { - result = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); + is_done = handle_channel_slave_in(dwc2, ch_id, is_period, hcint); } - // Transfer is complete (success, stalled, failed) or channel is disabled - if (result != XFER_RESULT_INVALID ) { - // notify usbh if transfer is complete (skip if channel is disabled) + // notify usbh if done + if (is_done) { const uint8_t ep_addr = tu_edpt_addr(hcchar_bm.ep_num, hcchar_bm.ep_dir); - hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, result, in_isr); + hcd_event_xfer_complete(hcchar_bm.dev_addr, ep_addr, xfer->total_bytes, xfer->result, in_isr); + + channel_dealloc(dwc2, ch_id); } } @@ -789,7 +797,7 @@ bool handle_txfifo_empty(dwc2_regs_t* dwc2, bool is_periodic) { const uint8_t max_channel = DWC2_CHANNEL_COUNT(dwc2); for (uint8_t ch_id = 0; ch_id < max_channel; ch_id++) { hcd_xfer_t* xfer = &_hcd_data.xfer[ch_id]; - if (xfer->state == HCD_XFER_STATE_ACTIVE) { + if (xfer->allocated) { dwc2_channel_t* channel = &dwc2->channel[ch_id]; const dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; if (hcchar_bm.ep_dir == TUSB_DIR_OUT) { @@ -846,6 +854,18 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { handle_hprt_irq(rhport, in_isr); } + // RxFIFO non-empty interrupt handling, must be handled before HCINT + if (int_status & GINTSTS_RXFLVL) { + // RXFLVL bit is read-only + dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading + + do { + handle_rxflvl_irq(rhport); // read all packets + } while(dwc2->gintsts & GINTSTS_RXFLVL); + + dwc2->gintmsk |= GINTMSK_RXFLVLM; + } + if (int_status & GINTSTS_HCINT) { // Host Channel interrupt: source is cleared in HCINT register handle_channel_irq(rhport, in_isr); @@ -859,19 +879,6 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) { dwc2->gintmsk &= ~GINTSTS_NPTX_FIFO_EMPTY; } } - - // RxFIFO non-empty interrupt handling. - if (int_status & GINTSTS_RXFLVL) { - // RXFLVL bit is read-only - dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading - - do { - handle_rxflvl_irq(rhport); // read all packets - } while(dwc2->gintsts & GINTSTS_RXFLVL); - - dwc2->gintmsk |= GINTMSK_RXFLVLM; - } - } #endif -- cgit v1.3.1 From 80ad7c4e87b74b1bccc4f566a3cee8d1ff0fafc9 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 4 Nov 2024 17:20:58 +0700 Subject: add tusb_time_millis_api() and default/weak tusb_time_delay_ms_api(). Which is required for host and some device without RTOS. --- src/common/tusb_private.h | 14 ++++++++---- src/common/tusb_types.h | 2 +- src/tusb.c | 29 ++++++++++++++++-------- src/tusb.h | 7 ++++-- test/unit-test/test/device/msc/test_msc_device.c | 4 ++++ test/unit-test/test/device/usbd/test_usbd.c | 4 ++++ 6 files changed, 44 insertions(+), 16 deletions(-) (limited to 'src/common') diff --git a/src/common/tusb_private.h b/src/common/tusb_private.h index 8a479c042..c71775abb 100644 --- a/src/common/tusb_private.h +++ b/src/common/tusb_private.h @@ -24,9 +24,8 @@ * This file is part of the TinyUSB stack. */ - -#ifndef _TUSB_PRIVATE_H_ -#define _TUSB_PRIVATE_H_ +#ifndef TUSB_PRIVATE_H_ +#define TUSB_PRIVATE_H_ // Internal Helper used by Host and Device Stack @@ -34,6 +33,13 @@ extern "C" { #endif +#define TUP_USBIP_CONTROLLER_NUM 2 +extern tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM]; + +//--------------------------------------------------------------------+ +// Endpoint +//--------------------------------------------------------------------+ + typedef struct TU_ATTR_PACKED { volatile uint8_t busy : 1; volatile uint8_t stalled : 1; @@ -163,4 +169,4 @@ bool tu_edpt_stream_peek(tu_edpt_stream_t* s, uint8_t* ch) { } #endif -#endif /* _TUSB_PRIVATE_H_ */ +#endif diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 92cebc42b..d045a3c8f 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -264,7 +264,7 @@ typedef enum { } microsoft_os_20_type_t; enum { - CONTROL_STAGE_IDLE, + CONTROL_STAGE_IDLE = 0, CONTROL_STAGE_SETUP, CONTROL_STAGE_DATA, CONTROL_STAGE_ACK diff --git a/src/tusb.c b/src/tusb.c index 6a0cc0805..a516cb9a1 100644 --- a/src/tusb.c +++ b/src/tusb.c @@ -39,14 +39,25 @@ #include "host/usbh_pvt.h" #endif -#define TUP_USBIP_CONTROLLER_NUM 2 - -static tusb_role_t _rhport_role[TUP_USBIP_CONTROLLER_NUM] = { 0 }; +tusb_role_t _tusb_rhport_role[TUP_USBIP_CONTROLLER_NUM] = { 0 }; + +//-------------------------------------------------------------------- +// Weak/Default API, can be overwritten by Application +//-------------------------------------------------------------------- + +TU_ATTR_WEAK void tusb_time_delay_ms_api(uint32_t ms) { +#if CFG_TUSB_OS != OPT_OS_NONE + osal_task_delay(ms); +#else + // delay using millis() (if implemented) and/or frame number if possible + const uint32_t time_ms = tusb_time_millis_api(); + while ((tusb_time_millis_api() - time_ms) < ms) {} +#endif +} //--------------------------------------------------------------------+ // Public API //--------------------------------------------------------------------+ - bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // backward compatible called with tusb_init(void) #if defined(TUD_OPT_RHPORT) || defined(TUH_OPT_RHPORT) @@ -58,7 +69,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { .speed = TUD_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL }; TU_ASSERT ( tud_rhport_init(rhport, &dev_init) ); - _rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE; + _tusb_rhport_role[TUD_OPT_RHPORT] = TUSB_ROLE_DEVICE; #endif #if CFG_TUH_ENABLED && defined(TUH_OPT_RHPORT) @@ -68,7 +79,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { .speed = TUH_OPT_HIGH_SPEED ? TUSB_SPEED_HIGH : TUSB_SPEED_FULL }; TU_ASSERT( tuh_rhport_init(TUH_OPT_RHPORT, &host_init) ); - _rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST; + _tusb_rhport_role[TUH_OPT_RHPORT] = TUSB_ROLE_HOST; #endif return true; @@ -77,7 +88,7 @@ bool tusb_rhport_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // new API with explicit rhport and role TU_ASSERT(rhport < TUP_USBIP_CONTROLLER_NUM && rh_init->role != TUSB_ROLE_INVALID); - _rhport_role[rhport] = rh_init->role; + _tusb_rhport_role[rhport] = rh_init->role; #if CFG_TUD_ENABLED if (rh_init->role == TUSB_ROLE_DEVICE) { @@ -112,14 +123,14 @@ void tusb_int_handler(uint8_t rhport, bool in_isr) { TU_VERIFY(rhport < TUP_USBIP_CONTROLLER_NUM,); #if CFG_TUD_ENABLED - if (_rhport_role[rhport] == TUSB_ROLE_DEVICE) { + if (_tusb_rhport_role[rhport] == TUSB_ROLE_DEVICE) { (void) in_isr; dcd_int_handler(rhport); } #endif #if CFG_TUH_ENABLED - if (_rhport_role[rhport] == TUSB_ROLE_HOST) { + if (_tusb_rhport_role[rhport] == TUSB_ROLE_HOST) { hcd_int_handler(rhport, in_isr); } #endif diff --git a/src/tusb.h b/src/tusb.h index 3ffaafded..cb6021b33 100644 --- a/src/tusb.h +++ b/src/tusb.h @@ -169,8 +169,11 @@ void tusb_int_handler(uint8_t rhport, bool in_isr); // API Implemented by user //--------------------------------------------------------------------+ -// Get current milliseconds, maybe required by some port with no RTOS -uint32_t tusb_time_millis(void); +// Get current milliseconds, required by some port/configuration without RTOS +uint32_t tusb_time_millis_api(void); + +// Delay in milliseconds, use tusb_time_millis_api() by default. required by some port/configuration with no RTOS +void tusb_time_delay_ms_api(uint32_t ms); #ifdef __cplusplus } diff --git a/test/unit-test/test/device/msc/test_msc_device.c b/test/unit-test/test/device/msc/test_msc_device.c index f2a4fa295..55b690313 100644 --- a/test/unit-test/test/device/msc/test_msc_device.c +++ b/test/unit-test/test/device/msc/test_msc_device.c @@ -42,6 +42,10 @@ TEST_FILE("msc_device.c") // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +uint32_t tusb_time_millis_api(void) { + return 0; +} + enum { EDPT_CTRL_OUT = 0x00, diff --git a/test/unit-test/test/device/usbd/test_usbd.c b/test/unit-test/test/device/usbd/test_usbd.c index 9879cd4ba..e7c6a8578 100644 --- a/test/unit-test/test/device/usbd/test_usbd.c +++ b/test/unit-test/test/device/usbd/test_usbd.c @@ -39,6 +39,10 @@ TEST_FILE("usbd_control.c") // MACRO TYPEDEF CONSTANT ENUM DECLARATION //--------------------------------------------------------------------+ +uint32_t tusb_time_millis_api(void) { + return 0; +} + enum { EDPT_CTRL_OUT = 0x00, -- cgit v1.3.1 From 5d00488907e5591f35772769a917cf6c3ff97181 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 8 Nov 2024 17:23:43 +0700 Subject: get hcd work esp32p4 but only in slave mode. In DMA channel's HCDMA is increased but data transfer over USB is not correct. --- hw/bsp/espressif/boards/family.c | 2 ++ hw/bsp/espressif/family.cmake | 8 +++++++- src/common/tusb_mcu.h | 3 +++ src/portable/synopsys/dwc2/dwc2_esp32.h | 5 +++-- src/portable/synopsys/dwc2/dwc2_type.h | 9 +++++++++ src/tusb_option.h | 13 ++++++++++--- 6 files changed, 34 insertions(+), 6 deletions(-) (limited to 'src/common') diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c index 7f4535d49..0b1e8badb 100644 --- a/hw/bsp/espressif/boards/family.c +++ b/hw/bsp/espressif/boards/family.c @@ -179,8 +179,10 @@ bool usb_init(void) { // maybe we can use USB_OTG_MODE_DEFAULT and switch using dwc2 driver #if CFG_TUD_ENABLED .otg_mode = USB_OTG_MODE_DEVICE, + .otg_speed = BOARD_TUD_RHPORT ? USB_PHY_SPEED_HIGH : USB_PHY_SPEED_FULL, #elif CFG_TUH_ENABLED .otg_mode = USB_OTG_MODE_HOST, + .otg_speed= BOARD_TUH_RHPORT ? USB_PHY_SPEED_HIGH : USB_PHY_SPEED_FULL, #endif }; diff --git a/hw/bsp/espressif/family.cmake b/hw/bsp/espressif/family.cmake index aaa611fac..daa12cdb4 100644 --- a/hw/bsp/espressif/family.cmake +++ b/hw/bsp/espressif/family.cmake @@ -10,9 +10,15 @@ set(RHPORT_SPEED OPT_MODE_FULL_SPEED OPT_MODE_HIGH_SPEED) if (NOT DEFINED RHPORT_DEVICE) if (IDF_TARGET STREQUAL "esp32p4") set(RHPORT_DEVICE 1) - set(RHPORT_HOST 1) else () set(RHPORT_DEVICE 0) + endif () +endif() + +if (NOT DEFINED RHPORT_HOST) + if (IDF_TARGET STREQUAL "esp32p4") + set(RHPORT_HOST 1) + else () set(RHPORT_HOST 0) endif () endif() diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index c77afecad..41f552d33 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -169,6 +169,7 @@ defined (STM32F107xB) || defined (STM32F107xC) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_STM32 + #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 #define TUP_DCD_ENDPOINT_MAX 4 #elif defined(STM32F102x6) || defined(STM32F102xB) || \ @@ -343,12 +344,14 @@ #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_ESP32 #define TUP_DCD_ENDPOINT_MAX 7 // only 5 TX FIFO for endpoint IN + #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 // TODO currently have issue with buffer DMA with espressif #elif TU_CHECK_MCU(OPT_MCU_ESP32P4) #define TUP_USBIP_DWC2 #define TUP_USBIP_DWC2_ESP32 #define TUP_RHPORT_HIGHSPEED 1 // port0 FS, port1 HS #define TUP_DCD_ENDPOINT_MAX 16 // FS 7 ep, HS 16 ep + #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 0 // TODO currently have issue with buffer DMA with espressif #elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) #if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index a86587786..42ab4b80f 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -100,14 +100,15 @@ TU_ATTR_ALWAYS_INLINE static inline void dwc2_remote_wakeup_delay(void) { TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_init(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { (void)dwc2; (void)hs_phy_type; - // nothing to do + // maybe usb_utmi_hal_init() + } // MCU specific PHY update, it is called AFTER init() and core reset TU_ATTR_ALWAYS_INLINE static inline void dwc2_phy_update(dwc2_regs_t* dwc2, uint8_t hs_phy_type) { (void)dwc2; (void)hs_phy_type; - // nothing to do + // maybe usb_utmi_hal_disable() } #ifdef __cplusplus diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index dfd61bc32..1247f19a3 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -541,13 +541,22 @@ typedef struct { volatile uint32_t gotgctl; // 000 OTG Control and Status volatile dwc2_gotgctl_t gotgctl_bm; }; + union { volatile uint32_t gotgint; // 004 OTG Interrupt + volatile dwc2_gotgint_t gotgint_bm; + }; + union { volatile uint32_t gahbcfg; // 008 AHB Configuration + volatile dwc2_gahbcfg_t gahbcfg_bm; + }; union { volatile uint32_t gusbcfg; // 00c USB Configuration volatile dwc2_gusbcfg_t gusbcfg_bm; }; + union { volatile uint32_t grstctl; // 010 Reset + volatile dwc2_grstctl_t grstctl_bm; + }; volatile uint32_t gintsts; // 014 Interrupt volatile uint32_t gintmsk; // 018 Interrupt Mask volatile uint32_t grxstsr; // 01c Receive Status Debug Read diff --git a/src/tusb_option.h b/src/tusb_option.h index b193d9c09..e9b6a5806 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -241,6 +241,8 @@ #include "tusb_config.h" #endif +#include "common/tusb_mcu.h" + //--------------------------------------------------------------------+ // USBIP //--------------------------------------------------------------------+ @@ -256,12 +258,18 @@ // Enable DWC2 Slave mode for host #ifndef CFG_TUH_DWC2_SLAVE_ENABLE - #define CFG_TUH_DWC2_SLAVE_ENABLE 1 + #ifndef CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT + #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT 1 + #endif + #define CFG_TUH_DWC2_SLAVE_ENABLE CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT #endif // Enable DWC2 DMA for host #ifndef CFG_TUH_DWC2_DMA_ENABLE - #define CFG_TUH_DWC2_DMA_ENABLE 1 + #ifndef CFG_TUH_DWC2_DMA_ENABLE_DEFAULT + #define CFG_TUH_DWC2_DMA_ENABLE_DEFAULT 1 + #endif + #define CFG_TUH_DWC2_DMA_ENABLE CFG_TUH_DWC2_DMA_ENABLE_DEFAULT #endif // Enable PIO-USB software host controller @@ -278,7 +286,6 @@ #define CFG_TUH_MAX3421 0 #endif -#include "common/tusb_mcu.h" //-------------------------------------------------------------------- // RootHub Mode detection -- cgit v1.3.1