diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake index aa5e037d1c..57fa63261e 100644 --- a/hw/bsp/family_support.cmake +++ b/hw/bsp/family_support.cmake @@ -289,6 +289,11 @@ function(family_add_tinyusb TARGET OPT_MCU RTOS) ) endif () + # compile define from command line + if(DEFINED CFLAGS_CLI) + target_compile_options(${TARGET}-tinyusb PUBLIC ${CFLAGS_CLI}) + endif() + endfunction() # Add bin/hex output diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 2a517f7a7c..3ee04b6087 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -548,7 +548,7 @@ #define TUP_DCD_EDPT_ISO_ALLOC #endif -#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA == 0 +#if defined(TUP_USBIP_DWC2) // && CFG_TUD_DWC2_DMA_ENABLE == 0 #define TUP_MEM_CONST_ADDR #endif diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index fe67de8dc1..2b3ef096f9 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -37,6 +37,12 @@ #include "device/dcd.h" #include "dwc2_common.h" +#if TU_CHECK_MCU(OPT_MCU_GD32VF103) + #define DWC2_EP_COUNT(_dwc2) DWC2_EP_MAX +#else + #define DWC2_EP_COUNT(_dwc2) ((_dwc2)->ghwcfg2_bm.num_dev_ep) +#endif + //--------------------------------------------------------------------+ // MACRO TYPEDEF CONSTANT ENUM //--------------------------------------------------------------------+ @@ -71,7 +77,7 @@ static bool _sof_en; 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; + return CFG_TUD_DWC2_DMA_ENABLE && dwc2->ghwcfg2_bm.arch == GHWCFG2_ARCH_INTERNAL_DMA; } static void dma_setup_prepare(uint8_t rhport) { @@ -211,24 +217,32 @@ static void dfifo_device_init(uint8_t rhport) { //-------------------------------------------------------------------- static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); - uint8_t const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); + const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress); + const uint8_t dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, dir); xfer->max_size = tu_edpt_packet_size(p_endpoint_desc); xfer->interval = p_endpoint_desc->bInterval; - // USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints. - uint32_t epctl = (1 << DOEPCTL_USBAEP_Pos) | - (p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) | - (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) | - (xfer->max_size << DOEPCTL_MPSIZ_Pos); + // Endpoint control + union { + uint32_t value; + dwc2_depctl_t bm; + } depctl; + depctl.value = 0; + + depctl.bm.mps = xfer->max_size; + depctl.bm.active = 1; + depctl.bm.type = p_endpoint_desc->bmAttributes.xfer; + if (p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) { + depctl.bm.set_data0_iso_even = 1; + } if (dir == TUSB_DIR_IN) { - epctl |= (epnum << DIEPCTL_TXFNUM_Pos); + depctl.bm.tx_fifo_num = epnum; } - dwc2_dep_t* dep = &dwc2->ep[1 - dir][epnum]; - dep->ctl = epctl; + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; + dep->ctl = depctl.value; dwc2->daintmsk |= TU_BIT(epnum + DAINT_SHIFT(dir)); } @@ -238,7 +252,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); const uint8_t epnum = tu_edpt_number(ep_addr); const uint8_t dir = tu_edpt_dir(ep_addr); - dwc2_dep_t* dep = &dwc2->ep[1 - dir][epnum]; + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; if (dir == TUSB_DIR_IN) { // Only disable currently enabled non-control endpoint @@ -282,124 +296,66 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) { } } -// Start of Bus Reset -static void bus_reset(uint8_t rhport) { - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; - - tu_memclr(xfer_status, sizeof(xfer_status)); - - _sof_en = false; - _allocated_ep_in_count = 1; - - // 1. NAK for all OUT endpoints - for (uint8_t n = 0; n < ep_count; n++) { - dwc2->epout[n].doepctl |= DOEPCTL_SNAK; - } - - // 2. Disable all IN endpoints - for (uint8_t n = 0; n < ep_count; n++) { - if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { - dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; - } - } - - dfifo_flush_tx(dwc2, 0x10); // all tx fifo - dfifo_flush_rx(dwc2); - - // 3. Set up interrupt mask for EP0 - dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); - dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; - dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; - - // 4. Set up DFIFO - dfifo_device_init(rhport); - - // 5. Reset device address - dwc2->dcfg &= ~DCFG_DAD_Msk; - - // Fixed both control EP0 size to 64 bytes - dwc2->epin[0].diepctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); - dwc2->epout[0].doepctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos); - - xfer_status[0][TUSB_DIR_OUT].max_size = 64; - xfer_status[0][TUSB_DIR_IN].max_size = 64; - - if(dma_device_enabled(dwc2)) { - dma_setup_prepare(rhport); - } else { - dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); - } - - dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; -} - -static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t const dir, uint16_t const num_packets, - uint16_t total_bytes) { - (void) rhport; - +static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; - // EP0 is limited to one packet each xfer - // We use multiple transaction of xfer->max_size length to get a whole transfer done + uint16_t num_packets; + uint16_t total_bytes; + + // EP0 is limited to one packet per xfer if (epnum == 0) { total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); ep0_pending[dir] -= total_bytes; + num_packets = 1; + } else { + total_bytes = xfer->total_len; + num_packets = tu_div_ceil(total_bytes, xfer->max_size); + if (num_packets == 0) { + num_packets = 1; // zero length packet still count as 1 + } } - // IN and OUT endpoint xfers are interrupt-driven, we just schedule them here. - const uint8_t is_epout = 1 - dir; - dwc2_dep_t* dep = &dwc2->ep[is_epout][epnum]; - - if (dir == TUSB_DIR_IN) { - // A full IN transfer (multiple packets, possibly) triggers XFRC. - dep->dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | - ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); - - if(dma_device_enabled(dwc2)) { - dep->diepdma = (uintptr_t)xfer->buffer; - - // For ISO endpoint set correct odd/even bit for next frame. - if ((dep->diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); - dep->diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); - } - - dep->diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; + // transfer size: A full OUT transfer (multiple packets, possibly) triggers XFRC. + union { + uint32_t value; + dwc2_ep_tsize_t bm; + } deptsiz; + deptsiz.value = 0; + deptsiz.bm.xfer_size = total_bytes; + deptsiz.bm.packet_count = num_packets; + + dep->tsiz = deptsiz.value; + + // control + union { + dwc2_depctl_t bm; + uint32_t value; + } depctl; + depctl.value = dep->ctl; + + depctl.bm.clear_nak = 1; + depctl.bm.enable = 1; + if (depctl.bm.type == DEPCTL_EPTYPE_ISOCHRONOUS && xfer->interval == 1) { + const uint32_t odd_now = (dwc2->dsts_bm.frame_number & 1u); + if (odd_now) { + depctl.bm.set_data0_iso_even = 1; } else { - dep->diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; - - // For ISO endpoint set correct odd/even bit for next frame. - if ((dep->diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); - dep->diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk); - } - // Enable fifo empty interrupt only if there are something to put in the fifo. - if (total_bytes != 0) { - dwc2->diepempmsk |= (1 << epnum); - } - } - } else { - // A full OUT transfer (multiple packets, possibly) triggers XFRC. - dep->doeptsiz &= ~(DOEPTSIZ_PKTCNT_Msk | DOEPTSIZ_XFRSIZ); - dep->doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) | - ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk); - - if ((dep->doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 && - XFER_CTL_BASE(epnum, dir)->interval == 1) { - // Take odd/even bit from frame counter. - uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos)); - dep->doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk); + depctl.bm.set_data1_iso_odd = 1; } + } - if(dma_device_enabled(dwc2)) { - dep->doepdma = (uintptr_t)xfer->buffer; - } + const bool is_dma = dma_device_enabled(dwc2); + if(is_dma) { + dep->diepdma = (uintptr_t) xfer->buffer; + } + + dep->diepctl = depctl.value; // enable endpoint - dep->doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; + // Slave: enable tx fifo empty interrupt only if there is data. Note must after depctl enable + if (!is_dma && dir == TUSB_DIR_IN && total_bytes != 0) { + dwc2->diepempmsk |= (1 << epnum); } } @@ -412,18 +368,10 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE); - TU_ASSERT(dwc2_core_init(rhport, is_highspeed)); - - if (dma_device_enabled(dwc2)) { - // 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 { - dwc2->gintmsk |= GINTSTS_RXFLVL; - } - - // Device Initialization - dcd_disconnect(rhport); + const bool is_dma = dma_device_enabled(dwc2); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); + //------------- 7.1 Device Initialization -------------// // Set device max speed uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk; if (is_highspeed) { @@ -434,20 +382,21 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) { dcfg |= DCFG_XCVRDLY; } - }else { + } else { dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos; } + + dcfg |= DCFG_NZLSOHSK; // send STALL back and discard if host send non-zlp during control status dwc2->dcfg = dcfg; + dcd_disconnect(rhport); + // 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; - // Enable required interrupts dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM; @@ -604,19 +553,11 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to // EP0 can only handle one packet 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]); - } else { - uint16_t num_packets = tu_div_ceil(total_bytes, xfer->max_size); - if (num_packets == 0) { - num_packets = 1; // zero length packet still count as 1 - } - - // 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); + return true; } @@ -636,16 +577,9 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t* ff, uint16_t xfer->ff = ff; xfer->total_len = total_bytes; - 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++; - } - // Schedule packets to be sent within interrupt - edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes); + // TODO xfer fifo may only available for slave mode + edpt_schedule_packets(rhport, epnum, dir); return true; } @@ -666,7 +600,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); uint8_t const epnum = tu_edpt_number(ep_addr); uint8_t const dir = tu_edpt_dir(ep_addr); - dwc2_dep_t* dep = &dwc2->ep[1 - dir][epnum]; + dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum]; // Clear stall and reset data toggle dep->ctl &= ~EPCTL_STALL;; @@ -677,6 +611,99 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { // Interrupt Handler //-------------------------------------------------------------------- +// 7.4.1 Initialization on USB Reset +static void handle_bus_reset(uint8_t rhport) { + dwc2_regs_t *dwc2 = DWC2_REG(rhport); + const uint8_t ep_count = DWC2_EP_COUNT(dwc2); + + tu_memclr(xfer_status, sizeof(xfer_status)); + + _sof_en = false; + _allocated_ep_in_count = 1; + + // 1. NAK for all OUT endpoints + for (uint8_t n = 0; n < ep_count; n++) { + dwc2->epout[n].doepctl |= DOEPCTL_SNAK; + } + + // Disable all IN endpoints + for (uint8_t n = 0; n < ep_count; n++) { + if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) { + dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS; + } + } + + // 2. Set up interrupt mask for EP0 + dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos); + dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM; + dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM; + + // 4. Set up DFIFO + dfifo_flush_tx(dwc2, 0x10); // all tx fifo + dfifo_flush_rx(dwc2); + dfifo_device_init(rhport); + + // 5. Reset device address + dwc2->dcfg_bm.address = 0; + + // Fixed both control EP0 size to 64 bytes + dwc2->epin[0].ctl &= ~(0x03 << DIEPCTL_MPSIZ_Pos); + dwc2->epout[0].ctl &= ~(0x03 << DOEPCTL_MPSIZ_Pos); + + xfer_status[0][TUSB_DIR_OUT].max_size = 64; + xfer_status[0][TUSB_DIR_IN].max_size = 64; + + if(dma_device_enabled(dwc2)) { + dma_setup_prepare(rhport); + } else { + dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); + } + + dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT; +} + +static void handle_enum_done(uint8_t rhport) { + dwc2_regs_t *dwc2 = DWC2_REG(rhport); + tusb_speed_t speed; + switch (dwc2->dsts_bm.enum_speed) { + case DCFG_SPEED_HIGH: + speed = TUSB_SPEED_HIGH; + break; + + case DCFG_SPEED_LOW: + speed = TUSB_SPEED_LOW; + break; + + case DCFG_SPEED_FULL_30_60MHZ: + case DCFG_SPEED_FULL_48MHZ: + default: + speed = TUSB_SPEED_FULL; + break; + } + + // TODO must update GUSBCFG_TRDT according to link speed + dcd_event_bus_reset(rhport, speed, true); +} + +#if 0 +TU_ATTR_ALWAYS_INLINE static inline void print_doepint(uint32_t doepint) { + const char* str[] = { + "XFRC", "DIS", "AHBERR", "SETUP_DONE", + "ORXED", "STATUS_RX", "SETUP_B2B", "RSV7", + "OPERR", "BNA", "RSV10", "ISODROP", + "BBLERR", "NAK", "NYET", "SETUP_RX" + }; + + for(uint32_t i=0; igrxstsp_bm; const uint8_t epnum = grxstsp_bm.ep_ch_num; - const uint16_t byte_count = grxstsp_bm.byte_count; - dwc2_epout_t* epout = &dwc2->epout[epnum]; + + dwc2_dep_t* epout = &dwc2->epout[epnum]; switch (grxstsp_bm.packet_status) { - // Global OUT NAK: do nothing - case GRXSTS_PKTSTS_GLOBALOUTNAK: + case GRXSTS_PKTSTS_GLOBAL_OUT_NAK: + // Global OUT NAK: do nothing break; - case GRXSTS_PKTSTS_SETUPRX: + case GRXSTS_PKTSTS_SETUP_RX: // Setup packet received - // We can receive up to three setup packets in succession, but only the last one is valid. + // 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 GRXSTS_PKTSTS_SETUPDONE: + case GRXSTS_PKTSTS_SETUP_DONE: // Setup packet done: // After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq() epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos); break; - case GRXSTS_PKTSTS_OUTRX: { + case GRXSTS_PKTSTS_RX_DATA: { // Out packet received + const uint16_t byte_count = grxstsp_bm.byte_count; xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - // Read packet off RxFIFO - if (xfer->ff) { - // Ring buffer - tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count); - } else { - // Linear buffer - dfifo_read_packet(dwc2, xfer->buffer, byte_count); - - // Increment pointer to xfer data - xfer->buffer += byte_count; - } + if (byte_count) { + // Read packet off RxFIFO + if (xfer->ff) { + tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count); + } else { + dfifo_read_packet(dwc2, xfer->buffer, byte_count); + xfer->buffer += byte_count; + } - // short packet, minus remaining bytes (xfer_size) - if (byte_count < xfer->max_size) { - xfer->total_len -= epout->doeptsiz_bm.xfer_size; - if (epnum == 0) { - xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; - ep0_pending[TUSB_DIR_OUT] = 0; + // short packet, minus remaining bytes (xfer_size) + if (byte_count < xfer->max_size) { + xfer->total_len -= epout->tsiz_bm.xfer_size; + if (epnum == 0) { + xfer->total_len -= ep0_pending[TUSB_DIR_OUT]; + ep0_pending[TUSB_DIR_OUT] = 0; + } } } break; } - case GRXSTS_PKTSTS_OUTDONE: - /* Out packet done - After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on - the specified OUT endpoint which will be handled by handle_epout_irq() */ + case GRXSTS_PKTSTS_RX_COMPLETE: + // Out packet done + // After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on + // the specified OUT endpoint which will be handled by handle_epout_irq() break; - default: - TU_BREAKPOINT(); - break; + default: break; } } -static void handle_epout_irq(uint8_t rhport) { - dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint8_t const ep_count = _dwc2_controller[rhport].ep_count; +static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) { + if (doepint_bm.setup_phase_done) { + dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + return; + } - // DAINT for a given EP clears when DOEPINTx is cleared. - // OEPINT will be cleared when DAINT's out bits are cleared. - for (uint8_t epnum = 0; epnum < ep_count; epnum++) { - if (dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + epnum)) { - dwc2_epout_t* epout = &dwc2->epout[epnum]; - const uint32_t doepint = epout->doepint; - TU_ASSERT((epout->doepint & DOEPINT_AHBERR) == 0, ); - - // Setup and/or STPKTRX/STSPHSRX (from 3.00a) can be set along with XFRC, and also set independently. - if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) { - if (doepint & DOEPINT_STSPHSRX) { - // Status phase received for control write: In token received from Host - epout->doepint = DOEPINT_STSPHSRX; - } + // Normal OUT transfer complete + if (doepint_bm.xfer_complete) { + // only handle data skip if it is setup or status related + // Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they + // can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done + if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) { + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - if (doepint & DOEPINT_STPKTRX) { - // New setup packet received, but wait for Setup done, since we can receive up to 3 setup consecutively - epout->doepint = DOEPINT_STPKTRX; - } + 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); + } else { + dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true); } + } + } +} - if (doepint & DOEPINT_SETUP) { - epout->doepint = DOEPINT_SETUP; +static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + dwc2_dep_t* epin = &dwc2->epin[epnum]; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN); - if(dma_device_enabled(dwc2)) { - dma_setup_prepare(rhport); - } + if (diepint_bm.xfer_complete) { + if ((epnum == 0) && ep0_pending[TUSB_DIR_IN]) { + // EP0 can only handle one packet. Schedule another packet to be transmitted. + edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN); + } else { + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } + + // TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than + // - 64 bytes or + // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL) + if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) { + const uint16_t remain_packets = epin->tsiz_bm.packet_count; - dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + // Process every single packet (only whole packets can be written to fifo) + for (uint16_t i = 0; i < remain_packets; i++) { + const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size; + const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size); + + // Check if dtxfsts has enough space available + if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) { + break; } - // OUT XFER complete - if (doepint & DOEPINT_XFRC) { - epout->doepint = DOEPINT_XFRC; - - // only handle data skip if it is setup or status related - // Normal OUT transfer complete - if (!(doepint & (DOEPINT_SETUP | DOEPINT_STPKTRX | DOEPINT_STSPHSRX))) { - xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); - - 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]); - } else { - // Fix packet length - uint16_t remain = (epout->doeptsiz & DOEPTSIZ_XFRSIZ_Msk) >> DOEPTSIZ_XFRSIZ_Pos; - xfer->total_len -= remain; - // this is ZLP, so prepare EP0 for next setup - if(epnum == 0 && xfer->total_len == 0) { - dma_setup_prepare(rhport); - } - - dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - } else { - // EP0 can only handle one packet - if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) { - // Schedule another packet to be received. - edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); - } else { - dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true); - } - } - } + // Push packet to Tx-FIFO + if (xfer->ff) { + volatile uint32_t* tx_fifo = dwc2->fifo[epnum]; + tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes); + } else { + dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes); + xfer->buffer += xact_bytes; } } + + // Turn off TXFE if all bytes are written. + if (epin->tsiz_bm.xfer_size == 0) { + dwc2->diepempmsk &= ~(1 << epnum); + } } } +#endif -static void handle_epin_irq(uint8_t rhport) { +#if CFG_TUD_DWC2_DMA_ENABLE +static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const uint8_t ep_count = _dwc2_controller[rhport].ep_count; - // DAINT for a given EP clears when DIEPINTx is cleared. - // IEPINT will be cleared when DAINT's out bits are cleared. - for (uint8_t n = 0; n < ep_count; 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); - dwc2_epin_t* epin = &dwc2->epin[n]; - - if (epin->diepint & DIEPINT_XFRC) { - epin->diepint = DIEPINT_XFRC; - - // EP0 can only handle one packet - if ((n == 0) && ep0_pending[TUSB_DIR_IN]) { - // Schedule another packet to be transmitted. - edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); - } else { - 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); + if (doepint_bm.setup_phase_done) { + dma_setup_prepare(rhport); + dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true); + return; + } + + // OUT XFER complete + if (doepint_bm.xfer_complete) { + // only handle data skip if it is setup or status related + // Normal OUT transfer complete + if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) { + 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); + } else { + dwc2_dep_t* epout = &dwc2->epout[epnum]; + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT); + + // determine actual received bytes + const uint16_t remain = epout->tsiz_bm.xfer_size; + xfer->total_len -= remain; + + // this is ZLP, so prepare EP0 for next setup + // TODO use status phase rx + if(epnum == 0 && xfer->total_len == 0) { + dma_setup_prepare(rhport); } + + dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true); } + } + } +} - // XFER FIFO empty - if ((epin->diepint & DIEPINT_TXFE) && (dwc2->diepempmsk & (1 << n))) { - // diepint's TXFE bit is read-only, software cannot clear it. - // It will only be cleared by hardware when written bytes is more than - // - 64 bytes or - // - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL) - const uint16_t remain_packets = epin->dieptsiz_bm.packet_count; - - // Process every single packet (only whole packets can be written to fifo) - for (uint16_t i = 0; i < remain_packets; i++) { - const uint16_t remain_bytes = (uint16_t) epin->dieptsiz_bm.xfer_size; - - // Packet can not be larger than ep max size - const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->max_size); - - // It's only possible to write full packets into FIFO. Therefore DTXFSTS register of current - // EP has to be checked if the buffer can take another WHOLE packet - if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) { - break; - } +static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) { + xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN); - // Push packet to Tx-FIFO - if (xfer->ff) { - volatile uint32_t* tx_fifo = dwc2->fifo[n]; - tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*) (uintptr_t) tx_fifo, xact_bytes); - } else { - dfifo_write_packet(dwc2, n, xfer->buffer, xact_bytes); - xfer->buffer += xact_bytes; - } - } + if (diepint_bm.xfer_complete) { + if ((epnum == 0) && ep0_pending[TUSB_DIR_IN]) { + // EP0 can only handle one packet. Schedule another packet to be transmitted. + edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN); + } else { + if(epnum == 0) { + dma_setup_prepare(rhport); + } + dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true); + } + } +} +#endif + +static void handle_ep_irq(uint8_t rhport, uint8_t dir) { + dwc2_regs_t* dwc2 = DWC2_REG(rhport); + const bool is_dma = dma_device_enabled(dwc2); + const uint8_t ep_count = DWC2_EP_COUNT(dwc2); + const uint8_t daint_offset = (dir == TUSB_DIR_IN) ? DAINT_IEPINT_Pos : DAINT_OEPINT_Pos; + dwc2_dep_t* ep_base = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][0]; - // Turn off TXFE if all bytes are written. - if (epin->dieptsiz_bm.xfer_size == 0) { - dwc2->diepempmsk &= ~(1 << n); + // DAINT for a given EP clears when DEPINTx is cleared. + // EPINT will be cleared when DAINT bits are cleared. + for (uint8_t epnum = 0; epnum < ep_count; epnum++) { + if (dwc2->daint & TU_BIT(daint_offset + epnum)) { + dwc2_dep_t* epout = &ep_base[epnum]; + union { + uint32_t value; + dwc2_diepint_t diepint_bm; + dwc2_doepint_t doepint_bm; + } intr; + intr.value = epout->intr; + + epout->intr = intr.value; // Clear interrupt + + if (is_dma) { + #if CFG_TUD_DWC2_DMA_ENABLE + if (dir == TUSB_DIR_IN) { + handle_epin_dma(rhport, epnum, intr.diepint_bm); + } else { + handle_epout_dma(rhport, epnum, intr.doepint_bm); + } + #endif + } else { + #if CFG_TUD_DWC2_SLAVE_ENABLE + if (dir == TUSB_DIR_IN) { + handle_epin_slave(rhport, epnum, intr.diepint_bm); + } else { + handle_epout_slave(rhport, epnum, intr.doepint_bm); } + #endif } } } } /* Interrupt Hierarchy - - DxEPINTn - | - DAINT.xEPn - | - GINTSTS: xEPInt + DIEPINT DIEPINT + \ / + \ / + DAINT + / \ + / \ + GINTSTS: OEPInt IEPInt | USBReset | EnumDone | USBSusp | WkUpInt | OTGInt | SOF | RXFLVL Note: when OTG_MULTI_PROC_INTRPT = 1, Device Each endpoint interrupt deachint/deachmsk/diepeachmsk/doepeachmsk are combined to generate dedicated interrupt line for each endpoint. */ - - void dcd_int_handler(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - uint32_t const int_mask = dwc2->gintmsk; - uint32_t const int_status = dwc2->gintsts & int_mask; + const uint32_t gintmask = dwc2->gintmsk; + const uint32_t gintsts = dwc2->gintsts & gintmask; - if (int_status & GINTSTS_USBRST) { + if (gintsts & GINTSTS_USBRST) { // USBRST is start of reset. dwc2->gintsts = GINTSTS_USBRST; - bus_reset(rhport); + handle_bus_reset(rhport); } - if (int_status & GINTSTS_ENUMDNE) { + if (gintsts & GINTSTS_ENUMDNE) { // ENUMDNE is the end of reset where speed of the link is detected dwc2->gintsts = GINTSTS_ENUMDNE; - - tusb_speed_t speed; - switch ((dwc2->dsts & DSTS_ENUMSPD_Msk) >> DSTS_ENUMSPD_Pos) { - case DSTS_ENUMSPD_HS: - speed = TUSB_SPEED_HIGH; - break; - - case DSTS_ENUMSPD_LS: - speed = TUSB_SPEED_LOW; - break; - - case DSTS_ENUMSPD_FS_HSPHY: - case DSTS_ENUMSPD_FS: - default: - speed = TUSB_SPEED_FULL; - break; - } - - // TODO must update GUSBCFG_TRDT according to link speed - - dcd_event_bus_reset(rhport, speed, true); + handle_enum_done(rhport); } - if (int_status & GINTSTS_USBSUSP) { + if (gintsts & GINTSTS_USBSUSP) { dwc2->gintsts = GINTSTS_USBSUSP; dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true); } - if (int_status & GINTSTS_WKUINT) { + if (gintsts & GINTSTS_WKUINT) { dwc2->gintsts = GINTSTS_WKUINT; dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true); } @@ -950,7 +981,7 @@ void dcd_int_handler(uint8_t rhport) { // TODO check GINTSTS_DISCINT for disconnect detection // if(int_status & GINTSTS_DISCINT) - if (int_status & GINTSTS_OTGINT) { + if (gintsts & GINTSTS_OTGINT) { // OTG INT bit is read-only uint32_t const otg_int = dwc2->gotgint; @@ -961,7 +992,7 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gotgint = otg_int; } - if(int_status & GINTSTS_SOF) { + if(gintsts & GINTSTS_SOF) { dwc2->gintsts = GINTSTS_SOF; const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos; @@ -973,8 +1004,9 @@ void dcd_int_handler(uint8_t rhport) { dcd_event_sof(rhport, frame, true); } +#if CFG_TUD_DWC2_SLAVE_ENABLE // RxFIFO non-empty interrupt handling. - if (int_status & GINTSTS_RXFLVL) { + if (gintsts & GINTSTS_RXFLVL) { // RXFLVL bit is read-only dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading @@ -984,24 +1016,19 @@ void dcd_int_handler(uint8_t rhport) { dwc2->gintmsk |= GINTMSK_RXFLVLM; } +#endif // OUT endpoint interrupt handling. - if (int_status & GINTSTS_OEPINT) { + if (gintsts & GINTSTS_OEPINT) { // OEPINT is read-only, clear using DOEPINTn - handle_epout_irq(rhport); + handle_ep_irq(rhport, TUSB_DIR_OUT); } // IN endpoint interrupt handling. - if (int_status & GINTSTS_IEPINT) { + if (gintsts & GINTSTS_IEPINT) { // IEPINT bit read-only, clear using DIEPINTn - handle_epin_irq(rhport); + handle_ep_irq(rhport, TUSB_DIR_IN); } - - // // Check for Incomplete isochronous IN transfer - // if(int_status & GINTSTS_IISOIXFR) { - // printf(" IISOIXFR!\r\n"); - //// TU_LOG(DWC2_DEBUG, " IISOIXFR!\r\n"); - // } } #if CFG_TUD_TEST_MODE diff --git a/src/portable/synopsys/dwc2/dwc2_common.c b/src/portable/synopsys/dwc2/dwc2_common.c index 4d62cf3c60..ef155c8f78 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.c +++ b/src/portable/synopsys/dwc2/dwc2_common.c @@ -194,7 +194,7 @@ bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role) { * In addition, UTMI+/ULPI can be shared to run at fullspeed mode with 48Mhz * */ -bool dwc2_core_init(uint8_t rhport, bool is_highspeed) { +bool dwc2_core_init(uint8_t rhport, bool is_highspeed, bool is_dma) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); // Check Synopsys ID register, failed if controller clock/power is not enabled @@ -229,6 +229,13 @@ bool dwc2_core_init(uint8_t rhport, bool is_highspeed) { dwc2->gotgint = 0xFFFFFFFFU; dwc2->gintmsk = 0; + if (is_dma) { + // 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 { + dwc2->gintmsk |= GINTSTS_RXFLVL; + } + return true; } diff --git a/src/portable/synopsys/dwc2/dwc2_common.h b/src/portable/synopsys/dwc2/dwc2_common.h index 3f7f23c3a1..18b93894f9 100644 --- a/src/portable/synopsys/dwc2/dwc2_common.h +++ b/src/portable/synopsys/dwc2/dwc2_common.h @@ -73,7 +73,7 @@ TU_ATTR_ALWAYS_INLINE static inline dwc2_regs_t* DWC2_REG(uint8_t rhport) { } bool dwc2_core_is_highspeed(dwc2_regs_t* dwc2, tusb_role_t role); -bool dwc2_core_init(uint8_t rhport, bool is_highspeed); +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); //--------------------------------------------------------------------+ diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index 1247f19a37..8120967598 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -149,17 +149,12 @@ enum { }; enum { - GRXSTS_PKTSTS_GLOBALOUTNAK = 1, - GRXSTS_PKTSTS_OUTRX = 2, - GRXSTS_PKTSTS_OUTDONE = 3, - GRXSTS_PKTSTS_SETUPDONE = 4, - GRXSTS_PKTSTS_SETUPRX = 6 -}; - -enum { - GRXSTS_PKTSTS_RX_DATA = 2, - GRXSTS_PKTSTS_RX_COMPLETE = 3, + GRXSTS_PKTSTS_GLOBAL_OUT_NAK = 1, + GRXSTS_PKTSTS_RX_DATA = 2, + GRXSTS_PKTSTS_RX_COMPLETE = 3, + GRXSTS_PKTSTS_SETUP_DONE = 4, GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5, + GRXSTS_PKTSTS_SETUP_RX = 6, GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7 }; @@ -171,6 +166,21 @@ enum { HCCHAR_EPTYPE_INTERRUPT = 3 }; +enum { + DCFG_SPEED_HIGH = 0, // Highspeed with 30/60 Mhz + DCFG_SPEED_FULL_30_60MHZ = 1, // Fullspeed with UTMI+/ULPI 30/60 Mhz + DCFG_SPEED_LOW = 2, // Lowspeed with FS PHY at 6 Mhz + DCFG_SPEED_FULL_48MHZ = 3, // Fullspeed with dedicated FS PHY at 48 Mhz +}; + +// Same as TUSB_XFER_* +enum { + DEPCTL_EPTYPE_CONTROL = 0, + DEPCTL_EPTYPE_ISOCHRONOUS = 1, + DEPCTL_EPTYPE_BULK = 2, + DEPCTL_EPTYPE_INTERRUPT = 3 +}; + //-------------------------------------------------------------------- // Common Register Bitfield //-------------------------------------------------------------------- @@ -469,58 +479,148 @@ typedef struct { // Device Register Bitfield //-------------------------------------------------------------------- typedef struct TU_ATTR_PACKED { - uint32_t xfer_size : 19; // 0..18 Transfer size in bytes - uint32_t packet_count : 10; // 19..28 Number of packets - uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID -} dwc2_ep_tsize_t; -TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size"); + uint32_t speed : 2; // 0..1 Speed + uint32_t nzsts_out_handshake : 1; // 2 Non-zero-length status OUT handshake + uint32_t en_32khz_suspsend : 1; // 3 Enable 32-kHz SUSPEND mode + uint32_t address : 7; // 4..10 Device address + uint32_t period_frame_interval : 2; // 11..12 Periodic frame interval + uint32_t en_out_nak : 1; // 13 Enable Device OUT NAK + uint32_t xcvr_delay : 1; // 14 Transceiver delay + uint32_t erratic_int_mask : 1; // 15 Erratic interrupt mask + uint32_t rsv16 : 1; // 16 Reserved + uint32_t ipg_iso_support : 1; // 17 Interpacket gap ISO support + uint32_t epin_mismatch_count : 5; // 18..22 EP IN mismatch count + uint32_t dma_desc : 1; // 23 Enable scatter/gatter DMA descriptor + uint32_t period_schedule_interval : 2; // 24..25 Periodic schedule interval for scatter/gatter DMA + uint32_t resume_valid : 6; // 26..31 Resume valid period +} dwc2_dcfg_t; +TU_VERIFY_STATIC(sizeof(dwc2_dcfg_t) == 4, "incorrect size"); -// Endpoint IN -typedef struct { - volatile uint32_t diepctl; // 900 + 20*ep Device IN Endpoint Control - uint32_t reserved04; // 904 - volatile uint32_t diepint; // 908 + 20*ep Device IN Endpoint Interrupt - uint32_t reserved0c; // 90C - union { - volatile uint32_t dieptsiz; // 910 + 20*ep Device IN Endpoint Transfer Size - volatile dwc2_ep_tsize_t dieptsiz_bm; - }; - volatile uint32_t diepdma; // 914 + 20*ep Device IN Endpoint DMA Address - volatile uint32_t dtxfsts; // 918 + 20*ep Device IN Endpoint Tx FIFO Status - uint32_t reserved1c; // 91C -} dwc2_epin_t; +typedef struct TU_ATTR_PACKED { + uint32_t remote_wakeup_signal : 1; // 0 Remote wakeup signal + uint32_t soft_disconnet : 1; // 1 Soft disconnect + uint32_t gnp_in_nak_status : 1; // 2 Global non-periodic NAK IN status + uint32_t gout_nak_status : 1; // 3 Global OUT NAK status + uint32_t test_control : 3; // 4..6 Test control + uint32_t set_gnp_in_nak : 1; // 7 Set global non-periodic IN NAK + uint32_t clear_gnp_in_nak : 1; // 8 Clear global non-periodic IN NAK + uint32_t set_gout_nak : 1; // 9 Set global OUT NAK + uint32_t clear_gout_nak : 1; // 10 Clear global OUT NAK + uint32_t poweron_prog_done : 1; // 11 Power-on programming done + uint32_t rsv12 : 1; // 12 Reserved + uint32_t global_multi_count : 2; // 13..14 Global multi-count + uint32_t ignore_frame_number : 1; // 15 Ignore frame number + uint32_t nak_on_babble : 1; // 16 NAK on babble + uint32_t en_cont_on_bna : 1; // 17 Enable continue on BNA + uint32_t deep_sleep_besl_reject : 1; // 18 Deep sleep BESL reject + uint32_t service_interval : 1; // 19 Service interval for ISO IN endpoint + uint32_t rsv20_31 :12; // 20..31 Reserved +} dwc2_dctl_t; +TU_VERIFY_STATIC(sizeof(dwc2_dctl_t) == 4, "incorrect size"); -// Endpoint OUT -typedef struct { - volatile uint32_t doepctl; // B00 + 20*ep Device OUT Endpoint Control - uint32_t reserved04; // B04 - volatile uint32_t doepint; // B08 + 20*ep Device OUT Endpoint Interrupt - uint32_t reserved0c; // B0C - union { - volatile uint32_t doeptsiz; // B10 + 20*ep Device OUT Endpoint Transfer Size - volatile dwc2_ep_tsize_t doeptsiz_bm; - }; - volatile uint32_t doepdma; // B14 + 20*ep Device OUT Endpoint DMA Address - uint32_t reserved18[2]; // B18..B1C -} dwc2_epout_t; +typedef struct TU_ATTR_PACKED { + uint32_t suspend_status : 1; // 0 Suspend status + uint32_t enum_speed : 2; // 1..2 Enumerated speed + uint32_t erratic_err : 1; // 3 Erratic error + uint32_t rsv4_7 : 4; // 4..7 Reserved + uint32_t frame_number : 14; // 8..21 Frame/MicroFrame number + uint32_t line_status : 2; // 22..23 Line status + uint32_t rsv24_31 : 8; // 24..31 Reserved +} dwc2_dsts_t; +TU_VERIFY_STATIC(sizeof(dwc2_dsts_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t xfer_complete : 1; // 0 Transfer complete + uint32_t disabled : 1; // 1 Endpoint disabled + uint32_t ahb_err : 1; // 2 AHB error + uint32_t timeout : 1; // 3 Timeout + uint32_t in_rx_txfe : 1; // 4 IN token received when TxFIFO is empty + uint32_t in_rx_ep_mismatch : 1; // 5 IN token received with EP mismatch + uint32_t in_ep_nak_effective : 1; // 6 IN endpoint NAK effective + uint32_t txfifo_empty : 1; // 7 TX FIFO empty + uint32_t txfifo_underrun : 1; // 8 Tx FIFO under run + uint32_t bna : 1; // 9 Buffer not available + uint32_t rsv10 : 1; // 10 Reserved + uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status + uint32_t babble_err : 1; // 12 Babble error + uint32_t nak : 1; // 13 NAK + uint32_t nyet : 1; // 14 NYET + uint32_t rsv14_31 :17; // 15..31 Reserved +} dwc2_diepint_t; +TU_VERIFY_STATIC(sizeof(dwc2_diepint_t) == 4, "incorrect size"); -// Universal Endpoint +typedef struct TU_ATTR_PACKED { + uint32_t mps : 11; // 0..10 Maximum packet size, EP0 only use 2 bit + uint32_t next_ep : 4; // 11..14 Next endpoint number + uint32_t active : 1; // 15 Active + const uint32_t dpid_iso_odd : 1; // 16 DATA0/DATA1 for bulk/interrupt, odd frame for isochronous + const uint32_t nak_status : 1; // 17 NAK status + uint32_t type : 2; // 18..19 Endpoint type + uint32_t rsv20 : 1; // 20 Reserved + uint32_t stall : 1; // 21 Stall + uint32_t tx_fifo_num : 4; // 22..25 Tx FIFO number (IN) + uint32_t clear_nak : 1; // 26 Clear NAK + uint32_t set_nak : 1; // 27 Set NAK + uint32_t set_data0_iso_even : 1; // 28 Set DATA0 if bulk/interrupt, even frame for isochronous + uint32_t set_data1_iso_odd : 1; // 29 Set DATA1 if bulk/interrupt, odd frame for isochronous + uint32_t disable : 1; // 30 Disable + uint32_t enable : 1; // 31 Enable +} dwc2_depctl_t; +TU_VERIFY_STATIC(sizeof(dwc2_depctl_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t xfer_complete : 1; // 0 Transfer complete + uint32_t disabled : 1; // 1 Endpoint disabled + uint32_t ahb_err : 1; // 2 AHB error + uint32_t setup_phase_done : 1; // 3 Setup phase done + uint32_t out_rx_ep_disabled : 1; // 4 OUT token received when endpoint disabled + uint32_t status_phase_rx : 1; // 5 Status phase received + uint32_t setup_b2b : 1; // 6 Setup packet back-to-back + uint32_t rsv7 : 1; // 7 Reserved + uint32_t out_packet_err : 1; // 8 OUT packet error + uint32_t bna : 1; // 9 Buffer not available + uint32_t rsv10 : 1; // 10 Reserved + uint32_t iso_packet_drop : 1; // 11 Isochronous OUT packet drop status + uint32_t babble_err : 1; // 12 Babble error + uint32_t nak : 1; // 13 NAK + uint32_t nyet : 1; // 14 NYET + uint32_t setup_packet_rx : 1; // 15 Setup packet received (Buffer DMA Mode only) + uint32_t rsv16_31 :16; // 16..31 Reserved +} dwc2_doepint_t; +TU_VERIFY_STATIC(sizeof(dwc2_doepint_t) == 4, "incorrect size"); + +typedef struct TU_ATTR_PACKED { + uint32_t xfer_size : 19; // 0..18 Transfer size in bytes + uint32_t packet_count : 10; // 19..28 Number of packets + uint32_t mc_pid : 2; // 29..30 IN: Multi Count, OUT: PID +} dwc2_ep_tsize_t; +TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size"); + +// Device IN/OUT Endpoint typedef struct { union { volatile uint32_t diepctl; volatile uint32_t doepctl; + volatile uint32_t ctl; + volatile dwc2_depctl_t ctl_bm; }; uint32_t rsv04; union { + volatile uint32_t intr; + volatile uint32_t diepint; + volatile dwc2_diepint_t diepint_bm; + volatile uint32_t doepint; + volatile dwc2_doepint_t doepint_bm; }; uint32_t rsv0c; union { volatile uint32_t dieptsiz; volatile uint32_t doeptsiz; - volatile dwc2_ep_tsize_t deptsiz_bm; + volatile uint32_t tsiz; + volatile dwc2_ep_tsize_t tsiz_bm; }; union { volatile uint32_t diepdma; @@ -631,12 +731,27 @@ typedef struct { uint32_t reserved700[64]; // 700..7FF //------------- Device -----------// + union { volatile uint32_t dcfg; // 800 Device Configuration + volatile dwc2_dcfg_t dcfg_bm; + }; + union { volatile uint32_t dctl; // 804 Device Control + volatile dwc2_dctl_t dctl_bm; + }; + union { volatile uint32_t dsts; // 808 Device Status (RO) + volatile dwc2_dsts_t dsts_bm; + }; uint32_t reserved80c; // 80C + union { volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask + volatile dwc2_diepint_t diepmsk_bm; + }; + union { volatile uint32_t doepmsk; // 814 Device OUT Endpoint Interrupt Mask + volatile dwc2_doepint_t doepmsk_bm; + }; volatile uint32_t daint; // 818 Device All Endpoints Interrupt volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1 @@ -658,8 +773,8 @@ typedef struct { union { dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT struct { - dwc2_epin_t epin[16]; // 900..AFF IN Endpoints - dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints + dwc2_dep_t epin[16]; // 900..AFF IN Endpoints + dwc2_dep_t epout[16]; // B00..CFF OUT Endpoints }; }; uint32_t reservedd00[64]; // D00..DFF diff --git a/src/portable/synopsys/dwc2/hcd_dwc2.c b/src/portable/synopsys/dwc2/hcd_dwc2.c index 53583426f3..8e0162ed6c 100644 --- a/src/portable/synopsys/dwc2/hcd_dwc2.c +++ b/src/portable/synopsys/dwc2/hcd_dwc2.c @@ -336,14 +336,8 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // Core Initialization const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST); - TU_ASSERT(dwc2_core_init(rhport, is_highspeed)); - - if (dma_host_enabled(dwc2)) { - // 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 { - dwc2->gintmsk |= GINTSTS_RXFLVL; - } + const bool is_dma = dma_host_enabled(dwc2); + TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma)); //------------- 3.1 Host Initialization -------------// @@ -1122,8 +1116,8 @@ static void handle_channel_irq(uint8_t rhport, bool in_isr) { TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,); dwc2_channel_char_t hcchar_bm = channel->hcchar_bm; - uint32_t hcint = channel->hcint; - channel->hcint = hcint; + const uint32_t hcint = channel->hcint; + channel->hcint = hcint; // clear interrupt bool is_done; if (is_dma) { diff --git a/src/tusb_option.h b/src/tusb_option.h index 8bc30e9403..51017221dc 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -248,13 +248,17 @@ // USBIP //--------------------------------------------------------------------+ +#ifndef CFG_TUD_DWC2_SLAVE_ENABLE + #define CFG_TUD_DWC2_SLAVE_ENABLE 1 +#endif + // DWC2 controller: use DMA for data transfer // For processors with data cache enabled, USB endpoint buffer region // (defined by CFG_TUSB_MEM_SECTION) must be declared as non-cacheable. // For example, on Cortex-M7 the MPU region can be configured as normal // non-cacheable, with RASR register value: TEX=1 C=0 B=0 S=0. -#ifndef CFG_TUD_DWC2_DMA - #define CFG_TUD_DWC2_DMA 0 +#ifndef CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUD_DWC2_DMA_ENABLE 0 #endif // Enable DWC2 Slave mode for host diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json index c15daf1d99..b6c9b540ac 100644 --- a/test/hil/tinyusb.json +++ b/test/hil/tinyusb.json @@ -17,7 +17,7 @@ "name": "espressif_s3_devkitm", "uid": "84F703C084E4", "build" : { - "flags_on": ["", "CFG_TUD_DWC2_DMA"] + "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"] }, "tests": { "only": ["device/cdc_msc_freertos", "device/hid_composite_freertos"] @@ -130,7 +130,7 @@ "name": "stm32f723disco", "uid": "460029001951373031313335", "build" : { - "flags_on": ["", "CFG_TUD_DWC2_DMA"] + "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"] }, "tests": { "device": true, "host": true, "dual": false, @@ -146,7 +146,7 @@ "name": "stm32h743nucleo", "uid": "110018000951383432343236", "build" : { - "flags_on": ["", "CFG_TUD_DWC2_DMA"] + "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"] }, "tests": { "device": true, "host": false, "dual": false @@ -175,7 +175,7 @@ "name": "stm32f769disco", "uid": "21002F000F51363531383437", "build" : { - "flags_on": ["", "CFG_TUD_DWC2_DMA"] + "flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"] }, "tests": { "device": true, "host": false, "dual": false