Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Anup Kulkarni and committed by
Greg Kroah-Hartman
4fcc287f 341a22fa

+56 -1
+56 -1
drivers/tty/serial/qcom_geni_serial.c
··· 192 192 }, 193 193 }; 194 194 195 + static const struct serial_rs485 qcom_geni_rs485_supported = { 196 + .flags = SER_RS485_ENABLED | SER_RS485_RTS_AFTER_SEND | SER_RS485_RTS_ON_SEND, 197 + }; 198 + 199 + /** 200 + * qcom_geni_set_rs485_mode - Set RTS pin state for RS485 mode 201 + * @uport: UART port 202 + * @flag: RS485 flag to determine RTS polarity 203 + * 204 + * Enables manual RTS control for RS485. Sets RTS to READY or NOT_READY 205 + * based on the specified flag if RS485 mode is enabled. 206 + */ 207 + static void qcom_geni_set_rs485_mode(struct uart_port *uport, u32 flag) 208 + { 209 + if (!(uport->rs485.flags & SER_RS485_ENABLED)) 210 + return; 211 + 212 + u32 rfr = UART_MANUAL_RFR_EN; 213 + 214 + if (uport->rs485.flags & flag) 215 + rfr |= UART_RFR_NOT_READY; 216 + else 217 + rfr |= UART_RFR_READY; 218 + 219 + writel(rfr, uport->membase + SE_UART_MANUAL_RFR); 220 + } 221 + 195 222 static int qcom_geni_serial_request_port(struct uart_port *uport) 196 223 { 197 224 struct platform_device *pdev = to_platform_device(uport->dev); ··· 691 664 xmit_size = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, 692 665 UART_XMIT_SIZE); 693 666 667 + qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_ON_SEND); 668 + 694 669 qcom_geni_serial_setup_tx(uport, xmit_size); 695 670 696 671 ret = geni_se_tx_dma_prep(&port->se, tail, xmit_size, ··· 1100 1071 } 1101 1072 1102 1073 if (dma) { 1103 - if (dma_tx_status & TX_DMA_DONE) 1074 + if (dma_tx_status & TX_DMA_DONE) { 1104 1075 qcom_geni_serial_handle_tx_dma(uport); 1076 + qcom_geni_set_rs485_mode(uport, SER_RS485_RTS_AFTER_SEND); 1077 + } 1105 1078 1106 1079 if (dma_rx_status) { 1107 1080 if (dma_rx_status & RX_RESET_DONE) ··· 1641 1610 } 1642 1611 } 1643 1612 1613 + /** 1614 + * qcom_geni_rs485_config - Configure RS485 settings for the UART port 1615 + * @uport: Pointer to the UART port structure 1616 + * @termios: Pointer to the termios structure 1617 + * @rs485: Pointer to the RS485 configuration structure 1618 + * This function configures the RTS (Request to Send) pin behavior for RS485 mode. 1619 + * When RS485 mode is enabled, the RTS pin is kept in default ACTIVE HIGH state. 1620 + * Return: Always returns 0. 1621 + */ 1622 + 1623 + static int qcom_geni_rs485_config(struct uart_port *uport, 1624 + struct ktermios *termios, struct serial_rs485 *rs485) 1625 + { 1626 + qcom_geni_set_rs485_mode(uport, SER_RS485_ENABLED); 1627 + 1628 + return 0; 1629 + } 1630 + 1644 1631 static const struct uart_ops qcom_geni_console_pops = { 1645 1632 .tx_empty = qcom_geni_serial_tx_empty, 1646 1633 .stop_tx = qcom_geni_serial_stop_tx_fifo, ··· 1751 1702 return -EINVAL; 1752 1703 uport->mapbase = res->start; 1753 1704 1705 + uport->rs485_config = qcom_geni_rs485_config; 1706 + uport->rs485_supported = qcom_geni_rs485_supported; 1754 1707 port->tx_fifo_depth = DEF_FIFO_DEPTH_WORDS; 1755 1708 port->rx_fifo_depth = DEF_FIFO_DEPTH_WORDS; 1756 1709 port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; ··· 1817 1766 dev_err(uport->dev, "Failed to get IRQ ret %d\n", ret); 1818 1767 return ret; 1819 1768 } 1769 + 1770 + ret = uart_get_rs485_mode(uport); 1771 + if (ret) 1772 + return ret; 1820 1773 1821 1774 ret = uart_add_one_port(drv, uport); 1822 1775 if (ret)