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: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Signed-off-by: Tharun Kumar P <tharunkumar.pasumarthi@microchip.com>
Signed-off-by: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20230207164814.3104605-4-kumaravel.thiagarajan@microchip.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Kumaravel Thiagarajan 2023-02-07 22:18:13 +05:30 committed by Greg Kroah-Hartman
parent 32bb477fa7
commit 08cedda0b3

View File

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