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 ****************************************************************************/