RK RS485流控驱动配置

说明

RS485这个协议最常用了,驱动主要是流控脚配置。TXRX和RS232驱动基本一样。

流控分为两种:

  1. RK里面的串口脚为一组。
/omit-if-no-ref/
uart7m0_xfer: uart7m0-xfer {
    rockchip,pins =
        /* uart7_rx_m0 */
        <2 RK_PB7 9 &pcfg_pull_up>,
        /* uart7_tx_m0 */
        <2 RK_PB6 9 &pcfg_pull_up>;
};

/omit-if-no-ref/
uart7m0_ctsn: uart7m0-ctsn {
    rockchip,pins =
        /* uart7m0_ctsn */
        <2 RK_PB4 9 &pcfg_pull_none>;
};
/omit-if-no-ref/
uart7m0_rtsn: uart7m0-rtsn {
    rockchip,pins =
        /* uart7m0_rtsn */
        <2 RK_PB5 9 &pcfg_pull_none>;
};

硬件上使用的是一组的串口,使用了对应的RTS脚,这个配置下设备树即可,RK的驱动是支持的,使用寄存器中断,性能和压力测试也是OK的。

  1. 普通GPIO为流控脚

步骤中主要讲的是这个。硬件上串口凑不成一组,这样的话性能没有第一种好。下面给个patch,RK的FAE给的,我修改了下,在RK的内核6.1上都是正常使用的。

步骤

  1. RK里面的串口脚为一组。

设备树:

&uart7 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart7m0_xfer &uart7m0_rtsn>;
    # 使用rs485功能
	linux,rs485-enabled-at-boot-time;
    # rs485 rts脚低有效,即流控脚。
	rs485-rts-active-low;
	status = "okay";
};
  1. 普通GPIO为流控脚

设备树:

&uart2 {
	pinctrl-names = "default";
	pinctrl-0 = <&uart2m0_xfer>;
	linux,rs485-enabled-at-boot-time;
	rs485-rts-active-low;
	rs485-de-gpios = <&gpio2 RK_PD6 GPIO_ACTIVE_HIGH>;
	status = "okay";
};

驱动:

调用serial8250_em485函数收发。

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 272256cc80f3..337b547db432 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1014,11 +1014,11 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
                uart->capabilities      = up->capabilities;
                uart->port.throttle     = up->port.throttle;
                uart->port.unthrottle   = up->port.unthrottle;
-               uart->port.rs485_config = up->port.rs485_config;
+               uart->port.rs485_config = serial8250_em485_config;
                uart->port.rs485_supported = up->port.rs485_supported;
                uart->port.rs485        = up->port.rs485;
-               uart->rs485_start_tx    = up->rs485_start_tx;
-               uart->rs485_stop_tx     = up->rs485_stop_tx;
+               uart->rs485_start_tx    = serial8250_em485_start_tx;
+               uart->rs485_stop_tx     = serial8250_em485_stop_tx;
                uart->lsr_save_mask     = up->lsr_save_mask;
                uart->dma               = up->dma;
 #ifdef CONFIG_ARCH_ROCKCHIP

RK给的:

commit bef4eb9d808015e2369faa4c85f1e9505d456901
Author: 
Date:   Mon Jun 17 10:54:37 2024 +0800

    serial: 8250: support em485

    Change-Id: I56966eda492d62632d430138d2beddf5690fa9c1
    Signed-off-by: 

diff --git a/Documentation/devicetree/bindings/serial/rs485.yaml b/Documentation/devicetree/bindings/serial/rs485.yaml
index 428da6faef3a..8da691837799 100644
--- a/Documentation/devicetree/bindings/serial/rs485.yaml
+++ b/Documentation/devicetree/bindings/serial/rs485.yaml
@@ -50,6 +50,9 @@ properties:
     description: GPIO pin to enable RS485 bus termination.
     maxItems: 1

+  rs485-de-gpios:
+    description: if using other gpio instead of rts, set this gpio.
+
 additionalProperties: true

 ...
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 983391f500f1..0b6628ea5ea0 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -185,7 +185,9 @@ static inline bool serial8250_set_THRI(struct uart_8250_port *up)
                return false;
        up->ier |= UART_IER_THRI;
 #if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
-       up->ier |= UART_IER_PTIME;
+       /* Disable PTIME when it is rs485 mode*/
+       if (!up->em485)
+               up->ier |= UART_IER_PTIME;
 #endif
        serial_out(up, UART_IER, up->ier);
        return true;
@@ -197,7 +199,8 @@ static inline bool serial8250_clear_THRI(struct uart_8250_port *up)
                return false;
        up->ier &= ~UART_IER_THRI;
 #if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
-       up->ier &= ~UART_IER_PTIME;
+       if (!up->em485)
+               up->ier &= ~UART_IER_PTIME;
 #endif
        serial_out(up, UART_IER, up->ier);
        return true;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index dd019fe86619..bfcc6fbea823 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -598,7 +598,11 @@ static int dw8250_probe(struct platform_device *pdev)
        p->serial_out   = dw8250_serial_out;
        p->set_ldisc    = dw8250_set_ldisc;
        p->set_termios  = dw8250_set_termios;
-
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       p->rs485_config = serial8250_em485_config;
+       uart.rs485_start_tx = serial8250_em485_start_tx;
+       uart.rs485_stop_tx = serial8250_em485_stop_tx;
+#endif
        p->membase = devm_ioremap(dev, regs->start, resource_size(regs));
        if (!p->membase)
                return -ENOMEM;
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 9602ecf6d2e8..b3139f25ee01 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -55,6 +55,14 @@
 #define DEBUG_AUTOCONF(fmt...) do { } while (0)
 #endif

