From a54fe4ee1ecf31a097a674615325f5938a815555 Mon Sep 17 00:00:00 2001 From: Sara Souza Date: Tue, 1 Jun 2021 10:07:27 -0300 Subject: [PATCH] xtensa/esp32-s2: Add support for serial HW flow control. --- arch/xtensa/src/esp32s2/Kconfig | 28 ++++- arch/xtensa/src/esp32s2/esp32s2_cpuint.c | 2 +- arch/xtensa/src/esp32s2/esp32s2_lowputc.c | 119 ++++++++++++++++++ arch/xtensa/src/esp32s2/esp32s2_lowputc.h | 42 +++++++ arch/xtensa/src/esp32s2/esp32s2_serial.c | 143 +++++++++++++++++++++- 5 files changed, 330 insertions(+), 4 deletions(-) diff --git a/arch/xtensa/src/esp32s2/Kconfig b/arch/xtensa/src/esp32s2/Kconfig index 2a5dbdcf67..e4ccd9a151 100644 --- a/arch/xtensa/src/esp32s2/Kconfig +++ b/arch/xtensa/src/esp32s2/Kconfig @@ -448,18 +448,42 @@ config ESP32S2_UART0_RXPIN default 44 range 0 46 +config ESP32S2_UART0_RTSPIN + int "UART0 RTS Pin" + depends on SERIAL_IFLOWCONTROL + default 16 + range 0 46 + +config ESP32S2_UART0_CTSPIN + int "UART0 CTS Pin" + depends on SERIAL_OFLOWCONTROL + default 15 + range 0 46 + endif # ESP32S2_UART0 if ESP32S2_UART1 config ESP32S2_UART1_TXPIN int "UART1 Tx Pin" - default 17 + default 37 range 0 46 config ESP32S2_UART1_RXPIN int "UART1 Rx Pin" - default 18 + default 38 + range 0 46 + +config ESP32S2_UART1_RTSPIN + int "UART1 RTS Pin" + depends on SERIAL_IFLOWCONTROL + default 35 + range 0 46 + +config ESP32S2_UART1_CTSPIN + int "UART1 CTS Pin" + depends on SERIAL_OFLOWCONTROL + default 36 range 0 46 endif # ESP32S2_UART1 diff --git a/arch/xtensa/src/esp32s2/esp32s2_cpuint.c b/arch/xtensa/src/esp32s2/esp32s2_cpuint.c index 4a5db2024a..ce1c3447b3 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_cpuint.c +++ b/arch/xtensa/src/esp32s2/esp32s2_cpuint.c @@ -519,7 +519,7 @@ void esp32s2_free_cpuint(int cpuint) * periphid - The peripheral number from irq.h to be assigned to * a CPU interrupt. * cpuint - The CPU interrupt to receive the peripheral interrupt - * assignment. This value is returned by + * assignment. This value is returned by * esp32s2_alloc_edgeint or esp32s2_alloc_levelint. * * Returned Value: diff --git a/arch/xtensa/src/esp32s2/esp32s2_lowputc.c b/arch/xtensa/src/esp32s2/esp32s2_lowputc.c index e6ac5170e5..b1e2aa3495 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_lowputc.c +++ b/arch/xtensa/src/esp32s2/esp32s2_lowputc.c @@ -73,6 +73,24 @@ struct esp32s2_uart_s g_uart0_config = .txsig = U0TXD_OUT_IDX, .rxpin = CONFIG_ESP32S2_UART0_RXPIN, .rxsig = U0RXD_IN_IDX, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rtspin = CONFIG_ESP32S2_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_ESP32S2_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_ESP32S2_UART0 */ @@ -94,6 +112,24 @@ struct esp32s2_uart_s g_uart1_config = .txsig = U1TXD_OUT_IDX, .rxpin = CONFIG_ESP32S2_UART1_RXPIN, .rxsig = U1RXD_IN_IDX, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rtspin = CONFIG_ESP32S2_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_ESP32S2_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_ESP32S2_UART1 */ @@ -103,6 +139,72 @@ struct esp32s2_uart_s g_uart1_config = * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: esp32s2_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 esp32s2_lowputc_set_iflow(const struct esp32s2_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: esp32s2_lowputc_set_oflow + * + * Description: + * Configure the output hardware flow control. + * + * Parameters: + * priv - Pointer to the private driver struct. + * enable - true = enable, false = disable + * + ****************************************************************************/ + +void esp32s2_lowputc_set_oflow(const struct esp32s2_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: esp32s2_lowputc_enable_sysclk * @@ -601,6 +703,23 @@ void esp32s2_lowputc_config_pins(const struct esp32s2_uart_s *priv) /* Route UART RX signal to the selected RX pin */ esp32s2_gpio_matrix_in(priv->rxpin, priv->rxsig, 0); + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->iflow) + { + esp32s2_configgpio(priv->rtspin, OUTPUT_FUNCTION_1); + esp32s2_gpio_matrix_out(priv->rtspin, priv->rtssig, + 0, 0); + } + +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + if (priv->oflow) + { + esp32s2_configgpio(priv->ctspin, INPUT_FUNCTION_1); + esp32s2_gpio_matrix_in(priv->ctspin, priv->ctssig, 0); + } +#endif } /**************************************************************************** diff --git a/arch/xtensa/src/esp32s2/esp32s2_lowputc.h b/arch/xtensa/src/esp32s2/esp32s2_lowputc.h index 1bf123d510..bdf4919553 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_lowputc.h +++ b/arch/xtensa/src/esp32s2/esp32s2_lowputc.h @@ -97,6 +97,16 @@ struct esp32s2_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 esp32s2_uart_s g_uart0_config; @@ -106,6 +116,38 @@ extern struct esp32s2_uart_s g_uart1_config; * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: esp32s2_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 esp32s2_lowputc_set_iflow(const struct esp32s2_uart_s *priv, + uint8_t threshold, bool enable); + +/**************************************************************************** + * Name: esp32s2_lowputc_set_oflow + * + * Description: + * Configure the output hardware flow control. + * + * Parameters: + * priv - Pointer to the private driver struct. + * enable - true = enable, false = disable + * + ****************************************************************************/ + +void esp32s2_lowputc_set_oflow(const struct esp32s2_uart_s *priv, + bool enable); + /**************************************************************************** * Name: esp32s2_lowputc_enable_sysclk * diff --git a/arch/xtensa/src/esp32s2/esp32s2_serial.c b/arch/xtensa/src/esp32s2/esp32s2_serial.c index 5110b1dfb6..91be10554e 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_serial.c +++ b/arch/xtensa/src/esp32s2/esp32s2_serial.c @@ -116,6 +116,10 @@ static bool esp32s2_txempty(struct uart_dev_s *dev); static void esp32s2_send(struct uart_dev_s *dev, int ch); static int esp32s2_receive(struct uart_dev_s *dev, unsigned int *status); static int esp32s2_ioctl(struct file *filep, int cmd, unsigned long arg); +#ifdef CONFIG_SERIAL_IFLOWCONTROL +static bool esp32s2_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper); +#endif /**************************************************************************** * Private Data @@ -138,7 +142,7 @@ static struct uart_ops_s g_uart_ops = .receive = esp32s2_receive, .ioctl = esp32s2_ioctl, #ifdef CONFIG_SERIAL_IFLOWCONTROL - .rxflowcontrol = NULL, + .rxflowcontrol = esp32s2_rxflowcontrol, #endif }; @@ -329,6 +333,44 @@ static int esp32s2_setup(struct uart_dev_s *dev) esp32s2_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. + */ + + esp32s2_lowputc_set_iflow(priv, (uint8_t)(UART_RX_FIFO_SIZE / 2), + true); + } + else + { + /* Just disable input flow control, threshold parameter + * will be discarded. + */ + + esp32s2_lowputc_set_iflow(priv, 0 , false); + } + +#endif +#ifdef CONFIG_SERIAL_OFLOWCONTROL + /* Configure the ouput flow control */ + + if (priv->oflow) + { + esp32s2_lowputc_set_oflow(priv, true); + } + else + { + esp32s2_lowputc_set_oflow(priv, false); + } +#endif + /* Reset FIFOs */ esp32s2_lowputc_rst_txfifo(priv); @@ -740,6 +782,13 @@ static int esp32s2_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 the termiosp using the * cfsetispeed interface. */ @@ -779,6 +828,12 @@ static int esp32s2_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) { @@ -830,6 +885,13 @@ static int esp32s2_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. */ @@ -843,6 +905,13 @@ static int esp32s2_ioctl(struct file *filep, int cmd, unsigned long arg) 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. * See nuttx/libs/libc/termios/lib_tcsetattr.c @@ -867,6 +936,78 @@ static int esp32s2_ioctl(struct file *filep, int cmd, unsigned long arg) return ret; } +/**************************************************************************** + * Name: esp32s2_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-S2 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 esp32s2_rxflowcontrol(struct uart_dev_s *dev, + unsigned int nbuffered, bool upper) +{ + bool ret = false; + struct esp32s2_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. + */ + + esp32s2_lowputc_set_iflow(priv, (uint8_t)(UART_RX_FIFO_SIZE / 2), + true); + esp32s2_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. + */ + + esp32s2_lowputc_set_iflow(priv, 0 , true); + esp32s2_rxint(dev, false); + ret = true; + } + } + + return ret; +} +#endif + /**************************************************************************** * Public Functions ****************************************************************************/