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>
This commit is contained in:
Anup Kulkarni 2025-06-03 16:31:45 +05:30 committed by Greg Kroah-Hartman
parent 341a22fa05
commit 4fcc287f3c

View File

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