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 2022/12/14 13:03:41 UTC
[nuttx] 02/05: s32k1xx:serial Add EDMA
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/nuttx.git
commit 9bb1226b04e14524acbb73797f38a9a825a6361c
Author: David Sidrane <Da...@NscDg.com>
AuthorDate: Fri Dec 9 05:15:02 2022 -0800
s32k1xx:serial Add EDMA
---
arch/arm/src/s32k1xx/Kconfig | 16 +
arch/arm/src/s32k1xx/s32k1xx_serial.c | 1071 ++++++++++++++++++++++++++++-----
arch/arm/src/s32k1xx/s32k1xx_serial.h | 109 ++++
3 files changed, 1045 insertions(+), 151 deletions(-)
diff --git a/arch/arm/src/s32k1xx/Kconfig b/arch/arm/src/s32k1xx/Kconfig
index 5da747b11b..1794c90a92 100644
--- a/arch/arm/src/s32k1xx/Kconfig
+++ b/arch/arm/src/s32k1xx/Kconfig
@@ -772,6 +772,22 @@ config LPI2C1_SLAVE_BUS
endmenu # LPI2C1 Slave Configuration
endmenu # LPI2C Configuration
+menu "LPUART Configuration"
+comment "LP Uart Driver Configuration"
+
+
+config S32K1XX_LPUART_RXDMA_BUFFER_SIZE
+ int "Rx DMA buffer size"
+ default 64
+ depends on LPUART0_RXDMA || LPUART1_RXDMA || LPUART2_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.
+
+endmenu # LPUART Configuration
+
menu "Ethernet Configuration"
depends on S32K1XX_ENET
diff --git a/arch/arm/src/s32k1xx/s32k1xx_serial.c b/arch/arm/src/s32k1xx/s32k1xx_serial.c
index e113182b28..1a11a984c3 100644
--- a/arch/arm/src/s32k1xx/s32k1xx_serial.c
+++ b/arch/arm/src/s32k1xx/s32k1xx_serial.c
@@ -50,10 +50,13 @@
#include "chip.h"
#include "arm_internal.h"
#include "hardware/s32k1xx_lpuart.h"
+#include "s32k1xx_edma.h"
+#include "hardware/s32k1xx_dmamux.h"
#include "hardware/s32k1xx_pinmux.h"
#include "s32k1xx_config.h"
#include "s32k1xx_pin.h"
#include "s32k1xx_lowputc.h"
+#include "s32k1xx_serial.h"
#include "s32k1xx_periphclocks.h"
@@ -71,27 +74,45 @@
/* First pick the console and ttys0. This could be any of LPUART0-2 */
#if defined(CONFIG_LPUART0_SERIAL_CONSOLE)
-# define CONSOLE_DEV g_uart0port /* LPUART0 is console */
-# define TTYS0_DEV g_uart0port /* LPUART0 is ttyS0 */
+# define CONSOLE_DEV g_lpuart0priv /* LPUART0 is console */
+# define TTYS0_DEV g_lpuart0priv /* LPUART0 is ttyS0 */
# define UART1_ASSIGNED 1
+# if defined(CONFIG_LPUART0_RXDMA)
+# define SERIAL_HAVE_CONSOLE_RXDMA 1
+# endif
+# if defined(CONFIG_LPUART0_TXDMA)
+# define SERIAL_HAVE_CONSOLE_TXDMA 1
+# endif
#elif defined(CONFIG_LPUART1_SERIAL_CONSOLE)
-# define CONSOLE_DEV g_uart1port /* LPUART1 is console */
-# define TTYS0_DEV g_uart1port /* LPUART1 is ttyS0 */
+# define CONSOLE_DEV g_lpuart1priv /* LPUART1 is console */
+# define TTYS0_DEV g_lpuart1priv /* LPUART1 is ttyS0 */
# define UART2_ASSIGNED 1
+# if defined(CONFIG_LPUART1_RXDMA)
+# define SERIAL_HAVE_CONSOLE_RXDMA 1
+# endif
+# if defined(CONFIG_LPUART1_TXDMA)
+# define SERIAL_HAVE_CONSOLE_TXDMA 1
+# endif
#elif defined(CONFIG_LPUART2_SERIAL_CONSOLE)
-# define CONSOLE_DEV g_uart2port /* LPUART2 is console */
-# define TTYS0_DEV g_uart2port /* LPUART2 is ttyS0 */
-# define UART3_ASSIGNED 1
+# define CONSOLE_DEV g_lpuart2priv /* LPUART2 is console */
+# define TTYS0_DEV g_lpuart2priv /* LPUART2 is ttyS0 */
+# define UART2_ASSIGNED 1
+# if defined(CONFIG_LPUART2_RXDMA)
+# define SERIAL_HAVE_CONSOLE_RXDMA 1
+# endif
+# if defined(CONFIG_LPUART2_TXDMA)
+# define SERIAL_HAVE_CONSOLE_TXDMA 1
+# endif
#else
# undef CONSOLE_DEV /* No console */
# if defined(CONFIG_S32K1XX_LPUART0)
-# define TTYS0_DEV g_uart0port /* LPUART0 is ttyS0 */
+# define TTYS0_DEV g_lpuart0priv /* LPUART0 is ttyS0 */
# define UART1_ASSIGNED 1
# elif defined(CONFIG_S32K1XX_LPUART1)
-# define TTYS0_DEV g_uart1port /* LPUART1 is ttyS0 */
+# define TTYS0_DEV g_lpuart1priv /* LPUART1 is ttyS0 */
# define UART2_ASSIGNED 1
# elif defined(CONFIG_S32K1XX_LPUART2)
-# define TTYS0_DEV g_uart2port /* LPUART2 is ttyS0 */
+# define TTYS0_DEV g_lpuart2priv /* LPUART2 is ttyS0 */
# define UART3_ASSIGNED 1
# endif
#endif
@@ -102,26 +123,30 @@
*/
#if defined(CONFIG_S32K1XX_LPUART0) && !defined(UART1_ASSIGNED)
-# define TTYS1_DEV g_uart0port /* LPUART0 is ttyS1 */
+# define TTYS1_DEV g_lpuart0priv /* LPUART0 is ttyS1 */
# define UART1_ASSIGNED 1
#elif defined(CONFIG_S32K1XX_LPUART1) && !defined(UART2_ASSIGNED)
-# define TTYS1_DEV g_uart1port /* LPUART1 is ttyS1 */
+# define TTYS1_DEV g_lpuart1priv /* LPUART1 is ttyS1 */
# define UART2_ASSIGNED 1
#elif defined(CONFIG_S32K1XX_LPUART2) && !defined(UART3_ASSIGNED)
-# define TTYS1_DEV g_uart2port /* LPUART2 is ttyS1 */
+# define TTYS1_DEV g_lpuart2priv /* LPUART2 is ttyS1 */
# define UART3_ASSIGNED 1
#endif
+#if defined(SERIAL_HAVE_CONSOLE_RXDMA) || defined(SERIAL_HAVE_CONSOLE_TXDMA)
+# define SERIAL_HAVE_CONSOLE_DMA
+#endif
+
/* Pick ttys2. This could be one of LPUART0-2. It can't be LPUART0 because
* that was either assigned as ttyS0 or ttys1. One of LPUART0-2 could be the
* console. One of UART1-2 has already been assigned to ttys0 or ttyS1.
*/
#if defined(CONFIG_S32K1XX_LPUART1) && !defined(UART2_ASSIGNED)
-# define TTYS2_DEV g_uart1port /* LPUART1 is ttyS2 */
+# define TTYS2_DEV g_lpuart1priv /* LPUART1 is ttyS2 */
# define UART2_ASSIGNED 1
#elif defined(CONFIG_S32K1XX_LPUART2) && !defined(UART3_ASSIGNED)
-# define TTYS2_DEV g_uart2port /* LPUART2 is ttyS2 */
+# define TTYS2_DEV g_lpuart2priv /* LPUART2 is ttyS2 */
# define UART3_ASSIGNED 1
#endif
@@ -140,12 +165,20 @@
#if defined(CONFIG_PM_SERIAL2_STANDBY) || defined(CONFIG_PM_SERIAL2_SLEEP)
# define CONFIG_PM_SERIAL2
#endif
+
+#if !defined(CONFIG_S32K1XX_SERIAL_RXDMA_BUFFER_SIZE)
+# define CONFIG_S32K1XX_SERIAL_RXDMA_BUFFER_SIZE 32
+#endif
+
+#define RXDMA_BUFFER_SIZE CONFIG_S32K1XX_SERIAL_RXDMA_BUFFER_SIZE
+
/****************************************************************************
* Private Types
****************************************************************************/
struct s32k1xx_uart_s
{
+ struct uart_dev_s dev; /* Generic UART device */
uint32_t uartbase; /* Base address of UART registers */
uint32_t baud; /* Configured baud */
uint32_t ie; /* Saved enabled interrupts */
@@ -169,6 +202,23 @@ struct s32k1xx_uart_s
#endif
#ifdef CONFIG_SERIAL_RS485CONTROL
uint8_t rs485mode:1; /* We are in RS485 (RTS on TX) mode */
+#endif
+ /* TX DMA state */
+
+#ifdef SERIAL_HAVE_TXDMA
+ const unsigned int dma_txreqsrc; /* DMAMUX source of TX DMA request */
+ DMACH_HANDLE txdma; /* currently-open trasnmit DMA stream */
+ sem_t txdmasem; /* Indicate TX DMA completion */
+#endif
+
+ /* RX DMA state */
+
+#ifdef SERIAL_HAVE_RXDMA
+ const unsigned int dma_rxreqsrc; /* DMAMUX source of RX DMA request */
+ DMACH_HANDLE rxdma; /* currently-open receive DMA stream */
+ bool rxenable; /* DMA-based reception en/disable */
+ uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */
+ char *const rxfifo; /* Receive DMA buffer */
#endif
};
@@ -191,12 +241,44 @@ static int s32k1xx_attach(struct uart_dev_s *dev);
static void s32k1xx_detach(struct uart_dev_s *dev);
static int s32k1xx_interrupt(int irq, void *context, void *arg);
static int s32k1xx_ioctl(struct file *filep, int cmd, unsigned long arg);
+#if !defined(SERIAL_HAVE_ONLY_RXDMA)
static int s32k1xx_receive(struct uart_dev_s *dev, unsigned int *status);
static void s32k1xx_rxint(struct uart_dev_s *dev, bool enable);
static bool s32k1xx_rxavailable(struct uart_dev_s *dev);
-static void s32k1xx_send(struct uart_dev_s *dev, int ch);
+#endif
+#if !defined(SERIAL_HAVE_ONLY_TXDMA)
static void s32k1xx_txint(struct uart_dev_s *dev, bool enable);
+#endif
+
+static void s32k1xx_send(struct uart_dev_s *dev, int ch);
+
static bool s32k1xx_txready(struct uart_dev_s *dev);
+#ifdef SERIAL_HAVE_TXDMA
+static void s32k1xx_dma_send(struct uart_dev_s *dev);
+static void s32k1xx_dma_txint(struct uart_dev_s *dev, bool enable);
+static void s32k1xx_dma_txavailable(struct uart_dev_s *dev);
+static void s32k1xx_dma_txcallback(DMACH_HANDLE handle, void *arg, bool done,
+ int result);
+#endif
+
+#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA)
+static int s32k1xx_dma_setup(struct uart_dev_s *dev);
+static void s32k1xx_dma_shutdown(struct uart_dev_s *dev);
+#endif
+
+#ifdef SERIAL_HAVE_RXDMA
+static int s32k1xx_dma_receive(struct uart_dev_s *dev,
+ unsigned int *status);
+#ifdef CONFIG_PM
+static void s32k1xx_dma_reenable(struct s32k1xx_uart_s *priv);
+#endif
+static void s32k1xx_dma_rxint(struct uart_dev_s *dev, bool enable);
+static bool s32k1xx_dma_rxavailable(struct uart_dev_s *dev);
+
+static void s32k1xx_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done,
+ int result);
+#endif
+
static bool s32k1xx_txempty(struct uart_dev_s *dev);
#ifdef CONFIG_PM
@@ -212,7 +294,8 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* Serial driver UART operations */
-static const struct uart_ops_s g_uart_ops =
+#if !defined(SERIAL_HAVE_ONLY_TXDMA) && !defined(SERIAL_HAVE_ONLY_RXDMA)
+static const struct uart_ops_s g_lpuart_ops =
{
.setup = s32k1xx_setup,
.shutdown = s32k1xx_shutdown,
@@ -230,167 +313,291 @@ static const struct uart_ops_s g_uart_ops =
.txready = s32k1xx_txready,
.txempty = s32k1xx_txempty,
};
+#endif
+
+#if defined(SERIAL_HAVE_RXDMA) && defined(SERIAL_HAVE_TXDMA)
+static const struct uart_ops_s g_lpuart_rxtxdma_ops =
+{
+ .setup = s32k1xx_dma_setup,
+ .shutdown = s32k1xx_dma_shutdown,
+ .attach = s32k1xx_attach,
+ .detach = s32k1xx_detach,
+ .ioctl = s32k1xx_ioctl,
+ .receive = s32k1xx_dma_receive,
+ .rxint = s32k1xx_dma_rxint,
+ .rxavailable = s32k1xx_dma_rxavailable,
+ .send = s32k1xx_send,
+ .txint = s32k1xx_dma_txint,
+ .txready = s32k1xx_txready,
+ .txempty = s32k1xx_txempty,
+ .dmatxavail = s32k1xx_dma_txavailable,
+ .dmasend = s32k1xx_dma_send,
+};
+#endif
+#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_RXDMA)
+static const struct uart_ops_s g_lpuart_rxdma_ops =
+{
+ .setup = s32k1xx_dma_setup,
+ .shutdown = s32k1xx_dma_shutdown,
+ .attach = s32k1xx_attach,
+ .detach = s32k1xx_detach,
+ .ioctl = s32k1xx_ioctl,
+ .receive = s32k1xx_dma_receive,
+ .rxint = s32k1xx_dma_rxint,
+ .rxavailable = s32k1xx_dma_rxavailable,
+ .send = s32k1xx_send,
+ .txint = s32k1xx_txint,
+ .txready = s32k1xx_txready,
+ .txempty = s32k1xx_txempty,
+};
+#endif
+
+#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_TXDMA)
+static const struct uart_ops_s g_lpuart_txdma_ops =
+{
+ .setup = s32k1xx_dma_setup,
+ .shutdown = s32k1xx_dma_shutdown,
+ .attach = s32k1xx_attach,
+ .detach = s32k1xx_detach,
+ .ioctl = s32k1xx_ioctl,
+ .receive = s32k1xx_receive,
+ .rxint = s32k1xx_rxint,
+ .rxavailable = s32k1xx_rxavailable,
+ .send = s32k1xx_send,
+ .txint = s32k1xx_dma_txint,
+ .txready = s32k1xx_txready,
+ .txempty = s32k1xx_txempty,
+ .dmatxavail = s32k1xx_dma_txavailable,
+ .dmasend = s32k1xx_dma_send,
+};
+#endif
+
+/* Avoid unused warning */
+#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_RXDMA)
+const struct uart_ops_s *g_o0 = &g_lpuart_rxdma_ops;
+#endif
+#if !defined(SERIAL_HAVE_ONLY_DMA) && defined(SERIAL_HAVE_TXDMA)
+const struct uart_ops_s *g_o1 = &g_lpuart_txdma_ops;
+#endif
+
+/* I/O buffers */
+
+#ifdef CONFIG_LPUART0_RXDMA
+static char g_lpuart0rxfifo[RXDMA_BUFFER_SIZE];
+#endif
+
+# ifdef CONFIG_LPUART1_RXDMA
+static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE];
+#endif
+
+#ifdef CONFIG_LPUART2_RXDMA
+static char g_lpuart2rxfifo[RXDMA_BUFFER_SIZE];
+#endif
/* I/O buffers */
#ifdef CONFIG_S32K1XX_LPUART0
-static char g_uart0rxbuffer[CONFIG_LPUART0_RXBUFSIZE];
-static char g_uart0txbuffer[CONFIG_LPUART0_TXBUFSIZE];
+static char g_lpuart0rxbuffer[CONFIG_LPUART0_RXBUFSIZE];
+static char g_lpuart0txbuffer[CONFIG_LPUART0_TXBUFSIZE];
#endif
#ifdef CONFIG_S32K1XX_LPUART1
-static char g_uart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
-static char g_uart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
+static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
+static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
#endif
#ifdef CONFIG_S32K1XX_LPUART2
-static char g_uart2rxbuffer[CONFIG_LPUART2_RXBUFSIZE];
-static char g_uart2txbuffer[CONFIG_LPUART2_TXBUFSIZE];
+static char g_lpuart2rxbuffer[CONFIG_LPUART2_RXBUFSIZE];
+static char g_lpuart2txbuffer[CONFIG_LPUART2_TXBUFSIZE];
#endif
/* This describes the state of the S32K1XX lpuart0 port. */
#ifdef CONFIG_S32K1XX_LPUART0
-static struct s32k1xx_uart_s g_uart0priv =
+static struct s32k1xx_uart_s g_lpuart0priv =
{
+ .dev =
+ {
+ .recv =
+ {
+ .size = CONFIG_LPUART0_RXBUFSIZE,
+ .buffer = g_lpuart0rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_LPUART0_TXBUFSIZE,
+ .buffer = g_lpuart0txbuffer,
+ },
+# if defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_rxtxdma_ops,
+# elif defined(CONFIG_LPUART1_RXDMA) && !defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_rxdma_ops,
+# elif !defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_txdma_ops,
+# else
+ .ops = &g_lpuart_ops,
+# endif
+ .priv = &g_lpuart0priv,
+ },
.uartbase = S32K1XX_LPUART0_BASE,
.baud = CONFIG_LPUART0_BAUD,
.irq = S32K1XX_IRQ_LPUART0,
.parity = CONFIG_LPUART0_PARITY,
.bits = CONFIG_LPUART0_BITS,
.stopbits2 = CONFIG_LPUART0_2STOP,
-#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART0_OFLOWCONTROL)
+# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART0_OFLOWCONTROL)
.oflow = 1,
.cts_gpio = GPIO_LPUART0_CTS,
-#endif
-#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART0_IFLOWCONTROL)
+# endif
+# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART0_IFLOWCONTROL)
.iflow = 1,
-#endif
-# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART0_RS485RTSCONTROL)) \
+# endif
+# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART0_RS485RTSCONTROL)) \
|| (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART0_IFLOWCONTROL)))
.rts_gpio = GPIO_LPUART0_RTS,
-#endif
+# endif
-#if (((defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL))) \
+# if (((defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL))) \
&& defined(CONFIG_LPUART0_INVERTIFLOWCONTROL))
.inviflow = 1,
-#endif
+# endif
-#if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART0_RS485RTSCONTROL)
+# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART0_RS485RTSCONTROL)
.rs485mode = 1,
-#endif
-};
-
-static struct uart_dev_s g_uart0port =
-{
- .recv =
- {
- .size = CONFIG_LPUART0_RXBUFSIZE,
- .buffer = g_uart0rxbuffer,
- },
- .xmit =
- {
- .size = CONFIG_LPUART0_TXBUFSIZE,
- .buffer = g_uart0txbuffer,
- },
- .ops = &g_uart_ops,
- .priv = &g_uart0priv,
+# endif
+# ifdef CONFIG_LPUART0_TXDMA
+ .dma_txreqsrc = S32K1XX_DMACHAN_LPUART0_TX,
+# endif
+# ifdef CONFIG_LPUART0_RXDMA
+ .dma_rxreqsrc = S32K1XX_DMACHAN_LPUART0_RX,
+ .rxfifo = g_lpuart0rxfifo,
+# endif
};
#endif
/* This describes the state of the S32K1XX lpuart1 port. */
#ifdef CONFIG_S32K1XX_LPUART1
-static struct s32k1xx_uart_s g_uart1priv =
+static struct s32k1xx_uart_s g_lpuart1priv =
{
+ .dev =
+ {
+ .recv =
+ {
+ .size = CONFIG_LPUART1_RXBUFSIZE,
+ .buffer = g_lpuart1rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_LPUART1_TXBUFSIZE,
+ .buffer = g_lpuart1txbuffer,
+ },
+# if defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_rxtxdma_ops,
+# elif defined(CONFIG_LPUART1_RXDMA) && !defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_rxdma_ops,
+# elif !defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_txdma_ops,
+# else
+ .ops = &g_lpuart_ops,
+# endif
+ .priv = &g_lpuart1priv,
+ },
+
.uartbase = S32K1XX_LPUART1_BASE,
.baud = CONFIG_LPUART1_BAUD,
.irq = S32K1XX_IRQ_LPUART1,
.parity = CONFIG_LPUART1_PARITY,
.bits = CONFIG_LPUART1_BITS,
.stopbits2 = CONFIG_LPUART1_2STOP,
-#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
+# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART1_OFLOWCONTROL)
.oflow = 1,
.cts_gpio = GPIO_LPUART1_CTS,
-#endif
-#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
+# endif
+# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)
.iflow = 1,
-#endif
-# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) \
+# endif
+# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)) \
|| (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART1_IFLOWCONTROL)))
.rts_gpio = GPIO_LPUART1_RTS,
-#endif
-#if (((defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL))) \
+# endif
+# if (((defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL))) \
&& defined(CONFIG_LPUART1_INVERTIFLOWCONTROL))
.inviflow = 1,
-#endif
+# endif
-#if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)
+# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART1_RS485RTSCONTROL)
.rs485mode = 1,
-#endif
-};
-
-static struct uart_dev_s g_uart1port =
-{
- .recv =
- {
- .size = CONFIG_LPUART1_RXBUFSIZE,
- .buffer = g_uart1rxbuffer,
- },
- .xmit =
- {
- .size = CONFIG_LPUART1_TXBUFSIZE,
- .buffer = g_uart1txbuffer,
- },
- .ops = &g_uart_ops,
- .priv = &g_uart1priv,
+# endif
+# ifdef CONFIG_LPUART1_TXDMA
+ .dma_txreqsrc = S32K1XX_DMACHAN_LPUART1_TX,
+# endif
+# ifdef CONFIG_LPUART1_RXDMA
+ .dma_rxreqsrc = S32K1XX_DMACHAN_LPUART1_RX,
+ .rxfifo = g_lpuart1rxfifo,
+# endif
};
#endif
#ifdef CONFIG_S32K1XX_LPUART2
-static struct s32k1xx_uart_s g_uart2priv =
+static struct s32k1xx_uart_s g_lpuart2priv =
{
+ .dev =
+ {
+ .recv =
+ {
+ .size = CONFIG_LPUART2_RXBUFSIZE,
+ .buffer = g_lpuart2rxbuffer,
+ },
+ .xmit =
+ {
+ .size = CONFIG_LPUART2_TXBUFSIZE,
+ .buffer = g_lpuart2txbuffer,
+ },
+# if defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_rxtxdma_ops,
+# elif defined(CONFIG_LPUART1_RXDMA) && !defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_rxdma_ops,
+# elif !defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_TXDMA)
+ .ops = &g_lpuart_txdma_ops,
+# else
+ .ops = &g_lpuart_ops,
+# endif
+ .priv = &g_lpuart2priv,
+ },
+
.uartbase = S32K1XX_LPUART2_BASE,
.baud = CONFIG_LPUART2_BAUD,
.irq = S32K1XX_IRQ_LPUART2,
.parity = CONFIG_LPUART2_PARITY,
.bits = CONFIG_LPUART2_BITS,
.stopbits2 = CONFIG_LPUART2_2STOP,
-#if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART2_OFLOWCONTROL)
+# if defined(CONFIG_SERIAL_OFLOWCONTROL) && defined(CONFIG_LPUART2_OFLOWCONTROL)
.oflow = 1,
.cts_gpio = GPIO_LPUART2_CTS,
-#endif
-#if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL)
+# endif
+# if defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL)
.iflow = 1,
-#endif
-# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) \
+# endif
+# if ((defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)) \
|| (defined(CONFIG_SERIAL_IFLOWCONTROL) && defined(CONFIG_LPUART2_IFLOWCONTROL)))
.rts_gpio = GPIO_LPUART2_RTS,
-#endif
-#if (((defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL))) \
+# endif
+# if (((defined(CONFIG_SERIAL_RS485CONTROL) || defined(CONFIG_SERIAL_IFLOWCONTROL))) \
&& defined(CONFIG_LPUART2_INVERTIFLOWCONTROL))
.inviflow = 1,
-#endif
+# endif
-#if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)
+# if defined(CONFIG_SERIAL_RS485CONTROL) && defined(CONFIG_LPUART2_RS485RTSCONTROL)
.rs485mode = 1,
-#endif
-};
-
-static struct uart_dev_s g_uart2port =
-{
- .recv =
- {
- .size = CONFIG_LPUART2_RXBUFSIZE,
- .buffer = g_uart2rxbuffer,
- },
- .xmit =
- {
- .size = CONFIG_LPUART2_TXBUFSIZE,
- .buffer = g_uart2txbuffer,
- },
- .ops = &g_uart_ops,
- .priv = &g_uart2priv,
+# endif
+# ifdef CONFIG_LPUART2_TXDMA
+ .dma_txreqsrc = S32K1XX_DMACHAN_LPUART2_TX,
+# endif
+# ifdef CONFIG_LPUART2_RXDMA
+ .dma_rxreqsrc = S32K1XX_DMACHAN_LPUART2_RX,
+ .rxfifo = g_lpuart2rxfifo,
+# endif
};
#endif
@@ -426,6 +633,24 @@ static inline void s32k1xx_serialout(struct s32k1xx_uart_s *priv,
putreg32(value, priv->uartbase + offset);
}
+/****************************************************************************
+ * Name: s32k1xx_dma_nextrx
+ *
+ * Description:
+ * Returns the index into the RX FIFO where the DMA will place the next
+ * byte that it receives.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int s32k1xx_dma_nextrx(struct s32k1xx_uart_s *priv)
+{
+ int dmaresidual = s32k1xx_dmach_getcount(priv->rxdma);
+
+ return RXDMA_BUFFER_SIZE - dmaresidual;
+}
+#endif
+
/****************************************************************************
* Name: s32k1xx_disableuartint
****************************************************************************/
@@ -473,6 +698,132 @@ static inline void s32k1xx_restoreuartint(struct s32k1xx_uart_s *priv,
spin_unlock_irqrestore(NULL, flags);
}
+/****************************************************************************
+ * Name: s32k1xx_dma_setup
+ *
+ * Description:
+ * Configure the LPUART baud, bits, parity, etc. This method is called the
+ * first time that the serial port is opened.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA)
+static int s32k1xx_dma_setup(struct uart_dev_s *dev)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+#if defined(SERIAL_HAVE_RXDMA)
+ struct s32k1xx_edma_xfrconfig_s config;
+#endif
+ int result;
+
+ /* Do the basic UART setup first, unless we are the console */
+
+ if (!dev->isconsole)
+ {
+ result = s32k1xx_setup(dev);
+ if (result != OK)
+ {
+ return result;
+ }
+ }
+
+#if defined(SERIAL_HAVE_TXDMA)
+ /* Acquire the Tx DMA channel. This should always succeed. */
+
+ if (priv->dma_txreqsrc != 0)
+ {
+ if (priv->txdma == NULL)
+ {
+ priv->txdma = s32k1xx_dmach_alloc(priv->dma_txreqsrc |
+ DMAMUX_CHCFG_ENBL, 0);
+ if (priv->txdma == NULL)
+ {
+ return -EBUSY;
+ }
+
+ nxsem_init(&priv->txdmasem, 0, 1);
+ nxsem_set_protocol(&priv->txdmasem, SEM_PRIO_NONE);
+ }
+
+ /* Enable Tx DMA for the UART */
+
+ modifyreg32(priv->uartbase + S32K1XX_LPUART_BAUD_OFFSET,
+ 0, LPUART_BAUD_TDMAE);
+ }
+#endif
+
+#if defined(SERIAL_HAVE_RXDMA)
+ /* Acquire the Rx DMA channel. This should always succeed. */
+
+ if (priv->dma_rxreqsrc != 0)
+ {
+ if (priv->rxdma == NULL)
+ {
+ priv->rxdma = s32k1xx_dmach_alloc(priv->dma_rxreqsrc |
+ DMAMUX_CHCFG_ENBL, 0);
+
+ if (priv->rxdma == NULL)
+ {
+ return -EBUSY;
+ }
+ }
+ else
+ {
+ s32k1xx_dmach_stop(priv->rxdma);
+ }
+
+ /* Configure for circular DMA reception into the RX FIFO */
+
+ config.saddr = priv->uartbase + S32K1XX_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 |
+ EDMA_CONFIG_INTHALF |
+ EDMA_CONFIG_INTMAJOR;
+ config.ssize = EDMA_8BIT;
+ config.dsize = EDMA_8BIT;
+ config.nbytes = 1;
+ #ifdef CONFIG_KINETIS_EDMA_ELINK
+ config.linkch = 0;
+ #endif
+
+ s32k1xx_dmach_xfrsetup(priv->rxdma , &config);
+
+ /* Reset our DMA shadow pointer and Rx data availability count to
+ * match the address just programmed above.
+ */
+
+ priv->rxdmanext = 0;
+
+ /* Enable receive Rx DMA for the UART */
+
+ modifyreg32(priv->uartbase + S32K1XX_LPUART_BAUD_OFFSET,
+ 0, LPUART_BAUD_RDMAE);
+
+ /* Enable itnerrupt on Idel and erros */
+
+ modifyreg32(priv->uartbase + S32K1XX_LPUART_CTRL_OFFSET, 0,
+ LPUART_CTRL_PEIE |
+ LPUART_CTRL_FEIE |
+ LPUART_CTRL_NEIE |
+ LPUART_CTRL_ILIE);
+
+ /* 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.
+ */
+
+ s32k1xx_dmach_start(priv->rxdma, s32k1xx_dma_rxcallback, (void *)priv);
+ }
+#endif
+
+ return OK;
+}
+#endif
+
/****************************************************************************
* Name: s32k1xx_setup
*
@@ -556,6 +907,55 @@ static void s32k1xx_shutdown(struct uart_dev_s *dev)
s32k1xx_serialout(priv, S32K1XX_LPUART_GLOBAL_OFFSET, 0);
}
+/****************************************************************************
+ * Name: s32k1xx_dma_shutdown
+ *
+ * Description:
+ * Disable the LPUART. This method is called when the serial
+ * port is closed
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA)
+static void s32k1xx_dma_shutdown(struct uart_dev_s *dev)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+
+ /* Perform the normal UART shutdown */
+
+ s32k1xx_shutdown(dev);
+
+#if defined(SERIAL_HAVE_RXDMA)
+ /* Stop the RX DMA channel */
+
+ if (priv->dma_rxreqsrc != 0)
+ {
+ s32k1xx_dmach_stop(priv->rxdma);
+
+ /* Release the RX DMA channel */
+
+ s32k1xx_dmach_free(priv->rxdma);
+ priv->rxdma = NULL;
+ }
+#endif
+
+#if defined(SERIAL_HAVE_TXDMA)
+ /* Stop the TX DMA channel */
+
+ if (priv->dma_txreqsrc != 0)
+ {
+ s32k1xx_dmach_stop(priv->txdma);
+
+ /* Release the TX DMA channel */
+
+ s32k1xx_dmach_free(priv->txdma);
+ priv->txdma = NULL;
+ nxsem_destroy(&priv->txdmasem);
+ }
+#endif
+}
+#endif
+
/****************************************************************************
* Name: s32k1xx_attach
*
@@ -651,10 +1051,11 @@ static int s32k1xx_interrupt(int irq, void *context, void *arg)
*/
usr = s32k1xx_serialin(priv, S32K1XX_LPUART_STAT_OFFSET);
- usr &= (LPUART_STAT_RDRF | LPUART_STAT_TC | LPUART_STAT_OR |
- LPUART_STAT_FE);
+ usr &= (LPUART_STAT_RDRF | LPUART_STAT_TDRE | LPUART_STAT_OR |
+ LPUART_STAT_FE | LPUART_STAT_NF | LPUART_STAT_PF |
+ LPUART_STAT_IDLE);
- /* Clear serial overrun and framing errors */
+ /* Clear serial overrun, parity and framing errors */
if ((usr & LPUART_STAT_OR) != 0)
{
@@ -662,12 +1063,42 @@ static int s32k1xx_interrupt(int irq, void *context, void *arg)
LPUART_STAT_OR);
}
+ if ((usr & LPUART_STAT_NF) != 0)
+ {
+ s32k1xx_serialout(priv, S32K1XX_LPUART_STAT_OFFSET,
+ LPUART_STAT_NF);
+ }
+
+ if ((usr & LPUART_STAT_PF) != 0)
+ {
+ s32k1xx_serialout(priv, S32K1XX_LPUART_STAT_OFFSET,
+ LPUART_STAT_PF);
+ }
+
if ((usr & LPUART_STAT_FE) != 0)
{
s32k1xx_serialout(priv, S32K1XX_LPUART_STAT_OFFSET,
LPUART_STAT_FE);
}
+ if ((usr & (LPUART_STAT_FE | LPUART_STAT_PF | LPUART_STAT_NF)) != 0)
+ {
+ /* Discard data */
+
+ s32k1xx_serialin(priv, S32K1XX_LPUART_DATA_OFFSET);
+ }
+
+#ifdef SERIAL_HAVE_RXDMA
+ /* The line going to idle, deliver any fractions of RX data */
+
+ if ((usr & LPUART_STAT_IDLE) != 0)
+ {
+ s32k1xx_serialout(priv, S32K1XX_LPUART_STAT_OFFSET,
+ LPUART_STAT_IDLE);
+ s32k1xx_dma_rxcallback(priv->rxdma, priv, false, LPUART_STAT_IDLE);
+ }
+#endif
+
/* Handle incoming, receive bytes */
if ((usr & LPUART_STAT_RDRF) != 0 &&
@@ -703,6 +1134,7 @@ static int s32k1xx_ioctl(struct file *filep, int cmd, unsigned long arg)
#if defined(CONFIG_SERIAL_TIOCSERGSTRUCT) || defined(CONFIG_SERIAL_TERMIOS)
struct inode *inode = filep->f_inode;
struct uart_dev_s *dev = inode->i_private;
+ irqstate_t flags;
#endif
int ret = OK;
@@ -877,6 +1309,7 @@ static int s32k1xx_ioctl(struct file *filep, int cmd, unsigned long arg)
* implement TCSADRAIN / TCSAFLUSH
*/
+ flags = spin_lock_irqsave(NULL);
s32k1xx_disableuartint(priv, &ie);
ret = s32k1xx_setup(dev);
@@ -884,6 +1317,7 @@ static int s32k1xx_ioctl(struct file *filep, int cmd, unsigned long arg)
s32k1xx_restoreuartint(priv, ie);
priv->ie = ie;
+ spin_unlock_irqrestore(NULL, flags);
}
}
break;
@@ -959,6 +1393,7 @@ static int s32k1xx_ioctl(struct file *filep, int cmd, unsigned long arg)
*
****************************************************************************/
+#ifndef SERIAL_HAVE_ONLY_RXDMA
static int s32k1xx_receive(struct uart_dev_s *dev, unsigned int *status)
{
struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev->priv;
@@ -968,6 +1403,7 @@ static int s32k1xx_receive(struct uart_dev_s *dev, unsigned int *status)
*status = rxd >> LPUART_DATA_STATUS_SHIFT;
return (rxd & LPUART_DATA_MASK) >> LPUART_DATA_SHIFT;
}
+#endif
/****************************************************************************
* Name: s32k1xx_rxint
@@ -977,6 +1413,7 @@ static int s32k1xx_receive(struct uart_dev_s *dev, unsigned int *status)
*
****************************************************************************/
+#ifndef SERIAL_HAVE_ONLY_RXDMA
static void s32k1xx_rxint(struct uart_dev_s *dev, bool enable)
{
struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev->priv;
@@ -1003,6 +1440,7 @@ static void s32k1xx_rxint(struct uart_dev_s *dev, bool enable)
s32k1xx_serialout(priv, S32K1XX_LPUART_CTRL_OFFSET, regval);
spin_unlock_irqrestore(NULL, flags);
}
+#endif
/****************************************************************************
* Name: s32k1xx_rxavailable
@@ -1012,6 +1450,7 @@ static void s32k1xx_rxint(struct uart_dev_s *dev, bool enable)
*
****************************************************************************/
+#ifndef SERIAL_HAVE_ONLY_RXDMA
static bool s32k1xx_rxavailable(struct uart_dev_s *dev)
{
struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev->priv;
@@ -1022,6 +1461,263 @@ static bool s32k1xx_rxavailable(struct uart_dev_s *dev)
regval = s32k1xx_serialin(priv, S32K1XX_LPUART_STAT_OFFSET);
return ((regval & LPUART_STAT_RDRF) != 0);
}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_receive
+ *
+ * Description:
+ * Called (usually) from the interrupt level to receive one
+ * character from the LPUART. Error bits associated with the
+ * receipt are provided in the return 'status'.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static int s32k1xx_dma_receive(struct uart_dev_s *dev, unsigned int *status)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+ uint32_t nextrx = s32k1xx_dma_nextrx(priv);
+ int c = 0;
+
+ /* Check if more data is available */
+
+ if (nextrx != priv->rxdmanext)
+ {
+ /* Now read from the DMA buffer */
+
+ c = priv->rxfifo[priv->rxdmanext];
+
+ priv->rxdmanext++;
+
+ if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
+ {
+ priv->rxdmanext = 0;
+ }
+ }
+
+ /* NOTE: If no data is available, then we would return NULL which is,
+ * of course, valid binary data. The protocol is that the upper half
+ * driver must call s32k1xx_dma_rxavailable prior to calling this
+ * function to assure that this never happens.
+ */
+
+ return c;
+}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_reenable
+ *
+ * Description:
+ * Call to re-enable RX DMA.
+ *
+ ****************************************************************************/
+
+#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_PM)
+static void s32k1xx_dma_reenable(struct s32k1xx_uart_s *priv)
+{
+ struct s32k1xx_edma_xfrconfig_s config;
+
+ /* Stop an reset the RX DMA */
+
+ s32k1xx_dmach_stop(priv->rxdma);
+
+ /* Configure for circular DMA reception into the RX FIFO */
+
+ config.saddr = priv->uartbase + S32K1XX_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 |
+ EDMA_CONFIG_INTHALF |
+ EDMA_CONFIG_INTMAJOR;
+ config.ssize = EDMA_8BIT;
+ config.dsize = EDMA_8BIT;
+ config.nbytes = 1;
+#ifdef CONFIG_KINETIS_EDMA_ELINK
+ config.linkch = 0;
+#endif
+
+ s32k1xx_dmach_xfrsetup(priv->rxdma, &config);
+
+ /* Reset our DMA shadow pointer and Rx data availability count to match
+ * the address just programmed above.
+ */
+
+ priv->rxdmanext = 0;
+
+ /* 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.
+ */
+
+ s32k1xx_dmach_start(priv->rxdma, s32k1xx_dma_rxcallback, (void *)priv);
+
+ /* Clear DMA suspended flag. */
+
+ priv->rxdmasusp = false;
+}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_rxint
+ *
+ * Description:
+ * Call to enable or disable RX interrupts
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void s32k1xx_dma_rxint(struct uart_dev_s *dev, bool enable)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+
+ /* Enable/disable DMA reception.
+ *
+ * Note that it is not safe to check for available bytes and immediately
+ * pass them to uart_recvchars as that could potentially recurse back
+ * to us again. Instead, bytes must wait until the next up_dma_poll or
+ * DMA event.
+ */
+
+ priv->rxenable = enable;
+}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_rxavailable
+ *
+ * Description:
+ * Return true if the receive register is not empty
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static bool s32k1xx_dma_rxavailable(struct uart_dev_s *dev)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+
+ /* Compare our receive pointer to the current DMA pointer, if they
+ * do not match, then there are bytes to be received.
+ */
+
+ return (s32k1xx_dma_nextrx(priv) != priv->rxdmanext);
+}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_txcallback
+ *
+ * Description:
+ * This function clears dma buffer at complete of DMA transfer and wakes up
+ * threads waiting for space in buffer.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_TXDMA
+static void s32k1xx_dma_txcallback(DMACH_HANDLE handle, void *arg, bool done,
+ int result)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)arg;
+ /* Update 'nbytes' indicating number of bytes actually transferred by DMA.
+ * This is important to free TX buffer space by 'uart_xmitchars_done'.
+ */
+
+ priv->dev.dmatx.nbytes = priv->dev.dmatx.length + priv->dev.dmatx.nlength;
+
+ /* Adjust the pointers */
+
+ uart_xmitchars_done(&priv->dev);
+
+ /* Release waiter */
+
+ nxsem_post(&priv->txdmasem);
+}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_txavailable
+ *
+ * Description:
+ * Informs DMA that Tx data is available and is ready for transfer.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_TXDMA
+static void s32k1xx_dma_txavailable(struct uart_dev_s *dev)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+
+ /* Only send when the DMA is idle */
+
+ nxsem_wait(&priv->txdmasem);
+
+ uart_xmitchars_dma(dev);
+}
+#endif
+
+/****************************************************************************
+ * Name: s32k1xx_dma_send
+ *
+ * Description:
+ * Called (usually) from the interrupt level to start DMA transfer.
+ * (Re-)Configures DMA Stream updating buffer and buffer length.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_TXDMA
+static void s32k1xx_dma_send(struct uart_dev_s *dev)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev;
+ struct s32k1xx_edma_xfrconfig_s config;
+
+ /* We need to stop DMA before reconfiguration */
+
+ s32k1xx_dmach_stop(priv->txdma);
+
+ /* Reset the number sent */
+
+ dev->dmatx.nbytes = 0;
+
+ /* Make use of setup function to update buffer and its length for next
+ * transfer
+ */
+
+ config.iter = dev->dmatx.length;
+ config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE;
+ config.ssize = EDMA_8BIT;
+ config.dsize = EDMA_8BIT;
+ config.nbytes = sizeof(dev->dmatx.buffer[0]);
+ config.saddr = (uint32_t) dev->dmatx.buffer;
+ config.daddr = priv->uartbase + S32K1XX_LPUART_DATA_OFFSET;
+ config.soff = sizeof(dev->dmatx.buffer[0]);
+ config.doff = 0;
+#ifdef CONFIG_S32K1XX_EDMA_ELINK
+ config.linkch = 0;
+#endif
+
+ /* Setup first half */
+
+ s32k1xx_dmach_xfrsetup(priv->txdma, &config);
+
+ /* Is this a split transfer? */
+
+ if (dev->dmatx.nbuffer)
+ {
+ config.iter = priv->dev.dmatx.nlength;
+ config.saddr = (uint32_t) priv->dev.dmatx.nbuffer;
+
+ s32k1xx_dmach_xfrsetup(priv->txdma, &config);
+ }
+
+ /* Start transmission with the callback on DMA completion */
+
+ s32k1xx_dmach_start(priv->txdma, s32k1xx_dma_txcallback, (void *)priv);
+}
+#endif
/****************************************************************************
* Name: s32k1xx_send
@@ -1037,6 +1733,28 @@ static void s32k1xx_send(struct uart_dev_s *dev, int ch)
s32k1xx_serialout(priv, S32K1XX_LPUART_DATA_OFFSET, (uint32_t)ch);
}
+/****************************************************************************
+ * Name: s32k1xx_dma_txint
+ *
+ * Description:
+ * Call to enable or disable TX interrupts from the UART.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_TXDMA
+static void s32k1xx_dma_txint(struct uart_dev_s *dev, bool enable)
+{
+ /* Nothing to do. */
+
+ /* In case of DMA transfer we do not want to make use of UART interrupts.
+ * Instead, we use DMA interrupts that are activated once during boot
+ * sequence. Furthermore we can use s32k1xx_dma_txcallback() to handle
+ * stuff at half DMA transfer or after transfer completion (depending on
+ * the configuration).
+ */
+}
+#endif
+
/****************************************************************************
* Name: s32k1xx_txint
*
@@ -1045,6 +1763,7 @@ static void s32k1xx_send(struct uart_dev_s *dev, int ch)
*
****************************************************************************/
+#if !defined(SERIAL_HAVE_ONLY_TXDMA)
static void s32k1xx_txint(struct uart_dev_s *dev, bool enable)
{
struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)dev->priv;
@@ -1071,6 +1790,7 @@ static void s32k1xx_txint(struct uart_dev_s *dev, bool enable)
s32k1xx_serialout(priv, S32K1XX_LPUART_CTRL_OFFSET, regval);
spin_unlock_irqrestore(NULL, flags);
}
+#endif
/****************************************************************************
* Name: s32k1xx_txready
@@ -1106,6 +1826,48 @@ static bool s32k1xx_txempty(struct uart_dev_s *dev)
return ((regval & LPUART_STAT_TDRE) != 0);
}
+/****************************************************************************
+ * Name: s32k1xx_dma_rxcallback
+ *
+ * Description:
+ * This function checks the current DMA state and calls the generic
+ * serial stack when bytes appear to be available.
+ *
+ ****************************************************************************/
+
+#ifdef SERIAL_HAVE_RXDMA
+static void s32k1xx_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done,
+ int result)
+{
+ struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)arg;
+ uint32_t sr;
+
+ if (priv->rxenable && s32k1xx_dma_rxavailable(&priv->dev))
+ {
+ uart_recvchars(&priv->dev);
+ }
+
+ /* Get the masked LPUART status word to check and clear error flags.
+ *
+ * When wake-up from low power mode was not fast enough, UART is resumed
+ * too late and sometimes exactly when character was coming over UART,
+ * resulting to frame error.
+ * If error flag is not cleared, Rx DMA will be stuck. Clearing errors
+ * will release Rx DMA.
+ */
+
+ sr = s32k1xx_serialin(priv, S32K1XX_LPUART_STAT_OFFSET);
+
+ if ((sr & (LPUART_STAT_OR | LPUART_STAT_NF | LPUART_STAT_FE)) != 0)
+ {
+ s32k1xx_serialout(priv, S32K1XX_LPUART_STAT_OFFSET,
+ sr & (LPUART_STAT_OR |
+ LPUART_STAT_NF |
+ LPUART_STAT_FE));
+ }
+}
+#endif
+
/****************************************************************************
* Name: up_pm_notify
*
@@ -1136,13 +1898,13 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
peripheral_clock_source_t clock_source;
#ifdef CONFIG_PM_SERIAL0
- struct s32k1xx_uart_s *priv0 = g_uart0port.priv;
+ struct s32k1xx_uart_s *priv0 = g_lpuart0priv.priv;
#endif
#ifdef CONFIG_PM_SERIAL1
- struct s32k1xx_uart_s *priv1 = g_uart1port.priv;
+ struct s32k1xx_uart_s *priv1 = g_lpuart1priv.priv;
#endif
#ifdef CONFIG_PM_SERIAL2
- struct s32k1xx_uart_s *priv2 = g_uart2port.priv;
+ struct s32k1xx_uart_s *priv2 = g_lpuart2priv.priv;
#endif
uint32_t ret_reg = 0;
@@ -1215,7 +1977,7 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart0port);
+ s32k1xx_shutdown(&g_lpuart0priv);
/* change the clock config for the new mode */
@@ -1223,16 +1985,16 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart0port);
+ s32k1xx_shutdown(&g_lpuart0priv);
/* set up the LPUART1 again for the new mode */
- s32k1xx_setup(&g_uart0port);
+ s32k1xx_setup(&g_lpuart0priv);
/* enable the interrupts */
- s32k1xx_rxint(&g_uart0port, true);
- s32k1xx_txint(&g_uart0port, true);
+ s32k1xx_rxint(&g_lpuart0priv, true);
+ s32k1xx_txint(&g_lpuart0priv, true);
#endif
#ifdef CONFIG_PM_SERIAL1
@@ -1262,7 +2024,7 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart1port);
+ s32k1xx_shutdown(&g_lpuart1priv);
/* change the clock config for the new mode */
@@ -1270,16 +2032,16 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart1port);
+ s32k1xx_shutdown(&g_lpuart1priv);
/* set up the LPUART1 again for the new mode */
- s32k1xx_setup(&g_uart1port);
+ s32k1xx_setup(&g_lpuart1priv);
/* enable the interrupts */
- s32k1xx_rxint(&g_uart1port, true);
- s32k1xx_txint(&g_uart1port, true);
+ s32k1xx_rxint(&g_lpuart1priv, true);
+ s32k1xx_txint(&g_lpuart1priv, true);
#endif
#ifdef CONFIG_PM_SERIAL2
@@ -1309,7 +2071,7 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart2port);
+ s32k1xx_shutdown(&g_lpuart2priv);
/* change the clock config for the new mode */
@@ -1317,16 +2079,16 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart2port);
+ s32k1xx_shutdown(&g_lpuart2priv);
/* set up the LPUART1 again for the new mode */
- s32k1xx_setup(&g_uart2port);
+ s32k1xx_setup(&g_lpuart2priv);
/* enable the interrupts */
- s32k1xx_rxint(&g_uart2port, true);
- s32k1xx_txint(&g_uart2port, true);
+ s32k1xx_rxint(&g_lpuart2priv, true);
+ s32k1xx_txint(&g_lpuart2priv, true);
#endif
}
@@ -1378,13 +2140,16 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
peripheral_clock_source_t clock_source;
#ifdef CONFIG_PM_SERIAL0
- struct s32k1xx_uart_s *priv0 = (struct s32k1xx_uart_s *)g_uart0port.priv;
+ struct s32k1xx_uart_s *priv0 =
+ (struct s32k1xx_uart_s *)g_lpuart0priv.priv;
#endif
#ifdef CONFIG_PM_SERIAL1
- struct s32k1xx_uart_s *priv1 = (struct s32k1xx_uart_s *)g_uart1port.priv;
+ struct s32k1xx_uart_s *priv1 =
+ (struct s32k1xx_uart_s *)g_lpuart1priv.priv;
#endif
#ifdef CONFIG_PM_SERIAL2
- struct s32k1xx_uart_s *priv2 = (struct s32k1xx_uart_s *)g_uart2port.priv;
+ struct s32k1xx_uart_s *priv2 =
+ (struct s32k1xx_uart_s *)g_lpuart2priv.priv;
#endif
uint32_t ret_reg = 0;
@@ -1461,7 +2226,7 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart0port);
+ s32k1xx_shutdown(&g_lpuart0priv);
/* change the clock config for the new mode */
@@ -1469,16 +2234,16 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart0port);
+ s32k1xx_shutdown(&g_lpuart0priv);
/* set up the LPUART1 again for the new mode */
- s32k1xx_setup(&g_uart0port);
+ s32k1xx_setup(&g_lpuart0priv);
/* enable the interrupts */
- s32k1xx_rxint(&g_uart0port, true);
- s32k1xx_txint(&g_uart0port, true);
+ s32k1xx_rxint(&g_lpuart0priv, true);
+ s32k1xx_txint(&g_lpuart0priv, true);
#endif
#ifdef CONFIG_PM_SERIAL1
@@ -1508,7 +2273,7 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart1port);
+ s32k1xx_shutdown(&g_lpuart1priv);
/* change the clock config for the new mode */
@@ -1516,16 +2281,16 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart1port);
+ s32k1xx_shutdown(&g_lpuart1priv);
/* set up the LPUART1 again for the new mode */
- s32k1xx_setup(&g_uart1port);
+ s32k1xx_setup(&g_lpuart1priv);
/* enable the interrupts */
- s32k1xx_rxint(&g_uart1port, true);
- s32k1xx_txint(&g_uart1port, true);
+ s32k1xx_rxint(&g_lpuart1priv, true);
+ s32k1xx_txint(&g_lpuart1priv, true);
#endif
#ifdef CONFIG_PM_SERIAL2
@@ -1555,7 +2320,7 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart2port);
+ s32k1xx_shutdown(&g_lpuart2priv);
/* change the clock config for the new mode */
@@ -1563,16 +2328,16 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
/* shutdown the LPUART1 (soft reset) */
- s32k1xx_shutdown(&g_uart2port);
+ s32k1xx_shutdown(&g_lpuart2priv);
/* set up the LPUART1 again for the new mode */
- s32k1xx_setup(&g_uart2port);
+ s32k1xx_setup(&g_lpuart2priv);
/* enable the interrupts */
- s32k1xx_rxint(&g_uart2port, true);
- s32k1xx_txint(&g_uart2port, true);
+ s32k1xx_rxint(&g_lpuart2priv, true);
+ s32k1xx_txint(&g_lpuart2priv, true);
#endif
@@ -1606,8 +2371,8 @@ void s32k1xx_earlyserialinit(void)
*/
#ifdef CONSOLE_DEV
- CONSOLE_DEV.isconsole = true;
- s32k1xx_setup(&CONSOLE_DEV);
+ CONSOLE_DEV.dev.isconsole = true;
+ s32k1xx_setup(&CONSOLE_DEV.dev);
#endif
}
@@ -1623,7 +2388,7 @@ void s32k1xx_earlyserialinit(void)
void arm_serialinit(void)
{
#ifdef CONFIG_PM
- #if defined(CONFIG_PM_SERIAL_STANDBY) || defined(CONFIG_PM_SERIAL_SLEEP)
+ #if defined(CONFIG_PM_SERIAL_STANDBY) || defined(CONFIG_PM_SERIAL_SLEEP)
int ret;
@@ -1636,17 +2401,20 @@ void arm_serialinit(void)
#endif
#ifdef CONSOLE_DEV
- uart_register("/dev/console", &CONSOLE_DEV);
+ uart_register("/dev/console", &CONSOLE_DEV.dev);
+# if defined(SERIAL_HAVE_CONSOLE_DMA)
+ s32k1xx_dma_setup(&CONSOLE_DEV.dev);
+# endif
#endif
/* Register all UARTs */
- uart_register("/dev/ttyS0", &TTYS0_DEV);
+ uart_register("/dev/ttyS0", &TTYS0_DEV.dev);
#ifdef TTYS1_DEV
- uart_register("/dev/ttyS1", &TTYS1_DEV);
+ uart_register("/dev/ttyS1", &TTYS1_DEV.dev);
#endif
#ifdef TTYS2_DEV
- uart_register("/dev/ttyS2", &TTYS2_DEV);
+ uart_register("/dev/ttyS2", &TTYS2_DEV.dev);
#endif
}
@@ -1661,7 +2429,8 @@ void arm_serialinit(void)
int up_putc(int ch)
{
#ifdef CONSOLE_DEV
- struct s32k1xx_uart_s *priv = (struct s32k1xx_uart_s *)CONSOLE_DEV.priv;
+ struct s32k1xx_uart_s *priv =
+ (struct s32k1xx_uart_s *)CONSOLE_DEV.dev.priv;
uint32_t ie;
s32k1xx_disableuartint(priv, &ie);
diff --git a/arch/arm/src/s32k1xx/s32k1xx_serial.h b/arch/arm/src/s32k1xx/s32k1xx_serial.h
index e0c26f2a83..9e33a0a367 100644
--- a/arch/arm/src/s32k1xx/s32k1xx_serial.h
+++ b/arch/arm/src/s32k1xx/s32k1xx_serial.h
@@ -34,6 +34,115 @@
* Pre-processor Definitions
****************************************************************************/
+#if defined(CONFIG_S32K1XX_LPUART0) || defined(CONFIG_S32K1XX_LPUART1) || \
+ defined(CONFIG_S32K1XX_LPUART2)
+# define HAVE_UART 1
+#endif
+
+/* Assume DMA is not used on the console UART */
+
+#undef SERIAL_HAVE_CONSOLE_RXDMA
+#undef SERIAL_HAVE_CONSOLE_TXDMA
+
+#if !defined(HAVE_UART) || !defined(CONFIG_ARCH_DMA)
+# undef CONFIG_LPUART0_RXDMA
+# undef CONFIG_LPUART0_TXDMA
+# undef CONFIG_LPUART1_RXDMA
+# undef CONFIG_LPUART1_TXDMA
+# undef CONFIG_LPUART2_RXDMA
+# undef CONFIG_LPUART2_TXDMA
+#endif
+
+/* Disable the DMA configuration on all unused LPUARTs */
+
+#ifndef CONFIG_S32K1XX_LPUART0
+# undef CONFIG_LPUART0_RXDMA
+# undef CONFIG_LPUART0_TXDMA
+#endif
+
+#ifndef CONFIG_S32K1XX_LPUART1
+# undef CONFIG_LPUART1_RXDMA
+# undef CONFIG_LPUART1_TXDMA
+#endif
+
+#ifndef CONFIG_S32K1XX_LPUART2
+# undef CONFIG_LPUART2_RXDMA
+# undef CONFIG_LPUART2_TXDMA
+#endif
+
+/* Is RX DMA available on any (enabled) LPUART? */
+
+#undef SERIAL_HAVE_RXDMA
+#if defined(CONFIG_LPUART0_RXDMA) || defined(CONFIG_LPUART1_RXDMA) || \
+ defined(CONFIG_LPUART2_RXDMA)
+# define SERIAL_HAVE_RXDMA 1
+#endif
+
+/* Is TX DMA available on any (enabled) LPUART? */
+#undef SERIAL_HAVE_TXDMA
+#if defined(CONFIG_LPUART0_TXDMA) || defined(CONFIG_LPUART1_TXDMA) || \
+ defined(CONFIG_LPUART2_TXDMA)
+# define SERIAL_HAVE_TXDMA 1
+#endif
+
+/* Is RX DMA used on all (enabled) LPUARTs */
+
+#define SERIAL_HAVE_ONLY_RXDMA 1
+#if defined(CONFIG_S32K1XX_LPUART0) && !defined(CONFIG_LPUART0_RXDMA)
+# undef SERIAL_HAVE_ONLY_RXDMA
+#elif defined(CONFIG_S32K1XX_LPUART1) && !defined(CONFIG_LPUART1_RXDMA)
+# undef SERIAL_HAVE_ONLY_RXDMA
+#elif defined(CONFIG_S32K1XX_LPUART2) && !defined(CONFIG_LPUART2_RXDMA)
+# undef SERIAL_HAVE_ONLY_RXDMA
+#endif
+
+/* Is TX DMA used on all (enabled) LPUARTs */
+
+#define SERIAL_HAVE_ONLY_TXDMA 1
+#if defined(CONFIG_S32K1XX_LPUART0) && !defined(CONFIG_LPUART0_TXDMA)
+# undef SERIAL_HAVE_ONLY_TXDMA
+#elif defined(CONFIG_S32K1XX_LPUART1) && !defined(CONFIG_LPUART1_TXDMA)
+# undef SERIAL_HAVE_ONLY_TXDMA
+#elif defined(CONFIG_S32K1XX_LPUART2) && !defined(CONFIG_LPUART2_TXDMA)
+# undef SERIAL_HAVE_ONLY_TXDMA
+#endif
+
+#undef SERIAL_HAVE_ONLY_DMA
+#if defined(SERIAL_HAVE_ONLY_RXDMA) && defined(SERIAL_HAVE_ONLY_TXDMA)
+#define SERIAL_HAVE_ONLY_DMA
+#endif
+
+/* Verify that DMA has been enabled and the DMA channel has been defined.
+ */
+
+# if defined(SERIAL_HAVE_TXDMA) || defined(SERIAL_HAVE_RXDMA)
+# ifndef CONFIG_S32K1XX_EDMA
+# error S32K1XX LPUART receive or transmit DMA requires CONFIG_S32K1XX_EDMA
+# endif
+# endif
+
+#if defined(SERIAL_HAVE_RXDMA)
+/* Currently RS-485 support cannot be enabled when RXDMA is in use due to
+ * lack of testing.
+ */
+
+# if (defined(CONFIG_LPUART0_RXDMA) && defined(CONFIG_LPUART0_RS485)) || \
+ (defined(CONFIG_LPUART1_RXDMA) && defined(CONFIG_LPUART1_RS485)) || \
+ (defined(CONFIG_LPUART2_RXDMA) && defined(CONFIG_LPUART2_RS485))
+# error "RXDMA and RS-485 cannot be enabled at the same time for the same LPUART"
+# endif
+#endif /* SERIAL_HAVE_RXDMA */
+
+/* Currently RS-485 support cannot be enabled when TXDMA is in use due to
+ * lack of testing.
+ */
+
+#if (defined(CONFIG_LPUART0_TXDMA) && defined(CONFIG_LPUART0_RS485)) || \
+ (defined(CONFIG_LPUART1_TXDMA) && defined(CONFIG_LPUART1_RS485)) || \
+ (defined(CONFIG_LPUART2_TXDMA) && defined(CONFIG_LPUART2_RS485))
+# error "TXDMA and RS-485 cannot be enabled at the same time for the same LPUART"
+#endif
+
/****************************************************************************
* Public Types
****************************************************************************/