diff options
author | Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com> | 2023-02-07 19:48:13 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2023-02-08 15:10:15 +0300 |
commit | 08cedda0b3852c0f47a94e64eb586b5507d797f1 (patch) | |
tree | 6a0a9f4740227b4a8f135052e1294991db6411df /drivers/tty/serial | |
parent | 32bb477fa7bf386ce87837691c4672854a5231e4 (diff) | |
download | linux-08cedda0b3852c0f47a94e64eb586b5507d797f1.tar.xz |
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>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r-- | drivers/tty/serial/8250/8250_pci1xxxx.c | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_pci1xxxx.c b/drivers/tty/serial/8250/8250_pci1xxxx.c index ea04c014edb9..58f47d18f7d8 100644 --- a/drivers/tty/serial/8250/8250_pci1xxxx.c +++ b/drivers/tty/serial/8250/8250_pci1xxxx.c @@ -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) |