]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
8250: microchip: pci1xxxx: Add workaround for RTS bit toggle
authorRengarajan S <rengarajan.s@microchip.com>
Wed, 18 Dec 2024 09:40:17 +0000 (15:10 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Dec 2024 17:59:44 +0000 (18:59 +0100)
In the B0 revision, the RTS pin remains high due to incorrect hardware
mapping. To address this issue, enable auto-direction control with the
RTS bit in ADCL_CFG_REG. This configuration ensures that the RTS pin
goes low when the terminal is opened and high when the terminal is
closed. Additionally, we reset the step counter for Rx and Tx engines
by writing into FRAC_DIV_CFG_REG.

Signed-off-by: Rengarajan S <rengarajan.s@microchip.com>
Link: https://lore.kernel.org/r/20241218094017.18290-1-rengarajan.s@microchip.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_pci1xxxx.c

index 838f181f929bf0f72dd3077938c93a28835eb755..e9c51d4e447dd29bcb63f10b1244501e59aeeb02 100644 (file)
 #define UART_TX_BYTE_FIFO                      0x00
 #define UART_FIFO_CTL                          0x02
 
+#define UART_MODEM_CTL_REG                     0x04
+#define UART_MODEM_CTL_RTS_SET                 BIT(1)
+
+#define UART_LINE_STAT_REG                     0x05
+#define UART_LINE_XMIT_CHECK_MASK              GENMASK(6, 5)
+
 #define UART_ACTV_REG                          0x11
 #define UART_BLOCK_SET_ACTIVE                  BIT(0)
 
 #define UART_BIT_SAMPLE_CNT_16                 16
 #define BAUD_CLOCK_DIV_INT_MSK                 GENMASK(31, 8)
 #define ADCL_CFG_RTS_DELAY_MASK                        GENMASK(11, 8)
+#define FRAC_DIV_TX_END_POINT_MASK             GENMASK(23, 20)
 
 #define UART_WAKE_REG                          0x8C
 #define UART_WAKE_MASK_REG                     0x90
 #define UART_BST_STAT_LSR_FRAME_ERR            0x8000000
 #define UART_BST_STAT_LSR_THRE                 0x20000000
 
+#define GET_MODEM_CTL_RTS_STATUS(reg)          ((reg) & UART_MODEM_CTL_RTS_SET)
+#define GET_RTS_PIN_STATUS(val)                        (((val) & TIOCM_RTS) >> 1)
+#define RTS_TOGGLE_STATUS_MASK(val, reg)       (GET_MODEM_CTL_RTS_STATUS(reg) \
+                                                != GET_RTS_PIN_STATUS(val))
+
 struct pci1xxxx_8250 {
        unsigned int nr;
        u8 dev_rev;
@@ -254,6 +266,47 @@ static void pci1xxxx_set_divisor(struct uart_port *port, unsigned int baud,
               port->membase + UART_BAUD_CLK_DIVISOR_REG);
 }
 
+static void pci1xxxx_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       u32 fract_div_cfg_reg;
+       u32 line_stat_reg;
+       u32 modem_ctl_reg;
+       u32 adcl_cfg_reg;
+
+       adcl_cfg_reg = readl(port->membase + ADCL_CFG_REG);
+
+       /* HW is responsible in ADCL_EN case */
+       if ((adcl_cfg_reg & (ADCL_CFG_EN | ADCL_CFG_PIN_SEL)))
+               return;
+
+       modem_ctl_reg = readl(port->membase + UART_MODEM_CTL_REG);
+
+       serial8250_do_set_mctrl(port, mctrl);
+
+       if (RTS_TOGGLE_STATUS_MASK(mctrl, modem_ctl_reg)) {
+               line_stat_reg = readl(port->membase + UART_LINE_STAT_REG);
+               if (line_stat_reg & UART_LINE_XMIT_CHECK_MASK) {
+                       fract_div_cfg_reg = readl(port->membase +
+                                                 FRAC_DIV_CFG_REG);
+
+                       writel((fract_div_cfg_reg &
+                              ~(FRAC_DIV_TX_END_POINT_MASK)),
+                              port->membase + FRAC_DIV_CFG_REG);
+
+                       /* Enable ADC and set the nRTS pin */
+                       writel((adcl_cfg_reg | (ADCL_CFG_EN |
+                              ADCL_CFG_PIN_SEL)),
+                              port->membase + ADCL_CFG_REG);
+
+                       /* Revert to the original settings */
+                       writel(adcl_cfg_reg, port->membase + ADCL_CFG_REG);
+
+                       writel(fract_div_cfg_reg, port->membase +
+                              FRAC_DIV_CFG_REG);
+               }
+       }
+}
+
 static int pci1xxxx_rs485_config(struct uart_port *port,
                                 struct ktermios *termios,
                                 struct serial_rs485 *rs485)
@@ -631,9 +684,14 @@ static int pci1xxxx_setup(struct pci_dev *pdev,
        port->port.rs485_config = pci1xxxx_rs485_config;
        port->port.rs485_supported = pci1xxxx_rs485_supported;
 
-       /* From C0 rev Burst operation is supported */
+       /*
+        * C0 and later revisions support Burst operation.
+        * RTS workaround in mctrl is applicable only to B0.
+        */
        if (rev >= 0xC0)
                port->port.handle_irq = pci1xxxx_handle_irq;
+       else if (rev == 0xB0)
+               port->port.set_mctrl = pci1xxxx_set_mctrl;
 
        ret = serial8250_pci_setup_port(pdev, port, 0, PORT_OFFSET * port_idx, 0);
        if (ret < 0)