From b54be4e946b3d9bf0bdf55207abc11dec78bdd76 Mon Sep 17 00:00:00 2001 From: Sara Souza Date: Fri, 28 May 2021 14:42:24 -0300 Subject: [PATCH] risc-v/esp32-c3: Add support for HW flow control. --- arch/risc-v/src/esp32c3/Kconfig | 28 +++- arch/risc-v/src/esp32c3/esp32c3_lowputc.c | 119 +++++++++++++++++ arch/risc-v/src/esp32c3/esp32c3_lowputc.h | 42 ++++++ arch/risc-v/src/esp32c3/esp32c3_serial.c | 150 +++++++++++++++++++++- 4 files changed, 332 insertions(+), 7 deletions(-) diff --git a/arch/risc-v/src/esp32c3/Kconfig b/arch/risc-v/src/esp32c3/Kconfig index 2222e6ac9c..e102dcb76d 100644 --- a/arch/risc-v/src/esp32c3/Kconfig +++ b/arch/risc-v/src/esp32c3/Kconfig @@ -413,17 +413,41 @@ config ESP32C3_UART0_RXPIN int "UART0 RX Pin" default 20 +config ESP32C3_UART0_RTSPIN + int "UART0 RTS Pin" + depends on SERIAL_IFLOWCONTROL + default 16 + range 0 21 + +config ESP32C3_UART0_CTSPIN + int "UART0 CTS Pin" + depends on SERIAL_OFLOWCONTROL + default 15 + range 0 21 + endif # ESP32C3_UART0 if ESP32C3_UART1 config ESP32C3_UART1_TXPIN int "UART1 TX Pin" - default 6 + default 8 config ESP32C3_UART1_RXPIN int "UART1 RX Pin" - default 7 + default 9 + +config ESP32C3_UART1_RTSPIN + int "UART1 RTS Pin" + depends on SERIAL_IFLOWCONTROL + default 1 + range 0 21 + +config ESP32C3_UART1_CTSPIN + int "UART1 CTS Pin" + depends on SERIAL_OFLOWCONTROL + default 2 + range 0 21 endif # ESP32C3_UART1 diff --git a/arch/risc-v/src/esp32c3/esp32c3_lowputc.c b/arch/risc-v/src/esp32c3/esp32c3_lowputc.c index 1aa4fdd02d..18e38426fc 100644 --- a/arch/risc-v/src/esp32c3/esp32c3_lowputc.c +++ b/arch/risc-v/src/esp32c3/esp32c3_lowputc.c @@ -74,6 +74,24 @@ struct esp32c3_uart_s g_uart0_config = .txsig = U0TXD_OUT_IDX, .rxpin = CONFIG_ESP32C3_UART0_RXPIN, .rxsig = U0RXD_IN_IDX, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rtspin = CONFIG_ESP32C3_UART0_RTSPIN, + .rtssig = U0RTS_OUT_IDX, +#ifdef CONFIG_UART0_IFLOWCONTROL + .iflow = true, /* input flow control (RTS) enabled */ +#else + .iflow = false, /* input flow control (RTS) disabled */ +#endif +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + .ctspin = CONFIG_ESP32C3_UART0_CTSPIN, + .ctssig = U0CTS_IN_IDX, +#ifdef CONFIG_UART0_OFLOWCONTROL + .oflow = true, /* output flow control (CTS) enabled */ +#else + .oflow = false, /* output flow control (CTS) disabled */ +#endif +#endif }; #endif /* CONFIG_ESP32C3_UART0 */ @@ -95,6 +113,24 @@ struct esp32c3_uart_s g_uart1_config = .txsig = U1TXD_OUT_IDX, .rxpin = CONFIG_ESP32C3_UART1_RXPIN, .rxsig = U1RXD_IN_IDX, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rtspin = CONFIG_ESP32C3_UART1_RTSPIN, + .rtssig = U1RTS_OUT_IDX, +#ifdef CONFIG_UART1_IFLOWCONTROL + .iflow = true, /* input flow control (RTS) enabled */ +#else + .iflow = false, /* input flow control (RTS) disabled */ +#endif +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + .ctspin = CONFIG_ESP32C3_UART1_CTSPIN, + .ctssig = U1CTS_IN_IDX, +#ifdef CONFIG_UART1_OFLOWCONTROL + .oflow = true, /* output flow control (CTS) enabled */ +#else + .oflow = false, /* output flow control (CTS) disabled */ +#endif +#endif }; #endif /* CONFIG_ESP32C3_UART1 */ @@ -104,6 +140,72 @@ struct esp32c3_uart_s g_uart1_config = * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: esp32c3_lowputc_set_iflow + * + * Description: + * Configure the input hardware flow control. + * + * Parameters: + * priv - Pointer to the private driver struct. + * threshold - RX FIFO value from which RST will automatically be + * asserted. + * enable - true = enable, false = disable + * + ****************************************************************************/ + +void esp32c3_lowputc_set_iflow(const struct esp32c3_uart_s *priv, + uint8_t threshold, bool enable) +{ + uint32_t mask; + if (enable) + { + /* Enable RX flow control */ + + modifyreg32(UART_CONF1_REG(priv->id), 0, UART_RX_FLOW_EN); + + /* Configure the threshold */ + + mask = VALUE_TO_FIELD(threshold, UART_RX_FLOW_THRHD); + modifyreg32(UART_MEM_CONF_REG(priv->id), UART_RX_FLOW_THRHD_M, mask); + } + else + { + /* Disable RX flow control */ + + modifyreg32(UART_CONF1_REG(priv->id), UART_RX_FLOW_EN, 0); + } +} + +/**************************************************************************** + * Name: esp32c3_lowputc_set_oflow + * + * Description: + * Configure the output hardware flow control. + * + * Parameters: + * priv - Pointer to the private driver struct. + * enable - true = enable, false = disable + * + ****************************************************************************/ + +void esp32c3_lowputc_set_oflow(const struct esp32c3_uart_s *priv, + bool enable) +{ + if (enable) + { + /* Enable TX flow control */ + + modifyreg32(UART_CONF0_REG(priv->id), 0, UART_TX_FLOW_EN); + } + else + { + /* Disable TX flow control */ + + modifyreg32(UART_CONF0_REG(priv->id), UART_TX_FLOW_EN, 0); + } +} + /**************************************************************************** * Name: esp32c3_lowputc_reset_core * @@ -642,6 +744,23 @@ void esp32c3_lowputc_config_pins(const struct esp32c3_uart_s *priv) esp32c3_configgpio(priv->rxpin, INPUT_FUNCTION_1); esp32c3_gpio_matrix_in(priv->rxpin, priv->rxsig, 0); + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->iflow) + { + esp32c3_configgpio(priv->rtspin, OUTPUT_FUNCTION_1); + esp32c3_gpio_matrix_out(priv->rtspin, priv->rtssig, + 0, 0); + } + +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + if (priv->oflow) + { + esp32c3_configgpio(priv->ctspin, INPUT_FUNCTION_1); + esp32c3_gpio_matrix_in(priv->ctspin, priv->ctssig, 0); + } +#endif } /**************************************************************************** diff --git a/arch/risc-v/src/esp32c3/esp32c3_lowputc.h b/arch/risc-v/src/esp32c3/esp32c3_lowputc.h index d429571906..e8455ca572 100644 --- a/arch/risc-v/src/esp32c3/esp32c3_lowputc.h +++ b/arch/risc-v/src/esp32c3/esp32c3_lowputc.h @@ -105,6 +105,16 @@ struct esp32c3_uart_s uint8_t txsig; /* TX signal */ uint8_t rxpin; /* RX pin */ uint8_t rxsig; /* RX signal */ +#ifdef CONFIG_SERIAL_IFLOWCONTROL + uint8_t rtspin; /* RTS pin number */ + uint8_t rtssig; /* RTS signal */ + bool iflow; /* Input flow control (RTS) enabled */ +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + uint8_t ctspin; /* CTS pin number */ + uint8_t ctssig; /* CTS signal */ + bool oflow; /* Output flow control (CTS) enabled */ +#endif }; extern struct esp32c3_uart_s g_uart0_config; @@ -114,6 +124,38 @@ extern struct esp32c3_uart_s g_uart1_config; * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: esp32c3_lowputc_set_iflow + * + * Description: + * Configure the input hardware flow control. + * + * Parameters: + * priv - Pointer to the private driver struct. + * threshold - RX FIFO value from which RST will automatically be + * asserted. + * enable - true = enable, false = disable + * + ****************************************************************************/ + +void esp32c3_lowputc_set_iflow(const struct esp32c3_uart_s *priv, + uint8_t threshold, bool enable); + +/**************************************************************************** + * Name: esp32c3_lowputc_set_oflow + * + * Description: + * Configure the output hardware flow control. + * + * Parameters: + * priv - Pointer to the private driver struct. + * enable - true = enable, false = disable + * + ****************************************************************************/ + +void esp32c3_lowputc_set_oflow(const struct esp32c3_uart_s *priv, + bool enable); + /**************************************************************************** * Name: esp32c3_lowputc_reset_core * diff --git a/arch/risc-v/src/esp32c3/esp32c3_serial.c b/arch/risc-v/src/esp32c3/esp32c3_serial.c index 196a8b0ff8..4670d20b6d 100644 --- a/arch/risc-v/src/esp32c3/esp32c3_serial.c +++ b/arch/risc-v/src/esp32c3/esp32c3_serial.c @@ -122,6 +122,10 @@ static bool esp32c3_txempty(struct uart_dev_s *dev); static void esp32c3_send(struct uart_dev_s *dev, int ch); static int esp32c3_receive(struct uart_dev_s *dev, unsigned int *status); static int esp32c3_ioctl(struct file *filep, int cmd, unsigned long arg); +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool esp32c3_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper); +#endif /**************************************************************************** * Private Data @@ -144,7 +148,7 @@ static struct uart_ops_s g_uart_ops = .receive = esp32c3_receive, .ioctl = esp32c3_ioctl, #ifdef CONFIG_SERIAL_IFLOWCONTROL - .rxflowcontrol = NULL, + .rxflowcontrol = esp32c3_rxflowcontrol, #endif }; @@ -244,16 +248,16 @@ static int uart_handler(int irq, FAR void *context, FAR void *arg) if (int_status & tx_mask) { - uart_xmitchars(dev); - modifyreg32(UART_INT_CLR_REG(priv->id), tx_mask, tx_mask); + uart_xmitchars(dev); + modifyreg32(UART_INT_CLR_REG(priv->id), tx_mask, tx_mask); } /* Rx fifo timeout interrupt or rx fifo full interrupt */ if (int_status & rx_mask) { - uart_recvchars(dev); - modifyreg32(UART_INT_CLR_REG(priv->id), rx_mask, rx_mask); + uart_recvchars(dev); + modifyreg32(UART_INT_CLR_REG(priv->id), rx_mask, rx_mask); } return OK; @@ -332,6 +336,44 @@ static int esp32c3_setup(struct uart_dev_s *dev) esp32c3_lowputc_stop_length(priv); +#ifdef CONFIG_SERIAL_IFLOWCONTROL + /* Configure the input flow control */ + + if (priv->iflow) + { + /* Enable input flow control and set the RX FIFO threshold + * to assert the RTS line to half the RX FIFO buffer. + * It will then save some space on the hardware fifo to + * remaining bytes that may arrive after RTS be asserted + * and before the transmitter stops sending data. + */ + + esp32c3_lowputc_set_iflow(priv, (uint8_t)(UART_RX_FIFO_SIZE / 2), + true); + } + else + { + /* Just disable input flow control, threshold parameter + * will be discarded. + */ + + esp32c3_lowputc_set_iflow(priv, 0 , false); + } + +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + /* Configure the ouput flow control */ + + if (priv->oflow) + { + esp32c3_lowputc_set_oflow(priv, true); + } + else + { + esp32c3_lowputc_set_oflow(priv, false); + } +#endif + /* No Tx idle interval */ esp32c3_lowputc_set_tx_idle_time(priv, 0); @@ -725,6 +767,13 @@ static int esp32c3_ioctl(struct file *filep, int cmd, unsigned long arg) termiosp->c_cflag |= (priv->stop_b2) ? CSTOPB : 0; +#ifdef CONFIG_SERIAL_OFLOWCONTROL + termiosp->c_cflag |= (priv->oflow) ? CCTS_OFLOW : 0; +#endif +#ifdef CONFIG_SERIAL_IFLOWCONTROL + termiosp->c_cflag |= (priv->iflow) ? CRTS_IFLOW : 0; +#endif + /* Set the baud rate in ther termiosp using the * cfsetispeed interface. */ @@ -764,6 +813,12 @@ static int esp32c3_ioctl(struct file *filep, int cmd, unsigned long arg) uint8_t parity; uint8_t bits; uint8_t stop2; +#ifdef CONFIG_SERIAL_IFLOWCONTROL + bool iflow; +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + bool oflow; +#endif if (!termiosp) { @@ -815,6 +870,13 @@ static int esp32c3_ioctl(struct file *filep, int cmd, unsigned long arg) stop2 = (termiosp->c_cflag & CSTOPB) ? 1 : 0; +#ifdef CONFIG_SERIAL_IFLOWCONTROL + iflow = (termiosp->c_cflag & CRTS_IFLOW) != 0; +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + oflow = (termiosp->c_cflag & CCTS_OFLOW) != 0; +#endif + /* Verify that all settings are valid before * performing the changes. */ @@ -827,6 +889,12 @@ static int esp32c3_ioctl(struct file *filep, int cmd, unsigned long arg) priv->parity = parity; priv->bits = bits; priv->stop_b2 = stop2; +#ifdef CONFIG_SERIAL_IFLOWCONTROL + priv->iflow = iflow; +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + priv->oflow = oflow; +#endif /* Effect the changes immediately - note that we do not * implement TCSADRAIN or TCSAFLUSH, only TCSANOW option. @@ -852,6 +920,78 @@ static int esp32c3_ioctl(struct file *filep, int cmd, unsigned long arg) return ret; } +/**************************************************************************** + * Name: esp32c3_rxflowcontrol + * + * Description: + * Called when upper half RX buffer is full (or exceeds configured + * watermark levels if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is defined). + * Return true if UART activated RX flow control to block more incoming + * data. + * NOTE: ESP32-C3 has a hardware RX FIFO threshold mechanism to control + * RTS line and to stop receiving data. This is very similar to the concept + * behind upper watermark level. The hardware threshold is used here + * to control the RTS line. When setting the threshold to zero, RTS will + * immediately be asserted. If nbuffered = 0 or the lower watermark is + * crossed and the serial driver decides to disable RX flow control, the + * threshold will be changed to UART_RX_FLOW_THRHD_VALUE, which is almost + * half the HW RX FIFO capacity. It keeps some space to keep the data + * received between the RTS assertion and the stop by the sender. + * + * Input Parameters: + * dev - UART device instance + * nbuffered - the number of characters currently buffered + * (if CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS is + * not defined the value will be 0 for an empty buffer or the + * defined buffer size for a full buffer) + * upper - true indicates the upper watermark was crossed where + * false indicates the lower watermark has been crossed + * + * Returned Value: + * true if RX flow control activated. + * + ****************************************************************************/ + +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool esp32c3_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper) +{ + bool ret = false; + struct esp32c3_uart_s *priv = dev->priv; + if (priv->iflow) + { + if (nbuffered == 0 || upper == false) + { + /* Empty buffer, RTS should be de-asserted and logic in above + * layers should re-enable RX interrupt. + */ + + esp32c3_lowputc_set_iflow(priv, (uint8_t)(UART_RX_FIFO_SIZE / 2), + true); + esp32c3_rxint(dev, true); + ret = false; + } + else + { + /* If the RX buffer is not zero and watermarks are not enabled, + * then this function is called to announce RX buffer is full. + * The first thing it should do is to immediately assert RTS. + * Software RX FIFO is full, so besides asserting RTS, it's + * necessary to disable RX interrupts to prevent remaining bytes + * (that arrive after asserting RTS) to be pushed to the + * SW RX FIFO. + */ + + esp32c3_lowputc_set_iflow(priv, 0 , true); + esp32c3_rxint(dev, false); + ret = true; + } + } + + return ret; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/