xtensa/esp32-s2: Add support for serial HW flow control.

This commit is contained in:
Sara Souza 2021-06-01 10:07:27 -03:00 committed by Xiang Xiao
parent b54be4e946
commit a54fe4ee1e
5 changed files with 330 additions and 4 deletions

View File

@ -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

View File

@ -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:

View File

@ -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
}
/****************************************************************************

View File

@ -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
*

View File

@ -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
****************************************************************************/