]> git.itanic.dy.fi Git - linux-stable/commitdiff
serial: 8250: 8250_omap: Support native RS485
authorLukas Wunner <lukas@wunner.de>
Sun, 16 Oct 2022 08:02:00 +0000 (10:02 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 3 Nov 2022 02:34:09 +0000 (03:34 +0100)
Recent TI Sitara SoCs such as AM64/AM65 have gained the ability to
automatically assert RTS when data is transmitted, obviating the need
to emulate this functionality in software.

The feature is controlled through new DIR_EN and DIR_POL bits in the
Mode Definition Register 3.  For details see page 8783 and 8890 of the
AM65 TRM:  https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf

Cc: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Cc: Su Bao Cheng <baocheng.su@siemens.com>
Cc: Vignesh Raghavendra <vigneshr@ti.com>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Bin Liu <b-liu@ti.com>
Tested-by: Zeng Chao <chao.zeng@siemens.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Link: https://lore.kernel.org/r/e9f25f5c9200a35d3162973c2b45d6b892cc9bf2.1665906869.git.lukas@wunner.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_omap.c

index 41b8c6b27136a9cdb624e5c7c7c8bbedaeb26b53..1c8a48fdc8f24479ed864987613d4a2b72320b5a 100644 (file)
@@ -44,6 +44,7 @@
 #define        UART_HAS_EFR2                   BIT(4)
 #define UART_HAS_RHR_IT_DIS            BIT(5)
 #define UART_RX_TIMEOUT_QUIRK          BIT(6)
+#define UART_HAS_NATIVE_RS485          BIT(7)
 
 #define OMAP_UART_FCR_RX_TRIG          6
 #define OMAP_UART_FCR_TX_TRIG          4
 #define UART_OMAP_IER2                 0x1B
 #define UART_OMAP_IER2_RHR_IT_DIS      BIT(2)
 
+/* Mode Definition Register 3 */
+#define UART_OMAP_MDR3                 0x20
+#define UART_OMAP_MDR3_DIR_POL         BIT(3)
+#define UART_OMAP_MDR3_DIR_EN          BIT(4)
+
 /* Enhanced features register 2 */
 #define UART_OMAP_EFR2                 0x23
 #define UART_OMAP_EFR2_TIMEOUT_BEHAVE  BIT(6)
@@ -112,6 +118,7 @@ struct omap8250_priv {
        int line;
        u8 habit;
        u8 mdr1;
+       u8 mdr3;
        u8 efr;
        u8 scr;
        u8 wer;
@@ -343,7 +350,10 @@ static void omap8250_restore_regs(struct uart_8250_port *up)
 
        up->port.ops->set_mctrl(&up->port, up->port.mctrl);
 
-       if (up->port.rs485.flags & SER_RS485_ENABLED)
+       serial_out(up, UART_OMAP_MDR3, priv->mdr3);
+
+       if (up->port.rs485.flags & SER_RS485_ENABLED &&
+           up->port.rs485_config == serial8250_em485_config)
                serial8250_em485_stop_tx(up);
 }
 
@@ -792,6 +802,74 @@ static void omap_8250_unthrottle(struct uart_port *port)
        pm_runtime_put_autosuspend(port->dev);
 }
 
+static int omap8250_rs485_config(struct uart_port *port,
+                                struct ktermios *termios,
+                                struct serial_rs485 *rs485)
+{
+       struct omap8250_priv *priv = port->private_data;
+       struct uart_8250_port *up = up_to_u8250p(port);
+       u32 fixed_delay_rts_before_send = 0;
+       u32 fixed_delay_rts_after_send = 0;
+       unsigned int baud;
+
+       /*
+        * There is a fixed delay of 3 bit clock cycles after the TX shift
+        * register is going empty to allow time for the stop bit to transition
+        * through the transceiver before direction is changed to receive.
+        *
+        * Additionally there appears to be a 1 bit clock delay between writing
+        * to the THR register and transmission of the start bit, per page 8783
+        * of the AM65 TRM:  https://www.ti.com/lit/ug/spruid7e/spruid7e.pdf
+        */
+       if (priv->quot) {
+               if (priv->mdr1 & UART_OMAP_MDR1_16X_MODE)
+                       baud = port->uartclk / (16 * priv->quot);
+               else
+                       baud = port->uartclk / (13 * priv->quot);
+
+               fixed_delay_rts_after_send  = 3 * MSEC_PER_SEC / baud;
+               fixed_delay_rts_before_send = 1 * MSEC_PER_SEC / baud;
+       }
+
+       /*
+        * Fall back to RS485 software emulation if the UART is missing
+        * hardware support, if the device tree specifies an mctrl_gpio
+        * (indicates that RTS is unavailable due to a pinmux conflict)
+        * or if the requested delays exceed the fixed hardware delays.
+        */
+       if (!(priv->habit & UART_HAS_NATIVE_RS485) ||
+           mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS) ||
+           rs485->delay_rts_after_send  > fixed_delay_rts_after_send ||
+           rs485->delay_rts_before_send > fixed_delay_rts_before_send) {
+               priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN;
+               serial_out(up, UART_OMAP_MDR3, priv->mdr3);
+
+               port->rs485_config = serial8250_em485_config;
+               return serial8250_em485_config(port, termios, rs485);
+       }
+
+       rs485->delay_rts_after_send  = fixed_delay_rts_after_send;
+       rs485->delay_rts_before_send = fixed_delay_rts_before_send;
+
+       if (rs485->flags & SER_RS485_ENABLED)
+               priv->mdr3 |= UART_OMAP_MDR3_DIR_EN;
+       else
+               priv->mdr3 &= ~UART_OMAP_MDR3_DIR_EN;
+
+       /*
+        * Retain same polarity semantics as RS485 software emulation,
+        * i.e. SER_RS485_RTS_ON_SEND means driving RTS low on send.
+        */
+       if (rs485->flags & SER_RS485_RTS_ON_SEND)
+               priv->mdr3 &= ~UART_OMAP_MDR3_DIR_POL;
+       else
+               priv->mdr3 |= UART_OMAP_MDR3_DIR_POL;
+
+       serial_out(up, UART_OMAP_MDR3, priv->mdr3);
+
+       return 0;
+}
+
 #ifdef CONFIG_SERIAL_8250_DMA
 static int omap_8250_rx_dma(struct uart_8250_port *p);
 
@@ -1241,7 +1319,7 @@ static struct omap8250_dma_params am33xx_dma = {
 static struct omap8250_platdata am654_platdata = {
        .dma_params     = &am654_dma,
        .habit          = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS |
-                         UART_RX_TIMEOUT_QUIRK,
+                         UART_RX_TIMEOUT_QUIRK | UART_HAS_NATIVE_RS485,
 };
 
 static struct omap8250_platdata am33xx_platdata = {
@@ -1334,7 +1412,8 @@ static int omap8250_probe(struct platform_device *pdev)
        up.port.shutdown = omap_8250_shutdown;
        up.port.throttle = omap_8250_throttle;
        up.port.unthrottle = omap_8250_unthrottle;
-       up.port.rs485_config = serial8250_em485_config;
+       up.port.rs485_config = omap8250_rs485_config;
+       /* same rs485_supported for software emulation and native RS485 */
        up.port.rs485_supported = serial8250_em485_supported;
        up.rs485_start_tx = serial8250_em485_start_tx;
        up.rs485_stop_tx = serial8250_em485_stop_tx;