dwc2: add dma support.
This commit is contained in:
		| @@ -67,6 +67,11 @@ | ||||
| // Debug level for DWC2 | ||||
| #define DWC2_DEBUG    2 | ||||
|  | ||||
| // DMA switch | ||||
| #ifndef DWC2_ENABLE_DMA | ||||
| #define DWC2_ENABLE_DMA    1 | ||||
| #endif | ||||
|  | ||||
| #ifndef dcache_clean | ||||
| #define dcache_clean(_addr, _size) | ||||
| #endif | ||||
| @@ -186,6 +191,32 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) { | ||||
|   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) { | ||||
|   dwc2_regs_t* dwc2 = DWC2_REG(rhport); | ||||
|   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 | ||||
|   _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 ) | ||||
|   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); | ||||
|  | ||||
|   if(dma_supported(rhport)) { | ||||
|     dma_stpkt_rx(rhport); | ||||
|   } | ||||
|  | ||||
|   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; | ||||
|  | ||||
|   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 | ||||
|   // We use multiple transaction of xfer->max_size length to get a whole transfer done | ||||
|   if (epnum == 0) { | ||||
|     xfer_ctl_t* const xfer = XFER_CTL_BASE(epnum, dir); | ||||
|     total_bytes = tu_min16(ep0_pending[dir], xfer->max_size); | ||||
|     ep0_pending[dir] -= total_bytes; | ||||
|   } | ||||
| @@ -386,17 +426,31 @@ static void edpt_schedule_packets(uint8_t rhport, uint8_t const epnum, uint8_t c | ||||
|     epin[epnum].dieptsiz = (num_packets << DIEPTSIZ_PKTCNT_Pos) | | ||||
|                            ((total_bytes << DIEPTSIZ_XFRSIZ_Pos) & DIEPTSIZ_XFRSIZ_Msk); | ||||
|  | ||||
|     epin[epnum].diepctl |= DIEPCTL_EPENA | DIEPCTL_CNAK; | ||||
|     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); | ||||
|     } | ||||
|     // Enable fifo empty interrupt only if there are something to put in the fifo. | ||||
|     if (total_bytes != 0) { | ||||
|       dwc2->diepempmsk |= (1 << epnum); | ||||
|       // 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; | ||||
|  | ||||
|       // 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); | ||||
|       } | ||||
|       // Enable fifo empty interrupt only if there are something to put in the fifo. | ||||
|       if (total_bytes != 0) { | ||||
|         dwc2->diepempmsk |= (1 << epnum); | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     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) | | ||||
|                              ((total_bytes << DOEPTSIZ_XFRSIZ_Pos) & DOEPTSIZ_XFRSIZ_Msk); | ||||
|  | ||||
|     epout[epnum].doepctl |= DOEPCTL_EPENA | DOEPCTL_CNAK; | ||||
|     if ((epout[epnum].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)); | ||||
|       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 | ||||
|   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 | ||||
| //  TU_ASSERT(!(dwc2->gintsts & GINTSTS_CMOD), ); | ||||
|  | ||||
| @@ -728,6 +792,11 @@ void dcd_edpt_close_all(uint8_t rhport) { | ||||
|   // reset allocated fifo IN | ||||
|   _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_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) { | ||||
|   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) { | ||||
| @@ -991,33 +1063,52 @@ static void handle_epout_irq(uint8_t rhport) { | ||||
|  | ||||
|       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 | ||||
|       if (epout->doepint & DOEPINT_XFRC) { | ||||
|         epout->doepint = DOEPINT_XFRC; | ||||
|  | ||||
|         xfer_ctl_t* xfer = XFER_CTL_BASE(n, TUSB_DIR_OUT); | ||||
|  | ||||
|         // EP0 can only handle one packet | ||||
|         if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { | ||||
|           // Schedule another packet to be received. | ||||
|           edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[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 { | ||||
|           dcd_event_xfer_complete(rhport, n, xfer->total_len, XFER_RESULT_SUCCESS, true); | ||||
|           // EP0 can only handle one packet | ||||
|           if ((n == 0) && ep0_pending[TUSB_DIR_OUT]) { | ||||
|             // Schedule another packet to be received. | ||||
|             edpt_schedule_packets(rhport, n, TUSB_DIR_OUT, 1, ep0_pending[TUSB_DIR_OUT]); | ||||
|           } 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); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // 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. | ||||
|           edpt_schedule_packets(rhport, n, TUSB_DIR_IN, 1, ep0_pending[TUSB_DIR_IN]); | ||||
|         } 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); | ||||
|         } | ||||
|       } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 HiFiPhile
					HiFiPhile