summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/config_chip.h3
-rw-r--r--chip/stm32/uart.c52
-rw-r--r--common/uart_buffering.c106
-rw-r--r--include/config.h25
-rw-r--r--include/uart.h20
5 files changed, 170 insertions, 36 deletions
diff --git a/chip/stm32/config_chip.h b/chip/stm32/config_chip.h
index 8c0845f90e..3d887ec621 100644
--- a/chip/stm32/config_chip.h
+++ b/chip/stm32/config_chip.h
@@ -50,7 +50,8 @@
/* Use DMA */
#define CONFIG_DMA
-/* Use DMA for UART output */
+/* Use DMA for UART I/O */
+#define CONFIG_UART_RX_DMA
#define CONFIG_UART_TX_DMA
/* Flash protection applies to the next boot, not the current one */
diff --git a/chip/stm32/uart.c b/chip/stm32/uart.c
index 3d510c16f1..78bbcc3e58 100644
--- a/chip/stm32/uart.c
+++ b/chip/stm32/uart.c
@@ -31,6 +31,17 @@ static const struct dma_option dma_tx_option = {
#define UART_TX_INT_ENABLE STM32_USART_CR1_TXEIE
#endif
+#ifdef CONFIG_UART_RX_DMA
+/* DMA channel options; assumes UART1 */
+static const struct dma_option dma_rx_option = {
+ STM32_DMAC_USART1_RX, (void *)&STM32_USART_DR(UARTN),
+ STM32_DMA_CCR_MSIZE_8_BIT | STM32_DMA_CCR_PSIZE_8_BIT |
+ STM32_DMA_CCR_CIRC
+};
+
+static int dma_rx_len; /* Size of receive DMA circular buffer */
+#endif
+
static int init_done; /* Initialization done? */
static int should_stop; /* Last TX control action */
@@ -95,6 +106,23 @@ int uart_rx_available(void)
return STM32_USART_SR(UARTN) & STM32_USART_SR_RXNE;
}
+#ifdef CONFIG_UART_RX_DMA
+
+void uart_rx_dma_start(char *dest, int len)
+{
+ /* Start receiving */
+ dma_rx_len = len;
+ dma_start_rx(&dma_rx_option, len, dest);
+}
+
+int uart_rx_dma_head(void)
+{
+ return dma_bytes_done(dma_get_channel(STM32_DMAC_USART1_RX),
+ dma_rx_len);
+}
+
+#endif
+
void uart_write_char(char c)
{
/* Wait for space */
@@ -134,14 +162,21 @@ static void uart_interrupt(void)
STM32_USART_CR1(UARTN) &= ~STM32_USART_CR1_TXEIE;
#endif
- /* Read input FIFO until empty, then fill output FIFO */
+#ifndef CONFIG_UART_RX_DMA
+ /*
+ * Read input FIFO until empty. DMA-based receive does this from a
+ * hook in the UART buffering module.
+ */
uart_process_input();
+#endif
+
+ /* Fill output FIFO */
uart_process_output();
#ifndef CONFIG_UART_TX_DMA
/*
* Re-enable TX empty interrupt only if it was not disabled by
- * uart_process.
+ * uart_process_output().
*/
if (!should_stop)
STM32_USART_CR1(UARTN) |= STM32_USART_CR1_TXEIE;
@@ -194,11 +229,10 @@ void uart_init(void)
/*
* UART enabled, 8 Data bits, oversampling x16, no parity,
- * RXNE interrupt, TX and RX enabled.
+ * TX and RX enabled.
*/
STM32_USART_CR1(UARTN) =
- STM32_USART_CR1_UE | STM32_USART_CR1_RXNEIE |
- STM32_USART_CR1_TE | STM32_USART_CR1_RE;
+ STM32_USART_CR1_UE | STM32_USART_CR1_TE | STM32_USART_CR1_RE;
/* 1 stop bit, no fancy stuff */
STM32_USART_CR2(UARTN) = 0x0000;
@@ -211,6 +245,14 @@ void uart_init(void)
STM32_USART_CR3(UARTN) = 0x0000;
#endif
+#ifdef CONFIG_UART_RX_DMA
+ /* Enable DMA receiver */
+ STM32_USART_CR3(UARTN) |= STM32_USART_CR3_DMAR;
+#else
+ /* Enable receive-not-empty interrupt */
+ STM32_USART_CR1(UARTN) |= STM32_USART_CR1_RXNEIE;
+#endif
+
#ifdef CHIP_FAMILY_stm32l
/* Use single-bit sampling */
STM32_USART_CR3(UARTN) |= STM32_USART_CR3_ONEBIT;
diff --git a/common/uart_buffering.c b/common/uart_buffering.c
index 560e783c41..779835ae24 100644
--- a/common/uart_buffering.c
+++ b/common/uart_buffering.c
@@ -9,10 +9,12 @@
#include "common.h"
#include "console.h"
+#include "hooks.h"
#include "host_command.h"
#include "printf.h"
#include "system.h"
#include "task.h"
+#include "timer.h"
#include "uart.h"
#include "util.h"
@@ -28,6 +30,14 @@
/* ASCII control character; for example, CTRL('C') = ^C */
#define CTRL(c) ((c) - '@')
+/*
+ * Interval between rechecking the receive DMA head pointer, after a character
+ * of input has been detected by the normal tick task. There will be
+ * CONFIG_UART_RX_DMA_RECHECKS rechecks between this tick and the next tick.
+ */
+#define RX_DMA_RECHECK_INTERVAL (HOOK_TICK_INTERVAL / \
+ (CONFIG_UART_RX_DMA_RECHECKS + 1))
+
/* Transmit and receive buffers */
static volatile char tx_buf[CONFIG_UART_TX_BUF_SIZE];
static volatile int tx_buf_head;
@@ -70,7 +80,7 @@ static int __tx_char(void *context, int c)
/**
* Process UART output via DMA
*/
-static void uart_process_output_dma(void)
+void uart_process_output(void)
{
/* Size of current DMA transfer */
static int tx_dma_in_progress;
@@ -81,6 +91,9 @@ static void uart_process_output_dma(void)
*/
int head = tx_buf_head;
+ if (uart_suspended)
+ return;
+
/* If DMA is still busy, nothing to do. */
if(!uart_tx_dma_ready())
return;
@@ -108,16 +121,13 @@ static void uart_process_output_dma(void)
uart_tx_dma_start((char *)(tx_buf + tx_buf_tail), tx_dma_in_progress);
}
-#endif /* CONFIG_UART_TX_DMA */
+#else /* !CONFIG_UART_TX_DMA */
void uart_process_output(void)
{
if (uart_suspended)
return;
-#ifdef CONFIG_UART_TX_DMA
- uart_process_output_dma();
-#else
/* Copy output from buffer until TX fifo full or output buffer empty */
while (uart_tx_ready() && (tx_buf_head != tx_buf_tail)) {
uart_write_char(tx_buf[tx_buf_tail]);
@@ -127,9 +137,57 @@ void uart_process_output(void)
/* If output buffer is empty, disable transmit interrupt */
if (tx_buf_tail == tx_buf_head)
uart_tx_stop();
-#endif
}
+#endif /* !CONFIG_UART_TX_DMA */
+
+#ifdef CONFIG_UART_RX_DMA
+
+void uart_process_input(void)
+{
+ static int fast_rechecks;
+ int cur_head = rx_buf_head;
+
+ int i;
+
+ /* Update receive buffer head from current DMA receive pointer */
+ rx_buf_head = uart_rx_dma_head();
+
+ /* Handle software flow control characters */
+ for (i = cur_head; i != rx_buf_head; i = RX_BUF_NEXT(i)) {
+ int c = rx_buf[i];
+
+ if (c == CTRL('Q')) {
+ /* Software flow control - XOFF */
+ uart_suspended = 1;
+ uart_tx_stop();
+ } else if (c == CTRL('S')) {
+ /* Software flow control - XON */
+ uart_suspended = 0;
+ uart_tx_start();
+ }
+ }
+
+ if (rx_buf_head != cur_head) {
+ console_has_input();
+ fast_rechecks = CONFIG_UART_RX_DMA_RECHECKS;
+ }
+
+ /*
+ * Input is checked once a tick when the console is idle. When input
+ * is received, check more frequently for a bit, so that the console is
+ * more responsive.
+ */
+ if (fast_rechecks) {
+ fast_rechecks--;
+ hook_call_deferred(uart_process_input, RX_DMA_RECHECK_INTERVAL);
+ }
+}
+DECLARE_HOOK(HOOK_TICK, uart_process_input, HOOK_PRIO_DEFAULT);
+DECLARE_DEFERRED(uart_process_input);
+
+#else /* !CONFIG_UART_RX_DMA */
+
void uart_process_input(void)
{
int got_input = 0;
@@ -151,15 +209,16 @@ void uart_process_input(void)
/* Buffer all other input */
rx_buf[rx_buf_head] = c;
rx_buf_head = rx_buf_next;
+ got_input = 1;
}
-
- got_input = 1;
}
if (got_input)
console_has_input();
}
+#endif /* !CONFIG_UART_RX_DMA */
+
int uart_putc(int c)
{
int rv = __tx_char(NULL, c);
@@ -241,26 +300,27 @@ void uart_flush_output(void)
int uart_getc(void)
{
- int c;
-
- /* Disable interrupts */
- uart_disable_interrupt();
-
- /* Call interrupt handler to empty the hardware FIFO */
- uart_process_input();
-
- if (rx_buf_tail == rx_buf_head) {
- c = -1; /* No pending input */
- } else {
- c = rx_buf[rx_buf_tail];
+ /* Look for a non-flow-control character */
+ while(rx_buf_tail != rx_buf_head) {
+ int c = rx_buf[rx_buf_tail];
rx_buf_tail = RX_BUF_NEXT(rx_buf_tail);
+
+ if (c != CTRL('Q') && c != CTRL('S'))
+ return c;
}
- /* Re-enable interrupts */
- uart_enable_interrupt();
+ /* If we're still here, no input */
+ return -1;
+}
- return c;
+#ifdef CONFIG_UART_RX_DMA
+static void uart_rx_dma_init(void)
+{
+ /* Start receiving */
+ uart_rx_dma_start((char *)rx_buf, CONFIG_UART_RX_BUF_SIZE);
}
+DECLARE_HOOK(HOOK_INIT, uart_rx_dma_init, HOOK_PRIO_DEFAULT);
+#endif
/*****************************************************************************/
/* Host commands */
diff --git a/include/config.h b/include/config.h
index 31e923ed98..148b5921e4 100644
--- a/include/config.h
+++ b/include/config.h
@@ -613,6 +613,24 @@
#undef CONFIG_UART_HOST
/*
+ * UART receive buffer size in bytes. Must be a power of 2 for macros in
+ * common/uart_buffering.c to work properly. Must be larger than
+ * CONFIG_CONSOLE_INPUT_LINE_SIZE to copy and paste scripts.
+ */
+#define CONFIG_UART_RX_BUF_SIZE 128
+
+/* Use DMA for UART input */
+#undef CONFIG_UART_RX_DMA
+
+/*
+ * On some platforms, UART receive DMA can't trigger an interrupt when a single
+ * character is received. Those platforms poll for characters every HOOK_TICK.
+ * When a character is received, make this many additional checks between then
+ * and the next HOOK_TICK, to increase responsiveness of the console to input.
+ */
+#define CONFIG_UART_RX_DMA_RECHECKS 5
+
+/*
* UART transmit buffer size in bytes. Must be a power of 2 for macros in
* common/uart_buffering.c to work properly.
*/
@@ -621,13 +639,6 @@
/* Use DMA for UART output */
#undef CONFIG_UART_TX_DMA
-/*
- * UART receive buffer size in bytes. Must be a power of 2 for macros in
- * common/uart_buffering.c to work properly. Must be larger than
- * CONFIG_CONSOLE_INPUT_LINE_SIZE to copy and paste scripts.
- */
-#define CONFIG_UART_RX_BUF_SIZE 128
-
/*****************************************************************************/
/* Compile support for simple control of power to the device's USB ports */
diff --git a/include/uart.h b/include/uart.h
index 9e182002e1..762d885f08 100644
--- a/include/uart.h
+++ b/include/uart.h
@@ -119,6 +119,26 @@ void uart_tx_dma_start(const char *src, int len);
int uart_rx_available(void);
/**
+ * Start a UART receive DMA transfer.
+ *
+ * DMA will be configured in circular buffer mode, so received characters
+ * will be stored into the buffer continuously.
+ *
+ * @param dest Pointer to destination buffer
+ * @param len Length of buffer in bytes
+ */
+void uart_rx_dma_start(char *dest, int len);
+
+/**
+ * Return the head of the receive DMA transfer buffer
+ *
+ * This is the next offset in the buffer which will receive a character, and
+ * will be from 0..(len-1) where len is the buffer length passed to
+ * uart_rx_dma_start().
+ */
+int uart_rx_dma_head(void);
+
+/**
* Send a character to the UART data register.
*
* If the transmit FIFO is full, blocks until there is space.