From a043657323d9fb07cd9194548267be44847dfee2 Mon Sep 17 00:00:00 2001 From: zhanghu5 Date: Mon, 22 May 2023 15:16:36 +0800 Subject: [PATCH] dma support 16550 uart Signed-off-by: zhanghu5 --- drivers/serial/Kconfig-16550 | 168 +++++++++++ drivers/serial/uart_16550.c | 444 +++++++++++++++++++++++++++--- include/nuttx/serial/uart_16550.h | 4 + 3 files changed, 576 insertions(+), 40 deletions(-) diff --git a/drivers/serial/Kconfig-16550 b/drivers/serial/Kconfig-16550 index b54b72990f3..576a2cb2fd8 100644 --- a/drivers/serial/Kconfig-16550 +++ b/drivers/serial/Kconfig-16550 @@ -27,6 +27,10 @@ config 16550_UART0_BASE config 16550_UART0_CLOCK int "16550 UART0 clock" +config 16550_UART0_CLOCK_NAME + string "16550 UART0 clock name" + depends on CLK + config 16550_UART0_IRQ int "16550 UART0 IRQ number" @@ -79,6 +83,44 @@ config 16550_UART0_OFLOWCONTROL ---help--- Enable 16550 UART0 CTS flow control +config 16550_UART0_DMA + bool "16550 UART0 DMA support" + default n + select ARCH_DMA + select SERIAL_DMA + ---help--- + Enable DMA transfers on 16550 UART0 + +if 16550_UART0_DMA + +config 16550_UART0_DMA_TX + int "16550 UART0 DMA Tx channel identity" + default -1 + ---help--- + -1 means don't use DMA for sending + +config 16550_UART0_DMA_RX + int "16550 UART0 DMA Rx channel identity" + default -1 + ---help--- + -1 means don't use DMA for receiving + +config 16550_UART0_DMA_RXBUFSIZE + int "16550 UART0 DMA Rx buffer size" + depends on 16550_UART0_DMA_RX != -1 + default 16550_UART0_RXBUFSIZE + ---help--- + 16550 UART0 DMA Rx buffer size. + +config 16550_UART0_DMA_RXTIMEOUT + int "16550 UART0 DMA Rx timeout(char)" + depends on 16550_UART0_DMA_RX != -1 + default 1 + ---help--- + 0 means DMA has no timeout for receiving + +endif + endif # 16550_UART0 config 16550_UART1 @@ -93,6 +135,10 @@ config 16550_UART1_BASE config 16550_UART1_CLOCK int "16550 UART1 clock" +config 16550_UART1_CLOCK_NAME + string "16550 UART1 clock name" + depends on CLK + config 16550_UART1_IRQ int "16550 UART1 IRQ number" @@ -145,6 +191,44 @@ config 16550_UART1_OFLOWCONTROL ---help--- Enable 16550 UART1 CTS flow control +config 16550_UART1_DMA + bool "16550 UART1 DMA support" + default n + select ARCH_DMA + select SERIAL_DMA + ---help--- + Enable DMA transfers on 16550 UART1 + +if 16550_UART1_DMA + +config 16550_UART1_DMA_TX + int "16550 UART1 DMA Tx channel identity" + default -1 + ---help--- + -1 means don't use DMA for sending + +config 16550_UART1_DMA_RX + int "16550 UART1 DMA Rx channel identity" + default -1 + ---help--- + -1 means don't use DMA for receiving + +config 16550_UART1_DMA_RXBUFSIZE + int "16550 UART1 DMA Rx buffer size" + depends on 16550_UART1_DMA_RX != -1 + default 16550_UART1_RXBUFSIZE + ---help--- + 16550 UART1 DMA Rx buffer size. + +config 16550_UART1_DMA_RXTIMEOUT + int "16550 UART1 DMA Rx timeout(char)" + depends on 16550_UART1_DMA_RX != -1 + default 1 + ---help--- + 0 means DMA has no timeout for receiving + +endif + endif # 16550_UART1 config 16550_UART2 @@ -159,6 +243,10 @@ config 16550_UART2_BASE config 16550_UART2_CLOCK int "16550 UART2 clock" +config 16550_UART2_CLOCK_NAME + string "16550 UART2 clock name" + depends on CLK + config 16550_UART2_IRQ int "16550 UART2 IRQ number" @@ -211,6 +299,44 @@ config 16550_UART2_OFLOWCONTROL ---help--- Enable 16550 UART2 CTS flow control +config 16550_UART2_DMA + bool "16550 UART2 DMA support" + default n + select ARCH_DMA + select SERIAL_DMA + ---help--- + Enable DMA transfers on 16550 UART2 + +if 16550_UART2_DMA + +config 16550_UART2_DMA_TX + int "16550 UART2 DMA Tx channel identity" + default -1 + ---help--- + -1 means don't use DMA for sending + +config 16550_UART2_DMA_RX + int "16550 UART2 DMA Rx channel identity" + default -1 + ---help--- + -1 means don't use DMA for receiving + +config 16550_UART2_DMA_RXBUFSIZE + int "16550 UART2 DMA Rx buffer size" + depends on 16550_UART2_DMA_RX != -1 + default 16550_UART2_RXBUFSIZE + ---help--- + 16550 UART2 DMA Rx buffer size. + +config 16550_UART2_DMA_RXTIMEOUT + int "16550 UART2 DMA Rx timeout(char)" + depends on 16550_UART2_DMA_RX != -1 + default 1 + ---help--- + 0 means DMA has no timeout for receiving + +endif + endif # 16550_UART2 config 16550_UART3 @@ -225,6 +351,10 @@ config 16550_UART3_BASE config 16550_UART3_CLOCK int "16550 UART3 clock" +config 16550_UART3_CLOCK_NAME + string "16550 UART3 clock name" + depends on CLK + config 16550_UART3_IRQ int "16550 UART3 IRQ number" @@ -277,6 +407,44 @@ config 16550_UART3_OFLOWCONTROL ---help--- Enable 16550 UART3 CTS flow control +config 16550_UART3_DMA + bool "16550 UART3 DMA support" + default n + select ARCH_DMA + select SERIAL_DMA + ---help--- + Enable DMA transfers on 16550 UART3 + +if 16550_UART3_DMA + +config 16550_UART3_DMA_TX + int "16550 UART3 DMA Tx channel identity" + default -1 + ---help--- + -1 means don't use DMA for sending + +config 16550_UART3_DMA_RX + int "16550 UART3 DMA Rx channel identity" + default -1 + ---help--- + -1 means don't use DMA for receiving + +config 16550_UART3_DMA_RXBUFSIZE + int "16550 UART3 DMA Rx buffer size" + depends on 16550_UART3_DMA_RX != -1 + default 16550_UART3_RXBUFSIZE + ---help--- + 16550 UART3 DMA Rx buffer size. + +config 16550_UART3_DMA_RXTIMEOUT + int "16550 UART3 DMA Rx timeout(char)" + depends on 16550_UART3_DMA_RX != -1 + default 1 + ---help--- + 0 means DMA has no timeout for receiving + +endif + endif # 16550_UART3 choice diff --git a/drivers/serial/uart_16550.c b/drivers/serial/uart_16550.c index fe84dc2a5fd..150ce84232a 100644 --- a/drivers/serial/uart_16550.c +++ b/drivers/serial/uart_16550.c @@ -37,17 +37,19 @@ #include #include +#include #include #include #include #include -#ifdef CONFIG_16550_UART +#if defined(CONFIG_16550_UART0_DMA) || defined(CONFIG_16550_UART1_DMA) \ + || defined(CONFIG_16550_UART2_DMA) || defined(CONFIG_16550_UART3_DMA) +# define HAVE_16550_UART_DMA 1 +#endif -/**************************************************************************** - * Pre-processor definitions - ****************************************************************************/ +#ifdef CONFIG_16550_UART /**************************************************************************** * Private Types @@ -55,19 +57,34 @@ struct u16550_s { - uart_addrwidth_t uartbase; /* Base address of UART registers */ -#ifndef CONFIG_16550_SUPRESS_CONFIG - uint32_t baud; /* Configured baud */ - uint32_t uartclk; /* UART clock frequency */ + uart_addrwidth_t uartbase; /* Base address of UART registers */ +#ifdef HAVE_16550_UART_DMA + int32_t dmatx; + FAR struct dma_chan_s *chantx; + int32_t dmarx; + FAR struct dma_chan_s *chanrx; + FAR char *dmarxbuf; + size_t dmarxsize; + volatile size_t dmarxhead; + volatile size_t dmarxtail; + int32_t dmarxtimeout; #endif - uart_datawidth_t ier; /* Saved IER value */ - uint8_t irq; /* IRQ associated with this UART */ +#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(HAVE_16550_UART_DMA) + uint32_t baud; /* Configured baud */ + uint32_t uartclk; /* UART clock frequency */ +#endif +#ifdef CONFIG_CLK + FAR const char *clk_name; /* UART clock name */ + FAR struct clk_s *mclk; /* UART clock descriptor */ +#endif + uart_datawidth_t ier; /* Saved IER value */ + uint8_t irq; /* IRQ associated with this UART */ #ifndef CONFIG_16550_SUPRESS_CONFIG - uint8_t parity; /* 0=none, 1=odd, 2=even */ - uint8_t bits; /* Number of bits (7 or 8) */ - bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ + uint8_t parity; /* 0=none, 1=odd, 2=even */ + uint8_t bits; /* Number of bits (7 or 8) */ + bool stopbits2; /* true: Configure with 2 stop bits instead of 1 */ #if defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) - bool flow; /* flow control (RTS/CTS) enabled */ + bool flow; /* flow control (RTS/CTS) enabled */ #endif #endif }; @@ -89,13 +106,13 @@ static bool u16550_rxavailable(FAR struct uart_dev_s *dev); static bool u16550_rxflowcontrol(struct uart_dev_s *dev, unsigned int nbuffered, bool upper); #endif -#ifdef CONFIG_SERIAL_TXDMA +#ifdef HAVE_16550_UART_DMA static void u16550_dmasend(FAR struct uart_dev_s *dev); static void u16550_dmatxavail(FAR struct uart_dev_s *dev); -#endif -#ifdef CONFIG_SERIAL_RXDMA +static void u16550_dmatxconfig(FAR struct uart_dev_s *dev); static void u16550_dmareceive(FAR struct uart_dev_s *dev); static void u16550_dmarxfree(FAR struct uart_dev_s *dev); +static void u16550_dmarxconfig(FAR struct uart_dev_s *dev); #endif static void u16550_send(FAR struct uart_dev_s *dev, int ch); static void u16550_txint(FAR struct uart_dev_s *dev, bool enable); @@ -119,14 +136,10 @@ static const struct uart_ops_s g_uart_ops = #ifdef CONFIG_SERIAL_IFLOWCONTROL .rxflowcontrol = u16550_rxflowcontrol, #endif -#ifdef CONFIG_SERIAL_TXDMA +#ifdef HAVE_16550_UART_DMA .dmasend = u16550_dmasend, -#endif -#ifdef CONFIG_SERIAL_RXDMA .dmareceive = u16550_dmareceive, .dmarxfree = u16550_dmarxfree, -#endif -#ifdef CONFIG_SERIAL_TXDMA .dmatxavail = u16550_dmatxavail, #endif .send = u16550_send, @@ -154,15 +167,45 @@ static char g_uart3rxbuffer[CONFIG_16550_UART3_RXBUFSIZE]; static char g_uart3txbuffer[CONFIG_16550_UART3_TXBUFSIZE]; #endif +/* DMA receive buffers */ + +#ifdef CONFIG_16550_UART0_DMA_RXBUFSIZE +static char g_uart0dmarxbuf[CONFIG_16550_UART0_DMA_RXBUFSIZE]; +#endif +#ifdef CONFIG_16550_UART1_DMA_RXBUFSIZE +static char g_uart1dmarxbuf[CONFIG_16550_UART1_DMA_RXBUFSIZE]; +#endif +#ifdef CONFIG_16550_UART2_DMA_RXBUFSIZE +static char g_uart2dmarxbuf[CONFIG_16550_UART2_DMA_RXBUFSIZE]; +#endif +#ifdef CONFIG_16550_UART3_DMA_RXBUFSIZE +static char g_uart3dmarxbuf[CONFIG_16550_UART3_DMA_RXBUFSIZE]; +#endif + /* This describes the state of the 16550 uart0 port. */ #ifdef CONFIG_16550_UART0 static struct u16550_s g_uart0priv = { .uartbase = CONFIG_16550_UART0_BASE, -#ifndef CONFIG_16550_SUPRESS_CONFIG +#ifdef CONFIG_16550_UART0_DMA + .dmatx = CONFIG_16550_UART0_DMA_TX, + .dmarx = CONFIG_16550_UART0_DMA_RX, +# if CONFIG_16550_UART0_DMA_RX != -1 + .dmarxbuf = g_uart0dmarxbuf, + .dmarxsize = CONFIG_16550_UART0_DMA_RXBUFSIZE, + .dmarxtimeout = CONFIG_16550_UART0_DMA_RXTIMEOUT, +# endif +#elif defined(HAVE_16550_UART_DMA) + .dmatx = -1, + .dmarx = -1, +#endif +#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(CONFIG_16550_UART0_DMA) .baud = CONFIG_16550_UART0_BAUD, .uartclk = CONFIG_16550_UART0_CLOCK, +#endif +#ifdef CONFIG_CLK + .clk_name = CONFIG_16550_UART0_CLOCK_NAME, #endif .irq = CONFIG_16550_UART0_IRQ, #ifndef CONFIG_16550_SUPRESS_CONFIG @@ -198,9 +241,24 @@ static uart_dev_t g_uart0port = static struct u16550_s g_uart1priv = { .uartbase = CONFIG_16550_UART1_BASE, -#ifndef CONFIG_16550_SUPRESS_CONFIG +#ifdef CONFIG_16550_UART1_DMA + .dmatx = CONFIG_16550_UART1_DMA_TX, + .dmarx = CONFIG_16550_UART1_DMA_RX, +# if CONFIG_16550_UART1_DMA_RX != -1 + .dmarxbuf = g_uart1dmarxbuf, + .dmarxsize = CONFIG_16550_UART1_DMA_RXBUFSIZE, + .dmarxtimeout = CONFIG_16550_UART1_DMA_RXTIMEOUT, +# endif +#elif defined(HAVE_16550_UART_DMA) + .dmatx = -1, + .dmarx = -1, +#endif +#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(CONFIG_16550_UART1_DMA) .baud = CONFIG_16550_UART1_BAUD, .uartclk = CONFIG_16550_UART1_CLOCK, +#endif +#ifdef CONFIG_CLK + .clk_name = CONFIG_16550_UART1_CLOCK_NAME, #endif .irq = CONFIG_16550_UART1_IRQ, #ifndef CONFIG_16550_SUPRESS_CONFIG @@ -236,9 +294,24 @@ static uart_dev_t g_uart1port = static struct u16550_s g_uart2priv = { .uartbase = CONFIG_16550_UART2_BASE, -#ifndef CONFIG_16550_SUPRESS_CONFIG +#ifdef CONFIG_16550_UART2_DMA + .dmatx = CONFIG_16550_UART2_DMA_TX, + .dmarx = CONFIG_16550_UART2_DMA_RX, +# if CONFIG_16550_UART2_DMA_RX != -1 + .dmarxbuf = g_uart2dmarxbuf, + .dmarxsize = CONFIG_16550_UART2_DMA_RXBUFSIZE, + .dmarxtimeout = CONFIG_16550_UART2_DMA_RXTIMEOUT, +# endif +#elif defined(HAVE_16550_UART_DMA) + .dmatx = -1, + .dmarx = -1, +#endif +#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(CONFIG_16550_UART2_DMA) .baud = CONFIG_16550_UART2_BAUD, .uartclk = CONFIG_16550_UART2_CLOCK, +#endif +#ifdef CONFIG_CLK + .clk_name = CONFIG_16550_UART2_CLOCK_NAME, #endif .irq = CONFIG_16550_UART2_IRQ, #ifndef CONFIG_16550_SUPRESS_CONFIG @@ -274,9 +347,24 @@ static uart_dev_t g_uart2port = static struct u16550_s g_uart3priv = { .uartbase = CONFIG_16550_UART3_BASE, -#ifndef CONFIG_16550_SUPRESS_CONFIG +#ifdef CONFIG_16550_UART3_DMA + .dmatx = CONFIG_16550_UART3_DMA_TX, + .dmarx = CONFIG_16550_UART3_DMA_RX, +# if CONFIG_16550_UART3_DMA_RX != -1 + .dmarxbuf = g_uart3dmarxbuf, + .dmarxsize = CONFIG_16550_UART3_DMA_RXBUFSIZE, + .dmarxtimeout = CONFIG_16550_UART3_DMA_RXTIMEOUT, +# endif +#elif defined(HAVE_16550_UART_DMA) + .dmatx = -1, + .dmarx = -1, +#endif +#if !defined(CONFIG_16550_SUPRESS_CONFIG) || defined(CONFIG_16550_UART3_DMA) .baud = CONFIG_16550_UART3_BAUD, .uartclk = CONFIG_16550_UART3_CLOCK, +#endif +#ifdef CONFIG_CLK + .clk_name = CONFIG_16550_UART3_CLOCK_NAME, #endif .irq = CONFIG_16550_UART3_IRQ, #ifndef CONFIG_16550_SUPRESS_CONFIG @@ -710,6 +798,12 @@ static int u16550_setup(FAR struct uart_dev_s *dev) u16550_serialout(priv, UART_MCR_OFFSET, mcr); #endif /* defined(CONFIG_SERIAL_IFLOWCONTROL) || defined(CONFIG_SERIAL_OFLOWCONTROL) */ + /* Reconfigure DMA Rx timeout value */ + +#ifdef HAVE_16550_UART_DMA + u16550_dmarxconfig(dev); +#endif + #endif return OK; } @@ -750,6 +844,17 @@ static int u16550_attach(struct uart_dev_s *dev) FAR struct u16550_s *priv = (FAR struct u16550_s *)dev->priv; int ret; +#ifdef CONFIG_CLK + /* Clk enable */ + + priv->mclk = clk_get(priv->clk_name); + if (priv->mclk) + { + clk_set_rate(priv->mclk, priv->uartclk); + clk_enable(priv->mclk); + } +#endif + /* Attach and enable the IRQ */ ret = irq_attach(priv->irq, u16550_interrupt, dev); @@ -761,6 +866,13 @@ static int u16550_attach(struct uart_dev_s *dev) */ up_enable_irq(priv->irq); + +#ifdef HAVE_16550_UART_DMA + if (priv->chanrx) + { + DMA_RESUME(priv->chanrx); + } +#endif } #endif @@ -781,8 +893,24 @@ static void u16550_detach(FAR struct uart_dev_s *dev) { FAR struct u16550_s *priv = (FAR struct u16550_s *)dev->priv; +#ifdef HAVE_16550_UART_DMA + if (priv->chanrx) + { + DMA_PAUSE(priv->chanrx); + } +#endif + up_disable_irq(priv->irq); irq_detach(priv->irq); + +#ifdef CONFIG_CLK + /* Clk disaable */ + + if (priv->mclk) + { + clk_disable(priv->mclk); + } +#endif } /**************************************************************************** @@ -1046,6 +1174,16 @@ static int u16550_ioctl(struct file *filep, int cmd, unsigned long arg) u16550_setup(dev); leave_critical_section(flags); + +#ifdef CONFIG_CLK + /* Clk enable */ + + priv->mclk = clk_get(priv->clk_name); + if (priv->mclk) + { + clk_set_rate(priv->mclk, priv->uartclk); + } +#endif } break; #endif @@ -1090,6 +1228,13 @@ static void u16550_rxint(struct uart_dev_s *dev, bool enable) { FAR struct u16550_s *priv = (FAR struct u16550_s *)dev->priv; +#ifdef HAVE_16550_UART_DMA + if (priv->chanrx) + { + return; /* Monitor DMA interrupt instead */ + } +#endif + if (enable) { priv->ier |= UART_IER_ERBFI; @@ -1133,14 +1278,38 @@ static bool u16550_rxflowcontrol(struct uart_dev_s *dev, if (priv->flow) { - /* Disable Rx interrupt to prevent more data being from - * peripheral if the RX buffer is near full. When hardware - * RTS is enabled, this will prevent more data from coming - * in. Otherwise, enable Rx interrupt to make sure that more - * input is received. - */ +#ifdef HAVE_16550_UART_DMA + if (priv->chanrx) + { + /* Pause Rx DMA receive to prevent more data being from + * peripheral if the RX buffer is near full. When hardware + * RTS is enabled, this will prevent more data from coming + * in. Otherwise, Resume Rx DMA to make sure that more + * input is received. + */ + + if (upper) + { + DMA_PAUSE(priv->chanrx); + } + else + { + DMA_RESUME(priv->chanrx); + } + } + else +#endif + { + /* Disable Rx interrupt to prevent more data being from + * peripheral if the RX buffer is near full. When hardware + * RTS is enabled, this will prevent more data from coming + * in. Otherwise, enable Rx interrupt to make sure that more + * input is received. + */ + + u16550_rxint(dev, !upper); + } - u16550_rxint(dev, !upper); return true; } #endif @@ -1157,25 +1326,213 @@ static bool u16550_rxflowcontrol(struct uart_dev_s *dev, * ****************************************************************************/ -#ifdef CONFIG_SERIAL_TXDMA +#ifdef HAVE_16550_UART_DMA +static void u16550_dmasend_done(FAR struct dma_chan_s *chan, + FAR void *arg, ssize_t len) +{ + FAR struct uart_dev_s *dev = arg; + + if (len > 0) + { + dev->dmatx.nbytes = len; + uart_xmitchars_done(dev); + uart_xmitchars_dma(dev); + } + else /* Fail, resend */ + { + u16550_dmasend(dev); + } +} + static void u16550_dmasend(FAR struct uart_dev_s *dev) { -} -#endif + FAR struct u16550_s *priv = dev->priv; + FAR void *buffer = dev->dmatx.buffer; + size_t length = dev->dmatx.length; + + up_clean_dcache((uintptr_t)buffer, (uintptr_t)buffer + length); + DMA_START(priv->chantx, u16550_dmasend_done, dev, + up_addrenv_va_to_pa((FAR void *)priv->uartbase), + up_addrenv_va_to_pa(buffer), length); +} + +static void u16550_dmareceive_done(FAR struct dma_chan_s *chan, + FAR void *arg, ssize_t len) +{ + FAR struct uart_dev_s *dev = arg; + FAR struct u16550_s *priv = dev->priv; + + if (len >= 0) + { + size_t slot = priv->dmarxhead / priv->dmarxsize; + size_t offset = priv->dmarxhead - slot * priv->dmarxsize; + + if (len >= priv->dmarxsize) + { + len = 0; + } + + if (len < offset) + { + slot++; /* Wrap, move to the next slot */ + } + + priv->dmarxhead = slot * priv->dmarxsize + len; + if (priv->dmarxhead - priv->dmarxtail >= priv->dmarxsize) + { + serr("The receive dma buffer is overrun\n"); + priv->dmarxtail = priv->dmarxhead - priv->dmarxsize / 2; + } + + /* The receive isn't in the process? */ + + if (dev->dmarx.length == 0) + { + /* Trigger the receive process */ + + uart_recvchars_dma(dev); + } + else + { + /* Copy the received data */ + + u16550_dmareceive(dev); + } + } +} -#ifdef CONFIG_SERIAL_RXDMA static void u16550_dmareceive(FAR struct uart_dev_s *dev) { + FAR struct u16550_s *priv = dev->priv; + + if (priv->dmarxhead != priv->dmarxtail) + { + size_t length = priv->dmarxhead - priv->dmarxtail; + size_t offset = priv->dmarxtail % priv->dmarxsize; + FAR char *buffer = priv->dmarxbuf + offset; + + if (offset + length > priv->dmarxsize) + { + length = priv->dmarxsize - offset; + } + + if (length > dev->dmarx.length) + { + length = dev->dmarx.length; + } + + up_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + length); + memcpy(dev->dmarx.buffer, buffer, length); + dev->dmarx.nbytes = length; + priv->dmarxtail += length; + + uart_recvchars_done(dev); + if (priv->dmarxhead != priv->dmarxtail) + { + /* Trigger the receive process again */ + + uart_recvchars_dma(dev); + } + } +} + +static void u16550_dmarxconfig(FAR struct uart_dev_s *dev) +{ + FAR struct u16550_s *priv = dev->priv; + struct dma_config_s config; + + if (priv->chanrx != NULL) + { + memset(&config, 0, sizeof(config)); + config.direction = DMA_DEV_TO_MEM; + + /* 12bit = 1bit start + 8bit data + 1bit parity + 2bit stop */ + + config.timeout = 12 * 1000000ull * priv->dmarxtimeout / priv->baud; + config.src_width = 1; + DMA_CONFIG(priv->chanrx, &config); + } } static void u16550_dmarxfree(FAR struct uart_dev_s *dev) { -} -#endif + FAR struct u16550_s *priv = dev->priv; + + if (priv->dmarx == -1) + { + return; /* Can't receive by DMA */ + } + + if (priv->chanrx == NULL) + { + priv->chanrx = uart_dmachan(priv->uartbase, priv->dmarx); + if (priv->chanrx == NULL) + { + return; /* Fail to get DMA channel */ + } + + u16550_dmarxconfig(dev); + + /* Start a never stop DMA cyclic transfer in the background */ + + DMA_START_CYCLIC(priv->chanrx, u16550_dmareceive_done, dev, + up_addrenv_va_to_pa(priv->dmarxbuf), + up_addrenv_va_to_pa((FAR void *)priv->uartbase), + priv->dmarxsize, priv->dmarxsize / 4); + } + + /* The receive isn't in the process? */ + + if (dev->dmarx.length == 0) + { + /* Trigger the receive process */ + + uart_recvchars_dma(dev); + } +} + +static void u16550_dmatxconfig(FAR struct uart_dev_s *dev) +{ + FAR struct u16550_s *priv = dev->priv; + struct dma_config_s config; + + if (priv->chantx != NULL) + { + memset(&config, 0, sizeof(config)); + config.direction = DMA_MEM_TO_DEV; + config.dst_width = 1; + DMA_CONFIG(priv->chantx, &config); + } +} -#ifdef CONFIG_SERIAL_TXDMA static void u16550_dmatxavail(FAR struct uart_dev_s *dev) { + FAR struct u16550_s *priv = dev->priv; + + if (priv->dmatx == -1) + { + return; /* Can't send by DMA */ + } + + if (priv->chantx == NULL) + { + priv->chantx = uart_dmachan(priv->uartbase, priv->dmatx); + if (priv->chantx == NULL) + { + return; /* Fail to get DMA channel */ + } + + u16550_dmatxconfig(dev); + } + + /* DMA isn't busy for sending? */ + + if (dev->dmatx.length == 0) + { + /* Start DMA for sending */ + + uart_xmitchars_dma(dev); + } } #endif @@ -1206,6 +1563,13 @@ static void u16550_txint(struct uart_dev_s *dev, bool enable) FAR struct u16550_s *priv = (FAR struct u16550_s *)dev->priv; irqstate_t flags; +#ifdef HAVE_16550_UART_DMA + if (priv->chantx) + { + return; /* Monitor DMA interrupt instead */ + } +#endif + flags = enter_critical_section(); if (enable) { diff --git a/include/nuttx/serial/uart_16550.h b/include/nuttx/serial/uart_16550.h index 3c545b5e601..e2aaa72ac12 100644 --- a/include/nuttx/serial/uart_16550.h +++ b/include/nuttx/serial/uart_16550.h @@ -376,5 +376,9 @@ void uart_putreg(uart_addrwidth_t base, struct file; /* Forward reference */ int uart_ioctl(struct file *filep, int cmd, unsigned long arg); +struct dma_chan_s; +FAR struct dma_chan_s *uart_dmachan(uart_addrwidth_t base, + unsigned int ident); + #endif /* CONFIG_16550_UART */ #endif /* __INCLUDE_NUTTX_SERIAL_UART_16550_H */