Commit 08cedda0 authored by Kumaravel Thiagarajan's avatar Kumaravel Thiagarajan Committed by Greg Kroah-Hartman
Browse files

serial: 8250_pci1xxxx: Add RS485 support to quad-uart driver



pci1xxxx uart supports RS485 mode of operation in the hardware with
auto-direction control with configurable delay for releasing RTS after
the transmission. This patch adds support for the RS485 mode.

Co-developed-by: default avatarTharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Signed-off-by: default avatarTharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Signed-off-by: default avatarKumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
Reviewed-by: default avatarIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20230207164814.3104605-4-kumaravel.thiagarajan@microchip.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 32bb477f
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -164,6 +164,54 @@ static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud,
	       port->membase + UART_BAUD_CLK_DIVISOR_REG);
}

static int pci1xxxx_rs485_config(struct uart_port *port,
				 struct ktermios *termios,
				 struct serial_rs485 *rs485)
{
	u32 delay_in_baud_periods;
	u32 baud_period_in_ns;
	u32 mode_cfg = 0;
	u32 clock_div;

	/*
	 * pci1xxxx's uart hardware supports only RTS delay after
	 * Tx and in units of bit times to a maximum of 15
	 */
	if (rs485->flags & SER_RS485_ENABLED) {
		mode_cfg = ADCL_CFG_EN | ADCL_CFG_PIN_SEL;

		if (!(rs485->flags & SER_RS485_RTS_ON_SEND))
			mode_cfg |= ADCL_CFG_POL_SEL;

		if (rs485->delay_rts_after_send) {
			clock_div = readl(port->membase + UART_BAUD_CLK_DIVISOR_REG);
			baud_period_in_ns =
				FIELD_GET(BAUD_CLOCK_DIV_INT_MSK, clock_div) *
				UART_BIT_SAMPLE_CNT;
			delay_in_baud_periods =
				rs485->delay_rts_after_send * NSEC_PER_MSEC /
				baud_period_in_ns;
			delay_in_baud_periods =
				min_t(u32, delay_in_baud_periods,
				      FIELD_MAX(ADCL_CFG_RTS_DELAY_MASK));
			mode_cfg |= FIELD_PREP(ADCL_CFG_RTS_DELAY_MASK,
					   delay_in_baud_periods);
			rs485->delay_rts_after_send =
				baud_period_in_ns * delay_in_baud_periods /
				NSEC_PER_MSEC;
		}
	}
	writel(mode_cfg, port->membase + ADCL_CFG_REG);
	return 0;
}

static const struct serial_rs485 pci1xxxx_rs485_supported = {
	.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
		 SER_RS485_RTS_AFTER_SEND,
	.delay_rts_after_send = 1,
	/* Delay RTS before send is not supported */
};

static int pci1xxxx_setup(struct pci_dev *pdev,
			  struct uart_8250_port *port, int port_idx)
{
@@ -174,6 +222,8 @@ static int pci1xxxx_setup(struct pci_dev *pdev,
	port->port.set_termios = serial8250_do_set_termios;
	port->port.get_divisor = pci1xxxx_get_divisor;
	port->port.set_divisor = pci1xxxx_set_divisor;
	port->port.rs485_config = pci1xxxx_rs485_config;
	port->port.rs485_supported = pci1xxxx_rs485_supported;

	ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0);
	if (ret < 0)