diff --git a/arch/arm/src/kinetis/Kconfig b/arch/arm/src/kinetis/Kconfig index cbda473db5..03e18e2815 100644 --- a/arch/arm/src/kinetis/Kconfig +++ b/arch/arm/src/kinetis/Kconfig @@ -1398,6 +1398,62 @@ config KINETIS_SERIAL_RXDMA_BUFFER_SIZE endmenu # Kinetis UART Configuration +menu "Kinetis LPUART Configuration" +if KINETIS_SERIALDRIVER || OTHER_SERIALDRIVER + +comment "LP Uart Driver Configuration" + +config KINETIS_LPUART0_RXDMA + bool "LPUART0 Rx DMA" + default n + depends on KINETIS_LPUART0 && KINETIS_EDMA + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + +config KINETIS_LPUART1_RXDMA + bool "LPUART1 Rx DMA" + default n + depends on KINETIS_LPUART1 && KINETIS_EDMA + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + +config KINETIS_LPUART2_RXDMA + bool "LPUART2 Rx DMA" + default n + depends on KINETIS_LPUART2 && KINETIS_EDMA + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + +config KINETIS_LPUART3_RXDMA + bool "LPUART3 Rx DMA" + default n + depends on KINETIS_LPUART3 && KINETIS_EDMA + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + +config KINETIS_LPUART4_RXDMA + bool "LPUART4 Rx DMA" + default n + depends on KINETIS_LPUART4 && KINETIS_EDMA + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + +config KINETIS_LPUART_RXDMA_BUFFER_SIZE + int "Rx DMA buffer size" + default 32 + depends on KINETIS_LPUART0_RXDMA || KINETIS_LPUART1_RXDMA || KINETIS_LPUART2_RXDMA || KINETIS_LPUART3_RXDMA || KINETIS_LPUART4_RXDMA + ---help--- + The DMA buffer size when using RX DMA to emulate a FIFO. + + When streaming data, the generic serial layer will be called + every time the FIFO receives half this number of bytes. + + Value given here will be rounded up to next multiple of 32 bytes. + +endif # KINETIS_SERIALDRIVER || OTHER_SERIALDRIVER + +endmenu # Kinetis LPUART Configuration + config KINETIS_MERGE_TTY bool "Kinetis Merge TTY names for LPUARTS" default n diff --git a/arch/arm/src/kinetis/kinetis_lpserial.c b/arch/arm/src/kinetis/kinetis_lpserial.c index 4ad9dedff5..be3bcf12ff 100644 --- a/arch/arm/src/kinetis/kinetis_lpserial.c +++ b/arch/arm/src/kinetis/kinetis_lpserial.c @@ -47,10 +47,14 @@ #include "arm_arch.h" #include "arm_internal.h" -#include "kinetis.h" +#include "kinetis_config.h" +#include "chip.h" #include "hardware/kinetis_lpuart.h" #include "hardware/kinetis_pinmux.h" - +#include "hardware/kinetis_dmamux.h" +#include "kinetis.h" +#include "kinetis_lpuart.h" +#include "kinetis_edma.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -69,6 +73,10 @@ #if defined(HAVE_LPUART_DEVICE) && defined(USE_SERIALDRIVER) +/* Assume DMA is not used on the console UART */ + +#undef SERIAL_HAVE_CONSOLE_DMA + /* Which LPUART with be tty0/console and which tty1? The console will always * be ttyS0. If there is no console then will use the lowest numbered * LPUART. @@ -80,22 +88,37 @@ # define CONSOLE_DEV g_lpuart0port /* LPUART0 is console */ # define TTYS0_DEV g_lpuart0port /* LPUART0 is ttyS0 */ # define LPUART0_ASSIGNED 1 +# if defined(CONFIG_KINETIS_LPUART0_RXDMA) +# define SERIAL_HAVE_CONSOLE_DMA 1 +# endif #elif defined(CONFIG_LPUART1_SERIAL_CONSOLE) # define CONSOLE_DEV g_lpuart1port /* LPUART1 is console */ # define TTYS0_DEV g_lpuart1port /* LPUART1 is ttyS0 */ # define LPUART1_ASSIGNED 1 +# if defined(CONFIG_KINETIS_LPUART1_RXDMA) +# define SERIAL_HAVE_CONSOLE_DMA 1 +# endif #elif defined(CONFIG_LPUART2_SERIAL_CONSOLE) # define CONSOLE_DEV g_lpuart2port /* LPUART2 is console */ # define TTYS0_DEV g_lpuart2port /* LPUART2 is ttyS0 */ # define LPUART2_ASSIGNED 1 +# if defined(CONFIG_KINETIS_LPUART2_RXDMA) +# define SERIAL_HAVE_CONSOLE_DMA 1 +# endif #elif defined(CONFIG_LPUART3_SERIAL_CONSOLE) # define CONSOLE_DEV g_lpuart3port /* LPUART3 is console */ # define TTYS0_DEV g_lpuart3port /* LPUART3 is ttyS0 */ # define LPUART3_ASSIGNED 1 +# if defined(CONFIG_KINETIS_LPUART3_RXDMA) +# define SERIAL_HAVE_CONSOLE_DMA 1 +# endif #elif defined(CONFIG_LPUART4_SERIAL_CONSOLE) # define CONSOLE_DEV g_lpuart4port /* LPUART4 is console */ # define TTYS0_DEV g_lpuart4port /* LPUART4 is ttyS0 */ # define LPUART4_ASSIGNED 1 +# if defined(CONFIG_KINETIS_LPUART4_RXDMA) +# define SERIAL_HAVE_CONSOLE_DMA 1 +# endif #else # undef CONSOLE_DEV /* No console */ # if defined(CONFIG_KINETIS_LPUART0) @@ -182,6 +205,37 @@ # define LPUART4_ASSIGNED 1 #endif +#ifdef LPSERIAL_HAVE_DMA + +/* The DMA buffer size when using RX DMA to emulate a FIFO. + * + * When streaming data, the generic serial layer will be called every time + * the FIFO receives half this number of bytes. + * + * This buffer size should be an even multiple of the Cortex-M7 D-Cache line + * size, ARMV7M_DCACHE_LINESIZE, so that it can be individually invalidated. + * + * Should there be a Cortex-M7 without a D-Cache, ARMV7M_DCACHE_LINESIZE + * would be zero! + */ + +# if !defined(ARMV7M_DCACHE_LINESIZE) || ARMV7M_DCACHE_LINESIZE == 0 +# undef ARMV7M_DCACHE_LINESIZE +# define ARMV7M_DCACHE_LINESIZE 32 +# endif + +# if !defined(CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE) || \ + (CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE < ARMV7M_DCACHE_LINESIZE) +# undef CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE +# define CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE ARMV7M_DCACHE_LINESIZE +# endif + +# define RXDMA_BUFFER_MASK ((uint32_t)(ARMV7M_DCACHE_LINESIZE - 1)) +# define RXDMA_BUFFER_SIZE ((CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE \ + + RXDMA_BUFFER_MASK) & ~RXDMA_BUFFER_MASK) + +#endif /* LPSERIAL_HAVE_DMA */ + #define LPUART_CTRL_ERROR_INTS (LPUART_CTRL_ORIE | LPUART_CTRL_FEIE | \ LPUART_CTRL_NEIE | LPUART_CTRL_PEIE) @@ -230,6 +284,12 @@ struct kinetis_dev_s #ifdef CONFIG_SERIAL_OFLOWCONTROL uint32_t cts_gpio; /* UART CTS GPIO pin configuration */ #endif +#ifdef LPSERIAL_HAVE_DMA + const uint8_t rxdma_reqsrc; + DMACH_HANDLE rxdma; /* currently-open receive DMA stream */ + uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ + char *const rxfifo; /* Receive DMA buffer */ +#endif }; /**************************************************************************** @@ -242,9 +302,11 @@ static int kinetis_attach(struct uart_dev_s *dev); static void kinetis_detach(struct uart_dev_s *dev); static int kinetis_interrupt(int irq, void *context, void *arg); static int kinetis_ioctl(struct file *filep, int cmd, unsigned long arg); -static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status); static void kinetis_rxint(struct uart_dev_s *dev, bool enable); +#if !defined(LPSERIAL_HAVE_ALL_DMA) +static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status); static bool kinetis_rxavailable(struct uart_dev_s *dev); +#endif #ifdef CONFIG_SERIAL_IFLOWCONTROL static bool kinetis_rxflowcontrol(struct uart_dev_s *dev, unsigned int nbuffered, bool upper); @@ -253,10 +315,22 @@ static void kinetis_send(struct uart_dev_s *dev, int ch); static void kinetis_txint(struct uart_dev_s *dev, bool enable); static bool kinetis_txready(struct uart_dev_s *dev); +#ifdef LPSERIAL_HAVE_DMA +static int kinetis_dma_nextrx(struct kinetis_dev_s *priv); +static int kinetis_dma_setup(struct uart_dev_s *dev); +static void kinetis_dma_shutdown(struct uart_dev_s *dev); +static int kinetis_dma_receive(struct uart_dev_s *dev, + unsigned int *status); +static bool kinetis_dma_rxavailable(struct uart_dev_s *dev); +static uint32_t get_and_clear_uart_status(struct kinetis_dev_s *priv); +static void kinetis_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done, + int result); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ - +#if !defined(LPSERIAL_HAVE_ALL_DMA) static const struct uart_ops_s g_lpuart_ops = { .setup = kinetis_setup, @@ -275,28 +349,70 @@ static const struct uart_ops_s g_lpuart_ops = .txready = kinetis_txready, .txempty = kinetis_txready, }; +#endif + +#ifdef LPSERIAL_HAVE_DMA +static const struct uart_ops_s g_lpuart_dma_ops = +{ + .setup = kinetis_dma_setup, + .shutdown = kinetis_dma_shutdown, + .attach = kinetis_attach, + .detach = kinetis_detach, + .ioctl = kinetis_ioctl, + .receive = kinetis_dma_receive, + .rxint = kinetis_rxint, + .rxavailable = kinetis_dma_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = kinetis_rxflowcontrol, +#endif + .send = kinetis_send, + .txint = kinetis_txint, + .txready = kinetis_txready, + .txempty = kinetis_txready, +}; +#endif /* I/O buffers */ #ifdef CONFIG_KINETIS_LPUART0 static char g_lpuart0rxbuffer[CONFIG_LPUART0_RXBUFSIZE]; static char g_lpuart0txbuffer[CONFIG_LPUART0_TXBUFSIZE]; +# ifdef CONFIG_KINETIS_LPUART0_RXDMA +static char g_lpuart0rxfifo[RXDMA_BUFFER_SIZE] + __attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +# endif #endif #ifdef CONFIG_KINETIS_LPUART1 static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE]; static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE]; +# ifdef CONFIG_KINETIS_LPUART1_RXDMA +static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE] + __attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +# endif #endif #ifdef CONFIG_KINETIS_LPUART2 static char g_lpuart2rxbuffer[CONFIG_LPUART2_RXBUFSIZE]; static char g_lpuart2txbuffer[CONFIG_LPUART2_TXBUFSIZE]; +# ifdef CONFIG_KINETIS_LPUART2_RXDMA +static char g_lpuart2rxfifo[RXDMA_BUFFER_SIZE] + __attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +# endif #endif #ifdef CONFIG_KINETIS_LPUART3 static char g_lpuart3rxbuffer[CONFIG_LPUART3_RXBUFSIZE]; static char g_lpuart3txbuffer[CONFIG_LPUART3_TXBUFSIZE]; +# ifdef CONFIG_KINETIS_LPUART3_RXDMA +static char g_lpuart3rxfifo[RXDMA_BUFFER_SIZE] + __attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +# endif #endif #ifdef CONFIG_KINETIS_LPUART4 static char g_lpuart4rxbuffer[CONFIG_LPUART4_RXBUFSIZE]; static char g_lpuart4txbuffer[CONFIG_LPUART4_TXBUFSIZE]; +# ifdef CONFIG_KINETIS_LPUART4_RXDMA +static char g_lpuart4rxfifo[RXDMA_BUFFER_SIZE] + __attribute__((aligned(ARMV7M_DCACHE_LINESIZE))); +# endif #endif /* This describes the state of the Kinetis LPUART0 port. */ @@ -319,6 +435,10 @@ static struct kinetis_dev_s g_lpuart0priv = .iflow = true, .rts_gpio = PIN_LPUART0_RTS, #endif +#ifdef CONFIG_KINETIS_LPUART0_RXDMA + .rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART0_RX, + .rxfifo = g_lpuart0rxfifo, +#endif }; static uart_dev_t g_lpuart0port = @@ -333,7 +453,11 @@ static uart_dev_t g_lpuart0port = .size = CONFIG_LPUART0_TXBUFSIZE, .buffer = g_lpuart0txbuffer, }, - .ops = &g_lpuart_ops, +#ifdef CONFIG_KINETIS_LPUART0_RXDMA + .ops = &g_lpuart_dma_ops, +#else + .ops = &g_lpuart_ops, +#endif .priv = &g_lpuart0priv, }; #endif @@ -358,6 +482,10 @@ static struct kinetis_dev_s g_lpuart1priv = .iflow = true, .rts_gpio = PIN_LPUART1_RTS, #endif +#ifdef CONFIG_KINETIS_LPUART1_RXDMA + .rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART1_RX, + .rxfifo = g_lpuart1rxfifo, +#endif }; static uart_dev_t g_lpuart1port = @@ -372,7 +500,11 @@ static uart_dev_t g_lpuart1port = .size = CONFIG_LPUART1_TXBUFSIZE, .buffer = g_lpuart1txbuffer, }, - .ops = &g_lpuart_ops, +#ifdef CONFIG_KINETIS_LPUART1_RXDMA + .ops = &g_lpuart_dma_ops, +#else + .ops = &g_lpuart_ops, +#endif .priv = &g_lpuart1priv, }; #endif @@ -397,6 +529,10 @@ static struct kinetis_dev_s g_lpuart2priv = .iflow = true, .rts_gpio = PIN_LPUART2_RTS, #endif +#ifdef CONFIG_KINETIS_LPUART2_RXDMA + .rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART2_RX, + .rxfifo = g_lpuart2rxfifo, +#endif }; static uart_dev_t g_lpuart2port = @@ -411,7 +547,11 @@ static uart_dev_t g_lpuart2port = .size = CONFIG_LPUART2_TXBUFSIZE, .buffer = g_lpuart2txbuffer, }, - .ops = &g_lpuart_ops, +#ifdef CONFIG_KINETIS_LPUART2_RXDMA + .ops = &g_lpuart_dma_ops, +#else + .ops = &g_lpuart_ops, +#endif .priv = &g_lpuart2priv, }; #endif @@ -436,6 +576,10 @@ static struct kinetis_dev_s g_lpuart3priv = .iflow = true, .rts_gpio = PIN_LPUART3_RTS, #endif +#ifdef CONFIG_KINETIS_LPUART3_RXDMA + .rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART3_RX, + .rxfifo = g_lpuart3rxfifo, +#endif }; static uart_dev_t g_lpuart3port = @@ -450,7 +594,11 @@ static uart_dev_t g_lpuart3port = .size = CONFIG_LPUART3_TXBUFSIZE, .buffer = g_lpuart3txbuffer, }, - .ops = &g_lpuart_ops, +#ifdef CONFIG_KINETIS_LPUART3_RXDMA + .ops = &g_lpuart_dma_ops, +#else + .ops = &g_lpuart_ops, +#endif .priv = &g_lpuart3priv, }; #endif @@ -475,6 +623,10 @@ static struct kinetis_dev_s g_lpuart4priv = .iflow = true, .rts_gpio = PIN_LPUART4_RTS, #endif +#ifdef CONFIG_KINETIS_LPUART4_RXDMA + .rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART4_RX, + .rxfifo = g_lpuart4rxfifo, +#endif }; static uart_dev_t g_lpuart4port = @@ -489,7 +641,11 @@ static uart_dev_t g_lpuart4port = .size = CONFIG_LPUART4_TXBUFSIZE, .buffer = g_lpuart4txbuffer, }, - .ops = &g_lpuart_ops, +#ifdef CONFIG_KINETIS_LPUART4_RXDMA + .ops = &g_lpuart_dma_ops, +#else + .ops = &g_lpuart_ops, +#endif .priv = &g_lpuart4priv, }; #endif @@ -577,6 +733,38 @@ static void kinetis_disableuartint(struct kinetis_dev_s *priv, uint32_t *ie) } #endif +/**************************************************************************** + * Name: get_and_clear_uart_status + * + * Description: + * Clears the error flags of the uart if an error occurred in s1 and + * returns the status + * + * Input Parameters: + * u_dev_s + * + * Returns Value: + * Uart status s1 + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +static uint32_t get_and_clear_uart_status(struct kinetis_dev_s *priv) +{ + uint32_t regval; + + regval = kinetis_serialin(priv, KINETIS_LPUART_STAT_OFFSET); + regval &= LPUART_STAT_ERRORS; + + if (regval != 0) + { + kinetis_serialout(priv, KINETIS_LPUART_STAT_OFFSET, regval); + } + + return regval; +} +#endif + /**************************************************************************** * Name: kinetis_setup * @@ -614,6 +802,83 @@ static int kinetis_setup(struct uart_dev_s *dev) return OK; } +/**************************************************************************** + * Name: kinetis_dma_setup + * + * Description: + * Configure the UART baud, bits, parity, etc. This method is called the + * first time that the serial port is opened. + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +static int kinetis_dma_setup(struct uart_dev_s *dev) +{ + struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv; + int result; + uint32_t regval; + DMACH_HANDLE rxdma = NULL; + + /* Do the basic UART setup first, unless we are the console */ + + if (!dev->isconsole) + { + result = kinetis_setup(dev); + if (result != OK) + { + return result; + } + } + + /* Acquire the DMA channel. */ + + rxdma = kinetis_dmach_alloc(priv->rxdma_reqsrc | DMAMUX_CHCFG_ENBL, 0); + if (rxdma == NULL) + { + return -EBUSY; + } + + /* Configure for circular DMA reception into the RX FIFO */ + + struct kinetis_edma_xfrconfig_s config; + config.saddr = priv->uartbase + KINETIS_LPUART_DATA_OFFSET; + config.daddr = (uint32_t) priv->rxfifo; + config.soff = 0; + config.doff = 1; + config.iter = RXDMA_BUFFER_SIZE; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE | EDMA_CONFIG_LOOPDEST; + config.ssize = EDMA_8BIT; + config.dsize = EDMA_8BIT; + config.ttype = EDMA_PERIPH2MEM; + config.nbytes = 1; +#ifdef CONFIG_KINETIS_EDMA_ELINK + config.linkch = NULL; +#endif + kinetis_dmach_xfrsetup(rxdma, &config); + + /* Reset our DMA shadow pointer to match the address just programmed + * above. + */ + + priv->rxdmanext = 0; + + /* Enable receive DMA for the UART */ + + regval = kinetis_serialin(priv, KINETIS_LPUART_BAUD_OFFSET); + regval |= LPUART_BAUD_RDMAE; + kinetis_serialout(priv, KINETIS_LPUART_BAUD_OFFSET, regval); + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + kinetis_dmach_start(rxdma, kinetis_dma_rxcallback, (void *)dev); + priv->rxdma = rxdma; + return OK; +} +#endif + /**************************************************************************** * Name: kinetis_shutdown * @@ -636,6 +901,37 @@ static void kinetis_shutdown(struct uart_dev_s *dev) kinetis_lpuartreset(priv->uartbase); } +/**************************************************************************** + * Name: kinetis_dma_shutdown + * + * Description: + * Disable the UART. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +static void kinetis_dma_shutdown(struct uart_dev_s *dev) +{ + struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv; + DMACH_HANDLE rxdma = priv->rxdma; + + /* Perform the normal UART shutdown */ + + kinetis_shutdown(dev); + + /* Stop the DMA channel */ + + kinetis_dmach_stop(rxdma); + + /* Release the DMA channel */ + + kinetis_dmach_free(rxdma); + + priv->rxdma = NULL; +} +#endif + /**************************************************************************** * Name: kinetis_attach * @@ -1102,7 +1398,7 @@ static int kinetis_ioctl(struct file *filep, int cmd, unsigned long arg) * return 'status'. * ****************************************************************************/ - +#if !defined(LPSERIAL_HAVE_ALL_DMA) static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status) { struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv; @@ -1143,6 +1439,66 @@ static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status) return data; } +#endif + +/**************************************************************************** + * Name: kinetis_dma_receive + * + * Description: + * Called (usually) from the interrupt level to receive one + * character from the UART. Error bits associated with the + * receipt are provided in the return 'status'. + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +static int kinetis_dma_receive(struct uart_dev_s *dev, unsigned int *status) +{ + struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv; + int c = 0; + uint32_t stat; + + /* Clear uart errors and return status information */ + + stat = get_and_clear_uart_status(priv); + if (status) + { + *status = stat; + } + + if (kinetis_dma_nextrx(priv) != priv->rxdmanext) + { + /* Invalidate the DMA buffer */ + + up_invalidate_dcache((uintptr_t)priv->rxfifo, + (uintptr_t)priv->rxfifo + RXDMA_BUFFER_SIZE); + + /* Now read from the DMA buffer */ + + c = priv->rxfifo[priv->rxdmanext]; + priv->rxdmanext++; + if (priv->rxdmanext == RXDMA_BUFFER_SIZE) + { + /* HACK: Skip the first byte since it is duplicate of last one. */ + + if (kinetis_dma_nextrx(priv) != 0) + { + priv->rxdmanext = 1; + } + else + { + /* Try to catch race conditions that will spin on the whole + * buffer again. + */ + + priv->rxdmanext = 0; + } + } + } + + return c; +} +#endif /**************************************************************************** * Name: kinetis_rxint @@ -1179,13 +1535,34 @@ static void kinetis_rxint(struct uart_dev_s *dev, bool enable) } /**************************************************************************** - * Name: kinetis_rxavailable + * Name: kinetis_dma_rxavailable * * Description: * Return true if the receive register is not empty * ****************************************************************************/ +#ifdef LPSERIAL_HAVE_DMA +static bool kinetis_dma_rxavailable(struct uart_dev_s *dev) +{ + struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv; + + /* Compare our receive pointer to the current DMA pointer, if they + * do not match, then there are bytes to be received. + */ + + return (kinetis_dma_nextrx(priv) != priv->rxdmanext); +} +#endif + +/**************************************************************************** + * Name: kinetis_rxavailable + * + * Description: + * Return true if the receive register is not empty + * + ****************************************************************************/ +#if !defined(LPSERIAL_HAVE_ALL_DMA) static bool kinetis_rxavailable(struct uart_dev_s *dev) { struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv; @@ -1195,6 +1572,7 @@ static bool kinetis_rxavailable(struct uart_dev_s *dev) return (kinetis_serialin(priv, KINETIS_LPUART_STAT_OFFSET) & LPUART_STAT_RDRF) != 0; } +#endif /**************************************************************************** * Name: kinetis_rxflowcontrol @@ -1269,6 +1647,26 @@ static bool kinetis_rxflowcontrol(struct uart_dev_s *dev, } #endif +/**************************************************************************** + * Name: kinetis_dma_nextrx + * + * Description: + * Returns the index into the RX FIFO where the DMA will place the next + * byte that it receives. + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +static int kinetis_dma_nextrx(struct kinetis_dev_s *priv) +{ + size_t dmaresidual; + + dmaresidual = kinetis_dmach_getcount(priv->rxdma); + + return (RXDMA_BUFFER_SIZE - (int)dmaresidual) % RXDMA_BUFFER_SIZE; +} +#endif + /**************************************************************************** * Name: kinetis_send * @@ -1341,6 +1739,28 @@ static bool kinetis_txready(struct uart_dev_s *dev) LPUART_STAT_TDRE) != 0; } +/**************************************************************************** + * Name: kinetis_dma_rxcallback + * + * Description: + * This function checks the current DMA state and calls the generic + * serial stack when bytes appear to be available. + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +static void kinetis_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct uart_dev_s *dev = (struct uart_dev_s *)arg; + + if (kinetis_dma_rxavailable(dev)) + { + uart_recvchars(dev); + } +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -1410,6 +1830,11 @@ unsigned int kinetis_lpuart_serialinit(unsigned int first) #ifdef HAVE_LPUART_CONSOLE uart_register("/dev/console", &CONSOLE_DEV); +# ifdef SERIAL_HAVE_CONSOLE_DMA + /* If we need to re-initialise the console to enable DMA do that here. */ + + kinetis_dma_setup(&CONSOLE_DEV); +# endif #endif #if !defined(CONFIG_KINETIS_MERGE_TTY) /* Register all LPUARTs as LPn devices */ @@ -1453,6 +1878,68 @@ unsigned int kinetis_lpuart_serialinit(unsigned int first) return first; } +/**************************************************************************** + * Name: kinetis_serial_dma_poll + * + * Description: + * Checks receive DMA buffers for received bytes that have not accumulated + * to the point where the DMA half/full interrupt has triggered. + * + * This function should be called from a timer or other periodic context. + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +void kinetis_lpserial_dma_poll(void) +{ + irqstate_t flags; + + flags = enter_critical_section(); + +#ifdef CONFIG_KINETIS_LPUART0_RXDMA + if (g_lpuart0priv.rxdma != NULL) + { + kinetis_dma_rxcallback(g_lpuart0priv.rxdma, (void *)&g_lpuart0port, + false, 0); + } +#endif + +#ifdef CONFIG_KINETIS_LPUART1_RXDMA + if (g_lpuart1priv.rxdma != NULL) + { + kinetis_dma_rxcallback(g_lpuart1priv.rxdma, (void *)&g_lpuart1port, + false, 0); + } +#endif + +#ifdef CONFIG_KINETIS_LPUART2_RXDMA + if (g_lpuart2priv.rxdma != NULL) + { + kinetis_dma_rxcallback(g_lpuart2priv.rxdma, (void *)&g_lpuart2port, + false, 0); + } +#endif + +#ifdef CONFIG_KINETIS_LPUART3_RXDMA + if (g_lpuart3priv.rxdma != NULL) + { + kinetis_dma_rxcallback(g_lpuart3priv.rxdma, (void *)&g_lpuart3port, + false, 0); + } +#endif + +#ifdef CONFIG_KINETIS_LPUART4_RXDMA + if (g_lpuart4priv.rxdma != NULL) + { + kinetis_dma_rxcallback(g_lpuart4priv.rxdma, (void *)&g_lpuart4port, + false, 0); + } +#endif + + leave_critical_section(flags); +} +#endif + /**************************************************************************** * Name: up_putc * diff --git a/arch/arm/src/kinetis/kinetis_lpuart.h b/arch/arm/src/kinetis/kinetis_lpuart.h new file mode 100644 index 0000000000..6a24ca3138 --- /dev/null +++ b/arch/arm/src/kinetis/kinetis_lpuart.h @@ -0,0 +1,101 @@ +/**************************************************************************** + * arch/arm/src/kinetis/kinetis_lpuart.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_KINETIS_KINETIS_LPUART_H +#define __ARCH_ARM_SRC_KINETIS_KINETIS_LPUART_H + +#if defined(HAVE_UART_DEVICE) && defined(USE_SERIALDRIVER) + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Is DMA available on any (enabled) LPUART? */ + +#undef LPSERIAL_HAVE_DMA +#if defined(CONFIG_KINETIS_LPUART0_RXDMA) || defined(CONFIG_KINETIS_LPUART1_RXDMA) || \ + defined(CONFIG_KINETIS_LPUART2_RXDMA) || defined(CONFIG_KINETIS_LPUART3_RXDMA) || \ + defined(CONFIG_KINETIS_LPUART4_RXDMA) +# define LPSERIAL_HAVE_DMA 1 + +/* Is DMA available on All (enabled) LPUART? */ + +#define LPSERIAL_HAVE_ALL_DMA 1 +# if (defined(CONFIG_KINETIS_LPUART0) && !defined(CONFIG_KINETIS_LPUART0_RXDMA)) || \ + (defined(CONFIG_KINETIS_LPUART1) && !defined(CONFIG_KINETIS_LPUART1_RXDMA)) || \ + (defined(CONFIG_KINETIS_LPUART2) && !defined(CONFIG_KINETIS_LPUART2_RXDMA)) || \ + (defined(CONFIG_KINETIS_LPUART3) && !defined(CONFIG_KINETIS_LPUART3_RXDMA)) || \ + (defined(CONFIG_KINETIS_LPUART4) && !defined(CONFIG_KINETIS_LPUART4_RXDMA)) +# undef LPSERIAL_HAVE_ALL_DMA +# endif +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: kinetis_serial_dma_poll + * + * Description: + * Must be called periodically if any Kinetis LPUART is configured for DMA. + * The DMA callback is triggered for each fifo size/2 bytes, but this can + * result in some bytes being transferred but not collected if the incoming + * data is not a whole multiple of half the FIFO size. + * + * May be safely called from either interrupt or thread context. + * + ****************************************************************************/ + +#ifdef LPSERIAL_HAVE_DMA +void kinetis_lpserial_dma_poll(void); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* HAVE_UART_DEVICE && USE_SERIALDRIVER) */ +#endif /* __ARCH_ARM_SRC_KINETIS_KINETIS_UART_H */