+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+#if 0
+#define DEBUG_EM485(fmt...)    printk(fmt)
+#else
+#define DEBUG_EM485(fmt...)    do { } while (0)
+#endif
+#endif
+
 /*
  * Here we define the default xmit fifo size used for each type of UART.
  */
@@ -612,6 +620,10 @@ EXPORT_SYMBOL_GPL(serial8250_rpm_put);
  */
 static int serial8250_em485_init(struct uart_8250_port *p)
 {
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       DEBUG_EM485("%s ttyS%d\n", __func__, p->port.line);
+#endif
+
        if (p->em485)
                goto deassert_rts;

@@ -1445,12 +1457,32 @@ void serial8250_em485_stop_tx(struct uart_8250_port *p)
 {
        unsigned char mcr = serial8250_in_MCR(p);

+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       int value = 0;
+
+       if (p->port.rs485_de_gpio) {
+               if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
+                       value = 0;
+               else
+                       value = 1;
+
+               gpiod_set_value(p->port.rs485_de_gpio, value);
+               DEBUG_EM485("%s ttyS%d gpio:%d\n", __func__, p->port.line, value);
+       } else {
+               if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
+                       mcr |= UART_MCR_RTS;
+               else
+                       mcr &= ~UART_MCR_RTS;
+               serial8250_out_MCR(p, mcr);
+               DEBUG_EM485("%s ttyS%d mcr:%02x\n", __func__, p->port.line, mcr);
+       }
+#else
        if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND)
                mcr |= UART_MCR_RTS;
        else
                mcr &= ~UART_MCR_RTS;
        serial8250_out_MCR(p, mcr);
-
+#endif
        /*
         * Empty the RX FIFO, we are not interested in anything
         * received during the half-duplex transmission.
@@ -1494,6 +1526,10 @@ static void __stop_tx_rs485(struct uart_8250_port *p, u64 stop_delay)
 {
        struct uart_8250_em485 *em485 = p->em485;

+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       DEBUG_EM485("%s ttyS%d\n", __func__, p->port.line);
+#endif
+
        stop_delay += (u64)p->port.rs485.delay_rts_after_send * NSEC_PER_MSEC;

        /*
@@ -1531,6 +1567,9 @@ static inline void __stop_tx(struct uart_8250_port *p)
                 * for emptying of the shift register.
                 */
                if (!(lsr & UART_LSR_TEMT)) {
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+                       stop_delay = p->port.frame_time + 10000;
+#else
                        if (!(p->capabilities & UART_CAP_NOTEMT))
                                return;
                        /*
@@ -1541,6 +1580,7 @@ static inline void __stop_tx(struct uart_8250_port *p)
                         * Roughly estimate 1 extra bit here with / 7.
                         */
                        stop_delay = p->port.frame_time + DIV_ROUND_UP(p->port.frame_time, 7);
+#endif
                }

                __stop_tx_rs485(p, stop_delay);
@@ -1610,15 +1650,30 @@ static inline void __start_tx(struct uart_port *port)
 void serial8250_em485_start_tx(struct uart_8250_port *up)
 {
        unsigned char mcr = serial8250_in_MCR(up);
-
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       int value = 0;
+#endif
        if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX))
                serial8250_stop_rx(&up->port);
-
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       if (up->port.rs485_de_gpio) {
+               if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND)
+                       value = 0;
+               else
+                       value = 1;
+               gpiod_set_value(up->port.rs485_de_gpio, value);
+               DEBUG_EM485("%s ttyS%d gpio:%d\n", __func__, up->port.line, value);
+               return;
+       }
+#endif
        if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND)
                mcr |= UART_MCR_RTS;
        else
                mcr &= ~UART_MCR_RTS;
        serial8250_out_MCR(up, mcr);
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       DEBUG_EM485("%s ttyS%d mcr:%02x\n", __func__, up->port.line, mcr);
+#endif
 }
 EXPORT_SYMBOL_GPL(serial8250_em485_start_tx);

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 19a53801ff9e..051214f7ae4f 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -3500,6 +3500,12 @@ int uart_get_rs485_mode(struct uart_port *port)
        if (port->rs485_term_gpio)
                port->rs485_supported.flags |= SER_RS485_TERMINATE_BUS;

+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       port->rs485_de_gpio = devm_gpiod_get_optional(dev, "rs485-de",
+                                                       GPIOD_OUT_LOW);
+       if (IS_ERR(port->rs485_de_gpio))
+               port->rs485_de_gpio = NULL;
+#endif
        return 0;
 }
 EXPORT_SYMBOL_GPL(uart_get_rs485_mode);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 5a83db0ac763..5157e59fef08 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -580,6 +580,9 @@ struct uart_port {
        struct serial_rs485     rs485;
        struct serial_rs485     rs485_supported;        /* Supported mask for serial_rs485 */
        struct gpio_desc        *rs485_term_gpio;       /* enable RS485 bus termination */
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+       struct gpio_desc        *rs485_de_gpio;         /* enable RS485 de */
+#endif
        struct serial_iso7816   iso7816;
        void                    *private_data;          /* generic platform data pointer */
 };

测试

正常收发测试即可,压力测试。

2026年5月26日补充:

RK3568的TRM手册上的关于自动流控的配置:

alt text

alt text

posted @ 2026-02-12 14:17  杨旭0324  阅读(166)  评论(0)    收藏  举报