diff --git a/hw/bsp/stm32l476disco/board.mk b/hw/bsp/stm32l476disco/board.mk index 28824efdc..8d9e267cf 100644 --- a/hw/bsp/stm32l476disco/board.mk +++ b/hw/bsp/stm32l476disco/board.mk @@ -21,8 +21,9 @@ CFLAGS += -Wno-error=maybe-uninitialized -Wno-error=cast-align # All source paths should be relative to the top level. LD_FILE = hw/bsp/$(BOARD)/STM32L476VGTx_FLASH.ld +#src/portable/st/synopsys/dcd_synopsys.c SRC_C += \ - src/portable/st/synopsys/dcd_synopsys.c \ + src/portable/synopsys/dwc2/dcd_dwc2.c \ $(ST_CMSIS)/Source/Templates/system_stm32$(ST_FAMILY)xx.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal.c \ $(ST_HAL_DRIVER)/Src/stm32$(ST_FAMILY)xx_hal_cortex.c \ @@ -52,5 +53,4 @@ JLINK_DEVICE = stm32l476vg STM32Prog = STM32_Programmer_CLI # flash target using on-board stlink -flash: $(BUILD)/$(PROJECT).elf - $(STM32Prog) --connect port=swd --write $< --go +flash: flash-stlink diff --git a/hw/bsp/stm32l476disco/stm32l476disco.c b/hw/bsp/stm32l476disco/stm32l476disco.c index b18846685..bc1b6c929 100644 --- a/hw/bsp/stm32l476disco/stm32l476disco.c +++ b/hw/bsp/stm32l476disco/stm32l476disco.c @@ -186,8 +186,12 @@ void board_init(void) /* Enable USB FS Clock */ __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); - // Enable VBUS sense (B device) via pin PA9 - USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; +// // Enable VBUS sense (B device) via pin PA9 +// USB_OTG_FS->GCCFG |= USB_OTG_GCCFG_VBDEN; + + // L476Disco use general GPIO PC11 for VBUS sensing instead of dedicated PA9 as others + // Disable VBUS Sense and force device mode + USB_OTG_FS->GCCFG &= ~USB_OTG_GCCFG_VBDEN; } //--------------------------------------------------------------------+ diff --git a/src/device/dcd_attr.h b/src/device/dcd_attr.h index 70854f9c6..952a8b8c8 100644 --- a/src/device/dcd_attr.h +++ b/src/device/dcd_attr.h @@ -125,6 +125,7 @@ #elif TU_CHECK_MCU(OPT_MCU_STM32L4) #if defined (STM32L475xx) || defined (STM32L476xx) || \ defined (STM32L485xx) || defined (STM32L486xx) || defined (STM32L496xx) || \ + defined (STM32L4A6xx) || defined (STM32L4P5xx) || defined (STM32L4Q5xx) || \ defined (STM32L4R5xx) || defined (STM32L4R7xx) || defined (STM32L4R9xx) || \ defined (STM32L4S5xx) || defined (STM32L4S7xx) || defined (STM32L4S9xx) #define DCD_ATTR_ENDPOINT_MAX 6 diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index e2a999ac0..83abc635b 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -123,8 +123,8 @@ static void bus_reset(uint8_t rhport) dwc2->epout[n].doepctl |= DOEPCTL_SNAK; } - // 2. Un-mask interrupt bits - dwc2->daintmsk = (1 << DAINTMSK_OEPM_Pos) | (1 << DAINTMSK_IEPM_Pos); + // 2. Set up interrupt mask + dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; @@ -472,9 +472,6 @@ void dcd_init (uint8_t rhport) // Restart PHY clock dwc2->pcgctl &= ~(PCGCTL_STOPPCLK | PCGCTL_GATEHCLK | PCGCTL_PWRCLMP | PCGCTL_RSTPDWNMODULE); - // Force device mode - dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; - /* Set HS/FS Timeout Calibration to 7 (max available value). * The number of PHY clocks that the application programs in * this field is added to the high/full speed interpacket timeout @@ -485,13 +482,16 @@ void dcd_init (uint8_t rhport) */ dwc2->gusbcfg |= (7ul << GUSBCFG_TOCAL_Pos); + // Force device mode + dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD; + + // Clear A override, force B Valid + dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + // If USB host misbehaves during status portion of control xfer // (non zero-length packet), send STALL back and discard. dwc2->dcfg |= DCFG_NZLSOHSK; - // Clear A,B, VBus valid override - dwc2->gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN); - // Clear all interrupts dwc2->gintsts |= dwc2->gintsts; dwc2->gotgint |= dwc2->gotgint; @@ -609,7 +609,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | (xfer->max_size << DOEPCTL_MPSIZ_Pos); - dwc2->daintmsk |= (1 << (DAINTMSK_OEPM_Pos + epnum)); + dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum); } else { @@ -696,19 +696,21 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t if(epnum == 0) { ep0_pending[dir] = total_bytes; + // Schedule the first transaction for EP0 transfer edpt_schedule_packets(rhport, epnum, dir, 1, ep0_pending[dir]); - return true; } + else + { + uint16_t num_packets = (total_bytes / xfer->max_size); + uint16_t const short_packet_size = total_bytes % xfer->max_size; - uint16_t num_packets = (total_bytes / xfer->max_size); - uint16_t const short_packet_size = total_bytes % xfer->max_size; + // Zero-size packet is special case. + if ( (short_packet_size > 0) || (total_bytes == 0) ) num_packets++; - // Zero-size packet is special case. - if ( short_packet_size > 0 || (total_bytes == 0) ) num_packets++; - - // Schedule packets to be sent within interrupt - edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + // Schedule packets to be sent within interrupt + edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + } return true; } @@ -924,21 +926,49 @@ static void write_fifo_packet(uint8_t rhport, uint8_t fifo_num, uint8_t const * static void handle_rxflvl_irq(uint8_t rhport) { dwc2_regs_t * dwc2 = DWC2_REG(rhport); - volatile uint32_t * rx_fifo = dwc2->fifo[0]; + volatile uint32_t const * rx_fifo = dwc2->fifo[0]; // Pop control word off FIFO - uint32_t ctl_word = dwc2->grxstsp; - uint8_t pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk ) >> GRXSTSP_PKTSTS_Pos; - uint8_t epnum = (ctl_word & GRXSTSP_EPNUM_Msk ) >> GRXSTSP_EPNUM_Pos; - uint16_t bcnt = (ctl_word & GRXSTSP_BCNT_Msk ) >> GRXSTSP_BCNT_Pos; + uint32_t const ctl_word = dwc2->grxstsp; + uint8_t const pktsts = (ctl_word & GRXSTSP_PKTSTS_Msk ) >> GRXSTSP_PKTSTS_Pos; + uint8_t const epnum = (ctl_word & GRXSTSP_EPNUM_Msk ) >> GRXSTSP_EPNUM_Pos; + uint16_t const bcnt = (ctl_word & GRXSTSP_BCNT_Msk ) >> GRXSTSP_BCNT_Pos; + + dwc2_epout_t* epout = &dwc2->epout[epnum]; + +#if CFG_TUSB_DEBUG >= (DWC2_DEBUG + 1) + const char * pktsts_str[] = + { + "ASSERT", "Global NAK (ISR)", "Out Data Received", "Out Transfer Complete (ISR)", + "Setup Complete (ISR)", "ASSERT", "Setup Data Received" + }; + TU_LOG_LOCATION(); + TU_LOG(DWC2_DEBUG, " EP %02X, Byte Count %u, %s\r\n", epnum, bcnt, pktsts_str[pktsts]); + TU_LOG(DWC2_DEBUG, " daint = %08lX, doepint = %04lX\r\n", dwc2->daint, epout->doepint); +#endif switch ( pktsts ) { - case 0x01: // Global OUT NAK (Interrupt) + // Global OUT NAK: do nothign + case GRXSTS_PKTSTS_GLOBALOUTNAK: break; + + case GRXSTS_PKTSTS_SETUPRX: + // Setup packet received + + // We can receive up to three setup packets in succession, but + // only the last one is valid. + _setup_packet[0] = (*rx_fifo); + _setup_packet[1] = (*rx_fifo); break; - case 0x02: // Out packet received + case GRXSTS_PKTSTS_SETUPDONE: + // Setup packet done (Interrupt) + epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + break; + + case GRXSTS_PKTSTS_OUTRX: { + // Out packet received xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); // Read packet off RxFIFO @@ -959,7 +989,7 @@ static void handle_rxflvl_irq(uint8_t rhport) // Truncate transfer length in case of short packet if ( bcnt < xfer->max_size ) { - xfer->total_len -= (dwc2->epout[epnum].doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; + xfer->total_len -= (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; if ( epnum == 0 ) { xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; @@ -969,18 +999,28 @@ static void handle_rxflvl_irq(uint8_t rhport) } break; - case 0x03: // Out packet done (Interrupt) - break; + // Out packet done (Interrupt) + case GRXSTS_PKTSTS_OUTDONE: + // Occurred on STM32L47 with dwc2 version 3.10a but not found on other version like 2.80a or 3.30a + // May (or not) be 3.10a specific feature/bug or depending on MCU configuration + // XFRC complete is additionally generated when + // - setup packet is received + // - complete the data stage of control write is complete + if ((epnum == 0) && (bcnt == 0) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) + { + if (epout->doepint & DOEPINT_STPKTRX) + { + // skip this "no-data" transfer complete event + // STPKTRX will be clear later by setup received handler + epout->doepint = DOEPINT_XFRC; + } - case 0x04: // Setup packet done (Interrupt) - dwc2->epout[epnum].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); - break; - - case 0x06: // Setup packet recvd - // We can receive up to three setup packets in succession, but - // only the last one is valid. - _setup_packet[0] = (*rx_fifo); - _setup_packet[1] = (*rx_fifo); + if (epout->doepint & DOEPINT_OTEPSPR) + { + // skip this "no-data" transfer complete event + epout->doepint = DOEPINT_XFRC | DOEPINT_OTEPSPR; + } + } break; default: // Invalid @@ -992,27 +1032,38 @@ static void handle_rxflvl_irq(uint8_t rhport) static void handle_epout_irq (uint8_t rhport) { dwc2_regs_t *dwc2 = DWC2_REG(rhport); - dwc2_epout_t* epout = dwc2->epout; // DAINT for a given EP clears when DOEPINTx is cleared. // OEPINT will be cleared when DAINT's out bits are cleared. for ( uint8_t n = 0; n < DWC2_EP_MAX; n++ ) { - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); - - if ( dwc2->daint & (1 << (DAINT_OEPINT_Pos + n)) ) + if ( dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + n) ) { + dwc2_epout_t* epout = &dwc2->epout[n]; + + uint32_t const doepint = epout->doepint; + // SETUP packet Setup Phase done. - if ( epout[n].doepint & DOEPINT_STUP ) + if ( doepint & DOEPINT_STUP ) { - epout[n].doepint = DOEPINT_STUP; - dcd_event_setup_received(rhport, (uint8_t*) &_setup_packet[0], true); + uint32_t clear_flag = DOEPINT_STUP; + + // STPKTRX is only available for version from 3_00a + if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) + { + clear_flag |= DOEPINT_STPKTRX; + } + + epout->doepint = clear_flag; + dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); } // OUT XFER complete - if ( epout[n].doepint & DOEPINT_XFRC ) + if ( epout->doepint & DOEPINT_XFRC ) { - epout[n].doepint = DOEPINT_XFRC; + epout->doepint = DOEPINT_XFRC; + + xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); // EP0 can only handle one packet if ( (n == 0) && ep0_pending[TUSB_DIR_OUT] ) @@ -1038,11 +1089,11 @@ static void handle_epin_irq (uint8_t rhport) // IEPINT will be cleared when DAINT's out bits are cleared. for ( uint8_t n = 0; n < DWC2_EP_MAX; n++ ) { - xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); - - if ( dwc2->daint & (1 << (DAINT_IEPINT_Pos + n)) ) + if ( dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n) ) { // IN XFER complete (entire xfer). + xfer_ctl_t *xfer = XFER_CTL_BASE(n, TUSB_DIR_IN); + if ( epin[n].diepint & DIEPINT_XFRC ) { epin[n].diepint = DIEPINT_XFRC; @@ -1215,14 +1266,14 @@ void dcd_int_handler(uint8_t rhport) // OUT endpoint interrupt handling. if(int_status & GINTSTS_OEPINT) { - // OEPINT is read-only + // OEPINT is read-only, clear using DOEPINTn handle_epout_irq(rhport); } // IN endpoint interrupt handling. if(int_status & GINTSTS_IEPINT) { - // IEPINT bit read-only + // IEPINT bit read-only, clear using DIEPINTn handle_epin_irq(rhport); } diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 583a7274a..6e52ebc7b 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -907,6 +907,17 @@ TU_VERIFY_STATIC(offsetof(dwc2_regs_t, fifo ) == 0x1000, "incorrect size"); #define GRXSTSP_PKTSTS_Msk (0xFUL << GRXSTSP_PKTSTS_Pos) // 0x001E0000 */ #define GRXSTSP_PKTSTS GRXSTSP_PKTSTS_Msk // OUT EP interrupt mask bits */ +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 + + /******************** Bit definition for DAINTMSK register ********************/ #define DAINTMSK_IEPM_Pos (0U) #define DAINTMSK_IEPM_Msk (0xFFFFUL << DAINTMSK_IEPM_Pos) // 0x0000FFFF */