Kintis:LPUART add RX DMA

This commit is contained in:
David Sidrane 2021-07-20 11:43:24 -07:00 committed by Xiang Xiao
parent 34eb918665
commit 19fddc451c
3 changed files with 655 additions and 11 deletions

View File

@ -1398,6 +1398,62 @@ config KINETIS_SERIAL_RXDMA_BUFFER_SIZE
endmenu # Kinetis UART Configuration
menu "Kinetis LPUART Configuration"
if KINETIS_SERIALDRIVER || OTHER_SERIALDRIVER
comment "LP Uart Driver Configuration"
config KINETIS_LPUART0_RXDMA
bool "LPUART0 Rx DMA"
default n
depends on KINETIS_LPUART0 && KINETIS_EDMA
---help---
In high data rate usage, Rx DMA may eliminate Rx overrun errors
config KINETIS_LPUART1_RXDMA
bool "LPUART1 Rx DMA"
default n
depends on KINETIS_LPUART1 && KINETIS_EDMA
---help---
In high data rate usage, Rx DMA may eliminate Rx overrun errors
config KINETIS_LPUART2_RXDMA
bool "LPUART2 Rx DMA"
default n
depends on KINETIS_LPUART2 && KINETIS_EDMA
---help---
In high data rate usage, Rx DMA may eliminate Rx overrun errors
config KINETIS_LPUART3_RXDMA
bool "LPUART3 Rx DMA"
default n
depends on KINETIS_LPUART3 && KINETIS_EDMA
---help---
In high data rate usage, Rx DMA may eliminate Rx overrun errors
config KINETIS_LPUART4_RXDMA
bool "LPUART4 Rx DMA"
default n
depends on KINETIS_LPUART4 && KINETIS_EDMA
---help---
In high data rate usage, Rx DMA may eliminate Rx overrun errors
config KINETIS_LPUART_RXDMA_BUFFER_SIZE
int "Rx DMA buffer size"
default 32
depends on KINETIS_LPUART0_RXDMA || KINETIS_LPUART1_RXDMA || KINETIS_LPUART2_RXDMA || KINETIS_LPUART3_RXDMA || KINETIS_LPUART4_RXDMA
---help---
The DMA buffer size when using RX DMA to emulate a FIFO.
When streaming data, the generic serial layer will be called
every time the FIFO receives half this number of bytes.
Value given here will be rounded up to next multiple of 32 bytes.
endif # KINETIS_SERIALDRIVER || OTHER_SERIALDRIVER
endmenu # Kinetis LPUART Configuration
config KINETIS_MERGE_TTY
bool "Kinetis Merge TTY names for LPUARTS"
default n

View File

