From 9edf4334c441afbcb24b723411c5cdef1fac56ec Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Thu, 1 Oct 2020 11:49:14 +0200 Subject: [PATCH 1/4] DA146xx: Allow receiving of packets larger then 64 bytes Internal FIFO for each endpoint is limited to 64 bytes. It is possible to have longer packets if respective FIFO is read during actual packet transmission. This change updates receive data path to allow packets (and endpoint size) larger then 64 bytes. If DMA is not used yet DMA is setup for reception of big packets. If DMA is already assigned to some transfer, code enables FIFO level warning interrupts and tries to read data before FIFO is filled up. --- src/portable/dialog/da146xx/dcd_da146xx.c | 258 +++++++++++++++++----- 1 file changed, 203 insertions(+), 55 deletions(-) diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 602836e3a..9cc02e821 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -40,6 +40,24 @@ // We disable SOF for now until needed later on #define USE_SOF 0 +// Size of RX or TX FIFO. +#define FIFO_SIZE 64 + +#ifndef TU_DA1469X_FIFO_READ_THRESHOLD +// RX FIFO is 64 bytes. When endpoint size is greater then 64, FIFO warning interrupt +// is enabled to allow read incoming data during frame reception. +// It is possible to stay in interrupt reading whole packet at once, but it may be +// more efficient for MCU to read as much data as possible and when FIFO is hardly +// filled exit interrupt handler waiting for next FIFO warning level interrupt +// or packet end. +// When running at 96MHz code that reads FIFO based on number of bytes stored in +// USB_RXSx_REG.USB_RXCOUNT takes enough time to fill FIFO with two additional bytes. +// Settings this threshold above this allows to leave interrupt handler and wait +// for more bytes to before next ISR. This allows reduce overall ISR time to 1/3 +// of time that would be needed if ISR read as fast as possible. +#define TU_DA1469X_FIFO_READ_THRESHOLD 4 +#endif + #define EP_MAX 4 #define NFSR_NODE_RESET 0 @@ -119,6 +137,54 @@ typedef struct #define EP_REGS(first_ep_reg) (EPx_REGS*)(&USB->first_ep_reg) +// DMA channel pair to use, channel 6 will be used for RX channel 7 for TX direction. +#ifndef TU_DA146XX_DMA_RX_CHANNEL +#define TU_DA146XX_DMA_RX_CHANNEL 6 +#endif +#define DA146XX_DMA_USB_MUX (0x6 << (TU_DA146XX_DMA_RX_CHANNEL * 2)) +#define DA146XX_DMA_USB_MUX_MASK (0xF << (TU_DA146XX_DMA_RX_CHANNEL * 2)) + +typedef struct +{ + __IOM uint32_t DMAx_A_START_REG; + __IOM uint32_t DMAx_B_START_REG; + __IOM uint32_t DMAx_INT_REG; + __IOM uint32_t DMAx_LEN_REG; + __IOM uint32_t DMAx_CTRL_REG; + __IOM uint32_t DMAx_IDX_REG; + __IM uint32_t RESERVED[2]; // Extend structure size for array like usage, registers for each channel are 0x20 bytes apart. +} da146xx_dma_channel_t; + +#define DMA_CHANNEL_REGS(n) ((da146xx_dma_channel_t *)(DMA) + n) +#define RX_DMA_REGS DMA_CHANNEL_REGS(TU_DA146XX_DMA_RX_CHANNEL) +#define TX_DMA_REGS DMA_CHANNEL_REGS((TU_DA146XX_DMA_RX_CHANNEL) + 1) + +#define RX_DMA_START ((1 << DMA_DMA0_CTRL_REG_DMA_ON_Pos) |\ + (0 << DMA_DMA0_CTRL_REG_BW_Pos) | \ + (1 << DMA_DMA0_CTRL_REG_DREQ_MODE_Pos) | \ + (1 << DMA_DMA0_CTRL_REG_BINC_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_AINC_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_CIRCULAR_Pos) | \ + (2 << DMA_DMA0_CTRL_REG_DMA_PRIO_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_DMA_IDLE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_DMA_INIT_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_REQ_SENSE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_BURST_MODE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_BUS_ERROR_DETECT_Pos)) + +#define TX_DMA_START ((1 << DMA_DMA0_CTRL_REG_DMA_ON_Pos) |\ + (0 << DMA_DMA0_CTRL_REG_BW_Pos) | \ + (1 << DMA_DMA0_CTRL_REG_DREQ_MODE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_BINC_Pos) | \ + (1 << DMA_DMA0_CTRL_REG_AINC_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_CIRCULAR_Pos) | \ + (2 << DMA_DMA0_CTRL_REG_DMA_PRIO_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_DMA_IDLE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_DMA_INIT_Pos) | \ + (1 << DMA_DMA0_CTRL_REG_REQ_SENSE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_BURST_MODE_Pos) | \ + (0 << DMA_DMA0_CTRL_REG_BUS_ERROR_DETECT_Pos)) + // Dialog register fields and bit mask are very long. Filed masks repeat register names. // Those convenience macros are a way to reduce complexity of register modification lines. #define GET_BIT(val, field) (val & field ## _Msk) >> field ## _Pos @@ -151,6 +217,8 @@ static struct bool vbus_present; bool in_reset; xfer_ctl_t xfer_status[EP_MAX][2]; + // Endpoints that use DMA, one for each direction + uint8_t dma_ep[2]; } _dcd = { .vbus_present = false, @@ -258,35 +326,93 @@ static void transmit_packet(xfer_ctl_t * xfer) regs->txc = txc; } -static void receive_packet(xfer_ctl_t *xfer, uint16_t bytes_in_fifo) +static bool try_allocate_dma(uint8_t epnum, uint8_t dir) +{ + // TODO: Disable interrupts while checking + if (_dcd.dma_ep[dir] == 0) + { + _dcd.dma_ep[dir] = epnum; + if (dir == TUSB_DIR_OUT) + USB->USB_DMA_CTRL_REG = (USB->USB_DMA_CTRL_REG & ~USB_USB_DMA_CTRL_REG_USB_DMA_RX_Msk) | + ((epnum - 1) << USB_USB_DMA_CTRL_REG_USB_DMA_RX_Pos); + else + USB->USB_DMA_CTRL_REG = (USB->USB_DMA_CTRL_REG & ~USB_USB_DMA_CTRL_REG_USB_DMA_TX_Msk) | + ((epnum - 1) << USB_USB_DMA_CTRL_REG_USB_DMA_TX_Pos); + USB->USB_DMA_CTRL_REG |= USB_USB_DMA_CTRL_REG_USB_DMA_EN_Msk; + } + return _dcd.dma_ep[dir] == epnum; +} + +static void start_rx_dma(volatile void *src, void *dst, uint16_t size) +{ + // Setup SRC and DST registers + RX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src; + RX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst; + // Don't need DMA interrupt, read end is determined by RX_LAST or RX_ERR events. + RX_DMA_REGS->DMAx_INT_REG = size - 1; + RX_DMA_REGS->DMAx_LEN_REG = size - 1; + RX_DMA_REGS->DMAx_CTRL_REG = RX_DMA_START; +} + +static void start_rx_packet(xfer_ctl_t *xfer) +{ + uint8_t const epnum = tu_edpt_number(xfer->ep_addr); + uint16_t remaining = xfer->total_len - xfer->transferred; + uint16_t size = tu_min16(remaining, xfer->max_packet_size); + + xfer->last_packet_size = 0; + if (xfer->max_packet_size > FIFO_SIZE && remaining > FIFO_SIZE) + { + if (try_allocate_dma(epnum, TUSB_DIR_OUT)) + { + start_rx_dma(&xfer->regs->rxd, xfer->buffer + xfer->transferred, size); + } + else + { + // Other endpoint is using DMA in that direction, fall back to interrupts. + // For endpoint size greater then FIFO size enable FIFO level warning interrupt + // when FIFO has less then 17 bytes free. + xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk; + USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos); + } + } + else if (epnum != 0) + { + // If max_packet_size would fit in FIFO no need for FIFO level warning interrupt. + xfer->regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk; + USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos)); + } + xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk; +} + +static void read_rx_fifo(xfer_ctl_t *xfer, uint16_t bytes_in_fifo) { EPx_REGS *regs = xfer->regs; - uint16_t remaining = xfer->total_len - xfer->transferred; + uint16_t remaining = xfer->total_len - xfer->transferred - xfer->last_packet_size; uint16_t receive_this_time = bytes_in_fifo; - if (remaining <= bytes_in_fifo) receive_this_time = remaining; + if (remaining < bytes_in_fifo) receive_this_time = remaining; uint8_t *buf = xfer->buffer + xfer->transferred + xfer->last_packet_size; for (int i = 0; i < receive_this_time; ++i) buf[i] = regs->rxd; - xfer->transferred += receive_this_time; xfer->last_packet_size += receive_this_time; } static void handle_ep0_rx(void) { - int packet_size; + int fifo_bytes; uint32_t rxs0 = USB->USB_RXS0_REG; xfer_ctl_t *xfer = XFER_CTL_BASE(0, TUSB_DIR_OUT); - packet_size = GET_BIT(rxs0, USB_USB_RXS0_REG_USB_RCOUNT); + fifo_bytes = GET_BIT(rxs0, USB_USB_RXS0_REG_USB_RCOUNT); if (rxs0 & USB_USB_RXS0_REG_USB_SETUP_Msk) { xfer_ctl_t *xfer_in = XFER_CTL_BASE(0, TUSB_DIR_IN); // Setup packet is in - for (int i = 0; i < packet_size; ++i) _setup_packet[i] = USB->USB_RXD0_REG; + for (int i = 0; i < fifo_bytes; ++i) _setup_packet[i] = USB->USB_RXD0_REG; xfer->stall = 0; xfer->data1 = 1; @@ -302,21 +428,26 @@ static void handle_ep0_rx(void) { // Toggle bit does not match discard packet REG_SET_BIT(USB_RXC0_REG, USB_FLUSH); + xfer->last_packet_size = 0; } else { - receive_packet(xfer, packet_size); - xfer->data1 ^= 1; + read_rx_fifo(xfer, fifo_bytes); + if (rxs0 & USB_USB_RXS0_REG_USB_RX_LAST_Msk) + { + xfer->transferred += xfer->last_packet_size; + xfer->data1 ^= 1; - if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size) - { - dcd_event_xfer_complete(0, 0, xfer->transferred, XFER_RESULT_SUCCESS, true); - } - else - { + if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size) + { + dcd_event_xfer_complete(0, 0, xfer->transferred, XFER_RESULT_SUCCESS, true); + } + else + { + // Re-enable reception + REG_SET_BIT(USB_RXC0_REG, USB_RX_EN); + } xfer->last_packet_size = 0; - // Re-enable reception - REG_SET_BIT(USB_RXC0_REG, USB_RX_EN); } } } @@ -357,44 +488,72 @@ static void handle_ep0_tx(void) static void handle_epx_rx_ev(uint8_t ep) { uint32_t rxs; - int packet_size; + int fifo_bytes; xfer_ctl_t *xfer = XFER_CTL_BASE(ep, TUSB_DIR_OUT); EPx_REGS *regs = xfer->regs; - rxs = regs->rxs; + do + { + rxs = regs->rxs; - if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_ERR)) - { - regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk; - } - else - { - packet_size = GET_BIT(rxs, USB_USB_RXS1_REG_USB_RXCOUNT); - receive_packet(xfer, packet_size); - if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST)) + if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_ERR)) { - if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1) + regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk; + xfer->last_packet_size = 0; + if (_dcd.dma_ep[TUSB_DIR_OUT] == ep) { - // Toggle bit does not match discard packet - regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk; + // Stop DMA + RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk; + // Restart DMA since packet was dropped, all parameters should still work. + RX_DMA_REGS->DMAx_CTRL_REG |= DMA_DMA0_CTRL_REG_DMA_ON_Msk; } - else + break; + } + else + { + if (_dcd.dma_ep[TUSB_DIR_OUT] == ep) { - xfer->data1 ^= 1; - if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size) + // Disable DMA and update last_packet_size with what DMA reported. + RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk; + xfer->last_packet_size = RX_DMA_REGS->DMAx_IDX_REG; + // When DMA did not finished (packet was smaller then MPS), DMAx_IDX_REG holds exact number of bytes transmitted. + // When DMA finished value in DMAx_IDX_REG is one less then actual number of transmitted bytes. + if (xfer->last_packet_size == RX_DMA_REGS->DMAx_LEN_REG) xfer->last_packet_size++; + // Release DMA to use by other endpoints. + _dcd.dma_ep[TUSB_DIR_OUT] = 0; + } + fifo_bytes = GET_BIT(rxs, USB_USB_RXS1_REG_USB_RXCOUNT); + // FIFO maybe empty if DMA read it before or it's final iteration and function already read all that was to read. + if (fifo_bytes > 0) + { + read_rx_fifo(xfer, fifo_bytes); + } + if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST)) + { + if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1) { - dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true); + // Toggle bit does not match discard packet + regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk; } else { - xfer->last_packet_size = 0; - // Re-enable reception - regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk; + xfer->data1 ^= 1; + xfer->transferred += xfer->last_packet_size; + if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size) + { + dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true); + } + else + { + // Re-enable reception + start_rx_packet(xfer); + } } + xfer->last_packet_size = 0; } } - } + } while (fifo_bytes > TU_DA1469X_FIFO_READ_THRESHOLD); } static void handle_rx_ev(void) @@ -459,6 +618,7 @@ static void handle_bus_reset(void) _dcd.in_reset = true; dcd_event_bus_signal(0, DCD_EVENT_BUS_RESET, true); + USB->USB_DMA_CTRL_REG = 0; USB->USB_MAMSK_REG = USB_USB_MAMSK_REG_USB_M_INTR_Msk | #if USE_SOF @@ -595,6 +755,9 @@ void dcd_connect(uint8_t rhport) (void)rhport; REG_SET_BIT(USB_MCTRL_REG, USB_NAT); + + // Select chosen DMA to be triggered by USB. + DMA->DMA_REQ_MUX_REG = (DMA->DMA_REQ_MUX_REG & ~DA146XX_DMA_USB_MUX_MASK) | DA146XX_DMA_USB_MUX; } void dcd_disconnect(uint8_t rhport) @@ -666,22 +829,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t if (dir == TUSB_DIR_OUT) { - if (epnum != 0) - { - if (xfer->max_packet_size > 64) - { - // For endpoint size greater then FIFO size enable FIFO level warning interrupt - // when FIFO has less then 17 bytes free. - xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk; - } - else - { - // If max_packet_size would fit in FIFO no need for FIFO level warning interrupt. - xfer->regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk; - } - } - // USB_RX_EN bit is in same place for all endpoints. - xfer->regs->rxc = USB_USB_RXC0_REG_USB_RX_EN_Msk; + start_rx_packet(xfer); } else // IN { From 6615dd90622ab72649c3a87cbcdc74038f5c412c Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Fri, 2 Oct 2020 12:38:43 +0200 Subject: [PATCH 2/4] DA146xx: Add dcd_edpt_close Closing endpoints can be important when there are alternate instances. This adds functionality of closing endpoints similar to what exists in other drivers. --- src/portable/dialog/da146xx/dcd_da146xx.c | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 9cc02e821..6489fca95 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -814,6 +814,50 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) return true; } +void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) +{ + uint8_t const epnum = tu_edpt_number(ep_addr); + uint8_t const dir = tu_edpt_dir(ep_addr); + xfer_ctl_t * xfer = XFER_CTL_BASE(epnum, dir); + + (void)rhport; + + TU_ASSERT(epnum < EP_MAX,); + + if (epnum == 0) + { + USB->USB_MAMSK_REG &= ~(USB_USB_MAMSK_REG_USB_M_EP0_RX_Msk | + USB_USB_MAMSK_REG_USB_M_EP0_TX_Msk); + } + else + { + if (dir == TUSB_DIR_OUT) + { + xfer->regs->rxc = USB_USB_RXC1_REG_USB_FLUSH_Msk; + xfer->regs->epc_out = 0; + USB->USB_RXMSK_REG &= ~(0x101 << (epnum - 1)); + // Release DMA if needed + if (_dcd.dma_ep[TUSB_DIR_OUT] == epnum) + { + RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk; + _dcd.dma_ep[TUSB_DIR_OUT] = 0; + } + } + else + { + xfer->regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk; + xfer->regs->epc_in = 0; + USB->USB_TXMSK_REG &= ~(0x101 << (epnum - 1)); + // Release DMA if needed + if (_dcd.dma_ep[TUSB_DIR_IN] == epnum) + { + TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk; + _dcd.dma_ep[TUSB_DIR_IN] = 0; + } + } + } +} + bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes) { uint8_t const epnum = tu_edpt_number(ep_addr); From 33a5081bd1c712726f879ba65fee973b23480c27 Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Fri, 2 Oct 2020 12:41:18 +0200 Subject: [PATCH 3/4] DA146xx: Add support for ISO endpoints Few changes were needed to have working ISO endpoints. --- src/portable/dialog/da146xx/dcd_da146xx.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 6489fca95..7f8f67383 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -210,6 +210,8 @@ typedef struct { uint8_t data1 : 1; // Endpoint is stalled uint8_t stall : 1; + // ISO endpoint + uint8_t iso : 1; } xfer_ctl_t; static struct @@ -290,7 +292,7 @@ static void transmit_packet(xfer_ctl_t * xfer) EPx_REGS *regs = xfer->regs; uint32_t txc; - txc = USB_USB_TXC1_REG_USB_TX_EN_Msk; + txc = USB_USB_TXC1_REG_USB_TX_EN_Msk | USB_USB_TXC1_REG_USB_IGN_ISOMSK_Msk; if (xfer->data1) txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk; src = &xfer->buffer[xfer->transferred]; @@ -531,7 +533,7 @@ static void handle_epx_rx_ev(uint8_t ep) } if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST)) { - if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1) + if (!xfer->iso && GET_BIT(rxs, USB_USB_RXS1_REG_USB_TOGGLE_RX) != xfer->data1) { // Toggle bit does not match discard packet regs->rxc |= USB_USB_RXC1_REG_USB_FLUSH_Msk; @@ -540,7 +542,7 @@ static void handle_epx_rx_ev(uint8_t ep) { xfer->data1 ^= 1; xfer->transferred += xfer->last_packet_size; - if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size) + if (xfer->total_len == xfer->transferred || xfer->last_packet_size < xfer->max_packet_size || xfer->iso) { dcd_event_xfer_complete(0, xfer->ep_addr, xfer->transferred, XFER_RESULT_SUCCESS, true); } @@ -787,8 +789,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) xfer->max_packet_size = desc_edpt->wMaxPacketSize.size; xfer->ep_addr = desc_edpt->bEndpointAddress; xfer->data1 = 0; + xfer->iso = 0; - if (epnum != 0 && desc_edpt->bmAttributes.xfer == 1) iso_mask = USB_USB_EPC1_REG_USB_ISO_Msk; + if (epnum != 0 && desc_edpt->bmAttributes.xfer == 1) + { + iso_mask = USB_USB_EPC1_REG_USB_ISO_Msk; + xfer->iso = 1; + } if (epnum == 0) { From d36bfddc301daf9993c24c75597d04c1c39347a8 Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Wed, 7 Oct 2020 09:32:17 +0200 Subject: [PATCH 4/4] DA146xx: Allow transmitting of packets larger then 64 bytes FIFO is limited to 64 bytes yet MCU is capable of transmitting larger packets provided that FIFO will be filled on the fly and USB_USB_TXCx_REG_USB_LAST_Msk bit is set after FIFO is filled with all the data that should be transmitted. This change allows to use FIFO level warning interrupt to fill FIFO. When DMA is available it will be used instead of interrupts. Some function names were changed to better reflect what each function does. --- src/portable/dialog/da146xx/dcd_da146xx.c | 97 +++++++++++++++++------ 1 file changed, 71 insertions(+), 26 deletions(-) diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index 7f8f67383..42ba867d7 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -285,15 +285,12 @@ void tusb_vbus_changed(bool present) } } -static void transmit_packet(xfer_ctl_t * xfer) +static void fill_tx_fifo(xfer_ctl_t * xfer) { int left_to_send; uint8_t const *src; EPx_REGS *regs = xfer->regs; - uint32_t txc; - - txc = USB_USB_TXC1_REG_USB_TX_EN_Msk | USB_USB_TXC1_REG_USB_IGN_ISOMSK_Msk; - if (xfer->data1) txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk; + uint8_t const epnum = tu_edpt_number(xfer->ep_addr); src = &xfer->buffer[xfer->transferred]; left_to_send = xfer->total_len - xfer->transferred; @@ -310,22 +307,23 @@ static void transmit_packet(xfer_ctl_t * xfer) xfer->last_packet_size++; left_to_send--; } - if (tu_edpt_number(xfer->ep_addr) != 0) + if (epnum != 0) { if (left_to_send > 0) { // Max packet size is set to value greater then FIFO. Enable fifo level warning // to handle larger packets. - txc |= USB_USB_TXC1_REG_USB_TFWL_Msk; + regs->txc |= (3 << USB_USB_TXC1_REG_USB_TFWL_Pos); + USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos); } else { + xfer->regs->txc &= ~USB_USB_TXC1_REG_USB_TFWL_Msk; + USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos)); // Whole packet already in fifo, no need to refill it later. Mark last. - txc |= USB_USB_TXC1_REG_USB_LAST_Msk; + regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk; } } - // Enable transfer with correct interrupts enabled - regs->txc = txc; } static bool try_allocate_dma(uint8_t epnum, uint8_t dir) @@ -387,6 +385,43 @@ static void start_rx_packet(xfer_ctl_t *xfer) xfer->regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk; } +static void start_tx_dma(void *src, volatile void *dst, uint16_t size) +{ + // Setup SRC and DST registers + TX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src; + TX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst; + // Interrupt not needed + TX_DMA_REGS->DMAx_INT_REG = size; + TX_DMA_REGS->DMAx_LEN_REG = size - 1; + TX_DMA_REGS->DMAx_CTRL_REG = TX_DMA_START; +} + +static void start_tx_packet(xfer_ctl_t *xfer) +{ + uint8_t const epnum = tu_edpt_number(xfer->ep_addr); + uint16_t remaining = xfer->total_len - xfer->transferred; + uint16_t size = tu_min16(remaining, xfer->max_packet_size); + EPx_REGS *regs = xfer->regs; + + xfer->last_packet_size = 0; + + regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk; + regs->txc = USB_USB_TXC1_REG_USB_IGN_ISOMSK_Msk; + if (xfer->data1) xfer->regs->txc |= USB_USB_TXC1_REG_USB_TOGGLE_TX_Msk; + + if (xfer->max_packet_size > FIFO_SIZE && remaining > FIFO_SIZE && try_allocate_dma(epnum, TUSB_DIR_IN)) + { + // Whole packet will be put in FIFO by DMA. Set LAST bit before start. + start_tx_dma(xfer->buffer + xfer->transferred, ®s->txd, size); + regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk; + } + else + { + fill_tx_fifo(xfer); + } + regs->txc |= USB_USB_TXC1_REG_USB_TX_EN_Msk; +} + static void read_rx_fifo(xfer_ctl_t *xfer, uint16_t bytes_in_fifo) { EPx_REGS *regs = xfer->regs; @@ -483,7 +518,7 @@ static void handle_ep0_tx(void) // Start from the beginning xfer->last_packet_size = 0; } - transmit_packet(xfer); + fill_tx_fifo(xfer); } } @@ -570,14 +605,23 @@ static void handle_rx_ev(void) static void handle_epx_tx_ev(xfer_ctl_t *xfer) { - uint32_t usb_txs1_reg; + uint8_t const epnum = tu_edpt_number(xfer->ep_addr); + uint32_t txs; EPx_REGS *regs = xfer->regs; - usb_txs1_reg = regs->USB_TXS1_REG; + txs = regs->txs; - if (GET_BIT(usb_txs1_reg, USB_USB_TXS1_REG_USB_TX_DONE)) + if (GET_BIT(txs, USB_USB_TXS1_REG_USB_TX_DONE)) { - if (GET_BIT(usb_txs1_reg, USB_USB_TXS1_REG_USB_ACK_STAT)) + if (_dcd.dma_ep[TUSB_DIR_IN] == epnum) + { + // Disable DMA and update last_packet_size with what DMA reported. + TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk; + xfer->last_packet_size = TX_DMA_REGS->DMAx_IDX_REG + 1; + // Release DMA to used by other endpoints. + _dcd.dma_ep[TUSB_DIR_IN] = 0; + } + if (GET_BIT(txs, USB_USB_TXS1_REG_USB_ACK_STAT)) { // ACK received, update transfer state and DATA0/1 bit xfer->transferred += xfer->last_packet_size; @@ -590,12 +634,13 @@ static void handle_epx_tx_ev(xfer_ctl_t *xfer) return; } } - else - { - xfer->last_packet_size = 0; - } - transmit_packet(xfer); } + if (txs & USB_USB_TXS1_REG_USB_TX_URUN_Msk) + { + TU_LOG1("EP %d FIFO underrun\n", epnum); + } + // Start next or repeated packet. + start_tx_packet(xfer); } static void handle_tx_ev(void) @@ -665,9 +710,9 @@ static void handle_alt_ev(void) } } -static void handle_epx_tx_refill(uint8_t ep) +static void handle_epx_tx_warn_ev(uint8_t ep) { - transmit_packet(XFER_CTL_BASE(ep, TUSB_DIR_IN)); + fill_tx_fifo(XFER_CTL_BASE(ep, TUSB_DIR_IN)); } static void handle_fifo_warning(void) @@ -675,11 +720,11 @@ static void handle_fifo_warning(void) uint32_t fifo_warning = USB->USB_FWEV_REG; if (fifo_warning & 0x01) - handle_epx_tx_refill(1); + handle_epx_tx_warn_ev(1); if (fifo_warning & 0x02) - handle_epx_tx_refill(2); + handle_epx_tx_warn_ev(2); if (fifo_warning & 0x04) - handle_epx_tx_refill(3); + handle_epx_tx_warn_ev(3); if (fifo_warning & 0x10) handle_epx_rx_ev(1); if (fifo_warning & 0x20) @@ -884,7 +929,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t } else // IN { - transmit_packet(xfer); + start_tx_packet(xfer); } return true;