Commit 4fcc287f authored by Anup Kulkarni's avatar Anup Kulkarni Committed by Greg Kroah-Hartman
Browse files

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: default avatarAnup Kulkarni <quic_anupkulk@quicinc.com>
Link: https://lore.kernel.org/r/20250603110145.3835111-1-quic_anupkulk@quicinc.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 341a22fa
Loading
Loading
Loading
Loading
+56 −1
Original line number Diff line number Diff line
@@ -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;