@ -47,10 +47,14 @@
#include "arm_arch.h"
#include "arm_internal.h"
#include "kinetis.h"
#include "kinetis_config.h"
#include "chip.h"
#include "hardware/kinetis_lpuart.h"
#include "hardware/kinetis_pinmux.h"
#include "hardware/kinetis_dmamux.h"
#include "kinetis.h"
#include "kinetis_lpuart.h"
#include "kinetis_edma.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@ -69,6 +73,10 @@
#if defined(HAVE_LPUART_DEVICE) && defined(USE_SERIALDRIVER)
/* Assume DMA is not used on the console UART */
#undef SERIAL_HAVE_CONSOLE_DMA
/* Which LPUART with be tty0/console and which tty1? The console will always
* be ttyS0. If there is no console then will use the lowest numbered
* LPUART.
@ -80,22 +88,37 @@
# define CONSOLE_DEV g_lpuart0port /* LPUART0 is console */
# define TTYS0_DEV g_lpuart0port /* LPUART0 is ttyS0 */
# define LPUART0_ASSIGNED 1
# if defined(CONFIG_KINETIS_LPUART0_RXDMA)
# define SERIAL_HAVE_CONSOLE_DMA 1
# endif
#elif defined(CONFIG_LPUART1_SERIAL_CONSOLE)
# define CONSOLE_DEV g_lpuart1port /* LPUART1 is console */
# define TTYS0_DEV g_lpuart1port /* LPUART1 is ttyS0 */
# define LPUART1_ASSIGNED 1
# if defined(CONFIG_KINETIS_LPUART1_RXDMA)
# define SERIAL_HAVE_CONSOLE_DMA 1
# endif
#elif defined(CONFIG_LPUART2_SERIAL_CONSOLE)
# define CONSOLE_DEV g_lpuart2port /* LPUART2 is console */
# define TTYS0_DEV g_lpuart2port /* LPUART2 is ttyS0 */
# define LPUART2_ASSIGNED 1
# if defined(CONFIG_KINETIS_LPUART2_RXDMA)
# define SERIAL_HAVE_CONSOLE_DMA 1
# endif
#elif defined(CONFIG_LPUART3_SERIAL_CONSOLE)
# define CONSOLE_DEV g_lpuart3port /* LPUART3 is console */
# define TTYS0_DEV g_lpuart3port /* LPUART3 is ttyS0 */
# define LPUART3_ASSIGNED 1
# if defined(CONFIG_KINETIS_LPUART3_RXDMA)
# define SERIAL_HAVE_CONSOLE_DMA 1
# endif
#elif defined(CONFIG_LPUART4_SERIAL_CONSOLE)
# define CONSOLE_DEV g_lpuart4port /* LPUART4 is console */
# define TTYS0_DEV g_lpuart4port /* LPUART4 is ttyS0 */
# define LPUART4_ASSIGNED 1
# if defined(CONFIG_KINETIS_LPUART4_RXDMA)
# define SERIAL_HAVE_CONSOLE_DMA 1
# endif
#else
# undef CONSOLE_DEV /* No console */
# if defined(CONFIG_KINETIS_LPUART0)
@ -182,6 +205,37 @@
# define LPUART4_ASSIGNED 1
#endif
#ifdef LPSERIAL_HAVE_DMA
/* The DMA buffer size when using RX DMA to emulate a FIFO.
*
* When streaming data, the generic serial layer will be called every time
* the FIFO receives half this number of bytes.
*
* This buffer size should be an even multiple of the Cortex-M7 D-Cache line
* size, ARMV7M_DCACHE_LINESIZE, so that it can be individually invalidated.
*
* Should there be a Cortex-M7 without a D-Cache, ARMV7M_DCACHE_LINESIZE
* would be zero!
*/
# if !defined(ARMV7M_DCACHE_LINESIZE) || ARMV7M_DCACHE_LINESIZE == 0
# undef ARMV7M_DCACHE_LINESIZE
# define ARMV7M_DCACHE_LINESIZE 32
# endif
# if !defined(CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE) || \
(CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE < ARMV7M_DCACHE_LINESIZE)
# undef CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE
# define CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE ARMV7M_DCACHE_LINESIZE
# endif
# define RXDMA_BUFFER_MASK ((uint32_t)(ARMV7M_DCACHE_LINESIZE - 1))
# define RXDMA_BUFFER_SIZE ((CONFIG_KINETIS_LPUART_RXDMA_BUFFER_SIZE \
+ RXDMA_BUFFER_MASK) & ~RXDMA_BUFFER_MASK)
#endif /* LPSERIAL_HAVE_DMA */
#define LPUART_CTRL_ERROR_INTS (LPUART_CTRL_ORIE | LPUART_CTRL_FEIE | \
LPUART_CTRL_NEIE | LPUART_CTRL_PEIE)
@ -230,6 +284,12 @@ struct kinetis_dev_s
#ifdef CONFIG_SERIAL_OFLOWCONTROL
uint32_t cts_gpio; /* UART CTS GPIO pin configuration */
#endif
#ifdef LPSERIAL_HAVE_DMA
const uint8_t rxdma_reqsrc;
DMACH_HANDLE rxdma; /* currently-open receive DMA stream */
uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */
char *const rxfifo; /* Receive DMA buffer */
#endif
};
/****************************************************************************
@ -242,9 +302,11 @@ static int kinetis_attach(struct uart_dev_s *dev);
static void kinetis_detach(struct uart_dev_s *dev);
static int kinetis_interrupt(int irq, void *context, void *arg);
static int kinetis_ioctl(struct file *filep, int cmd, unsigned long arg);
static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status);
static void kinetis_rxint(struct uart_dev_s *dev, bool enable);
#if !defined(LPSERIAL_HAVE_ALL_DMA)
static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status);
static bool kinetis_rxavailable(struct uart_dev_s *dev);
#endif
#ifdef CONFIG_SERIAL_IFLOWCONTROL
static bool kinetis_rxflowcontrol(struct uart_dev_s *dev,
unsigned int nbuffered, bool upper);
@ -253,10 +315,22 @@ static void kinetis_send(struct uart_dev_s *dev, int ch);
static void kinetis_txint(struct uart_dev_s *dev, bool enable);
static bool kinetis_txready(struct uart_dev_s *dev);
#ifdef LPSERIAL_HAVE_DMA
static int kinetis_dma_nextrx(struct kinetis_dev_s *priv);
static int kinetis_dma_setup(struct uart_dev_s *dev);
static void kinetis_dma_shutdown(struct uart_dev_s *dev);
static int kinetis_dma_receive(struct uart_dev_s *dev,
unsigned int *status);
static bool kinetis_dma_rxavailable(struct uart_dev_s *dev);
static uint32_t get_and_clear_uart_status(struct kinetis_dev_s *priv);
static void kinetis_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done,
int result);
#endif
/****************************************************************************
* Private Data
****************************************************************************/
#if !defined(LPSERIAL_HAVE_ALL_DMA)
static const struct uart_ops_s g_lpuart_ops =
{
.setup = kinetis_setup,
@ -275,28 +349,70 @@ static const struct uart_ops_s g_lpuart_ops =
.txready = kinetis_txready,
.txempty = kinetis_txready,
};
#endif
#ifdef LPSERIAL_HAVE_DMA
static const struct uart_ops_s g_lpuart_dma_ops =
{
.setup = kinetis_dma_setup,
.shutdown = kinetis_dma_shutdown,
.attach = kinetis_attach,
.detach = kinetis_detach,
.ioctl = kinetis_ioctl,
.receive = kinetis_dma_receive,
.rxint = kinetis_rxint,
.rxavailable = kinetis_dma_rxavailable,
#ifdef CONFIG_SERIAL_IFLOWCONTROL
.rxflowcontrol = kinetis_rxflowcontrol,
#endif
.send = kinetis_send,
.txint = kinetis_txint,
.txready = kinetis_txready,
.txempty = kinetis_txready,
};
#endif
/* I/O buffers */
#ifdef CONFIG_KINETIS_LPUART0
static char g_lpuart0rxbuffer[CONFIG_LPUART0_RXBUFSIZE];
static char g_lpuart0txbuffer[CONFIG_LPUART0_TXBUFSIZE];
# ifdef CONFIG_KINETIS_LPUART0_RXDMA
static char g_lpuart0rxfifo[RXDMA_BUFFER_SIZE]
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
# endif
#endif
#ifdef CONFIG_KINETIS_LPUART1
static char g_lpuart1rxbuffer[CONFIG_LPUART1_RXBUFSIZE];
static char g_lpuart1txbuffer[CONFIG_LPUART1_TXBUFSIZE];
# ifdef CONFIG_KINETIS_LPUART1_RXDMA
static char g_lpuart1rxfifo[RXDMA_BUFFER_SIZE]
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
# endif
#endif
#ifdef CONFIG_KINETIS_LPUART2
static char g_lpuart2rxbuffer[CONFIG_LPUART2_RXBUFSIZE];
static char g_lpuart2txbuffer[CONFIG_LPUART2_TXBUFSIZE];
# ifdef CONFIG_KINETIS_LPUART2_RXDMA
static char g_lpuart2rxfifo[RXDMA_BUFFER_SIZE]
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
# endif
#endif
#ifdef CONFIG_KINETIS_LPUART3
static char g_lpuart3rxbuffer[CONFIG_LPUART3_RXBUFSIZE];
static char g_lpuart3txbuffer[CONFIG_LPUART3_TXBUFSIZE];
# ifdef CONFIG_KINETIS_LPUART3_RXDMA
static char g_lpuart3rxfifo[RXDMA_BUFFER_SIZE]
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
# endif
#endif
#ifdef CONFIG_KINETIS_LPUART4
static char g_lpuart4rxbuffer[CONFIG_LPUART4_RXBUFSIZE];
static char g_lpuart4txbuffer[CONFIG_LPUART4_TXBUFSIZE];
# ifdef CONFIG_KINETIS_LPUART4_RXDMA
static char g_lpuart4rxfifo[RXDMA_BUFFER_SIZE]
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
# endif
#endif
/* This describes the state of the Kinetis LPUART0 port. */
@ -319,6 +435,10 @@ static struct kinetis_dev_s g_lpuart0priv =
.iflow = true,
.rts_gpio = PIN_LPUART0_RTS,
#endif
#ifdef CONFIG_KINETIS_LPUART0_RXDMA
.rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART0_RX,
.rxfifo = g_lpuart0rxfifo,
#endif
};
static uart_dev_t g_lpuart0port =
@ -333,7 +453,11 @@ static uart_dev_t g_lpuart0port =
.size = CONFIG_LPUART0_TXBUFSIZE,
.buffer = g_lpuart0txbuffer,
},
.ops = &g_lpuart_ops,
#ifdef CONFIG_KINETIS_LPUART0_RXDMA
.ops = &g_lpuart_dma_ops,
#else
.ops = &g_lpuart_ops,
#endif
.priv = &g_lpuart0priv,
};
#endif
@ -358,6 +482,10 @@ static struct kinetis_dev_s g_lpuart1priv =
.iflow = true,
.rts_gpio = PIN_LPUART1_RTS,
#endif
#ifdef CONFIG_KINETIS_LPUART1_RXDMA
.rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART1_RX,
.rxfifo = g_lpuart1rxfifo,
#endif
};
static uart_dev_t g_lpuart1port =
@ -372,7 +500,11 @@ static uart_dev_t g_lpuart1port =
.size = CONFIG_LPUART1_TXBUFSIZE,
.buffer = g_lpuart1txbuffer,
},
.ops = &g_lpuart_ops,
#ifdef CONFIG_KINETIS_LPUART1_RXDMA
.ops = &g_lpuart_dma_ops,
#else
.ops = &g_lpuart_ops,
#endif
.priv = &g_lpuart1priv,
};
#endif
@ -397,6 +529,10 @@ static struct kinetis_dev_s g_lpuart2priv =
.iflow = true,
.rts_gpio = PIN_LPUART2_RTS,
#endif
#ifdef CONFIG_KINETIS_LPUART2_RXDMA
.rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART2_RX,
.rxfifo = g_lpuart2rxfifo,
#endif
};
static uart_dev_t g_lpuart2port =
@ -411,7 +547,11 @@ static uart_dev_t g_lpuart2port =
.size = CONFIG_LPUART2_TXBUFSIZE,
.buffer = g_lpuart2txbuffer,
},
.ops = &g_lpuart_ops,
#ifdef CONFIG_KINETIS_LPUART2_RXDMA
.ops = &g_lpuart_dma_ops,
#else
.ops = &g_lpuart_ops,
#endif
.priv = &g_lpuart2priv,
};
#endif
@ -436,6 +576,10 @@ static struct kinetis_dev_s g_lpuart3priv =
.iflow = true,
.rts_gpio = PIN_LPUART3_RTS,
#endif
#ifdef CONFIG_KINETIS_LPUART3_RXDMA
.rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART3_RX,
.rxfifo = g_lpuart3rxfifo,
#endif
};
static uart_dev_t g_lpuart3port =
@ -450,7 +594,11 @@ static uart_dev_t g_lpuart3port =
.size = CONFIG_LPUART3_TXBUFSIZE,
.buffer = g_lpuart3txbuffer,
},
.ops = &g_lpuart_ops,
#ifdef CONFIG_KINETIS_LPUART3_RXDMA
.ops = &g_lpuart_dma_ops,
#else
.ops = &g_lpuart_ops,
#endif
.priv = &g_lpuart3priv,
};
#endif
@ -475,6 +623,10 @@ static struct kinetis_dev_s g_lpuart4priv =
.iflow = true,
.rts_gpio = PIN_LPUART4_RTS,
#endif
#ifdef CONFIG_KINETIS_LPUART4_RXDMA
.rxdma_reqsrc = KINETIS_DMA_REQUEST_SRC_LPUART4_RX,
.rxfifo = g_lpuart4rxfifo,
#endif
};
static uart_dev_t g_lpuart4port =
@ -489,7 +641,11 @@ static uart_dev_t g_lpuart4port =
.size = CONFIG_LPUART4_TXBUFSIZE,
.buffer = g_lpuart4txbuffer,
},
.ops = &g_lpuart_ops,
#ifdef CONFIG_KINETIS_LPUART4_RXDMA
.ops = &g_lpuart_dma_ops,
#else
.ops = &g_lpuart_ops,
#endif
.priv = &g_lpuart4priv,
};
#endif
@ -577,6 +733,38 @@ static void kinetis_disableuartint(struct kinetis_dev_s *priv, uint32_t *ie)
}
#endif
/****************************************************************************
* Name: get_and_clear_uart_status
*
* Description:
* Clears the error flags of the uart if an error occurred in s1 and
* returns the status
*
* Input Parameters:
* u_dev_s
*
* Returns Value:
* Uart status s1
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static uint32_t get_and_clear_uart_status(struct kinetis_dev_s *priv)
{
uint32_t regval;
regval = kinetis_serialin(priv, KINETIS_LPUART_STAT_OFFSET);
regval &= LPUART_STAT_ERRORS;
if (regval != 0)
{
kinetis_serialout(priv, KINETIS_LPUART_STAT_OFFSET, regval);
}
return regval;
}
#endif
/****************************************************************************
* Name: kinetis_setup
*
@ -614,6 +802,83 @@ static int kinetis_setup(struct uart_dev_s *dev)
return OK;
}
/****************************************************************************
* Name: kinetis_dma_setup
*
* Description:
* Configure the UART baud, bits, parity, etc. This method is called the
* first time that the serial port is opened.
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static int kinetis_dma_setup(struct uart_dev_s *dev)
{
struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv;
int result;
uint32_t regval;
DMACH_HANDLE rxdma = NULL;
/* Do the basic UART setup first, unless we are the console */
if (!dev->isconsole)
{
result = kinetis_setup(dev);
if (result != OK)
{
return result;
}
}
/* Acquire the DMA channel. */
rxdma = kinetis_dmach_alloc(priv->rxdma_reqsrc | DMAMUX_CHCFG_ENBL, 0);
if (rxdma == NULL)
{
return -EBUSY;
}
/* Configure for circular DMA reception into the RX FIFO */
struct kinetis_edma_xfrconfig_s config;
config.saddr = priv->uartbase + KINETIS_LPUART_DATA_OFFSET;
config.daddr = (uint32_t) priv->rxfifo;
config.soff = 0;
config.doff = 1;
config.iter = RXDMA_BUFFER_SIZE;
config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE | EDMA_CONFIG_LOOPDEST;
config.ssize = EDMA_8BIT;
config.dsize = EDMA_8BIT;
config.ttype = EDMA_PERIPH2MEM;
config.nbytes = 1;
#ifdef CONFIG_KINETIS_EDMA_ELINK
config.linkch = NULL;
#endif
kinetis_dmach_xfrsetup(rxdma, &config);
/* Reset our DMA shadow pointer to match the address just programmed
* above.
*/
priv->rxdmanext = 0;
/* Enable receive DMA for the UART */
regval = kinetis_serialin(priv, KINETIS_LPUART_BAUD_OFFSET);
regval |= LPUART_BAUD_RDMAE;
kinetis_serialout(priv, KINETIS_LPUART_BAUD_OFFSET, regval);
/* Start the DMA channel, and arrange for callbacks at the half and
* full points in the FIFO. This ensures that we have half a FIFO
* worth of time to claim bytes before they are overwritten.
*/
kinetis_dmach_start(rxdma, kinetis_dma_rxcallback, (void *)dev);
priv->rxdma = rxdma;
return OK;
}
#endif
/****************************************************************************
* Name: kinetis_shutdown
*
@ -636,6 +901,37 @@ static void kinetis_shutdown(struct uart_dev_s *dev)
kinetis_lpuartreset(priv->uartbase);
}
/****************************************************************************
* Name: kinetis_dma_shutdown
*
* Description:
* Disable the UART. This method is called when the serial
* port is closed
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static void kinetis_dma_shutdown(struct uart_dev_s *dev)
{
struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv;
DMACH_HANDLE rxdma = priv->rxdma;
/* Perform the normal UART shutdown */
kinetis_shutdown(dev);
/* Stop the DMA channel */
kinetis_dmach_stop(rxdma);
/* Release the DMA channel */
kinetis_dmach_free(rxdma);
priv->rxdma = NULL;
}
#endif
/****************************************************************************
* Name: kinetis_attach
*
@ -1102,7 +1398,7 @@ static int kinetis_ioctl(struct file *filep, int cmd, unsigned long arg)
* return 'status'.
*
****************************************************************************/
#if !defined(LPSERIAL_HAVE_ALL_DMA)
static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status)
{
struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv;
@ -1143,6 +1439,66 @@ static int kinetis_receive(struct uart_dev_s *dev, unsigned int *status)
return data;
}
#endif
/****************************************************************************
* Name: kinetis_dma_receive
*
* Description:
* Called (usually) from the interrupt level to receive one
* character from the UART. Error bits associated with the
* receipt are provided in the return 'status'.
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static int kinetis_dma_receive(struct uart_dev_s *dev, unsigned int *status)
{
struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv;
int c = 0;
uint32_t stat;
/* Clear uart errors and return status information */
stat = get_and_clear_uart_status(priv);
if (status)
{
*status = stat;
}
if (kinetis_dma_nextrx(priv) != priv->rxdmanext)
{
/* Invalidate the DMA buffer */
up_invalidate_dcache((uintptr_t)priv->rxfifo,
(uintptr_t)priv->rxfifo + RXDMA_BUFFER_SIZE);
/* Now read from the DMA buffer */
c = priv->rxfifo[priv->rxdmanext];
priv->rxdmanext++;
if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
{
/* HACK: Skip the first byte since it is duplicate of last one. */
if (kinetis_dma_nextrx(priv) != 0)
{
priv->rxdmanext = 1;
}
else
{
/* Try to catch race conditions that will spin on the whole
* buffer again.
*/
priv->rxdmanext = 0;
}
}
}
return c;
}
#endif
/****************************************************************************
* Name: kinetis_rxint
@ -1179,13 +1535,34 @@ static void kinetis_rxint(struct uart_dev_s *dev, bool enable)
}
/****************************************************************************
* Name: kinetis_rxavailable
* Name: kinetis_dma_rxavailable
*
* Description:
* Return true if the receive register is not empty
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static bool kinetis_dma_rxavailable(struct uart_dev_s *dev)
{
struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv;
/* Compare our receive pointer to the current DMA pointer, if they
* do not match, then there are bytes to be received.
*/
return (kinetis_dma_nextrx(priv) != priv->rxdmanext);
}
#endif
/****************************************************************************
* Name: kinetis_rxavailable
*
* Description:
* Return true if the receive register is not empty
*
****************************************************************************/
#if !defined(LPSERIAL_HAVE_ALL_DMA)
static bool kinetis_rxavailable(struct uart_dev_s *dev)
{
struct kinetis_dev_s *priv = (struct kinetis_dev_s *)dev->priv;
@ -1195,6 +1572,7 @@ static bool kinetis_rxavailable(struct uart_dev_s *dev)
return (kinetis_serialin(priv, KINETIS_LPUART_STAT_OFFSET) &
LPUART_STAT_RDRF) != 0;
}
#endif
/****************************************************************************
* Name: kinetis_rxflowcontrol
@ -1269,6 +1647,26 @@ static bool kinetis_rxflowcontrol(struct uart_dev_s *dev,
}
#endif
/****************************************************************************
* Name: kinetis_dma_nextrx
*
* Description:
* Returns the index into the RX FIFO where the DMA will place the next
* byte that it receives.
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static int kinetis_dma_nextrx(struct kinetis_dev_s *priv)
{
size_t dmaresidual;
dmaresidual = kinetis_dmach_getcount(priv->rxdma);
return (RXDMA_BUFFER_SIZE - (int)dmaresidual) % RXDMA_BUFFER_SIZE;
}
#endif
/****************************************************************************
* Name: kinetis_send
*
@ -1341,6 +1739,28 @@ static bool kinetis_txready(struct uart_dev_s *dev)
LPUART_STAT_TDRE) != 0;
}
/****************************************************************************
* Name: kinetis_dma_rxcallback
*
* Description:
* This function checks the current DMA state and calls the generic
* serial stack when bytes appear to be available.
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
static void kinetis_dma_rxcallback(DMACH_HANDLE handle, void *arg, bool done,
int result)
{
struct uart_dev_s *dev = (struct uart_dev_s *)arg;
if (kinetis_dma_rxavailable(dev))
{
uart_recvchars(dev);
}
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
@ -1410,6 +1830,11 @@ unsigned int kinetis_lpuart_serialinit(unsigned int first)
#ifdef HAVE_LPUART_CONSOLE
uart_register("/dev/console", &CONSOLE_DEV);
# ifdef SERIAL_HAVE_CONSOLE_DMA
/* If we need to re-initialise the console to enable DMA do that here. */
kinetis_dma_setup(&CONSOLE_DEV);
# endif
#endif
#if !defined(CONFIG_KINETIS_MERGE_TTY)
/* Register all LPUARTs as LPn devices */
@ -1453,6 +1878,68 @@ unsigned int kinetis_lpuart_serialinit(unsigned int first)
return first;
}
/****************************************************************************
* Name: kinetis_serial_dma_poll
*
* Description:
* Checks receive DMA buffers for received bytes that have not accumulated
* to the point where the DMA half/full interrupt has triggered.
*
* This function should be called from a timer or other periodic context.
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
void kinetis_lpserial_dma_poll(void)
{
irqstate_t flags;
flags = enter_critical_section();
#ifdef CONFIG_KINETIS_LPUART0_RXDMA
if (g_lpuart0priv.rxdma != NULL)
{
kinetis_dma_rxcallback(g_lpuart0priv.rxdma, (void *)&g_lpuart0port,
false, 0);
}
#endif
#ifdef CONFIG_KINETIS_LPUART1_RXDMA
if (g_lpuart1priv.rxdma != NULL)
{
kinetis_dma_rxcallback(g_lpuart1priv.rxdma, (void *)&g_lpuart1port,
false, 0);
}
#endif
#ifdef CONFIG_KINETIS_LPUART2_RXDMA
if (g_lpuart2priv.rxdma != NULL)
{
kinetis_dma_rxcallback(g_lpuart2priv.rxdma, (void *)&g_lpuart2port,
false, 0);
}
#endif
#ifdef CONFIG_KINETIS_LPUART3_RXDMA
if (g_lpuart3priv.rxdma != NULL)
{
kinetis_dma_rxcallback(g_lpuart3priv.rxdma, (void *)&g_lpuart3port,
false, 0);
}
#endif
#ifdef CONFIG_KINETIS_LPUART4_RXDMA
if (g_lpuart4priv.rxdma != NULL)
{
kinetis_dma_rxcallback(g_lpuart4priv.rxdma, (void *)&g_lpuart4port,
false, 0);
}
#endif
leave_critical_section(flags);
}
#endif
/****************************************************************************
* Name: up_putc
*

View File

@ -0,0 +1,101 @@
/****************************************************************************
* arch/arm/src/kinetis/kinetis_lpuart.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_ARM_SRC_KINETIS_KINETIS_LPUART_H
#define __ARCH_ARM_SRC_KINETIS_KINETIS_LPUART_H
#if defined(HAVE_UART_DEVICE) && defined(USE_SERIALDRIVER)
/****************************************************************************
* Included Files
****************************************************************************/
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Is DMA available on any (enabled) LPUART? */
#undef LPSERIAL_HAVE_DMA
#if defined(CONFIG_KINETIS_LPUART0_RXDMA) || defined(CONFIG_KINETIS_LPUART1_RXDMA) || \
defined(CONFIG_KINETIS_LPUART2_RXDMA) || defined(CONFIG_KINETIS_LPUART3_RXDMA) || \
defined(CONFIG_KINETIS_LPUART4_RXDMA)
# define LPSERIAL_HAVE_DMA 1
/* Is DMA available on All (enabled) LPUART? */
#define LPSERIAL_HAVE_ALL_DMA 1
# if (defined(CONFIG_KINETIS_LPUART0) && !defined(CONFIG_KINETIS_LPUART0_RXDMA)) || \
(defined(CONFIG_KINETIS_LPUART1) && !defined(CONFIG_KINETIS_LPUART1_RXDMA)) || \
(defined(CONFIG_KINETIS_LPUART2) && !defined(CONFIG_KINETIS_LPUART2_RXDMA)) || \
(defined(CONFIG_KINETIS_LPUART3) && !defined(CONFIG_KINETIS_LPUART3_RXDMA)) || \
(defined(CONFIG_KINETIS_LPUART4) && !defined(CONFIG_KINETIS_LPUART4_RXDMA))
# undef LPSERIAL_HAVE_ALL_DMA
# endif
#endif
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
#ifndef __ASSEMBLY__
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Public Functions Prototypes
****************************************************************************/
/****************************************************************************
* Name: kinetis_serial_dma_poll
*
* Description:
* Must be called periodically if any Kinetis LPUART is configured for DMA.
* The DMA callback is triggered for each fifo size/2 bytes, but this can
* result in some bytes being transferred but not collected if the incoming
* data is not a whole multiple of half the FIFO size.
*
* May be safely called from either interrupt or thread context.
*
****************************************************************************/
#ifdef LPSERIAL_HAVE_DMA
void kinetis_lpserial_dma_poll(void);
#endif
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* HAVE_UART_DEVICE && USE_SERIALDRIVER) */
#endif /* __ARCH_ARM_SRC_KINETIS_KINETIS_UART_H */