diff options
author | Anup Kulkarni <quic_anupkulk@quicinc.com> | 2025-06-03 16:31:45 +0530 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2025-06-19 13:24:10 +0200 |
commit | 4fcc287f3c692450ffd91c72a19c58740494491a (patch) | |
tree | 77452338125c4ab874dc4d836a79628acca68a58 | |
parent | 341a22fa056ddc01341b5e2a1ca0dbe61fbdb1ea (diff) |
serial: qcom-geni: Enable support for half-duplex mode
Enable the use of the RTS pin for direction control in half-duplex modes to
prevent data collisions. Utilize the rs485 structure and callbacks in the
serial core framework to support half-duplex modes. Implement support for
the TIOCSRS485 IOCTL value and the struct serial_rs485.
Signed-off-by: Anup Kulkarni <quic_anupkulk@quicinc.com>
Link: https://lore.kernel.org/r/20250603110145.3835111-1-quic_anupkulk@quicinc.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/qcom_geni_serial.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c index 8ba6df8abd4d..004f9a0d80f7 100644 --- a/drivers/tty/serial/qcom_geni_serial.c +++ b/drivers/tty/serial/qcom_geni_serial.c @@ -192,6 +192,33 @@ static struct qcom_geni_serial_port qcom_geni_console_port = { }, }; +static const struct serial_rs485 qcom_geni_rs485_supported = { + .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND | SER_RS485_RTS_ON_SEND, +}; + +/** + * qcom_geni_set_rs485_mode - Set RTS pin state for RS485 mode + * @uport: UART port + * @flag: RS485 flag to determine RTS polarity + * + * Enables manual RTS control for RS485. Sets RTS to READY or NOT_READY + * based on the specified flag if RS485 mode is enabled. + */ +static void qcom_geni_set_rs485_mode(struct uart_port *uport, u32 flag) +{ + if (!(uport->rs485.flags & SER_RS485_ENABLED)) + return; + + u32 rfr = UART_MANUAL_RFR_EN; + + if (uport->rs485.flags & flag) + rfr |= UART_RFR_NOT_READY; + else + rfr |= UART_RFR_READY; + + writel(rfr, uport->membase + SE_UART_MANUAL_RFR); +} + static int qcom_geni_serial_request_port(struct uart_port *uport) { struct platform_device *pdev = to_platform_device(uport->dev); @@ -664,6 +691,8 @@ static void qcom_geni_serial_start_tx_dma(struct uart_port *uport) xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, UART_XMIT_SIZE); + qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_ON_SEND); + qcom_geni_serial_setup_tx(uport, xmit_size); ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size, @@ -1071,8 +1100,10 @@ static irqreturn_t qcom_geni_serial_isr(int isr, void *dev) } if (dma) { - if (dma_tx_status & TX_DMA_DONE) + if (dma_tx_status & TX_DMA_DONE) { qcom_geni_serial_handle_tx_dma(uport); + qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_AFTER_SEND); + } if (dma_rx_status) { if (dma_rx_status & RX_RESET_DONE) @@ -1610,6 +1641,24 @@ static void qcom_geni_serial_pm(struct uart_port *uport, } } +/** + * qcom_geni_rs485_config - Configure RS485 settings for the UART port + * @uport: Pointer to the UART port structure + * @termios: Pointer to the termios structure + * @rs485: Pointer to the RS485 configuration structure + * This function configures the RTS (Request to Send) pin behavior for RS485 mode. + * When RS485 mode is enabled, the RTS pin is kept in default ACTIVE HIGH state. + * Return: Always returns 0. + */ + +static int qcom_geni_rs485_config(struct uart_port *uport, + struct ktermios *termios, struct serial_rs485 *rs485) +{ + qcom_geni_set_rs485_mode(uport, SER_RS485_ENABLED); + + return 0; +} + static const struct uart_ops qcom_geni_console_pops = { .tx_empty = qcom_geni_serial_tx_empty, .stop_tx = qcom_geni_serial_stop_tx_fifo, @@ -1702,6 +1751,8 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return -EINVAL; uport->mapbase = res->start; + uport->rs485_config = qcom_geni_rs485_config; + uport->rs485_supported = qcom_geni_rs485_supported; port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; @@ -1767,6 +1818,10 @@ static int qcom_geni_serial_probe(struct platform_device *pdev) return ret; } + ret = uart_get_rs485_mode(uport); + if (ret) + return ret; + ret = uart_add_one_port(drv, uport); if (ret) return ret; |