RK RS485流控驱动配置
说明
RS485这个协议最常用了,驱动主要是流控脚配置。TXRX和RS232驱动基本一样。
流控分为两种:
- 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的。
- 普通GPIO为流控脚
步骤中主要讲的是这个。硬件上串口凑不成一组,这样的话性能没有第一种好。下面给个patch,RK的FAE给的,我修改了下,在RK的内核6.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";
};
- 普通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手册上的关于自动流控的配置:


本文来自博客园,作者:杨旭0324,转载请注明原文链接:https://www.cnblogs.com/allalonewithyou/p/19608326,个人邮箱:yaonie4444@foxmail.com

浙公网安备 33010602011771号