diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 6ca1433a6..706661706 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -629,12 +629,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++; + 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 @@ -839,6 +836,7 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi 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); } @@ -877,10 +875,10 @@ static void handle_epout_irq(uint8_t rhport) { for (uint8_t epnum = 0; epnum < ep_count; epnum++) { if (dwc2->daint & TU_BIT(DAINT_OEPINT_Pos + epnum)) { dwc2_dep_t* epout = &dwc2->epout[epnum]; - const uint32_t doepint = epout->doepint; + const uint32_t doepint = epout->intr; const dwc2_doepint_t doepint_bm = epout->doepint_bm; - epout->doepint = doepint; // Clear interrupt + epout->intr = doepint; // Clear interrupt // print_doepint(doepint); if (is_dma) { @@ -892,68 +890,91 @@ static void handle_epout_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); + 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, 1, ep0_pending[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))) { + dwc2_dep_t* epin = &dwc2->epin[epnum]; + const uint16_t remain_packets = epin->tsiz_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->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; + } + + // 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); + } + } +} + +static void handle_epin_dma(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 (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, 1, ep0_pending[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); + } + } +} + static void handle_epin_irq(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); - const uint8_t ep_count = _dwc2_controller[rhport].ep_count; + const bool is_dma = dma_device_enabled(dwc2); + const uint8_t ep_count = DWC2_EP_COUNT(dwc2); // 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_dep_t* epin = &dwc2->epin[n]; + for (uint8_t epnum = 0; epnum < ep_count; epnum++) { + if (dwc2->daint & TU_BIT(DAINT_IEPINT_Pos + epnum)) { + dwc2_dep_t* epin = &dwc2->epin[epnum]; + const uint32_t diepint = epin->intr; + const dwc2_diepint_t diepint_bm = epin->diepint_bm; - if (epin->diepint & DIEPINT_XFRC) { - epin->diepint = DIEPINT_XFRC; + epin->intr = diepint; // Clear interrupt - // 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); - } - } - - // 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->tsiz_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->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); - - // 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; - } - - // 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; - } - } - - // Turn off TXFE if all bytes are written. - if (epin->tsiz_bm.xfer_size == 0) { - dwc2->diepempmsk &= ~(1 << n); - } + // print_doepint(doepint); + if (is_dma) { + handle_epin_dma(rhport, epnum, diepint_bm); + } else { + handle_epin_slave(rhport, epnum, diepint_bm); } } } diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h index b67771a4d..812096759 100644 --- a/src/portable/synopsys/dwc2/dwc2_type.h +++ b/src/portable/synopsys/dwc2/dwc2_type.h @@ -537,12 +537,15 @@ typedef struct TU_ATTR_PACKED { 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 rsv7 : 1; // 7 Reserved - uint32_t txfifo_underrun : 1; // 8 Tx FIFO underrun + 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_12 : 3; // 10..12 Reserved + 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 rsv14_31 : 18; // 14..31 Reserved + 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"); @@ -582,7 +585,7 @@ typedef struct TU_ATTR_PACKED { 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 :15; // 16..31 Reserved + uint32_t rsv16_31 :16; // 16..31 Reserved } dwc2_doepint_t; TU_VERIFY_STATIC(sizeof(dwc2_doepint_t) == 4, "incorrect size"); @@ -604,6 +607,8 @@ typedef struct { }; uint32_t rsv04; union { + volatile uint32_t intr; + volatile uint32_t diepint; volatile dwc2_diepint_t diepint_bm;