serial: 8250_pci1xxxx: Add RS485 support to quad-uart driver
authorKumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
Tue, 7 Feb 2023 16:48:13 +0000 (22:18 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 8 Feb 2023 12:10:15 +0000 (13:10 +0100)
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>
drivers/tty/serial/8250/8250_pci1xxxx.c

index ea04c01..58f47d1 100644 (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)