Merge pull request #2881 from hathach/enhance-dwc2-dcd
This commit is contained in:
@@ -289,6 +289,11 @@ function(family_add_tinyusb TARGET OPT_MCU RTOS)
|
|||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# compile define from command line
|
||||||
|
if(DEFINED CFLAGS_CLI)
|
||||||
|
target_compile_options(${TARGET}-tinyusb PUBLIC ${CFLAGS_CLI})
|
||||||
|
endif()
|
||||||
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
# Add bin/hex output
|
# Add bin/hex output
|
||||||
|
@@ -548,7 +548,7 @@
|
|||||||
#define TUP_DCD_EDPT_ISO_ALLOC
|
#define TUP_DCD_EDPT_ISO_ALLOC
|
||||||
#endif
|
#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
|
#define TUP_MEM_CONST_ADDR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -37,6 +37,12 @@
|
|||||||
#include "device/dcd.h"
|
#include "device/dcd.h"
|
||||||
#include "dwc2_common.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
|
// 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) {
|
TU_ATTR_ALWAYS_INLINE static inline bool dma_device_enabled(const dwc2_regs_t* dwc2) {
|
||||||
(void) dwc2;
|
(void) dwc2;
|
||||||
// Internal DMA only
|
// 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) {
|
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) {
|
static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) {
|
||||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
const uint8_t epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
||||||
uint8_t const dir = tu_edpt_dir(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_ctl_t* xfer = XFER_CTL_BASE(epnum, dir);
|
||||||
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
|
xfer->max_size = tu_edpt_packet_size(p_endpoint_desc);
|
||||||
xfer->interval = p_endpoint_desc->bInterval;
|
xfer->interval = p_endpoint_desc->bInterval;
|
||||||
|
|
||||||
// USBAEP, EPTYP, SD0PID_SEVNFRM, MPSIZ are the same for IN and OUT endpoints.
|
// Endpoint control
|
||||||
uint32_t epctl = (1 << DOEPCTL_USBAEP_Pos) |
|
union {
|
||||||
(p_endpoint_desc->bmAttributes.xfer << DOEPCTL_EPTYP_Pos) |
|
uint32_t value;
|
||||||
(p_endpoint_desc->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS ? DOEPCTL_SD0PID_SEVNFRM : 0) |
|
dwc2_depctl_t bm;
|
||||||
(xfer->max_size << DOEPCTL_MPSIZ_Pos);
|
} 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) {
|
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];
|
dwc2_dep_t* dep = &dwc2->ep[dir == TUSB_DIR_IN ? 0 : 1][epnum];
|
||||||
dep->ctl = epctl;
|
dep->ctl = depctl.value;
|
||||||
dwc2->daintmsk |= TU_BIT(epnum + DAINT_SHIFT(dir));
|
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);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
const uint8_t epnum = tu_edpt_number(ep_addr);
|
const uint8_t epnum = tu_edpt_number(ep_addr);
|
||||||
const uint8_t dir = tu_edpt_dir(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) {
|
if (dir == TUSB_DIR_IN) {
|
||||||
// Only disable currently enabled non-control endpoint
|
// Only disable currently enabled non-control endpoint
|
||||||
@@ -282,125 +296,67 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start of Bus Reset
|
static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uint8_t dir) {
|
||||||
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;
|
|
||||||
|
|
||||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
|
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
|
uint16_t num_packets;
|
||||||
// We use multiple transaction of xfer->max_size length to get a whole transfer done
|
uint16_t total_bytes;
|
||||||
|
|
||||||
|
// EP0 is limited to one packet per xfer
|
||||||
if (epnum == 0) {
|
if (epnum == 0) {
|
||||||
total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
|
total_bytes = tu_min16(ep0_pending[dir], xfer->max_size);
|
||||||
ep0_pending[dir] -= total_bytes;
|
ep0_pending[dir] -= total_bytes;
|
||||||
}
|
num_packets = 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;
|
|
||||||
} else {
|
} else {
|
||||||
dep->diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
|
total_bytes = xfer->total_len;
|
||||||
|
num_packets = tu_div_ceil(total_bytes, xfer->max_size);
|
||||||
// For ISO endpoint set correct odd/even bit for next frame.
|
if (num_packets == 0) {
|
||||||
if ((dep->diepctl & DIEPCTL_EPTYP) == DIEPCTL_EPTYP_0 && (XFER_CTL_BASE(epnum, dir))->interval == 1) {
|
num_packets = 1; // zero length packet still count as 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) {
|
|
||||||
|
// 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 {
|
||||||
|
depctl.bm.set_data1_iso_odd = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool is_dma = dma_device_enabled(dwc2);
|
||||||
|
if(is_dma) {
|
||||||
|
dep->diepdma = (uintptr_t) xfer->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
dep->diepctl = depctl.value; // enable endpoint
|
||||||
|
|
||||||
|
// 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);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dma_device_enabled(dwc2)) {
|
|
||||||
dep->doepdma = (uintptr_t)xfer->buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
dep->doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
@@ -412,18 +368,10 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
|||||||
|
|
||||||
// Core Initialization
|
// Core Initialization
|
||||||
const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE);
|
const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_DEVICE);
|
||||||
TU_ASSERT(dwc2_core_init(rhport, is_highspeed));
|
const bool is_dma = dma_device_enabled(dwc2);
|
||||||
|
TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
|
||||||
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);
|
|
||||||
|
|
||||||
|
//------------- 7.1 Device Initialization -------------//
|
||||||
// Set device max speed
|
// Set device max speed
|
||||||
uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
|
uint32_t dcfg = dwc2->dcfg & ~DCFG_DSPD_Msk;
|
||||||
if (is_highspeed) {
|
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) {
|
if (dwc2->ghwcfg2_bm.hs_phy_type == GHWCFG2_HSPHY_ULPI) {
|
||||||
dcfg |= DCFG_XCVRDLY;
|
dcfg |= DCFG_XCVRDLY;
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
dcfg |= DCFG_DSPD_FS << DCFG_DSPD_Pos;
|
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;
|
dwc2->dcfg = dcfg;
|
||||||
|
|
||||||
|
dcd_disconnect(rhport);
|
||||||
|
|
||||||
// Force device mode
|
// Force device mode
|
||||||
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD;
|
dwc2->gusbcfg = (dwc2->gusbcfg & ~GUSBCFG_FHMOD) | GUSBCFG_FDMOD;
|
||||||
|
|
||||||
// Clear A override, force B Valid
|
// Clear A override, force B Valid
|
||||||
dwc2->gotgctl = (dwc2->gotgctl & ~GOTGCTL_AVALOEN) | GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL;
|
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
|
// Enable required interrupts
|
||||||
dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
|
dwc2->gintmsk |= GINTMSK_OTGINT | GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
|
||||||
|
|
||||||
@@ -604,18 +553,10 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
|
|||||||
// EP0 can only handle one packet
|
// EP0 can only handle one packet
|
||||||
if (epnum == 0) {
|
if (epnum == 0) {
|
||||||
ep0_pending[dir] = total_bytes;
|
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
|
// Schedule packets to be sent within interrupt
|
||||||
edpt_schedule_packets(rhport, epnum, dir, num_packets, total_bytes);
|
edpt_schedule_packets(rhport, epnum, dir);
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
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->ff = ff;
|
||||||
xfer->total_len = total_bytes;
|
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
|
// 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;
|
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);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||||
uint8_t const dir = tu_edpt_dir(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
|
// Clear stall and reset data toggle
|
||||||
dep->ctl &= ~EPCTL_STALL;;
|
dep->ctl &= ~EPCTL_STALL;;
|
||||||
@@ -677,6 +611,99 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
|||||||
// Interrupt Handler
|
// 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; i<TU_ARRAY_SIZE(str); i++) {
|
||||||
|
if (doepint & TU_BIT(i)) {
|
||||||
|
TU_LOG1("%s ", str[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TU_LOG1("\r\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CFG_TUD_DWC2_SLAVE_ENABLE
|
||||||
// Process shared receive FIFO, this interrupt is only used in Slave mode
|
// Process shared receive FIFO, this interrupt is only used in Slave mode
|
||||||
static void handle_rxflvl_irq(uint8_t rhport) {
|
static void handle_rxflvl_irq(uint8_t rhport) {
|
||||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
@@ -685,264 +712,268 @@ static void handle_rxflvl_irq(uint8_t rhport) {
|
|||||||
// Pop control word off FIFO
|
// Pop control word off FIFO
|
||||||
const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
|
const dwc2_grxstsp_t grxstsp_bm = dwc2->grxstsp_bm;
|
||||||
const uint8_t epnum = grxstsp_bm.ep_ch_num;
|
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) {
|
switch (grxstsp_bm.packet_status) {
|
||||||
|
case GRXSTS_PKTSTS_GLOBAL_OUT_NAK:
|
||||||
// Global OUT NAK: do nothing
|
// Global OUT NAK: do nothing
|
||||||
case GRXSTS_PKTSTS_GLOBALOUTNAK:
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GRXSTS_PKTSTS_SETUPRX:
|
case GRXSTS_PKTSTS_SETUP_RX:
|
||||||
// Setup packet received
|
// 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[0] = (*rx_fifo);
|
||||||
_setup_packet[1] = (*rx_fifo);
|
_setup_packet[1] = (*rx_fifo);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GRXSTS_PKTSTS_SETUPDONE:
|
case GRXSTS_PKTSTS_SETUP_DONE:
|
||||||
// Setup packet done:
|
// Setup packet done:
|
||||||
// After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq()
|
// After popping this out, dwc2 asserts a DOEPINT_SETUP interrupt which is handled by handle_epout_irq()
|
||||||
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
|
epout->doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GRXSTS_PKTSTS_OUTRX: {
|
case GRXSTS_PKTSTS_RX_DATA: {
|
||||||
// Out packet received
|
// Out packet received
|
||||||
|
const uint16_t byte_count = grxstsp_bm.byte_count;
|
||||||
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
|
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
|
||||||
|
|
||||||
|
if (byte_count) {
|
||||||
// Read packet off RxFIFO
|
// Read packet off RxFIFO
|
||||||
if (xfer->ff) {
|
if (xfer->ff) {
|
||||||
// Ring buffer
|
|
||||||
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count);
|
tu_fifo_write_n_const_addr_full_words(xfer->ff, (const void*) (uintptr_t) rx_fifo, byte_count);
|
||||||
} else {
|
} else {
|
||||||
// Linear buffer
|
|
||||||
dfifo_read_packet(dwc2, xfer->buffer, byte_count);
|
dfifo_read_packet(dwc2, xfer->buffer, byte_count);
|
||||||
|
|
||||||
// Increment pointer to xfer data
|
|
||||||
xfer->buffer += byte_count;
|
xfer->buffer += byte_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// short packet, minus remaining bytes (xfer_size)
|
// short packet, minus remaining bytes (xfer_size)
|
||||||
if (byte_count < xfer->max_size) {
|
if (byte_count < xfer->max_size) {
|
||||||
xfer->total_len -= epout->doeptsiz_bm.xfer_size;
|
xfer->total_len -= epout->tsiz_bm.xfer_size;
|
||||||
if (epnum == 0) {
|
if (epnum == 0) {
|
||||||
xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
|
xfer->total_len -= ep0_pending[TUSB_DIR_OUT];
|
||||||
ep0_pending[TUSB_DIR_OUT] = 0;
|
ep0_pending[TUSB_DIR_OUT] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GRXSTS_PKTSTS_OUTDONE:
|
case GRXSTS_PKTSTS_RX_COMPLETE:
|
||||||
/* Out packet done
|
// Out packet done
|
||||||
After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
|
// 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() */
|
// the specified OUT endpoint which will be handled by handle_epout_irq()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default: break;
|
||||||
TU_BREAKPOINT();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_epout_irq(uint8_t rhport) {
|
static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
|
||||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
if (doepint_bm.setup_phase_done) {
|
||||||
uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (doepint & DOEPINT_SETUP) {
|
|
||||||
epout->doepint = DOEPINT_SETUP;
|
|
||||||
|
|
||||||
if(dma_device_enabled(dwc2)) {
|
|
||||||
dma_setup_prepare(rhport);
|
|
||||||
}
|
|
||||||
|
|
||||||
dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
|
dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// Normal OUT transfer complete
|
||||||
if (!(doepint & (DOEPINT_SETUP | DOEPINT_STPKTRX | DOEPINT_STSPHSRX))) {
|
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);
|
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
|
||||||
|
|
||||||
if(dma_device_enabled(dwc2)) {
|
|
||||||
if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) {
|
if ((epnum == 0) && ep0_pending[TUSB_DIR_OUT]) {
|
||||||
// EP0 can only handle one packet Schedule another packet to be received.
|
// 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]);
|
edpt_schedule_packets(rhport, epnum, 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 {
|
} else {
|
||||||
dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_epin_irq(uint8_t rhport) {
|
static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepint_bm) {
|
||||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
const uint8_t ep_count = _dwc2_controller[rhport].ep_count;
|
dwc2_dep_t* epin = &dwc2->epin[epnum];
|
||||||
|
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_IN);
|
||||||
|
|
||||||
// DAINT for a given EP clears when DIEPINTx is cleared.
|
if (diepint_bm.xfer_complete) {
|
||||||
// IEPINT will be cleared when DAINT's out bits are cleared.
|
if ((epnum == 0) && ep0_pending[TUSB_DIR_IN]) {
|
||||||
for (uint8_t n = 0; n < ep_count; n++) {
|
// EP0 can only handle one packet. Schedule another packet to be transmitted.
|
||||||
if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + n)) {
|
edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
|
||||||
// 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 {
|
} else {
|
||||||
if((n == 0) && dma_device_enabled(dwc2)) {
|
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
||||||
dma_setup_prepare(rhport);
|
|
||||||
}
|
|
||||||
dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XFER FIFO empty
|
// TX FIFO empty bit is read-only. It will only be cleared by hardware when written bytes is more than
|
||||||
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
|
// - 64 bytes or
|
||||||
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
|
// - Half/Empty of TX FIFO size (configured by GAHBCFG.TXFELVL)
|
||||||
const uint16_t remain_packets = epin->dieptsiz_bm.packet_count;
|
if (diepint_bm.txfifo_empty && (dwc2->diepempmsk & (1 << epnum))) {
|
||||||
|
const uint16_t remain_packets = epin->tsiz_bm.packet_count;
|
||||||
|
|
||||||
// Process every single packet (only whole packets can be written to fifo)
|
// Process every single packet (only whole packets can be written to fifo)
|
||||||
for (uint16_t i = 0; i < remain_packets; i++) {
|
for (uint16_t i = 0; i < remain_packets; i++) {
|
||||||
const uint16_t remain_bytes = (uint16_t) epin->dieptsiz_bm.xfer_size;
|
const uint16_t remain_bytes = (uint16_t) epin->tsiz_bm.xfer_size;
|
||||||
|
|
||||||
// Packet can not be larger than ep max size
|
|
||||||
const uint16_t xact_bytes = tu_min16(remain_bytes, xfer->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
|
// Check if dtxfsts has enough space available
|
||||||
// EP has to be checked if the buffer can take another WHOLE packet
|
|
||||||
if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
|
if (xact_bytes > ((epin->dtxfsts & DTXFSTS_INEPTFSAV_Msk) << 2)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push packet to Tx-FIFO
|
// Push packet to Tx-FIFO
|
||||||
if (xfer->ff) {
|
if (xfer->ff) {
|
||||||
volatile uint32_t* tx_fifo = dwc2->fifo[n];
|
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);
|
tu_fifo_read_n_const_addr_full_words(xfer->ff, (void*)(uintptr_t)tx_fifo, xact_bytes);
|
||||||
} else {
|
} else {
|
||||||
dfifo_write_packet(dwc2, n, xfer->buffer, xact_bytes);
|
dfifo_write_packet(dwc2, epnum, xfer->buffer, xact_bytes);
|
||||||
xfer->buffer += xact_bytes;
|
xfer->buffer += xact_bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn off TXFE if all bytes are written.
|
// Turn off TXFE if all bytes are written.
|
||||||
if (epin->dieptsiz_bm.xfer_size == 0) {
|
if (epin->tsiz_bm.xfer_size == 0) {
|
||||||
dwc2->diepempmsk &= ~(1 << n);
|
dwc2->diepempmsk &= ~(1 << epnum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
// 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
|
/* Interrupt Hierarchy
|
||||||
|
DIEPINT DIEPINT
|
||||||
DxEPINTn
|
\ /
|
||||||
|
|
\ /
|
||||||
DAINT.xEPn
|
DAINT
|
||||||
|
|
/ \
|
||||||
GINTSTS: xEPInt
|
/ \
|
||||||
|
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
|
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.
|
are combined to generate dedicated interrupt line for each endpoint.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
void dcd_int_handler(uint8_t rhport) {
|
void dcd_int_handler(uint8_t rhport) {
|
||||||
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
|
|
||||||
uint32_t const int_mask = dwc2->gintmsk;
|
const uint32_t gintmask = dwc2->gintmsk;
|
||||||
uint32_t const int_status = dwc2->gintsts & int_mask;
|
const uint32_t gintsts = dwc2->gintsts & gintmask;
|
||||||
|
|
||||||
if (int_status & GINTSTS_USBRST) {
|
if (gintsts & GINTSTS_USBRST) {
|
||||||
// USBRST is start of reset.
|
// USBRST is start of reset.
|
||||||
dwc2->gintsts = GINTSTS_USBRST;
|
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
|
// ENUMDNE is the end of reset where speed of the link is detected
|
||||||
dwc2->gintsts = GINTSTS_ENUMDNE;
|
dwc2->gintsts = GINTSTS_ENUMDNE;
|
||||||
|
handle_enum_done(rhport);
|
||||||
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
|
if (gintsts & GINTSTS_USBSUSP) {
|
||||||
|
|
||||||
dcd_event_bus_reset(rhport, speed, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (int_status & GINTSTS_USBSUSP) {
|
|
||||||
dwc2->gintsts = GINTSTS_USBSUSP;
|
dwc2->gintsts = GINTSTS_USBSUSP;
|
||||||
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
|
dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int_status & GINTSTS_WKUINT) {
|
if (gintsts & GINTSTS_WKUINT) {
|
||||||
dwc2->gintsts = GINTSTS_WKUINT;
|
dwc2->gintsts = GINTSTS_WKUINT;
|
||||||
dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
|
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
|
// TODO check GINTSTS_DISCINT for disconnect detection
|
||||||
// if(int_status & GINTSTS_DISCINT)
|
// if(int_status & GINTSTS_DISCINT)
|
||||||
|
|
||||||
if (int_status & GINTSTS_OTGINT) {
|
if (gintsts & GINTSTS_OTGINT) {
|
||||||
// OTG INT bit is read-only
|
// OTG INT bit is read-only
|
||||||
uint32_t const otg_int = dwc2->gotgint;
|
uint32_t const otg_int = dwc2->gotgint;
|
||||||
|
|
||||||
@@ -961,7 +992,7 @@ void dcd_int_handler(uint8_t rhport) {
|
|||||||
dwc2->gotgint = otg_int;
|
dwc2->gotgint = otg_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(int_status & GINTSTS_SOF) {
|
if(gintsts & GINTSTS_SOF) {
|
||||||
dwc2->gintsts = GINTSTS_SOF;
|
dwc2->gintsts = GINTSTS_SOF;
|
||||||
const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
|
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);
|
dcd_event_sof(rhport, frame, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CFG_TUD_DWC2_SLAVE_ENABLE
|
||||||
// RxFIFO non-empty interrupt handling.
|
// RxFIFO non-empty interrupt handling.
|
||||||
if (int_status & GINTSTS_RXFLVL) {
|
if (gintsts & GINTSTS_RXFLVL) {
|
||||||
// RXFLVL bit is read-only
|
// RXFLVL bit is read-only
|
||||||
dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
|
dwc2->gintmsk &= ~GINTMSK_RXFLVLM; // disable RXFLVL interrupt while reading
|
||||||
|
|
||||||
@@ -984,24 +1016,19 @@ void dcd_int_handler(uint8_t rhport) {
|
|||||||
|
|
||||||
dwc2->gintmsk |= GINTMSK_RXFLVLM;
|
dwc2->gintmsk |= GINTMSK_RXFLVLM;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// OUT endpoint interrupt handling.
|
// OUT endpoint interrupt handling.
|
||||||
if (int_status & GINTSTS_OEPINT) {
|
if (gintsts & GINTSTS_OEPINT) {
|
||||||
// OEPINT is read-only, clear using DOEPINTn
|
// OEPINT is read-only, clear using DOEPINTn
|
||||||
handle_epout_irq(rhport);
|
handle_ep_irq(rhport, TUSB_DIR_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IN endpoint interrupt handling.
|
// IN endpoint interrupt handling.
|
||||||
if (int_status & GINTSTS_IEPINT) {
|
if (gintsts & GINTSTS_IEPINT) {
|
||||||
// IEPINT bit read-only, clear using DIEPINTn
|
// 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
|
#if CFG_TUD_TEST_MODE
|
||||||
|
@@ -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
|
* 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);
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
|
|
||||||
// Check Synopsys ID register, failed if controller clock/power is not enabled
|
// 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->gotgint = 0xFFFFFFFFU;
|
||||||
dwc2->gintmsk = 0;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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_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);
|
void dwc2_core_handle_common_irq(uint8_t rhport, bool in_isr);
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
|
@@ -149,17 +149,12 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GRXSTS_PKTSTS_GLOBALOUTNAK = 1,
|
GRXSTS_PKTSTS_GLOBAL_OUT_NAK = 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_DATA = 2,
|
||||||
GRXSTS_PKTSTS_RX_COMPLETE = 3,
|
GRXSTS_PKTSTS_RX_COMPLETE = 3,
|
||||||
|
GRXSTS_PKTSTS_SETUP_DONE = 4,
|
||||||
GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5,
|
GRXSTS_PKTSTS_HOST_DATATOGGLE_ERR = 5,
|
||||||
|
GRXSTS_PKTSTS_SETUP_RX = 6,
|
||||||
GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7
|
GRXSTS_PKTSTS_HOST_CHANNEL_HALTED = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -171,6 +166,21 @@ enum {
|
|||||||
HCCHAR_EPTYPE_INTERRUPT = 3
|
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
|
// Common Register Bitfield
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
@@ -468,6 +478,117 @@ typedef struct {
|
|||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
// Device Register Bitfield
|
// Device Register Bitfield
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
typedef struct TU_ATTR_PACKED {
|
||||||
|
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");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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 {
|
typedef struct TU_ATTR_PACKED {
|
||||||
uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
|
uint32_t xfer_size : 19; // 0..18 Transfer size in bytes
|
||||||
uint32_t packet_count : 10; // 19..28 Number of packets
|
uint32_t packet_count : 10; // 19..28 Number of packets
|
||||||
@@ -475,52 +596,31 @@ typedef struct TU_ATTR_PACKED {
|
|||||||
} dwc2_ep_tsize_t;
|
} dwc2_ep_tsize_t;
|
||||||
TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
|
TU_VERIFY_STATIC(sizeof(dwc2_ep_tsize_t) == 4, "incorrect size");
|
||||||
|
|
||||||
// Endpoint IN
|
// Device IN/OUT Endpoint
|
||||||
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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// Universal Endpoint
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
volatile uint32_t diepctl;
|
volatile uint32_t diepctl;
|
||||||
volatile uint32_t doepctl;
|
volatile uint32_t doepctl;
|
||||||
|
|
||||||
volatile uint32_t ctl;
|
volatile uint32_t ctl;
|
||||||
|
volatile dwc2_depctl_t ctl_bm;
|
||||||
};
|
};
|
||||||
uint32_t rsv04;
|
uint32_t rsv04;
|
||||||
union {
|
union {
|
||||||
|
volatile uint32_t intr;
|
||||||
|
|
||||||
volatile uint32_t diepint;
|
volatile uint32_t diepint;
|
||||||
|
volatile dwc2_diepint_t diepint_bm;
|
||||||
|
|
||||||
volatile uint32_t doepint;
|
volatile uint32_t doepint;
|
||||||
|
volatile dwc2_doepint_t doepint_bm;
|
||||||
};
|
};
|
||||||
uint32_t rsv0c;
|
uint32_t rsv0c;
|
||||||
union {
|
union {
|
||||||
volatile uint32_t dieptsiz;
|
volatile uint32_t dieptsiz;
|
||||||
volatile uint32_t doeptsiz;
|
volatile uint32_t doeptsiz;
|
||||||
volatile dwc2_ep_tsize_t deptsiz_bm;
|
volatile uint32_t tsiz;
|
||||||
|
volatile dwc2_ep_tsize_t tsiz_bm;
|
||||||
};
|
};
|
||||||
union {
|
union {
|
||||||
volatile uint32_t diepdma;
|
volatile uint32_t diepdma;
|
||||||
@@ -631,12 +731,27 @@ typedef struct {
|
|||||||
uint32_t reserved700[64]; // 700..7FF
|
uint32_t reserved700[64]; // 700..7FF
|
||||||
|
|
||||||
//------------- Device -----------//
|
//------------- Device -----------//
|
||||||
|
union {
|
||||||
volatile uint32_t dcfg; // 800 Device Configuration
|
volatile uint32_t dcfg; // 800 Device Configuration
|
||||||
|
volatile dwc2_dcfg_t dcfg_bm;
|
||||||
|
};
|
||||||
|
union {
|
||||||
volatile uint32_t dctl; // 804 Device Control
|
volatile uint32_t dctl; // 804 Device Control
|
||||||
|
volatile dwc2_dctl_t dctl_bm;
|
||||||
|
};
|
||||||
|
union {
|
||||||
volatile uint32_t dsts; // 808 Device Status (RO)
|
volatile uint32_t dsts; // 808 Device Status (RO)
|
||||||
|
volatile dwc2_dsts_t dsts_bm;
|
||||||
|
};
|
||||||
uint32_t reserved80c; // 80C
|
uint32_t reserved80c; // 80C
|
||||||
|
union {
|
||||||
volatile uint32_t diepmsk; // 810 Device IN Endpoint Interrupt Mask
|
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 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 daint; // 818 Device All Endpoints Interrupt
|
||||||
volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
|
volatile uint32_t daintmsk; // 81C Device All Endpoints Interrupt Mask
|
||||||
volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
|
volatile uint32_t dtknqr1; // 820 Device IN token sequence learning queue read1
|
||||||
@@ -658,8 +773,8 @@ typedef struct {
|
|||||||
union {
|
union {
|
||||||
dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
|
dwc2_dep_t ep[2][16]; // 0: IN, 1 OUT
|
||||||
struct {
|
struct {
|
||||||
dwc2_epin_t epin[16]; // 900..AFF IN Endpoints
|
dwc2_dep_t epin[16]; // 900..AFF IN Endpoints
|
||||||
dwc2_epout_t epout[16]; // B00..CFF OUT Endpoints
|
dwc2_dep_t epout[16]; // B00..CFF OUT Endpoints
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
uint32_t reservedd00[64]; // D00..DFF
|
uint32_t reservedd00[64]; // D00..DFF
|
||||||
|
@@ -336,14 +336,8 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
|||||||
|
|
||||||
// Core Initialization
|
// Core Initialization
|
||||||
const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
|
const bool is_highspeed = dwc2_core_is_highspeed(dwc2, TUSB_ROLE_HOST);
|
||||||
TU_ASSERT(dwc2_core_init(rhport, is_highspeed));
|
const bool is_dma = dma_host_enabled(dwc2);
|
||||||
|
TU_ASSERT(dwc2_core_init(rhport, is_highspeed, is_dma));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------- 3.1 Host Initialization -------------//
|
//------------- 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,);
|
TU_ASSERT(xfer->ep_id < CFG_TUH_DWC2_ENDPOINT_MAX,);
|
||||||
dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
|
dwc2_channel_char_t hcchar_bm = channel->hcchar_bm;
|
||||||
|
|
||||||
uint32_t hcint = channel->hcint;
|
const uint32_t hcint = channel->hcint;
|
||||||
channel->hcint = hcint;
|
channel->hcint = hcint; // clear interrupt
|
||||||
|
|
||||||
bool is_done;
|
bool is_done;
|
||||||
if (is_dma) {
|
if (is_dma) {
|
||||||
|
@@ -248,13 +248,17 @@
|
|||||||
// USBIP
|
// USBIP
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#ifndef CFG_TUD_DWC2_SLAVE_ENABLE
|
||||||
|
#define CFG_TUD_DWC2_SLAVE_ENABLE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
// DWC2 controller: use DMA for data transfer
|
// DWC2 controller: use DMA for data transfer
|
||||||
// For processors with data cache enabled, USB endpoint buffer region
|
// For processors with data cache enabled, USB endpoint buffer region
|
||||||
// (defined by CFG_TUSB_MEM_SECTION) must be declared as non-cacheable.
|
// (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
|
// 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.
|
// non-cacheable, with RASR register value: TEX=1 C=0 B=0 S=0.
|
||||||
#ifndef CFG_TUD_DWC2_DMA
|
#ifndef CFG_TUD_DWC2_DMA_ENABLE
|
||||||
#define CFG_TUD_DWC2_DMA 0
|
#define CFG_TUD_DWC2_DMA_ENABLE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Enable DWC2 Slave mode for host
|
// Enable DWC2 Slave mode for host
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
"name": "espressif_s3_devkitm",
|
"name": "espressif_s3_devkitm",
|
||||||
"uid": "84F703C084E4",
|
"uid": "84F703C084E4",
|
||||||
"build" : {
|
"build" : {
|
||||||
"flags_on": ["", "CFG_TUD_DWC2_DMA"]
|
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
|
||||||
},
|
},
|
||||||
"tests": {
|
"tests": {
|
||||||
"only": ["device/cdc_msc_freertos", "device/hid_composite_freertos"]
|
"only": ["device/cdc_msc_freertos", "device/hid_composite_freertos"]
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
"name": "stm32f723disco",
|
"name": "stm32f723disco",
|
||||||
"uid": "460029001951373031313335",
|
"uid": "460029001951373031313335",
|
||||||
"build" : {
|
"build" : {
|
||||||
"flags_on": ["", "CFG_TUD_DWC2_DMA"]
|
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
|
||||||
},
|
},
|
||||||
"tests": {
|
"tests": {
|
||||||
"device": true, "host": true, "dual": false,
|
"device": true, "host": true, "dual": false,
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
"name": "stm32h743nucleo",
|
"name": "stm32h743nucleo",
|
||||||
"uid": "110018000951383432343236",
|
"uid": "110018000951383432343236",
|
||||||
"build" : {
|
"build" : {
|
||||||
"flags_on": ["", "CFG_TUD_DWC2_DMA"]
|
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
|
||||||
},
|
},
|
||||||
"tests": {
|
"tests": {
|
||||||
"device": true, "host": false, "dual": false
|
"device": true, "host": false, "dual": false
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
"name": "stm32f769disco",
|
"name": "stm32f769disco",
|
||||||
"uid": "21002F000F51363531383437",
|
"uid": "21002F000F51363531383437",
|
||||||
"build" : {
|
"build" : {
|
||||||
"flags_on": ["", "CFG_TUD_DWC2_DMA"]
|
"flags_on": ["", "CFG_TUD_DWC2_DMA_ENABLE"]
|
||||||
},
|
},
|
||||||
"tests": {
|
"tests": {
|
||||||
"device": true, "host": false, "dual": false
|
"device": true, "host": false, "dual": false
|
||||||
|
Reference in New Issue
Block a user