dwc2: add dma support.
This commit is contained in:
@@ -67,6 +67,11 @@
|
|||||||
// Debug level for DWC2
|
// Debug level for DWC2
|
||||||
#define DWC2_DEBUG 2
|
#define DWC2_DEBUG 2
|
||||||
|
|
||||||
|
// DMA switch
|
||||||
|
#ifndef DWC2_ENABLE_DMA
|
||||||
|
#define DWC2_ENABLE_DMA 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef dcache_clean
|
#ifndef dcache_clean
|
||||||
#define dcache_clean(_addr, _size)
|
#define dcache_clean(_addr, _size)
|
||||||
#endif
|
#endif
|
||||||
@@ -186,6 +191,32 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool dma_supported(uint8_t rhport)
|
||||||
|
{
|
||||||
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
|
// Internal DMA only
|
||||||
|
return (dwc2->ghwcfg2_bm.arch == 2) && DWC2_ENABLE_DMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dma_stpkt_rx(uint8_t rhport)
|
||||||
|
{
|
||||||
|
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
|
||||||
|
|
||||||
|
if (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a) {
|
||||||
|
if(dwc2->epout[0].doepctl & DOEPCTL_EPENA)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive only 1 packet
|
||||||
|
dwc2->epout[0].doeptsiz = 0;
|
||||||
|
dwc2->epout[0].doeptsiz |= (1 << DOEPTSIZ_STUPCNT_Pos);
|
||||||
|
dwc2->epout[0].doeptsiz |= 8;
|
||||||
|
dwc2->epout[0].doeptsiz |= (1 << DOEPTSIZ_PKTCNT_Pos);
|
||||||
|
dwc2->epout[0].doepdma = (uintptr_t)_setup_packet;
|
||||||
|
|
||||||
|
dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
uint8_t const epnum = tu_edpt_number(p_endpoint_desc->bEndpointAddress);
|
||||||
@@ -351,6 +382,11 @@ static void bus_reset(uint8_t rhport) {
|
|||||||
// Setup the control endpoint 0
|
// Setup the control endpoint 0
|
||||||
_allocated_fifo_words_tx = 16;
|
_allocated_fifo_words_tx = 16;
|
||||||
|
|
||||||
|
// DMA needs extra space for processing, needs size confirmation
|
||||||
|
if(dma_supported(rhport)) {
|
||||||
|
_allocated_fifo_words_tx += 72;
|
||||||
|
}
|
||||||
|
|
||||||
// Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
|
// Control IN uses FIFO 0 with 64 bytes ( 16 32-bit word )
|
||||||
dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx);
|
dwc2->dieptxf0 = (16 << DIEPTXF0_TX0FD_Pos) | (_dwc2_controller[rhport].ep_fifo_size / 4 - _allocated_fifo_words_tx);
|
||||||
|
|
||||||
@@ -361,6 +397,10 @@ static void bus_reset(uint8_t rhport) {
|
|||||||
|
|
||||||
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
|
dwc2->epout[0].doeptsiz |= (3 << DOEPTSIZ_STUPCNT_Pos);
|
||||||
|
|
||||||
|
if(dma_supported(rhport)) {
|
||||||
|
dma_stpkt_rx(rhport);
|
||||||
|
}
|
||||||
|
|
||||||
dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
|
dwc2->gintmsk |= GINTMSK_OEPINT | GINTMSK_IEPINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,11 +409,11 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
|
|||||||
(void) rhport;
|
(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);
|
||||||
|
|
||||||
// EP0 is limited to one packet each xfer
|
// EP0 is limited to one packet each xfer
|
||||||
// We use multiple transaction of xfer->max_size length to get a whole transfer done
|
// We use multiple transaction of xfer->max_size length to get a whole transfer done
|
||||||
if (epnum == 0) {
|
if (epnum == 0) {
|
||||||
xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir);
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -386,6 +426,19 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
|
|||||||
epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) |
|
epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) |
|
||||||
((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
|
((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk);
|
||||||
|
|
||||||
|
if(dma_supported(rhport)) {
|
||||||
|
epin[epnum].diepdma = (uintptr_t)xfer->buffer;
|
||||||
|
|
||||||
|
// For ISO endpoint set correct odd/even bit for next frame.
|
||||||
|
if ((epin[epnum].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));
|
||||||
|
epin[epnum].diepctl |= (odd_frame_now ? DIEPCTL_SD0PID_SEVNFRM_Msk : DIEPCTL_SODDFRM_Msk);
|
||||||
|
}
|
||||||
|
|
||||||
|
epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
|
||||||
|
} else {
|
||||||
|
|
||||||
epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
|
epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK;
|
||||||
|
|
||||||
// For ISO endpoint set correct odd/even bit for next frame.
|
// For ISO endpoint set correct odd/even bit for next frame.
|
||||||
@@ -398,6 +451,7 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
|
|||||||
if (total_bytes != 0) {
|
if (total_bytes != 0) {
|
||||||
dwc2->diepempmsk |= (1 << epnum);
|
dwc2->diepempmsk |= (1 << epnum);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
dwc2_epout_t* epout = dwc2->epout;
|
dwc2_epout_t* epout = dwc2->epout;
|
||||||
|
|
||||||
@@ -406,13 +460,18 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c
|
|||||||
epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) |
|
epout[epnum].doeptsiz |= (num_packets << DOEPTSIZ_PKTCNT_Pos) |
|
||||||
((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk);
|
((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk);
|
||||||
|
|
||||||
epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK;
|
|
||||||
if ((epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 &&
|
if ((epout[epnum].doepctl & DOEPCTL_EPTYP) == DOEPCTL_EPTYP_0 &&
|
||||||
XFER_CTL_BASE(epnum, dir)->interval == 1) {
|
XFER_CTL_BASE(epnum, dir)->interval == 1) {
|
||||||
// Take odd/even bit from frame counter.
|
// Take odd/even bit from frame counter.
|
||||||
uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
|
uint32_t const odd_frame_now = (dwc2->dsts & (1u << DSTS_FNSOF_Pos));
|
||||||
epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk);
|
epout[epnum].doepctl |= (odd_frame_now ? DOEPCTL_SD0PID_SEVNFRM_Msk : DOEPCTL_SODDFRM_Msk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(dma_supported(rhport)) {
|
||||||
|
epout[epnum].doepdma = (uintptr_t)xfer->buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,6 +678,11 @@ void dcd_init(uint8_t rhport) {
|
|||||||
// Enable global interrupt
|
// Enable global interrupt
|
||||||
dwc2->gahbcfg |= GAHBCFG_GINT;
|
dwc2->gahbcfg |= GAHBCFG_GINT;
|
||||||
|
|
||||||
|
if (dma_supported(rhport)) {
|
||||||
|
dwc2->gahbcfg |= GAHBCFG_DMAEN | GAHBCFG_HBSTLEN_2;
|
||||||
|
dwc2->gintmsk &=~GINTMSK_RXFLVLM;
|
||||||
|
}
|
||||||
|
|
||||||
// make sure we are in device mode
|
// make sure we are in device mode
|
||||||
// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), );
|
// TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), );
|
||||||
|
|
||||||
@@ -728,6 +792,11 @@ void dcd_edpt_close_all(uint8_t rhport) {
|
|||||||
// reset allocated fifo IN
|
// reset allocated fifo IN
|
||||||
_allocated_fifo_words_tx = 16;
|
_allocated_fifo_words_tx = 16;
|
||||||
|
|
||||||
|
// DMA needs extra space for processing, needs size confirmation
|
||||||
|
if(dma_supported(rhport)) {
|
||||||
|
_allocated_fifo_words_tx += 72;
|
||||||
|
}
|
||||||
|
|
||||||
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
fifo_flush_tx(dwc2, 0x10); // all tx fifo
|
||||||
fifo_flush_rx(dwc2);
|
fifo_flush_rx(dwc2);
|
||||||
}
|
}
|
||||||
@@ -809,6 +878,9 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
|
|||||||
|
|
||||||
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||||
edpt_disable(rhport, ep_addr, true);
|
edpt_disable(rhport, ep_addr, true);
|
||||||
|
if((tu_edpt_number(ep_addr) == 0) && dma_supported(rhport)) {
|
||||||
|
dma_stpkt_rx(rhport);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
|
||||||
@@ -991,34 +1063,53 @@ static void handle_epout_irq(uint8_t rhport) {
|
|||||||
|
|
||||||
uint32_t const doepint = epout->doepint;
|
uint32_t const doepint = epout->doepint;
|
||||||
|
|
||||||
// SETUP packet Setup Phase done.
|
|
||||||
if (doepint & DOEPINT_STUP) {
|
|
||||||
uint32_t clear_flag = DOEPINT_STUP;
|
|
||||||
|
|
||||||
// STPKTRX is only available for version from 3_00a
|
|
||||||
if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) {
|
|
||||||
clear_flag |= DOEPINT_STPKTRX;
|
|
||||||
}
|
|
||||||
|
|
||||||
epout->doepint = clear_flag;
|
|
||||||
dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// OUT XFER complete
|
// OUT XFER complete
|
||||||
if (epout->doepint & DOEPINT_XFRC) {
|
if (epout->doepint & DOEPINT_XFRC) {
|
||||||
epout->doepint = DOEPINT_XFRC;
|
epout->doepint = DOEPINT_XFRC;
|
||||||
|
|
||||||
xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
|
xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT);
|
||||||
|
|
||||||
|
if (doepint & DOEPINT_STUP) {
|
||||||
|
// STPKTRX is only available for version from 3_00a
|
||||||
|
if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) {
|
||||||
|
epout->doepint = DOEPINT_STPKTRX;
|
||||||
|
}
|
||||||
|
} else if (doepint & DOEPINT_OTEPSPR) {
|
||||||
|
epout->doepint = DOEPINT_OTEPSPR;
|
||||||
|
} else {
|
||||||
// EP0 can only handle one packet
|
// EP0 can only handle one packet
|
||||||
if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) {
|
if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) {
|
||||||
// Schedule another packet to be received.
|
// Schedule another packet to be received.
|
||||||
edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
|
edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]);
|
||||||
} else {
|
} else {
|
||||||
|
if(dma_supported(rhport)) {
|
||||||
|
// 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(n == 0 && xfer->total_len == 0) {
|
||||||
|
dma_stpkt_rx(rhport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SETUP packet Setup Phase done.
|
||||||
|
if (doepint & DOEPINT_STUP) {
|
||||||
|
epout->doepint = DOEPINT_STUP;
|
||||||
|
if ((doepint & DOEPINT_STPKTRX) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) {
|
||||||
|
epout->doepint = DOEPINT_STPKTRX;
|
||||||
|
}
|
||||||
|
if(dma_supported(rhport) && (dwc2->gsnpsid >= DWC2_CORE_REV_3_00a)) {
|
||||||
|
dma_stpkt_rx(rhport);
|
||||||
|
}
|
||||||
|
|
||||||
|
dcd_event_setup_received(rhport, (uint8_t*) _setup_packet, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1042,6 +1133,9 @@ static void handle_epin_irq(uint8_t rhport) {
|
|||||||
// Schedule another packet to be transmitted.
|
// Schedule another packet to be transmitted.
|
||||||
edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
|
edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]);
|
||||||
} else {
|
} else {
|
||||||
|
if((n == 0) && dma_supported(rhport)) {
|
||||||
|
dma_stpkt_rx(rhport);
|
||||||
|
}
|
||||||
dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
dcd_event_xfer_complete(rhport, n | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user