This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 9a6a13f110ae4d117e237011872d635b1df7b320 Author: raiden00pl <[email protected]> AuthorDate: Tue Oct 20 14:20:42 2026 +0200 arch/arm/nrf52,nrf53,nrf91: drive the UART with UARTE EasyDMA TX was sent polled with the interrupts disabled, which masked them for milliseconds per console write. Use UARTE with interrupt-driven one-byte DMA transfers as nrf54l does. nRF52832/nRF52833 baud codes change to the UARTE values from the SVD. Assisted-by: Claude Code Signed-off-by: raiden00pl <[email protected]> --- arch/arm/src/nrf52/hardware/nrf52_uarte.h | 78 ++---- arch/arm/src/nrf52/nrf52_lowputc.c | 65 +++-- arch/arm/src/nrf52/nrf52_serial.c | 441 ++++++++++++++++++++++-------- arch/arm/src/nrf53/hardware/nrf53_uarte.h | 2 + arch/arm/src/nrf53/nrf53_lowputc.c | 65 +++-- arch/arm/src/nrf53/nrf53_serial.c | 441 ++++++++++++++++++++++-------- arch/arm/src/nrf91/hardware/nrf91_uarte.h | 2 + arch/arm/src/nrf91/nrf91_lowputc.c | 65 +++-- arch/arm/src/nrf91/nrf91_serial.c | 441 ++++++++++++++++++++++-------- 9 files changed, 1140 insertions(+), 460 deletions(-) diff --git a/arch/arm/src/nrf52/hardware/nrf52_uarte.h b/arch/arm/src/nrf52/hardware/nrf52_uarte.h index 02fbab9230c..fd429a79ec6 100644 --- a/arch/arm/src/nrf52/hardware/nrf52_uarte.h +++ b/arch/arm/src/nrf52/hardware/nrf52_uarte.h @@ -277,6 +277,8 @@ #define NRF52_UART_ENABLE_DISABLE (0) #define NRF52_UART_ENABLE_ENABLE (4) +#define NRF52_UARTE_ENABLE_DISABLE (0) +#define NRF52_UARTE_ENABLE_ENABLE (8) /* INTEN/INTENSET/INTENCLR Register */ @@ -294,64 +296,24 @@ /* BAUDRATE Register */ -#if defined(CONFIG_ARCH_CHIP_NRF52832) -# define UART_BAUDRATE_1200 (0x0004f000) -# define UART_BAUDRATE_2400 (0x0009d000) -# define UART_BAUDRATE_4800 (0x0013b000) -# define UART_BAUDRATE_9600 (0x00275000) -# define UART_BAUDRATE_14400 (0x003b0000) -# define UART_BAUDRATE_19200 (0x004ea000) -# define UART_BAUDRATE_28800 (0x0075f000) -# define UART_BAUDRATE_38400 (0x009d5000) -# define UART_BAUDRATE_57600 (0x00ebf000) -# define UART_BAUDRATE_76000 (0x013a9000) -# define UART_BAUDRATE_115200 (0x01d7e000) -# define UART_BAUDRATE_230400 (0x03afb000) -# define UART_BAUDRATE_250000 (0x04000000) -# define UART_BAUDRATE_460800 (0x075f7000) -# define UART_BAUDRATE_921600 (0x0ebed000) -# define UART_BAUDRATE_1000000 (0x10000000) -#elif defined(CONFIG_ARCH_CHIP_NRF52833) -# define UART_BAUDRATE_1200 (0x0004f000) -# define UART_BAUDRATE_2400 (0x0009d000) -# define UART_BAUDRATE_4800 (0x0013b000) -# define UART_BAUDRATE_9600 (0x00275000) -# define UART_BAUDRATE_14400 (0x003b0000) -# define UART_BAUDRATE_19200 (0x004ea000) -# define UART_BAUDRATE_28800 (0x0075f000) -# define UART_BAUDRATE_31250 (0x00800000) -# define UART_BAUDRATE_38400 (0x009d5000) -# define UART_BAUDRATE_56000 (0x00e50000) -# define UART_BAUDRATE_57600 (0x00ebf000) -# define UART_BAUDRATE_76000 (0x013a9000) -# define UART_BAUDRATE_115200 (0x01d7e000) -# define UART_BAUDRATE_230400 (0x03afb000) -# define UART_BAUDRATE_250000 (0x04000000) -# define UART_BAUDRATE_460800 (0x075f7000) -# define UART_BAUDRATE_921600 (0x0ebed000) -# define UART_BAUDRATE_1000000 (0x10000000) -#elif defined(CONFIG_ARCH_CHIP_NRF52840) -# define UART_BAUDRATE_1200 (0x0004f000) -# define UART_BAUDRATE_2400 (0x0009d000) -# define UART_BAUDRATE_4800 (0x0013b000) -# define UART_BAUDRATE_9600 (0x00275000) -# define UART_BAUDRATE_14400 (0x003af000) -# define UART_BAUDRATE_19200 (0x004ea000) -# define UART_BAUDRATE_28800 (0x0075c000) -# define UART_BAUDRATE_31250 (0x00800000) -# define UART_BAUDRATE_38400 (0x009d0000) -# define UART_BAUDRATE_56000 (0x00e50000) -# define UART_BAUDRATE_57600 (0x00eb0000) -# define UART_BAUDRATE_76000 (0x013a9000) -# define UART_BAUDRATE_115200 (0x01d60000) -# define UART_BAUDRATE_230400 (0x03b00000) -# define UART_BAUDRATE_250000 (0x04000000) -# define UART_BAUDRATE_460800 (0x07400000) -# define UART_BAUDRATE_921600 (0x0f000000) -# define UART_BAUDRATE_1000000 (0x10000000) -#else -# error Unknown NRF52 chip ! -#endif +#define UART_BAUDRATE_1200 (0x0004f000) +#define UART_BAUDRATE_2400 (0x0009d000) +#define UART_BAUDRATE_4800 (0x0013b000) +#define UART_BAUDRATE_9600 (0x00275000) +#define UART_BAUDRATE_14400 (0x003af000) +#define UART_BAUDRATE_19200 (0x004ea000) +#define UART_BAUDRATE_28800 (0x0075c000) +#define UART_BAUDRATE_31250 (0x00800000) +#define UART_BAUDRATE_38400 (0x009d0000) +#define UART_BAUDRATE_56000 (0x00e50000) +#define UART_BAUDRATE_57600 (0x00eb0000) +#define UART_BAUDRATE_76000 (0x013a9000) +#define UART_BAUDRATE_115200 (0x01d60000) +#define UART_BAUDRATE_230400 (0x03b00000) +#define UART_BAUDRATE_250000 (0x04000000) +#define UART_BAUDRATE_460800 (0x07400000) +#define UART_BAUDRATE_921600 (0x0f000000) +#define UART_BAUDRATE_1000000 (0x10000000) /* CONFIG Register */ diff --git a/arch/arm/src/nrf52/nrf52_lowputc.c b/arch/arm/src/nrf52/nrf52_lowputc.c index 4a1e866f78c..2ee16bd91d1 100644 --- a/arch/arm/src/nrf52/nrf52_lowputc.c +++ b/arch/arm/src/nrf52/nrf52_lowputc.c @@ -79,6 +79,10 @@ static const struct uart_config_s g_console_config = .txpin = CONSOLE_TX_PIN, .rxpin = CONSOLE_RX_PIN, }; + +/* Console byte transmitted by EasyDMA */ + +static uint8_t g_console_tx; #endif /* HAVE_UART_CONSOLE */ /**************************************************************************** @@ -219,7 +223,7 @@ static void nrf52_setbaud(uintptr_t base, const struct uart_config_s *config) } } - putreg32(br, base + NRF52_UART_BAUDRATE_OFFSET); + putreg32(br, base + NRF52_UARTE_BAUDRATE_OFFSET); } /**************************************************************************** @@ -231,7 +235,7 @@ static void nrf52_setparity(uintptr_t base, { uint32_t regval = 0; - regval = getreg32(base + NRF52_UART_CONFIG_OFFSET); + regval = getreg32(base + NRF52_UARTE_CONFIG_OFFSET); if (config->parity == 2) { @@ -246,7 +250,7 @@ static void nrf52_setparity(uintptr_t base, regval &= ~UART_CONFIG_PARITY; } - putreg32(regval, base + NRF52_UART_CONFIG_OFFSET); + putreg32(regval, base + NRF52_UARTE_CONFIG_OFFSET); } /**************************************************************************** @@ -259,7 +263,7 @@ static void nrf52_setstops(uintptr_t base, { uint32_t regval = 0; - regval = getreg32(base + NRF52_UART_CONFIG_OFFSET); + regval = getreg32(base + NRF52_UARTE_CONFIG_OFFSET); if (config->stopbits2 == true) { @@ -270,7 +274,7 @@ static void nrf52_setstops(uintptr_t base, regval &= ~UART_CONFIG_STOP; } - putreg32(regval, base + NRF52_UART_CONFIG_OFFSET); + putreg32(regval, base + NRF52_UARTE_CONFIG_OFFSET); } #endif @@ -328,9 +332,10 @@ void nrf52_usart_configure(uintptr_t base, uint32_t port = 0; uint32_t regval = 0; - putreg32(1, base + NRF52_UART_TASKS_STOPRX_OFFSET); - putreg32(1, base + NRF52_UART_TASKS_STOPTX_OFFSET); - putreg32(NRF52_UART_ENABLE_DISABLE, base + NRF52_UART_ENABLE_OFFSET); + putreg32(0xffffffff, base + NRF52_UARTE_INTENCLR_OFFSET); + putreg32(1, base + NRF52_UARTE_TASKS_STOPRX_OFFSET); + putreg32(1, base + NRF52_UARTE_TASKS_STOPTX_OFFSET); + putreg32(NRF52_UARTE_ENABLE_DISABLE, base + NRF52_UARTE_ENABLE_OFFSET); /* Set UART format */ @@ -348,7 +353,7 @@ void nrf52_usart_configure(uintptr_t base, regval = (pin << UART_PSELTXD_PIN_SHIFT); regval |= (port << UART_PSELTXD_PORT_SHIFT); - putreg32(regval, base + NRF52_UART_PSELTXD_OFFSET); + putreg32(regval, base + NRF52_UARTE_PSEL_TXD_OFFSET); /* Setect RX pins for UART */ @@ -357,11 +362,14 @@ void nrf52_usart_configure(uintptr_t base, regval = (pin << UART_PSELRXD_PIN_SHIFT); regval |= (port << UART_PSELRXD_PORT_SHIFT); - putreg32(regval, base + NRF52_UART_PSELRXD_OFFSET); + putreg32(regval, base + NRF52_UARTE_PSEL_RXD_OFFSET); + putreg32(UART_PSELCTS_RESET, base + NRF52_UARTE_PSEL_CTS_OFFSET); + putreg32(UART_PSELRTS_RESET, base + NRF52_UARTE_PSEL_RTS_OFFSET); + putreg32(0, base + NRF52_UARTE_SHORTS_OFFSET); /* Enable UART */ - putreg32(NRF52_UART_ENABLE_ENABLE, base + NRF52_UART_ENABLE_OFFSET); + putreg32(NRF52_UARTE_ENABLE_ENABLE, base + NRF52_UARTE_ENABLE_OFFSET); } /**************************************************************************** @@ -379,12 +387,12 @@ void nrf52_usart_disable(uintptr_t base, const struct uart_config_s *config) /* Disable the UART */ - putreg32(1, base + NRF52_UART_TASKS_STOPRX_OFFSET); - putreg32(1, base + NRF52_UART_TASKS_STOPTX_OFFSET); - putreg32(NRF52_UART_ENABLE_DISABLE, base + NRF52_UART_ENABLE_OFFSET); + putreg32(1, base + NRF52_UARTE_TASKS_STOPRX_OFFSET); + putreg32(1, base + NRF52_UARTE_TASKS_STOPTX_OFFSET); + putreg32(NRF52_UARTE_ENABLE_DISABLE, base + NRF52_UARTE_ENABLE_OFFSET); - putreg32(0xffffffff, base + NRF52_UART_PSELTXD_OFFSET); - putreg32(0xffffffff, base + NRF52_UART_PSELRXD_OFFSET); + putreg32(0xffffffff, base + NRF52_UARTE_PSEL_TXD_OFFSET); + putreg32(0xffffffff, base + NRF52_UARTE_PSEL_RXD_OFFSET); /* Unconfigure GPIO */ @@ -393,8 +401,8 @@ void nrf52_usart_disable(uintptr_t base, const struct uart_config_s *config) /* Deatach TWI from GPIO */ - putreg32(UART_PSELTXD_RESET, base + NRF52_UART_PSELTXD_OFFSET); - putreg32(UART_PSELRXD_RESET, base + NRF52_UART_PSELRXD_OFFSET); + putreg32(UART_PSELTXD_RESET, base + NRF52_UARTE_PSEL_TXD_OFFSET); + putreg32(UART_PSELRXD_RESET, base + NRF52_UARTE_PSEL_RXD_OFFSET); } /**************************************************************************** @@ -439,13 +447,24 @@ void nrf52_usart_setformat(uintptr_t base, void arm_lowputc(char ch) { #ifdef HAVE_UART_CONSOLE - putreg32(1, CONSOLE_BASE + NRF52_UART_TASKS_STARTTX_OFFSET); - putreg32(0, CONSOLE_BASE + NRF52_UART_EVENTS_TXDRDY_OFFSET); - putreg32(ch, CONSOLE_BASE + NRF52_UART_TXD_OFFSET); - while (getreg32(CONSOLE_BASE + NRF52_UART_EVENTS_TXDRDY_OFFSET) == 0) + g_console_tx = ch; + + putreg32(0, CONSOLE_BASE + NRF52_UARTE_EVENTS_ENDTX_OFFSET); + putreg32(0, CONSOLE_BASE + NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET); + putreg32((uintptr_t)&g_console_tx, + CONSOLE_BASE + NRF52_UARTE_TXD_PTR_OFFSET); + putreg32(1, CONSOLE_BASE + NRF52_UARTE_TXD_MAXCNT_OFFSET); + + UP_DMB(); + + putreg32(1, CONSOLE_BASE + NRF52_UARTE_TASKS_STARTTX_OFFSET); + while (getreg32(CONSOLE_BASE + NRF52_UARTE_EVENTS_ENDTX_OFFSET) == 0) { } - putreg32(1, CONSOLE_BASE + NRF52_UART_TASKS_STOPTX_OFFSET); + putreg32(1, CONSOLE_BASE + NRF52_UARTE_TASKS_STOPTX_OFFSET); + while (getreg32(CONSOLE_BASE + NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET) == 0) + { + } #endif } diff --git a/arch/arm/src/nrf52/nrf52_serial.c b/arch/arm/src/nrf52/nrf52_serial.c index 047df233e3a..25da3063aa9 100644 --- a/arch/arm/src/nrf52/nrf52_serial.c +++ b/arch/arm/src/nrf52/nrf52_serial.c @@ -93,6 +93,9 @@ # define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ #endif +#define RX_INTERRUPTS (UART_INT_ENDRX | UART_INT_ERROR | UART_INT_RXTO) +#define TX_INTERRUPTS (UART_INT_ENDTX | UART_INT_TXSTOPPED) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -101,9 +104,17 @@ struct nrf52_dev_s { - uintptr_t uartbase; /* Base address of UART registers */ - uint8_t irq; /* IRQ associated with this UART */ - bool rx_available; /* rx byte available */ + uintptr_t uartbase; /* Base address of UART registers */ + uint8_t irq; /* IRQ associated with this UART */ + uint8_t rxdma; /* Receive DMA buffer */ + uint8_t txdma; /* Transmit DMA buffer */ + uint8_t rxchar; /* Completed receive byte */ + uint32_t rxstatus; /* Receive error status */ + bool rxvalid; /* Receive byte available */ + bool rxenabled; /* Upper-half reception enabled */ + bool rxactive; /* Receiver started */ + bool txenabled; /* Upper-half transmission enabled */ + volatile bool txbusy; /* DMA transmitter owns txdma */ /* UART configuration */ @@ -169,7 +180,6 @@ static struct nrf52_dev_s g_uart0priv = { .uartbase = NRF52_UART0_BASE, .irq = NRF52_IRQ_UART0, - .rx_available = false, .config = { .baud = CONFIG_UART0_BAUD, @@ -211,7 +221,6 @@ static struct nrf52_dev_s g_uart1priv = { .uartbase = NRF52_UART1_BASE, .irq = NRF52_IRQ_UART1, - .rx_available = false, .config = { .baud = CONFIG_UART1_BAUD, @@ -262,6 +271,192 @@ static struct uart_dev_s * const g_uart_devs[NRF52_NUART] = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: nrf52_uart_putreg + * + * Description: + * Write a UART register. + * + ****************************************************************************/ + +static inline void nrf52_uart_putreg(struct nrf52_dev_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->uartbase + offset); +} + +/**************************************************************************** + * Name: nrf52_uart_getreg + * + * Description: + * Read a UART register. + * + ****************************************************************************/ + +static inline uint32_t nrf52_uart_getreg(struct nrf52_dev_s *priv, + uint32_t offset) +{ + return getreg32(priv->uartbase + offset); +} + +/**************************************************************************** + * Name: nrf52_uart_clrevent + * + * Description: + * Clear a UARTE event and read it back before returning. + * + ****************************************************************************/ + +static void nrf52_uart_clrevent(struct nrf52_dev_s *priv, uint32_t event) +{ + nrf52_uart_putreg(priv, event, 0); + (void)nrf52_uart_getreg(priv, event); +} + +/**************************************************************************** + * Name: nrf52_rxstart + * + * Description: + * Start reception and queue the first one-byte DMA buffer. + * + ****************************************************************************/ + +static void nrf52_rxstart(struct nrf52_dev_s *priv) +{ + priv->rxactive = true; + priv->rxvalid = false; + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ENDRX_OFFSET); + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_RXTO_OFFSET); + nrf52_uart_putreg(priv, NRF52_UARTE_RXD_PTR_OFFSET, + (uintptr_t)&priv->rxdma); + nrf52_uart_putreg(priv, NRF52_UARTE_RXD_MAXCNT_OFFSET, 1); + UP_DMB(); + nrf52_uart_putreg(priv, NRF52_UARTE_TASKS_STARTRX_OFFSET, 1); +} + +/**************************************************************************** + * Name: nrf52_rxdeliver + * + * Description: + * Deliver the completed byte and restart the one-byte DMA. A byte + * received with the delivery disabled is kept, the UART FIFO holds the + * next bytes until rxint() enables the delivery. + * + ****************************************************************************/ + +static void nrf52_rxdeliver(struct uart_dev_s *dev) +{ + struct nrf52_dev_s *priv = dev->priv; + + if (priv->rxvalid && priv->rxenabled) + { + uart_recvchars(dev); + if (!priv->rxvalid) + { + UP_DMB(); + nrf52_uart_putreg(priv, NRF52_UARTE_TASKS_STARTRX_OFFSET, 1); + } + } +} + +/**************************************************************************** + * Name: nrf52_rxstop + * + * Description: + * Stop reception and deliver a completed byte. Interrupts must be + * disabled. + * + ****************************************************************************/ + +static void nrf52_rxstop(struct uart_dev_s *dev) +{ + struct nrf52_dev_s *priv = dev->priv; + + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_RXTO_OFFSET); + nrf52_uart_putreg(priv, NRF52_UARTE_TASKS_STOPRX_OFFSET, 1); + while (!nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_RXTO_OFFSET)) + { + } + + if (nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_ERROR_OFFSET)) + { + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ERROR_OFFSET); + priv->rxstatus |= + nrf52_uart_getreg(priv, NRF52_UARTE_ERRORSRC_OFFSET); + nrf52_uart_putreg(priv, NRF52_UARTE_ERRORSRC_OFFSET, priv->rxstatus); + } + + /* STOP also generates ENDRX for an empty buffer. Deliver a completed + * byte, but never deliver an empty buffer. + */ + + if (nrf52_uart_getreg(priv, NRF52_UARTE_RXD_AMOUNT_OFFSET) != 0) + { + UP_DMB(); + priv->rxchar = priv->rxdma; + priv->rxvalid = true; + if (priv->rxenabled) + { + uart_recvchars(dev); + } + + priv->rxvalid = false; + } + + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ENDRX_OFFSET); +} + +/**************************************************************************** + * Name: nrf52_txstart + * + * Description: + * Start a one-byte DMA transfer. The caller must serialize access. + * + ****************************************************************************/ + +static void nrf52_txstart(struct nrf52_dev_s *priv, int ch) +{ + priv->txdma = ch; + priv->txbusy = true; + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ENDTX_OFFSET); + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET); + nrf52_uart_putreg(priv, NRF52_UARTE_TXD_PTR_OFFSET, + (uintptr_t)&priv->txdma); + nrf52_uart_putreg(priv, NRF52_UARTE_TXD_MAXCNT_OFFSET, 1); + UP_DMB(); + nrf52_uart_putreg(priv, NRF52_UARTE_TASKS_STARTTX_OFFSET, 1); +} + +/**************************************************************************** + * Name: nrf52_txstop + * + * Description: + * Wait for the transmitter to stop. Interrupts must be disabled. + * + ****************************************************************************/ + +static void nrf52_txstop(struct nrf52_dev_s *priv) +{ + if (priv->txbusy) + { + while (!nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_ENDTX_OFFSET) && + !nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + } + + if (!nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + nrf52_uart_putreg(priv, NRF52_UARTE_TASKS_STOPTX_OFFSET, 1); + while (!nrf52_uart_getreg(priv, + NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + } + } + + priv->txbusy = false; + } +} + /**************************************************************************** * Name: nrf52_setup * @@ -318,15 +513,10 @@ static void nrf52_shutdown(struct uart_dev_s *dev) * Name: nrf52_attach * * Description: - * Configure the UART to operation in interrupt driven mode. This method - * is called when the serial port is opened. Normally, this is just after - * the setup() method is called, however, the serial console may - * operate in a non-interrupt driven mode during the boot phase. - * - * RX and TX interrupts are not enabled when by the attach method (unless - * the hardware supports multiple levels of interrupt enabling). - * The RX and TX interrupts are not enabled until the txint() and rxint() - * methods are called. + * Configure the UART to operate in interrupt driven mode. This method + * is called when the serial port is opened, normally just after setup. + * DMA completion interrupts release buffers even when delivery to the + * upper half is disabled by the rxint() and txint() methods. * ****************************************************************************/ @@ -335,14 +525,17 @@ static int nrf52_attach(struct uart_dev_s *dev) struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; int ret; - /* Attach and enable the IRQ(s). The interrupts are (probably) still - * disabled in the C2 register. - */ + /* Attach and enable the IRQ */ ret = irq_attach(priv->irq, nrf52_interrupt, dev); if (ret == OK) { +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + nrf52_uart_putreg(priv, NRF52_UARTE_INTENSET_OFFSET, + RX_INTERRUPTS | TX_INTERRUPTS); up_enable_irq(priv->irq); + nrf52_rxstart(priv); +#endif } return ret; @@ -352,7 +545,7 @@ static int nrf52_attach(struct uart_dev_s *dev) * Name: nrf52_detach * * Description: - * Detach UART interrupts. This method is called when the serial port is + * Detach UART interrupts. This method is called when the serial port is * closed normally just before the shutdown method is called. * The exception is the serial console which is never shutdown. * @@ -361,14 +554,23 @@ static int nrf52_attach(struct uart_dev_s *dev) static void nrf52_detach(struct uart_dev_s *dev) { struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; + irqstate_t flags; - /* Disable interrupts */ + /* Stop DMA before detaching the interrupt handler */ - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF52_UART_INTENCLR_OFFSET); - up_disable_irq(priv->irq); + flags = enter_critical_section(); + nrf52_uart_putreg(priv, NRF52_UARTE_INTENCLR_OFFSET, UINT32_MAX); + + if (priv->rxactive) + { + nrf52_rxstop(dev); + priv->rxactive = false; + } - /* Detach from the interrupt(s) */ + nrf52_txstop(priv); + leave_critical_section(flags); + up_disable_irq(priv->irq); irq_detach(priv->irq); } @@ -376,37 +578,56 @@ static void nrf52_detach(struct uart_dev_s *dev) * Name: nrf52_interrupt * * Description: - * This is the UART interrupt handler. It will be invoked when an - * interrupt is received on the 'irq'. It should call uart_xmitchars or - * uart_recvchars to perform the appropriate data transfers. The - * interrupt handling logic must be able to map the 'arg' to the - * appropriate uart_dev_s structure in order to call these functions. + * Handle receive buffers and transfer completion. * ****************************************************************************/ static int nrf52_interrupt(int irq, void *context, void *arg) { - struct uart_dev_s *dev = (struct uart_dev_s *)arg; - struct nrf52_dev_s *priv; - uint32_t regval; - - DEBUGASSERT(dev != NULL && dev->priv != NULL); - priv = (struct nrf52_dev_s *)dev->priv; + struct uart_dev_s *dev = arg; + struct nrf52_dev_s *priv = dev->priv; - /* Check RX event */ + if (nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_ERROR_OFFSET)) + { + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ERROR_OFFSET); + priv->rxstatus |= + nrf52_uart_getreg(priv, NRF52_UARTE_ERRORSRC_OFFSET); + nrf52_uart_putreg(priv, NRF52_UARTE_ERRORSRC_OFFSET, priv->rxstatus); + } - regval = getreg32(priv->uartbase + NRF52_UART_EVENTS_RXDRDY_OFFSET); + /* Copy the completed byte before restarting DMA. Automatic ENDRX-to- + * STARTRX would let DMA overwrite a buffer before the upper half + * consumes it. The UART FIFO holds incoming bytes between transfers. + */ - if (regval != 0) + if (nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_ENDRX_OFFSET)) { - putreg32(0, priv->uartbase + NRF52_UART_EVENTS_RXDRDY_OFFSET); - priv->rx_available = true; - uart_recvchars(dev); + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ENDRX_OFFSET); + UP_DMB(); + priv->rxchar = priv->rxdma; + priv->rxvalid = true; } - /* Clear errors */ + nrf52_rxdeliver(dev); + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_RXTO_OFFSET); - putreg32(0, priv->uartbase + NRF52_UART_ERRORSRC_OFFSET); + /* There is no ENDTX-to-STOPTX shortcut */ + + if (nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_ENDTX_OFFSET)) + { + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_ENDTX_OFFSET); + nrf52_uart_putreg(priv, NRF52_UARTE_TASKS_STOPTX_OFFSET, 1); + } + + if (nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + nrf52_uart_clrevent(priv, NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET); + priv->txbusy = false; + if (priv->txenabled) + { + uart_xmitchars(dev); + } + } return OK; } @@ -415,7 +636,7 @@ static int nrf52_interrupt(int irq, void *context, void *arg) * Name: nrf52_set_format * * Description: - * Set the serial line format and speed. + * Set the serial line format and speed with DMA stopped. * ****************************************************************************/ @@ -423,8 +644,29 @@ static int nrf52_interrupt(int irq, void *context, void *arg) void nrf52_set_format(struct uart_dev_s *dev) { struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; + irqstate_t flags; + + flags = enter_critical_section(); + + if (priv->rxactive) + { + nrf52_rxstop(dev); + } + + nrf52_txstop(priv); + nrf52_uart_putreg(priv, NRF52_UARTE_ENABLE_OFFSET, + NRF52_UARTE_ENABLE_DISABLE); nrf52_usart_setformat(priv->uartbase, &priv->config); + nrf52_uart_putreg(priv, NRF52_UARTE_ENABLE_OFFSET, + NRF52_UARTE_ENABLE_ENABLE); + + if (priv->rxactive) + { + nrf52_rxstart(priv); + } + + leave_critical_section(flags); } #endif @@ -563,61 +805,48 @@ static int nrf52_ioctl(struct file *filep, int cmd, unsigned long arg) static int nrf52_receive(struct uart_dev_s *dev, unsigned int *status) { struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; - uint32_t data; - - /* Get input data along with receiver control information */ - - data = getreg32(priv->uartbase + NRF52_UART_RXD_OFFSET); - priv->rx_available = false; - - /* Return receiver control information */ if (status) { - *status = 0x00; + *status = priv->rxstatus; } - /* Then return the actual received data. */ - - return data; + priv->rxstatus = 0; + priv->rxvalid = false; + return priv->rxchar; } /**************************************************************************** * Name: nrf52_rxint * * Description: - * Call to enable or disable RX interrupts + * Enable or disable delivery of received characters to the upper half. * ****************************************************************************/ static void nrf52_rxint(struct uart_dev_s *dev, bool enable) { struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); - if (enable) - { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - /* Receive an interrupt when their is anything in the Rx data register - * (or an Rx timeout occurs). - */ - - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF52_UART_INTENSET_OFFSET); - putreg32(1, priv->uartbase + NRF52_UART_TASKS_STARTRX_OFFSET); - +#ifdef CONFIG_SUPPRESS_SERIAL_INTS + enable = false; #endif - } - else + + priv->rxenabled = enable; + if (enable) { - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF52_UART_INTENCLR_OFFSET); - putreg32(1, priv->uartbase + NRF52_UART_TASKS_STOPRX_OFFSET); + nrf52_rxdeliver(dev); } + + leave_critical_section(flags); } /**************************************************************************** * Name: nrf52_rxavailable * * Description: - * Return true if the receive register is not empty + * Report whether a received character is waiting for the upper half. * ****************************************************************************/ @@ -625,82 +854,71 @@ static bool nrf52_rxavailable(struct uart_dev_s *dev) { struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; - /* Return true if the receive buffer/fifo is not "empty." */ - - return priv->rx_available; + return priv->rxvalid; } /**************************************************************************** * Name: nrf52_send * * Description: - * This method will send one byte on the UART. + * Start transmitting the character supplied by the upper half. * ****************************************************************************/ static void nrf52_send(struct uart_dev_s *dev, int ch) { struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); - putreg32(0, priv->uartbase + NRF52_UART_EVENTS_TXDRDY_OFFSET); - putreg32(1, priv->uartbase + NRF52_UART_TASKS_STARTTX_OFFSET); - - putreg32(ch, priv->uartbase + NRF52_UART_TXD_OFFSET); - while (getreg32(priv->uartbase + NRF52_UART_EVENTS_TXDRDY_OFFSET) == 0) - { - } - - putreg32(1, priv->uartbase + NRF52_UART_TASKS_STOPTX_OFFSET); + nrf52_txstart(priv, ch); + leave_critical_section(flags); } /**************************************************************************** * Name: nrf52_txint * * Description: - * Call to enable or disable TX interrupts + * Enable or disable transmission from the upper-half transmit buffer. * ****************************************************************************/ static void nrf52_txint(struct uart_dev_s *dev, bool enable) { - /* struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; */ + struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); +#ifdef CONFIG_SUPPRESS_SERIAL_INTS + enable = false; +#endif + + priv->txenabled = enable; if (enable) { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - irqstate_t flags; - - /* Enable the TX interrupt */ - - flags = enter_critical_section(); - - /* Fake a TX interrupt here by just calling uart_xmitchars() with - * interrupts disabled (note this may recurse). - */ - uart_xmitchars(dev); - leave_critical_section(flags); -#endif - } - else - { - /* Disable the TX interrupt */ } + + leave_critical_section(flags); } /**************************************************************************** * Name: nrf52_txready * * Description: - * Return true if the tranmsit data register is empty + * Report whether the transmitter can accept another character. The + * events are read, the upper half also polls with interrupts disabled. + * A transfer ended without STOPTX can be restarted. * ****************************************************************************/ static bool nrf52_txready(struct uart_dev_s *dev) { - /* struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; */ + struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; - /* Return true if the transmit FIFO is "not full." */ + if (priv->txbusy) + { + return nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_ENDTX_OFFSET) || + nrf52_uart_getreg(priv, NRF52_UARTE_EVENTS_TXSTOPPED_OFFSET); + } return true; } @@ -715,11 +933,7 @@ static bool nrf52_txready(struct uart_dev_s *dev) static bool nrf52_txempty(struct uart_dev_s *dev) { - /* struct nrf52_dev_s *priv = (struct nrf52_dev_s *)dev->priv; */ - - /* Return true if the transmit FIFO is "empty." */ - - return true; + return nrf52_txready(dev); } /**************************************************************************** @@ -817,9 +1031,14 @@ void arm_serialinit(void) void up_putc(int ch) { #ifdef HAVE_UART_CONSOLE - /* struct nrf52_dev_s *priv = (struct nrf52_dev_s *)CONSOLE_DEV.priv; */ + struct nrf52_dev_s *priv = CONSOLE_DEV.priv; + irqstate_t flags = enter_critical_section(); + + /* Serialize polled debug output with the interrupt-driven transmitter */ + nrf52_txstop(priv); arm_lowputc(ch); + leave_critical_section(flags); #endif } diff --git a/arch/arm/src/nrf53/hardware/nrf53_uarte.h b/arch/arm/src/nrf53/hardware/nrf53_uarte.h index 3df7fecb459..9d76f039d9a 100644 --- a/arch/arm/src/nrf53/hardware/nrf53_uarte.h +++ b/arch/arm/src/nrf53/hardware/nrf53_uarte.h @@ -262,6 +262,8 @@ #define NRF53_UART_ENABLE_DISABLE (0) #define NRF53_UART_ENABLE_ENABLE (4) +#define NRF53_UARTE_ENABLE_DISABLE (0) +#define NRF53_UARTE_ENABLE_ENABLE (8) /* INTEN/INTENSET/INTENCLR Register */ diff --git a/arch/arm/src/nrf53/nrf53_lowputc.c b/arch/arm/src/nrf53/nrf53_lowputc.c index dfcf39c6f03..2c6a8ef1441 100644 --- a/arch/arm/src/nrf53/nrf53_lowputc.c +++ b/arch/arm/src/nrf53/nrf53_lowputc.c @@ -79,6 +79,10 @@ static const struct uart_config_s g_console_config = .txpin = CONSOLE_TX_PIN, .rxpin = CONSOLE_RX_PIN, }; + +/* Console byte transmitted by EasyDMA */ + +static uint8_t g_console_tx; #endif /* HAVE_UART_CONSOLE */ /**************************************************************************** @@ -219,7 +223,7 @@ static void nrf53_setbaud(uintptr_t base, const struct uart_config_s *config) } } - putreg32(br, base + NRF53_UART_BAUDRATE_OFFSET); + putreg32(br, base + NRF53_UARTE_BAUDRATE_OFFSET); } /**************************************************************************** @@ -231,7 +235,7 @@ static void nrf53_setparity(uintptr_t base, { uint32_t regval = 0; - regval = getreg32(base + NRF53_UART_CONFIG_OFFSET); + regval = getreg32(base + NRF53_UARTE_CONFIG_OFFSET); if (config->parity == 2) { @@ -246,7 +250,7 @@ static void nrf53_setparity(uintptr_t base, regval &= ~UART_CONFIG_PARITY; } - putreg32(regval, base + NRF53_UART_CONFIG_OFFSET); + putreg32(regval, base + NRF53_UARTE_CONFIG_OFFSET); } /**************************************************************************** @@ -259,7 +263,7 @@ static void nrf53_setstops(uintptr_t base, { uint32_t regval = 0; - regval = getreg32(base + NRF53_UART_CONFIG_OFFSET); + regval = getreg32(base + NRF53_UARTE_CONFIG_OFFSET); if (config->stopbits2 == true) { @@ -270,7 +274,7 @@ static void nrf53_setstops(uintptr_t base, regval &= ~UART_CONFIG_STOP; } - putreg32(regval, base + NRF53_UART_CONFIG_OFFSET); + putreg32(regval, base + NRF53_UARTE_CONFIG_OFFSET); } #endif @@ -328,9 +332,10 @@ void nrf53_usart_configure(uintptr_t base, uint32_t port = 0; uint32_t regval = 0; - putreg32(1, base + NRF53_UART_TASKS_STOPRX_OFFSET); - putreg32(1, base + NRF53_UART_TASKS_STOPTX_OFFSET); - putreg32(NRF53_UART_ENABLE_DISABLE, base + NRF53_UART_ENABLE_OFFSET); + putreg32(0xffffffff, base + NRF53_UARTE_INTENCLR_OFFSET); + putreg32(1, base + NRF53_UARTE_TASKS_STOPRX_OFFSET); + putreg32(1, base + NRF53_UARTE_TASKS_STOPTX_OFFSET); + putreg32(NRF53_UARTE_ENABLE_DISABLE, base + NRF53_UARTE_ENABLE_OFFSET); /* Set UART format */ @@ -348,7 +353,7 @@ void nrf53_usart_configure(uintptr_t base, regval = (pin << UART_PSELTXD_PIN_SHIFT); regval |= (port << UART_PSELTXD_PORT_SHIFT); - putreg32(regval, base + NRF53_UART_PSELTXD_OFFSET); + putreg32(regval, base + NRF53_UARTE_PSEL_TXD_OFFSET); /* Setect RX pins for UART */ @@ -357,11 +362,14 @@ void nrf53_usart_configure(uintptr_t base, regval = (pin << UART_PSELRXD_PIN_SHIFT); regval |= (port << UART_PSELRXD_PORT_SHIFT); - putreg32(regval, base + NRF53_UART_PSELRXD_OFFSET); + putreg32(regval, base + NRF53_UARTE_PSEL_RXD_OFFSET); + putreg32(UART_PSELCTS_RESET, base + NRF53_UARTE_PSEL_CTS_OFFSET); + putreg32(UART_PSELRTS_RESET, base + NRF53_UARTE_PSEL_RTS_OFFSET); + putreg32(0, base + NRF53_UARTE_SHORTS_OFFSET); /* Enable UART */ - putreg32(NRF53_UART_ENABLE_ENABLE, base + NRF53_UART_ENABLE_OFFSET); + putreg32(NRF53_UARTE_ENABLE_ENABLE, base + NRF53_UARTE_ENABLE_OFFSET); } /**************************************************************************** @@ -379,12 +387,12 @@ void nrf53_usart_disable(uintptr_t base, const struct uart_config_s *config) /* Disable the UART */ - putreg32(1, base + NRF53_UART_TASKS_STOPRX_OFFSET); - putreg32(1, base + NRF53_UART_TASKS_STOPTX_OFFSET); - putreg32(NRF53_UART_ENABLE_DISABLE, base + NRF53_UART_ENABLE_OFFSET); + putreg32(1, base + NRF53_UARTE_TASKS_STOPRX_OFFSET); + putreg32(1, base + NRF53_UARTE_TASKS_STOPTX_OFFSET); + putreg32(NRF53_UARTE_ENABLE_DISABLE, base + NRF53_UARTE_ENABLE_OFFSET); - putreg32(0xffffffff, base + NRF53_UART_PSELTXD_OFFSET); - putreg32(0xffffffff, base + NRF53_UART_PSELRXD_OFFSET); + putreg32(0xffffffff, base + NRF53_UARTE_PSEL_TXD_OFFSET); + putreg32(0xffffffff, base + NRF53_UARTE_PSEL_RXD_OFFSET); /* Unconfigure GPIO */ @@ -393,8 +401,8 @@ void nrf53_usart_disable(uintptr_t base, const struct uart_config_s *config) /* Deatach TWI from GPIO */ - putreg32(UART_PSELTXD_RESET, base + NRF53_UART_PSELTXD_OFFSET); - putreg32(UART_PSELRXD_RESET, base + NRF53_UART_PSELRXD_OFFSET); + putreg32(UART_PSELTXD_RESET, base + NRF53_UARTE_PSEL_TXD_OFFSET); + putreg32(UART_PSELRXD_RESET, base + NRF53_UARTE_PSEL_RXD_OFFSET); } /**************************************************************************** @@ -439,13 +447,24 @@ void nrf53_usart_setformat(uintptr_t base, void arm_lowputc(char ch) { #ifdef HAVE_UART_CONSOLE - putreg32(1, CONSOLE_BASE + NRF53_UART_TASKS_STARTTX_OFFSET); - putreg32(0, CONSOLE_BASE + NRF53_UART_EVENTS_TXDRDY_OFFSET); - putreg32(ch, CONSOLE_BASE + NRF53_UART_TXD_OFFSET); - while (getreg32(CONSOLE_BASE + NRF53_UART_EVENTS_TXDRDY_OFFSET) == 0) + g_console_tx = ch; + + putreg32(0, CONSOLE_BASE + NRF53_UARTE_EVENTS_ENDTX_OFFSET); + putreg32(0, CONSOLE_BASE + NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET); + putreg32((uintptr_t)&g_console_tx, + CONSOLE_BASE + NRF53_UARTE_TXD_PTR_OFFSET); + putreg32(1, CONSOLE_BASE + NRF53_UARTE_TXD_MAXCNT_OFFSET); + + UP_DMB(); + + putreg32(1, CONSOLE_BASE + NRF53_UARTE_TASKS_STARTTX_OFFSET); + while (getreg32(CONSOLE_BASE + NRF53_UARTE_EVENTS_ENDTX_OFFSET) == 0) { } - putreg32(1, CONSOLE_BASE + NRF53_UART_TASKS_STOPTX_OFFSET); + putreg32(1, CONSOLE_BASE + NRF53_UARTE_TASKS_STOPTX_OFFSET); + while (getreg32(CONSOLE_BASE + NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET) == 0) + { + } #endif } diff --git a/arch/arm/src/nrf53/nrf53_serial.c b/arch/arm/src/nrf53/nrf53_serial.c index c5924a8cb45..05f247a8410 100644 --- a/arch/arm/src/nrf53/nrf53_serial.c +++ b/arch/arm/src/nrf53/nrf53_serial.c @@ -93,6 +93,9 @@ # define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ #endif +#define RX_INTERRUPTS (UART_INT_ENDRX | UART_INT_ERROR | UART_INT_RXTO) +#define TX_INTERRUPTS (UART_INT_ENDTX | UART_INT_TXSTOPPED) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -101,9 +104,17 @@ struct nrf53_dev_s { - uintptr_t uartbase; /* Base address of UART registers */ - uint8_t irq; /* IRQ associated with this UART */ - bool rx_available; /* rx byte available */ + uintptr_t uartbase; /* Base address of UART registers */ + uint8_t irq; /* IRQ associated with this UART */ + uint8_t rxdma; /* Receive DMA buffer */ + uint8_t txdma; /* Transmit DMA buffer */ + uint8_t rxchar; /* Completed receive byte */ + uint32_t rxstatus; /* Receive error status */ + bool rxvalid; /* Receive byte available */ + bool rxenabled; /* Upper-half reception enabled */ + bool rxactive; /* Receiver started */ + bool txenabled; /* Upper-half transmission enabled */ + volatile bool txbusy; /* DMA transmitter owns txdma */ /* UART configuration */ @@ -169,7 +180,6 @@ static struct nrf53_dev_s g_uart0priv = { .uartbase = NRF53_UART0_BASE, .irq = NRF53_IRQ_SERIAL0, - .rx_available = false, .config = { .baud = CONFIG_UART0_BAUD, @@ -211,7 +221,6 @@ static struct nrf53_dev_s g_uart1priv = { .uartbase = NRF53_UART1_BASE, .irq = NRF53_IRQ_SERIAL1, - .rx_available = false, .config = { .baud = CONFIG_UART1_BAUD, @@ -262,6 +271,192 @@ static struct uart_dev_s * const g_uart_devs[NRF53_NUART] = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: nrf53_uart_putreg + * + * Description: + * Write a UART register. + * + ****************************************************************************/ + +static inline void nrf53_uart_putreg(struct nrf53_dev_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->uartbase + offset); +} + +/**************************************************************************** + * Name: nrf53_uart_getreg + * + * Description: + * Read a UART register. + * + ****************************************************************************/ + +static inline uint32_t nrf53_uart_getreg(struct nrf53_dev_s *priv, + uint32_t offset) +{ + return getreg32(priv->uartbase + offset); +} + +/**************************************************************************** + * Name: nrf53_uart_clrevent + * + * Description: + * Clear a UARTE event and read it back before returning. + * + ****************************************************************************/ + +static void nrf53_uart_clrevent(struct nrf53_dev_s *priv, uint32_t event) +{ + nrf53_uart_putreg(priv, event, 0); + (void)nrf53_uart_getreg(priv, event); +} + +/**************************************************************************** + * Name: nrf53_rxstart + * + * Description: + * Start reception and queue the first one-byte DMA buffer. + * + ****************************************************************************/ + +static void nrf53_rxstart(struct nrf53_dev_s *priv) +{ + priv->rxactive = true; + priv->rxvalid = false; + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ENDRX_OFFSET); + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_RXTO_OFFSET); + nrf53_uart_putreg(priv, NRF53_UARTE_RXD_PTR_OFFSET, + (uintptr_t)&priv->rxdma); + nrf53_uart_putreg(priv, NRF53_UARTE_RXD_MAXCNT_OFFSET, 1); + UP_DMB(); + nrf53_uart_putreg(priv, NRF53_UARTE_TASKS_STARTRX_OFFSET, 1); +} + +/**************************************************************************** + * Name: nrf53_rxdeliver + * + * Description: + * Deliver the completed byte and restart the one-byte DMA. A byte + * received with the delivery disabled is kept, the UART FIFO holds the + * next bytes until rxint() enables the delivery. + * + ****************************************************************************/ + +static void nrf53_rxdeliver(struct uart_dev_s *dev) +{ + struct nrf53_dev_s *priv = dev->priv; + + if (priv->rxvalid && priv->rxenabled) + { + uart_recvchars(dev); + if (!priv->rxvalid) + { + UP_DMB(); + nrf53_uart_putreg(priv, NRF53_UARTE_TASKS_STARTRX_OFFSET, 1); + } + } +} + +/**************************************************************************** + * Name: nrf53_rxstop + * + * Description: + * Stop reception and deliver a completed byte. Interrupts must be + * disabled. + * + ****************************************************************************/ + +static void nrf53_rxstop(struct uart_dev_s *dev) +{ + struct nrf53_dev_s *priv = dev->priv; + + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_RXTO_OFFSET); + nrf53_uart_putreg(priv, NRF53_UARTE_TASKS_STOPRX_OFFSET, 1); + while (!nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_RXTO_OFFSET)) + { + } + + if (nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_ERROR_OFFSET)) + { + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ERROR_OFFSET); + priv->rxstatus |= + nrf53_uart_getreg(priv, NRF53_UARTE_ERRORSRC_OFFSET); + nrf53_uart_putreg(priv, NRF53_UARTE_ERRORSRC_OFFSET, priv->rxstatus); + } + + /* STOP also generates ENDRX for an empty buffer. Deliver a completed + * byte, but never deliver an empty buffer. + */ + + if (nrf53_uart_getreg(priv, NRF53_UARTE_RXD_AMOUNT_OFFSET) != 0) + { + UP_DMB(); + priv->rxchar = priv->rxdma; + priv->rxvalid = true; + if (priv->rxenabled) + { + uart_recvchars(dev); + } + + priv->rxvalid = false; + } + + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ENDRX_OFFSET); +} + +/**************************************************************************** + * Name: nrf53_txstart + * + * Description: + * Start a one-byte DMA transfer. The caller must serialize access. + * + ****************************************************************************/ + +static void nrf53_txstart(struct nrf53_dev_s *priv, int ch) +{ + priv->txdma = ch; + priv->txbusy = true; + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ENDTX_OFFSET); + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET); + nrf53_uart_putreg(priv, NRF53_UARTE_TXD_PTR_OFFSET, + (uintptr_t)&priv->txdma); + nrf53_uart_putreg(priv, NRF53_UARTE_TXD_MAXCNT_OFFSET, 1); + UP_DMB(); + nrf53_uart_putreg(priv, NRF53_UARTE_TASKS_STARTTX_OFFSET, 1); +} + +/**************************************************************************** + * Name: nrf53_txstop + * + * Description: + * Wait for the transmitter to stop. Interrupts must be disabled. + * + ****************************************************************************/ + +static void nrf53_txstop(struct nrf53_dev_s *priv) +{ + if (priv->txbusy) + { + while (!nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_ENDTX_OFFSET) && + !nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + } + + if (!nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + nrf53_uart_putreg(priv, NRF53_UARTE_TASKS_STOPTX_OFFSET, 1); + while (!nrf53_uart_getreg(priv, + NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + } + } + + priv->txbusy = false; + } +} + /**************************************************************************** * Name: nrf53_setup * @@ -318,15 +513,10 @@ static void nrf53_shutdown(struct uart_dev_s *dev) * Name: nrf53_attach * * Description: - * Configure the UART to operation in interrupt driven mode. This method - * is called when the serial port is opened. Normally, this is just after - * the setup() method is called, however, the serial console may - * operate in a non-interrupt driven mode during the boot phase. - * - * RX and TX interrupts are not enabled when by the attach method (unless - * the hardware supports multiple levels of interrupt enabling). - * The RX and TX interrupts are not enabled until the txint() and rxint() - * methods are called. + * Configure the UART to operate in interrupt driven mode. This method + * is called when the serial port is opened, normally just after setup. + * DMA completion interrupts release buffers even when delivery to the + * upper half is disabled by the rxint() and txint() methods. * ****************************************************************************/ @@ -335,14 +525,17 @@ static int nrf53_attach(struct uart_dev_s *dev) struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; int ret; - /* Attach and enable the IRQ(s). The interrupts are (probably) still - * disabled in the C2 register. - */ + /* Attach and enable the IRQ */ ret = irq_attach(priv->irq, nrf53_interrupt, dev); if (ret == OK) { +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + nrf53_uart_putreg(priv, NRF53_UARTE_INTENSET_OFFSET, + RX_INTERRUPTS | TX_INTERRUPTS); up_enable_irq(priv->irq); + nrf53_rxstart(priv); +#endif } return ret; @@ -352,7 +545,7 @@ static int nrf53_attach(struct uart_dev_s *dev) * Name: nrf53_detach * * Description: - * Detach UART interrupts. This method is called when the serial port is + * Detach UART interrupts. This method is called when the serial port is * closed normally just before the shutdown method is called. * The exception is the serial console which is never shutdown. * @@ -361,14 +554,23 @@ static int nrf53_attach(struct uart_dev_s *dev) static void nrf53_detach(struct uart_dev_s *dev) { struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; + irqstate_t flags; - /* Disable interrupts */ + /* Stop DMA before detaching the interrupt handler */ - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF53_UART_INTENCLR_OFFSET); - up_disable_irq(priv->irq); + flags = enter_critical_section(); + nrf53_uart_putreg(priv, NRF53_UARTE_INTENCLR_OFFSET, UINT32_MAX); + + if (priv->rxactive) + { + nrf53_rxstop(dev); + priv->rxactive = false; + } - /* Detach from the interrupt(s) */ + nrf53_txstop(priv); + leave_critical_section(flags); + up_disable_irq(priv->irq); irq_detach(priv->irq); } @@ -376,37 +578,56 @@ static void nrf53_detach(struct uart_dev_s *dev) * Name: nrf53_interrupt * * Description: - * This is the UART interrupt handler. It will be invoked when an - * interrupt is received on the 'irq'. It should call uart_xmitchars or - * uart_recvchars to perform the appropriate data transfers. The - * interrupt handling logic must be able to map the 'arg' to the - * appropriate uart_dev_s structure in order to call these functions. + * Handle receive buffers and transfer completion. * ****************************************************************************/ static int nrf53_interrupt(int irq, void *context, void *arg) { - struct uart_dev_s *dev = (struct uart_dev_s *)arg; - struct nrf53_dev_s *priv; - uint32_t regval; - - DEBUGASSERT(dev != NULL && dev->priv != NULL); - priv = (struct nrf53_dev_s *)dev->priv; + struct uart_dev_s *dev = arg; + struct nrf53_dev_s *priv = dev->priv; - /* Check RX event */ + if (nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_ERROR_OFFSET)) + { + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ERROR_OFFSET); + priv->rxstatus |= + nrf53_uart_getreg(priv, NRF53_UARTE_ERRORSRC_OFFSET); + nrf53_uart_putreg(priv, NRF53_UARTE_ERRORSRC_OFFSET, priv->rxstatus); + } - regval = getreg32(priv->uartbase + NRF53_UART_EVENTS_RXDRDY_OFFSET); + /* Copy the completed byte before restarting DMA. Automatic ENDRX-to- + * STARTRX would let DMA overwrite a buffer before the upper half + * consumes it. The UART FIFO holds incoming bytes between transfers. + */ - if (regval != 0) + if (nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_ENDRX_OFFSET)) { - putreg32(0, priv->uartbase + NRF53_UART_EVENTS_RXDRDY_OFFSET); - priv->rx_available = true; - uart_recvchars(dev); + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ENDRX_OFFSET); + UP_DMB(); + priv->rxchar = priv->rxdma; + priv->rxvalid = true; } - /* Clear errors */ + nrf53_rxdeliver(dev); + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_RXTO_OFFSET); - putreg32(0, priv->uartbase + NRF53_UART_ERRORSRC_OFFSET); + /* There is no ENDTX-to-STOPTX shortcut */ + + if (nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_ENDTX_OFFSET)) + { + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_ENDTX_OFFSET); + nrf53_uart_putreg(priv, NRF53_UARTE_TASKS_STOPTX_OFFSET, 1); + } + + if (nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + nrf53_uart_clrevent(priv, NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET); + priv->txbusy = false; + if (priv->txenabled) + { + uart_xmitchars(dev); + } + } return OK; } @@ -415,7 +636,7 @@ static int nrf53_interrupt(int irq, void *context, void *arg) * Name: nrf53_set_format * * Description: - * Set the serial line format and speed. + * Set the serial line format and speed with DMA stopped. * ****************************************************************************/ @@ -423,8 +644,29 @@ static int nrf53_interrupt(int irq, void *context, void *arg) void nrf53_set_format(struct uart_dev_s *dev) { struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; + irqstate_t flags; + + flags = enter_critical_section(); + + if (priv->rxactive) + { + nrf53_rxstop(dev); + } + + nrf53_txstop(priv); + nrf53_uart_putreg(priv, NRF53_UARTE_ENABLE_OFFSET, + NRF53_UARTE_ENABLE_DISABLE); nrf53_usart_setformat(priv->uartbase, &priv->config); + nrf53_uart_putreg(priv, NRF53_UARTE_ENABLE_OFFSET, + NRF53_UARTE_ENABLE_ENABLE); + + if (priv->rxactive) + { + nrf53_rxstart(priv); + } + + leave_critical_section(flags); } #endif @@ -563,61 +805,48 @@ static int nrf53_ioctl(struct file *filep, int cmd, unsigned long arg) static int nrf53_receive(struct uart_dev_s *dev, unsigned int *status) { struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; - uint32_t data; - - /* Get input data along with receiver control information */ - - data = getreg32(priv->uartbase + NRF53_UART_RXD_OFFSET); - priv->rx_available = false; - - /* Return receiver control information */ if (status) { - *status = 0x00; + *status = priv->rxstatus; } - /* Then return the actual received data. */ - - return data; + priv->rxstatus = 0; + priv->rxvalid = false; + return priv->rxchar; } /**************************************************************************** * Name: nrf53_rxint * * Description: - * Call to enable or disable RX interrupts + * Enable or disable delivery of received characters to the upper half. * ****************************************************************************/ static void nrf53_rxint(struct uart_dev_s *dev, bool enable) { struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); - if (enable) - { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - /* Receive an interrupt when their is anything in the Rx data register - * (or an Rx timeout occurs). - */ - - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF53_UART_INTENSET_OFFSET); - putreg32(1, priv->uartbase + NRF53_UART_TASKS_STARTRX_OFFSET); - +#ifdef CONFIG_SUPPRESS_SERIAL_INTS + enable = false; #endif - } - else + + priv->rxenabled = enable; + if (enable) { - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF53_UART_INTENCLR_OFFSET); - putreg32(1, priv->uartbase + NRF53_UART_TASKS_STOPRX_OFFSET); + nrf53_rxdeliver(dev); } + + leave_critical_section(flags); } /**************************************************************************** * Name: nrf53_rxavailable * * Description: - * Return true if the receive register is not empty + * Report whether a received character is waiting for the upper half. * ****************************************************************************/ @@ -625,82 +854,71 @@ static bool nrf53_rxavailable(struct uart_dev_s *dev) { struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; - /* Return true if the receive buffer/fifo is not "empty." */ - - return priv->rx_available; + return priv->rxvalid; } /**************************************************************************** * Name: nrf53_send * * Description: - * This method will send one byte on the UART. + * Start transmitting the character supplied by the upper half. * ****************************************************************************/ static void nrf53_send(struct uart_dev_s *dev, int ch) { struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); - putreg32(0, priv->uartbase + NRF53_UART_EVENTS_TXDRDY_OFFSET); - putreg32(1, priv->uartbase + NRF53_UART_TASKS_STARTTX_OFFSET); - - putreg32(ch, priv->uartbase + NRF53_UART_TXD_OFFSET); - while (getreg32(priv->uartbase + NRF53_UART_EVENTS_TXDRDY_OFFSET) == 0) - { - } - - putreg32(1, priv->uartbase + NRF53_UART_TASKS_STOPTX_OFFSET); + nrf53_txstart(priv, ch); + leave_critical_section(flags); } /**************************************************************************** * Name: nrf53_txint * * Description: - * Call to enable or disable TX interrupts + * Enable or disable transmission from the upper-half transmit buffer. * ****************************************************************************/ static void nrf53_txint(struct uart_dev_s *dev, bool enable) { - /* struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; */ + struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); +#ifdef CONFIG_SUPPRESS_SERIAL_INTS + enable = false; +#endif + + priv->txenabled = enable; if (enable) { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - irqstate_t flags; - - /* Enable the TX interrupt */ - - flags = enter_critical_section(); - - /* Fake a TX interrupt here by just calling uart_xmitchars() with - * interrupts disabled (note this may recurse). - */ - uart_xmitchars(dev); - leave_critical_section(flags); -#endif - } - else - { - /* Disable the TX interrupt */ } + + leave_critical_section(flags); } /**************************************************************************** * Name: nrf53_txready * * Description: - * Return true if the tranmsit data register is empty + * Report whether the transmitter can accept another character. The + * events are read, the upper half also polls with interrupts disabled. + * A transfer ended without STOPTX can be restarted. * ****************************************************************************/ static bool nrf53_txready(struct uart_dev_s *dev) { - /* struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; */ + struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; - /* Return true if the transmit FIFO is "not full." */ + if (priv->txbusy) + { + return nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_ENDTX_OFFSET) || + nrf53_uart_getreg(priv, NRF53_UARTE_EVENTS_TXSTOPPED_OFFSET); + } return true; } @@ -715,11 +933,7 @@ static bool nrf53_txready(struct uart_dev_s *dev) static bool nrf53_txempty(struct uart_dev_s *dev) { - /* struct nrf53_dev_s *priv = (struct nrf53_dev_s *)dev->priv; */ - - /* Return true if the transmit FIFO is "empty." */ - - return true; + return nrf53_txready(dev); } /**************************************************************************** @@ -817,9 +1031,14 @@ void arm_serialinit(void) void up_putc(int ch) { #ifdef HAVE_UART_CONSOLE - /* struct nrf53_dev_s *priv = (struct nrf53_dev_s *)CONSOLE_DEV.priv; */ + struct nrf53_dev_s *priv = CONSOLE_DEV.priv; + irqstate_t flags = enter_critical_section(); + + /* Serialize polled debug output with the interrupt-driven transmitter */ + nrf53_txstop(priv); arm_lowputc(ch); + leave_critical_section(flags); #endif } diff --git a/arch/arm/src/nrf91/hardware/nrf91_uarte.h b/arch/arm/src/nrf91/hardware/nrf91_uarte.h index 5a1e79e091b..4246d6dbf05 100644 --- a/arch/arm/src/nrf91/hardware/nrf91_uarte.h +++ b/arch/arm/src/nrf91/hardware/nrf91_uarte.h @@ -262,6 +262,8 @@ #define NRF91_UART_ENABLE_DISABLE (0) #define NRF91_UART_ENABLE_ENABLE (8) +#define NRF91_UARTE_ENABLE_DISABLE (0) +#define NRF91_UARTE_ENABLE_ENABLE (8) /* INTEN/INTENSET/INTENCLR Register */ diff --git a/arch/arm/src/nrf91/nrf91_lowputc.c b/arch/arm/src/nrf91/nrf91_lowputc.c index f73b19823ad..70aa0a7b9e4 100644 --- a/arch/arm/src/nrf91/nrf91_lowputc.c +++ b/arch/arm/src/nrf91/nrf91_lowputc.c @@ -79,6 +79,10 @@ static const struct uart_config_s g_console_config = .txpin = CONSOLE_TX_PIN, .rxpin = CONSOLE_RX_PIN, }; + +/* Console byte transmitted by EasyDMA */ + +static uint8_t g_console_tx; #endif /* HAVE_UART_CONSOLE */ /**************************************************************************** @@ -219,7 +223,7 @@ static void nrf91_setbaud(uintptr_t base, const struct uart_config_s *config) } } - putreg32(br, base + NRF91_UART_BAUDRATE_OFFSET); + putreg32(br, base + NRF91_UARTE_BAUDRATE_OFFSET); } /**************************************************************************** @@ -231,7 +235,7 @@ static void nrf91_setparity(uintptr_t base, { uint32_t regval = 0; - regval = getreg32(base + NRF91_UART_CONFIG_OFFSET); + regval = getreg32(base + NRF91_UARTE_CONFIG_OFFSET); if (config->parity == 2) { @@ -246,7 +250,7 @@ static void nrf91_setparity(uintptr_t base, regval &= ~UART_CONFIG_PARITY; } - putreg32(regval, base + NRF91_UART_CONFIG_OFFSET); + putreg32(regval, base + NRF91_UARTE_CONFIG_OFFSET); } /**************************************************************************** @@ -259,7 +263,7 @@ static void nrf91_setstops(uintptr_t base, { uint32_t regval = 0; - regval = getreg32(base + NRF91_UART_CONFIG_OFFSET); + regval = getreg32(base + NRF91_UARTE_CONFIG_OFFSET); if (config->stopbits2 == true) { @@ -270,7 +274,7 @@ static void nrf91_setstops(uintptr_t base, regval &= ~UART_CONFIG_STOP; } - putreg32(regval, base + NRF91_UART_CONFIG_OFFSET); + putreg32(regval, base + NRF91_UARTE_CONFIG_OFFSET); } #endif @@ -328,9 +332,10 @@ void nrf91_usart_configure(uintptr_t base, uint32_t port = 0; uint32_t regval = 0; - putreg32(1, base + NRF91_UART_TASKS_STOPRX_OFFSET); - putreg32(1, base + NRF91_UART_TASKS_STOPTX_OFFSET); - putreg32(NRF91_UART_ENABLE_DISABLE, base + NRF91_UART_ENABLE_OFFSET); + putreg32(0xffffffff, base + NRF91_UARTE_INTENCLR_OFFSET); + putreg32(1, base + NRF91_UARTE_TASKS_STOPRX_OFFSET); + putreg32(1, base + NRF91_UARTE_TASKS_STOPTX_OFFSET); + putreg32(NRF91_UARTE_ENABLE_DISABLE, base + NRF91_UARTE_ENABLE_OFFSET); /* Set UART format */ @@ -348,7 +353,7 @@ void nrf91_usart_configure(uintptr_t base, regval = (pin << UART_PSELTXD_PIN_SHIFT); regval |= (port << UART_PSELTXD_PORT_SHIFT); - putreg32(regval, base + NRF91_UART_PSELTXD_OFFSET); + putreg32(regval, base + NRF91_UARTE_PSEL_TXD_OFFSET); /* Setect RX pins for UART */ @@ -357,11 +362,14 @@ void nrf91_usart_configure(uintptr_t base, regval = (pin << UART_PSELRXD_PIN_SHIFT); regval |= (port << UART_PSELRXD_PORT_SHIFT); - putreg32(regval, base + NRF91_UART_PSELRXD_OFFSET); + putreg32(regval, base + NRF91_UARTE_PSEL_RXD_OFFSET); + putreg32(UART_PSELCTS_RESET, base + NRF91_UARTE_PSEL_CTS_OFFSET); + putreg32(UART_PSELRTS_RESET, base + NRF91_UARTE_PSEL_RTS_OFFSET); + putreg32(0, base + NRF91_UARTE_SHORTS_OFFSET); /* Enable UART */ - putreg32(NRF91_UART_ENABLE_ENABLE, base + NRF91_UART_ENABLE_OFFSET); + putreg32(NRF91_UARTE_ENABLE_ENABLE, base + NRF91_UARTE_ENABLE_OFFSET); } /**************************************************************************** @@ -379,12 +387,12 @@ void nrf91_usart_disable(uintptr_t base, const struct uart_config_s *config) /* Disable the UART */ - putreg32(1, base + NRF91_UART_TASKS_STOPRX_OFFSET); - putreg32(1, base + NRF91_UART_TASKS_STOPTX_OFFSET); - putreg32(NRF91_UART_ENABLE_DISABLE, base + NRF91_UART_ENABLE_OFFSET); + putreg32(1, base + NRF91_UARTE_TASKS_STOPRX_OFFSET); + putreg32(1, base + NRF91_UARTE_TASKS_STOPTX_OFFSET); + putreg32(NRF91_UARTE_ENABLE_DISABLE, base + NRF91_UARTE_ENABLE_OFFSET); - putreg32(0xffffffff, base + NRF91_UART_PSELTXD_OFFSET); - putreg32(0xffffffff, base + NRF91_UART_PSELRXD_OFFSET); + putreg32(0xffffffff, base + NRF91_UARTE_PSEL_TXD_OFFSET); + putreg32(0xffffffff, base + NRF91_UARTE_PSEL_RXD_OFFSET); /* Unconfigure GPIO */ @@ -393,8 +401,8 @@ void nrf91_usart_disable(uintptr_t base, const struct uart_config_s *config) /* Deatach TWI from GPIO */ - putreg32(UART_PSELTXD_RESET, base + NRF91_UART_PSELTXD_OFFSET); - putreg32(UART_PSELRXD_RESET, base + NRF91_UART_PSELRXD_OFFSET); + putreg32(UART_PSELTXD_RESET, base + NRF91_UARTE_PSEL_TXD_OFFSET); + putreg32(UART_PSELRXD_RESET, base + NRF91_UARTE_PSEL_RXD_OFFSET); } /**************************************************************************** @@ -439,13 +447,24 @@ void nrf91_usart_setformat(uintptr_t base, void arm_lowputc(char ch) { #ifdef HAVE_UART_CONSOLE - putreg32(1, CONSOLE_BASE + NRF91_UART_TASKS_STARTTX_OFFSET); - putreg32(0, CONSOLE_BASE + NRF91_UART_EVENTS_TXDRDY_OFFSET); - putreg32(ch, CONSOLE_BASE + NRF91_UART_TXD_OFFSET); - while (getreg32(CONSOLE_BASE + NRF91_UART_EVENTS_TXDRDY_OFFSET) == 0) + g_console_tx = ch; + + putreg32(0, CONSOLE_BASE + NRF91_UARTE_EVENTS_ENDTX_OFFSET); + putreg32(0, CONSOLE_BASE + NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET); + putreg32((uintptr_t)&g_console_tx, + CONSOLE_BASE + NRF91_UARTE_TXD_PTR_OFFSET); + putreg32(1, CONSOLE_BASE + NRF91_UARTE_TXD_MAXCNT_OFFSET); + + UP_DMB(); + + putreg32(1, CONSOLE_BASE + NRF91_UARTE_TASKS_STARTTX_OFFSET); + while (getreg32(CONSOLE_BASE + NRF91_UARTE_EVENTS_ENDTX_OFFSET) == 0) { } - putreg32(1, CONSOLE_BASE + NRF91_UART_TASKS_STOPTX_OFFSET); + putreg32(1, CONSOLE_BASE + NRF91_UARTE_TASKS_STOPTX_OFFSET); + while (getreg32(CONSOLE_BASE + NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET) == 0) + { + } #endif } diff --git a/arch/arm/src/nrf91/nrf91_serial.c b/arch/arm/src/nrf91/nrf91_serial.c index 13fb2beba10..f99891f3309 100644 --- a/arch/arm/src/nrf91/nrf91_serial.c +++ b/arch/arm/src/nrf91/nrf91_serial.c @@ -93,6 +93,9 @@ # define TTYS0_DEV g_uart1port /* UART1 is ttyS0 */ #endif +#define RX_INTERRUPTS (UART_INT_ENDRX | UART_INT_ERROR | UART_INT_RXTO) +#define TX_INTERRUPTS (UART_INT_ENDTX | UART_INT_TXSTOPPED) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -101,9 +104,17 @@ struct nrf91_dev_s { - uintptr_t uartbase; /* Base address of UART registers */ - uint8_t irq; /* IRQ associated with this UART */ - bool rx_available; /* rx byte available */ + uintptr_t uartbase; /* Base address of UART registers */ + uint8_t irq; /* IRQ associated with this UART */ + uint8_t rxdma; /* Receive DMA buffer */ + uint8_t txdma; /* Transmit DMA buffer */ + uint8_t rxchar; /* Completed receive byte */ + uint32_t rxstatus; /* Receive error status */ + bool rxvalid; /* Receive byte available */ + bool rxenabled; /* Upper-half reception enabled */ + bool rxactive; /* Receiver started */ + bool txenabled; /* Upper-half transmission enabled */ + volatile bool txbusy; /* DMA transmitter owns txdma */ /* UART configuration */ @@ -169,7 +180,6 @@ static struct nrf91_dev_s g_uart0priv = { .uartbase = NRF91_UART0_BASE, .irq = NRF91_IRQ_SERIAL0, - .rx_available = false, .config = { .baud = CONFIG_UART0_BAUD, @@ -211,7 +221,6 @@ static struct nrf91_dev_s g_uart1priv = { .uartbase = NRF91_UART1_BASE, .irq = NRF91_IRQ_SERIAL1, - .rx_available = false, .config = { .baud = CONFIG_UART1_BAUD, @@ -262,6 +271,192 @@ static struct uart_dev_s * const g_uart_devs[NRF91_NUART] = * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: nrf91_uart_putreg + * + * Description: + * Write a UART register. + * + ****************************************************************************/ + +static inline void nrf91_uart_putreg(struct nrf91_dev_s *priv, + uint32_t offset, uint32_t value) +{ + putreg32(value, priv->uartbase + offset); +} + +/**************************************************************************** + * Name: nrf91_uart_getreg + * + * Description: + * Read a UART register. + * + ****************************************************************************/ + +static inline uint32_t nrf91_uart_getreg(struct nrf91_dev_s *priv, + uint32_t offset) +{ + return getreg32(priv->uartbase + offset); +} + +/**************************************************************************** + * Name: nrf91_uart_clrevent + * + * Description: + * Clear a UARTE event and read it back before returning. + * + ****************************************************************************/ + +static void nrf91_uart_clrevent(struct nrf91_dev_s *priv, uint32_t event) +{ + nrf91_uart_putreg(priv, event, 0); + (void)nrf91_uart_getreg(priv, event); +} + +/**************************************************************************** + * Name: nrf91_rxstart + * + * Description: + * Start reception and queue the first one-byte DMA buffer. + * + ****************************************************************************/ + +static void nrf91_rxstart(struct nrf91_dev_s *priv) +{ + priv->rxactive = true; + priv->rxvalid = false; + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ENDRX_OFFSET); + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_RXTO_OFFSET); + nrf91_uart_putreg(priv, NRF91_UARTE_RXD_PTR_OFFSET, + (uintptr_t)&priv->rxdma); + nrf91_uart_putreg(priv, NRF91_UARTE_RXD_MAXCNT_OFFSET, 1); + UP_DMB(); + nrf91_uart_putreg(priv, NRF91_UARTE_TASKS_STARTRX_OFFSET, 1); +} + +/**************************************************************************** + * Name: nrf91_rxdeliver + * + * Description: + * Deliver the completed byte and restart the one-byte DMA. A byte + * received with the delivery disabled is kept, the UART FIFO holds the + * next bytes until rxint() enables the delivery. + * + ****************************************************************************/ + +static void nrf91_rxdeliver(struct uart_dev_s *dev) +{ + struct nrf91_dev_s *priv = dev->priv; + + if (priv->rxvalid && priv->rxenabled) + { + uart_recvchars(dev); + if (!priv->rxvalid) + { + UP_DMB(); + nrf91_uart_putreg(priv, NRF91_UARTE_TASKS_STARTRX_OFFSET, 1); + } + } +} + +/**************************************************************************** + * Name: nrf91_rxstop + * + * Description: + * Stop reception and deliver a completed byte. Interrupts must be + * disabled. + * + ****************************************************************************/ + +static void nrf91_rxstop(struct uart_dev_s *dev) +{ + struct nrf91_dev_s *priv = dev->priv; + + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_RXTO_OFFSET); + nrf91_uart_putreg(priv, NRF91_UARTE_TASKS_STOPRX_OFFSET, 1); + while (!nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_RXTO_OFFSET)) + { + } + + if (nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_ERROR_OFFSET)) + { + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ERROR_OFFSET); + priv->rxstatus |= + nrf91_uart_getreg(priv, NRF91_UARTE_ERRORSRC_OFFSET); + nrf91_uart_putreg(priv, NRF91_UARTE_ERRORSRC_OFFSET, priv->rxstatus); + } + + /* STOP also generates ENDRX for an empty buffer. Deliver a completed + * byte, but never deliver an empty buffer. + */ + + if (nrf91_uart_getreg(priv, NRF91_UARTE_RXD_AMOUNT_OFFSET) != 0) + { + UP_DMB(); + priv->rxchar = priv->rxdma; + priv->rxvalid = true; + if (priv->rxenabled) + { + uart_recvchars(dev); + } + + priv->rxvalid = false; + } + + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ENDRX_OFFSET); +} + +/**************************************************************************** + * Name: nrf91_txstart + * + * Description: + * Start a one-byte DMA transfer. The caller must serialize access. + * + ****************************************************************************/ + +static void nrf91_txstart(struct nrf91_dev_s *priv, int ch) +{ + priv->txdma = ch; + priv->txbusy = true; + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ENDTX_OFFSET); + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET); + nrf91_uart_putreg(priv, NRF91_UARTE_TXD_PTR_OFFSET, + (uintptr_t)&priv->txdma); + nrf91_uart_putreg(priv, NRF91_UARTE_TXD_MAXCNT_OFFSET, 1); + UP_DMB(); + nrf91_uart_putreg(priv, NRF91_UARTE_TASKS_STARTTX_OFFSET, 1); +} + +/**************************************************************************** + * Name: nrf91_txstop + * + * Description: + * Wait for the transmitter to stop. Interrupts must be disabled. + * + ****************************************************************************/ + +static void nrf91_txstop(struct nrf91_dev_s *priv) +{ + if (priv->txbusy) + { + while (!nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_ENDTX_OFFSET) && + !nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + } + + if (!nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + nrf91_uart_putreg(priv, NRF91_UARTE_TASKS_STOPTX_OFFSET, 1); + while (!nrf91_uart_getreg(priv, + NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + } + } + + priv->txbusy = false; + } +} + /**************************************************************************** * Name: nrf91_setup * @@ -318,15 +513,10 @@ static void nrf91_shutdown(struct uart_dev_s *dev) * Name: nrf91_attach * * Description: - * Configure the UART to operation in interrupt driven mode. This method - * is called when the serial port is opened. Normally, this is just after - * the setup() method is called, however, the serial console may - * operate in a non-interrupt driven mode during the boot phase. - * - * RX and TX interrupts are not enabled when by the attach method (unless - * the hardware supports multiple levels of interrupt enabling). - * The RX and TX interrupts are not enabled until the txint() and rxint() - * methods are called. + * Configure the UART to operate in interrupt driven mode. This method + * is called when the serial port is opened, normally just after setup. + * DMA completion interrupts release buffers even when delivery to the + * upper half is disabled by the rxint() and txint() methods. * ****************************************************************************/ @@ -335,14 +525,17 @@ static int nrf91_attach(struct uart_dev_s *dev) struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; int ret; - /* Attach and enable the IRQ(s). The interrupts are (probably) still - * disabled in the C2 register. - */ + /* Attach and enable the IRQ */ ret = irq_attach(priv->irq, nrf91_interrupt, dev); if (ret == OK) { +#ifndef CONFIG_SUPPRESS_SERIAL_INTS + nrf91_uart_putreg(priv, NRF91_UARTE_INTENSET_OFFSET, + RX_INTERRUPTS | TX_INTERRUPTS); up_enable_irq(priv->irq); + nrf91_rxstart(priv); +#endif } return ret; @@ -352,7 +545,7 @@ static int nrf91_attach(struct uart_dev_s *dev) * Name: nrf91_detach * * Description: - * Detach UART interrupts. This method is called when the serial port is + * Detach UART interrupts. This method is called when the serial port is * closed normally just before the shutdown method is called. * The exception is the serial console which is never shutdown. * @@ -361,14 +554,23 @@ static int nrf91_attach(struct uart_dev_s *dev) static void nrf91_detach(struct uart_dev_s *dev) { struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; + irqstate_t flags; - /* Disable interrupts */ + /* Stop DMA before detaching the interrupt handler */ - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF91_UART_INTENCLR_OFFSET); - up_disable_irq(priv->irq); + flags = enter_critical_section(); + nrf91_uart_putreg(priv, NRF91_UARTE_INTENCLR_OFFSET, UINT32_MAX); + + if (priv->rxactive) + { + nrf91_rxstop(dev); + priv->rxactive = false; + } - /* Detach from the interrupt(s) */ + nrf91_txstop(priv); + leave_critical_section(flags); + up_disable_irq(priv->irq); irq_detach(priv->irq); } @@ -376,37 +578,56 @@ static void nrf91_detach(struct uart_dev_s *dev) * Name: nrf91_interrupt * * Description: - * This is the UART interrupt handler. It will be invoked when an - * interrupt is received on the 'irq'. It should call uart_xmitchars or - * uart_recvchars to perform the appropriate data transfers. The - * interrupt handling logic must be able to map the 'arg' to the - * appropriate uart_dev_s structure in order to call these functions. + * Handle receive buffers and transfer completion. * ****************************************************************************/ static int nrf91_interrupt(int irq, void *context, void *arg) { - struct uart_dev_s *dev = (struct uart_dev_s *)arg; - struct nrf91_dev_s *priv; - uint32_t regval; - - DEBUGASSERT(dev != NULL && dev->priv != NULL); - priv = (struct nrf91_dev_s *)dev->priv; + struct uart_dev_s *dev = arg; + struct nrf91_dev_s *priv = dev->priv; - /* Check RX event */ + if (nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_ERROR_OFFSET)) + { + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ERROR_OFFSET); + priv->rxstatus |= + nrf91_uart_getreg(priv, NRF91_UARTE_ERRORSRC_OFFSET); + nrf91_uart_putreg(priv, NRF91_UARTE_ERRORSRC_OFFSET, priv->rxstatus); + } - regval = getreg32(priv->uartbase + NRF91_UART_EVENTS_RXDRDY_OFFSET); + /* Copy the completed byte before restarting DMA. Automatic ENDRX-to- + * STARTRX would let DMA overwrite a buffer before the upper half + * consumes it. The UART FIFO holds incoming bytes between transfers. + */ - if (regval != 0) + if (nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_ENDRX_OFFSET)) { - putreg32(0, priv->uartbase + NRF91_UART_EVENTS_RXDRDY_OFFSET); - priv->rx_available = true; - uart_recvchars(dev); + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ENDRX_OFFSET); + UP_DMB(); + priv->rxchar = priv->rxdma; + priv->rxvalid = true; } - /* Clear errors */ + nrf91_rxdeliver(dev); + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_RXTO_OFFSET); - putreg32(0, priv->uartbase + NRF91_UART_ERRORSRC_OFFSET); + /* There is no ENDTX-to-STOPTX shortcut */ + + if (nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_ENDTX_OFFSET)) + { + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_ENDTX_OFFSET); + nrf91_uart_putreg(priv, NRF91_UARTE_TASKS_STOPTX_OFFSET, 1); + } + + if (nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET)) + { + nrf91_uart_clrevent(priv, NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET); + priv->txbusy = false; + if (priv->txenabled) + { + uart_xmitchars(dev); + } + } return OK; } @@ -415,7 +636,7 @@ static int nrf91_interrupt(int irq, void *context, void *arg) * Name: nrf91_set_format * * Description: - * Set the serial line format and speed. + * Set the serial line format and speed with DMA stopped. * ****************************************************************************/ @@ -423,8 +644,29 @@ static int nrf91_interrupt(int irq, void *context, void *arg) void nrf91_set_format(struct uart_dev_s *dev) { struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; + irqstate_t flags; + + flags = enter_critical_section(); + + if (priv->rxactive) + { + nrf91_rxstop(dev); + } + + nrf91_txstop(priv); + nrf91_uart_putreg(priv, NRF91_UARTE_ENABLE_OFFSET, + NRF91_UARTE_ENABLE_DISABLE); nrf91_usart_setformat(priv->uartbase, &priv->config); + nrf91_uart_putreg(priv, NRF91_UARTE_ENABLE_OFFSET, + NRF91_UARTE_ENABLE_ENABLE); + + if (priv->rxactive) + { + nrf91_rxstart(priv); + } + + leave_critical_section(flags); } #endif @@ -563,61 +805,48 @@ static int nrf91_ioctl(struct file *filep, int cmd, unsigned long arg) static int nrf91_receive(struct uart_dev_s *dev, unsigned int *status) { struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; - uint32_t data; - - /* Get input data along with receiver control information */ - - data = getreg32(priv->uartbase + NRF91_UART_RXD_OFFSET); - priv->rx_available = false; - - /* Return receiver control information */ if (status) { - *status = 0x00; + *status = priv->rxstatus; } - /* Then return the actual received data. */ - - return data; + priv->rxstatus = 0; + priv->rxvalid = false; + return priv->rxchar; } /**************************************************************************** * Name: nrf91_rxint * * Description: - * Call to enable or disable RX interrupts + * Enable or disable delivery of received characters to the upper half. * ****************************************************************************/ static void nrf91_rxint(struct uart_dev_s *dev, bool enable) { struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); - if (enable) - { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - /* Receive an interrupt when their is anything in the Rx data register - * (or an Rx timeout occurs). - */ - - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF91_UART_INTENSET_OFFSET); - putreg32(1, priv->uartbase + NRF91_UART_TASKS_STARTRX_OFFSET); - +#ifdef CONFIG_SUPPRESS_SERIAL_INTS + enable = false; #endif - } - else + + priv->rxenabled = enable; + if (enable) { - putreg32(UART_INT_RXDRDY, priv->uartbase + NRF91_UART_INTENCLR_OFFSET); - putreg32(1, priv->uartbase + NRF91_UART_TASKS_STOPRX_OFFSET); + nrf91_rxdeliver(dev); } + + leave_critical_section(flags); } /**************************************************************************** * Name: nrf91_rxavailable * * Description: - * Return true if the receive register is not empty + * Report whether a received character is waiting for the upper half. * ****************************************************************************/ @@ -625,82 +854,71 @@ static bool nrf91_rxavailable(struct uart_dev_s *dev) { struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; - /* Return true if the receive buffer/fifo is not "empty." */ - - return priv->rx_available; + return priv->rxvalid; } /**************************************************************************** * Name: nrf91_send * * Description: - * This method will send one byte on the UART. + * Start transmitting the character supplied by the upper half. * ****************************************************************************/ static void nrf91_send(struct uart_dev_s *dev, int ch) { struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); - putreg32(0, priv->uartbase + NRF91_UART_EVENTS_TXDRDY_OFFSET); - putreg32(1, priv->uartbase + NRF91_UART_TASKS_STARTTX_OFFSET); - - putreg32(ch, priv->uartbase + NRF91_UART_TXD_OFFSET); - while (getreg32(priv->uartbase + NRF91_UART_EVENTS_TXDRDY_OFFSET) == 0) - { - } - - putreg32(1, priv->uartbase + NRF91_UART_TASKS_STOPTX_OFFSET); + nrf91_txstart(priv, ch); + leave_critical_section(flags); } /**************************************************************************** * Name: nrf91_txint * * Description: - * Call to enable or disable TX interrupts + * Enable or disable transmission from the upper-half transmit buffer. * ****************************************************************************/ static void nrf91_txint(struct uart_dev_s *dev, bool enable) { - /* struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; */ + struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; + irqstate_t flags = enter_critical_section(); +#ifdef CONFIG_SUPPRESS_SERIAL_INTS + enable = false; +#endif + + priv->txenabled = enable; if (enable) { -#ifndef CONFIG_SUPPRESS_SERIAL_INTS - irqstate_t flags; - - /* Enable the TX interrupt */ - - flags = enter_critical_section(); - - /* Fake a TX interrupt here by just calling uart_xmitchars() with - * interrupts disabled (note this may recurse). - */ - uart_xmitchars(dev); - leave_critical_section(flags); -#endif - } - else - { - /* Disable the TX interrupt */ } + + leave_critical_section(flags); } /**************************************************************************** * Name: nrf91_txready * * Description: - * Return true if the tranmsit data register is empty + * Report whether the transmitter can accept another character. The + * events are read, the upper half also polls with interrupts disabled. + * A transfer ended without STOPTX can be restarted. * ****************************************************************************/ static bool nrf91_txready(struct uart_dev_s *dev) { - /* struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; */ + struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; - /* Return true if the transmit FIFO is "not full." */ + if (priv->txbusy) + { + return nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_ENDTX_OFFSET) || + nrf91_uart_getreg(priv, NRF91_UARTE_EVENTS_TXSTOPPED_OFFSET); + } return true; } @@ -715,11 +933,7 @@ static bool nrf91_txready(struct uart_dev_s *dev) static bool nrf91_txempty(struct uart_dev_s *dev) { - /* struct nrf91_dev_s *priv = (struct nrf91_dev_s *)dev->priv; */ - - /* Return true if the transmit FIFO is "empty." */ - - return true; + return nrf91_txready(dev); } /**************************************************************************** @@ -817,9 +1031,14 @@ void arm_serialinit(void) void up_putc(int ch) { #ifdef HAVE_UART_CONSOLE - /* struct nrf91_dev_s *priv = (struct nrf91_dev_s *)CONSOLE_DEV.priv; */ + struct nrf91_dev_s *priv = CONSOLE_DEV.priv; + irqstate_t flags = enter_critical_section(); + + /* Serialize polled debug output with the interrupt-driven transmitter */ + nrf91_txstop(priv); arm_lowputc(ch); + leave_critical_section(flags); #endif }
