You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/07/21 02:29:01 UTC

[incubator-nuttx] 02/02: Kintis:LPUART add RX DMA

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 19fddc451ca7f9d43f5a57e591f58c453cd61a0b
Author: David Sidrane <Da...@NscDg.com>
AuthorDate: Tue Jul 20 11:43:24 2021 -0700

    Kintis:LPUART add RX DMA
---
 arch/arm/src/kinetis/Kconfig            |  56 ++++
 arch/arm/src/kinetis/kinetis_lpserial.c | 509 +++++++++++++++++++++++++++++++-
 arch/arm/src/kinetis/kinetis_lpuart.h   | 101 +++++++
 3 files changed, 655 insertions(+), 11 deletions(-)

diff --git a/arch/arm/src/kinetis/Kconfig b/arch/arm/src/kinetis/Kconfig
index cbda473..03e18e2 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 4ad9ded..be3bcf1 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
@@ -578,6 +734,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
  *
  * Description:
@@ -615,6 +803,83 @@ static int kinetis_setup(struct uart_dev_s *dev)
 }
 
 /****************************************************************************
+ * 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
  *
  * Description:
@@ -637,6 +902,37 @@ static void kinetis_shutdown(struct uart_dev_s *dev)
 }
 
 /****************************************************************************
+ * 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
  *
  * Description:
@@ -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
@@ -1270,6 +1648,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
  *
  * Description:
@@ -1342,6 +1740,28 @@ static bool kinetis_txready(struct uart_dev_s *dev)
 }
 
 /****************************************************************************
+ * 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 */
@@ -1454,6 +1879,68 @@ unsigned int kinetis_lpuart_serialinit(unsigned int 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
  *
  * Description:
diff --git a/arch/arm/src/kinetis/kinetis_lpuart.h b/arch/arm/src/kinetis/kinetis_lpuart.h
new file mode 100644
index 0000000..6a24ca3
--- /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 */