Linux RS485串口驱动修改方法--基于Rockchip及Amlogic验证

在设备开发过程中,经常需要使用到RS485,但很多驱动工程师的处理方式基本都是:默认收,告诉应用自己去控制gpio引脚已达到收发数据的目的。其实可以通过修改驱动来控制收发,应用不需要关心控制IO,直接按串口的使用方式即可。

不同的平台代码会有差异,但思路是一样。

基本思路:

        1)通过dts的uart节点compatible,找到对应的驱动代码。如rk的compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart"; 可以在kernel/driver目录中找到对应驱动代码:tty/serial/8250

        2) 需要修改dts,区别于RS232等串口,以识别是RS485串口以及相应的GPIO引脚、高低电平及延迟参数。示例:

&uart4 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&uart4m1_xfer>;
	rts-gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>;
	rs485-rts-active-low;
	rs485-rts-delay = <5 100>; // in milliseconds
	linux,rs485-enabled-at-boot-time;
};

        3)修改驱动代码,两条线:(1) 加载dts配置(xxx_probe) 2) 发送数据前,根据配置改变引脚状态,发送完成后,根据配置恢复引脚状态,改动示例:

diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h
index 3fdd0dee8b41..22ee0720326e 100644
--- a/include/uapi/linux/serial.h
+++ b/include/uapi/linux/serial.h
@@ -130,6 +130,7 @@ struct serial_rs485 {
 	__u32	delay_rts_after_send;	/* Delay after send (milliseconds) */
 	__u32	padding[5];		/* Memory is cheap, new structs
 					   are a royal PITA .. */
+	__u32	rts_gpio;
 };
 
 #endif /* _UAPI_LINUX_SERIAL_H */
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 2882547b8553..288d4a453d89 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -24,7 +24,8 @@
 #include <linux/clk.h>
 #include <linux/reset.h>
 #include <linux/pm_runtime.h>
-
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <asm/byteorder.h>
 
 #include "8250.h"
@@ -537,6 +538,81 @@ static void dw8250_setup_port(struct uart_port *p)
 		up->capabilities |= UART_CAP_IRDA;
 }
 
+static int dw8250_rs485_config(struct uart_port *port,
+				  struct serial_rs485 *rs485)
+{
+	struct uart_8250_port *up = up_to_u8250p(port);
+	/* Clamp the delays to [0, 100ms] */
+	rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
+	rs485->delay_rts_after_send  = min(rs485->delay_rts_after_send, 100U);
+
+	port->rs485 = *rs485;
+	/*
+	 * Both serial8250_em485_init and serial8250_em485_destroy
+	 * are idempotent
+	 */
+	if (rs485->flags & SER_RS485_ENABLED) {
+		int ret = serial8250_em485_init(up);
+		if (ret) {
+			rs485->flags &= ~SER_RS485_ENABLED;
+			port->rs485.flags &= ~SER_RS485_ENABLED;
+		}
+
+		gpio_set_value(rs485->rts_gpio, (rs485->flags & SER_RS485_RTS_AFTER_SEND ? 1 : 0));
+		return ret;
+	}
+
+	serial8250_em485_destroy(up);
+
+	return 0;
+}
+
+static int dw8250_probe_rs485(struct uart_8250_port *up,
+				   struct device_node *np)
+{
+	struct serial_rs485 *rs485conf = &up->port.rs485;
+	int ret;
+	rs485conf->flags = 0;
+	rs485conf->rts_gpio = -EINVAL;
+
+	if (!np)
+		return 0;
+
+	uart_get_rs485_mode(up->port.dev, rs485conf);
+	if(rs485conf->flags & SER_RS485_ENABLED) {
+		if (of_property_read_bool(np, "rs485-rts-active-high")) {
+			rs485conf->flags |= SER_RS485_RTS_ON_SEND;
+			rs485conf->flags &= ~SER_RS485_RTS_AFTER_SEND;
+		} else {
+			rs485conf->flags &= ~SER_RS485_RTS_ON_SEND;
+			rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+		}
+
+		/* check for tx enable gpio */
+		rs485conf->rts_gpio = of_get_named_gpio(np, "rts-gpio", 0);
+		if (gpio_is_valid(rs485conf->rts_gpio)) {
+			ret = devm_gpio_request(up->port.dev, rs485conf->rts_gpio, "snps,dw-apb-uart");
+			if (ret < 0) {
+				return ret;
+			}
+			ret = rs485conf->flags & SER_RS485_RTS_AFTER_SEND ? 1 : 0;
+			ret = gpio_direction_output(rs485conf->rts_gpio, ret);
+			if (ret < 0) {
+				return ret;
+			}
+		} else {
+			rs485conf->flags &= ~SER_RS485_ENABLED;
+		}
+	}
+
+	if(rs485conf->flags & SER_RS485_ENABLED) {
+		struct uart_port *p = &up->port;
+		p->rs485_config = dw8250_rs485_config;
+	}
+
+	return 0;
+}
+
 static int dw8250_probe(struct platform_device *pdev)
 {
 	struct uart_8250_port uart = {};
@@ -591,6 +667,8 @@ static int dw8250_probe(struct platform_device *pdev)
 	data->uart_16550_compatible = device_property_read_bool(dev,
 						"snps,uart-16550-compatible");
 
+	dw8250_probe_rs485(&uart, p->dev->of_node);
+
 	err = device_property_read_u32(dev, "reg-shift", &val);
 	if (!err)
 		p->regshift = val;
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 39156ecbeb11..8d784ddab37e 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -34,7 +34,7 @@
 #include <linux/uaccess.h>
 #include <linux/pm_runtime.h>
 #include <linux/ktime.h>
-
+#include <linux/of_gpio.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
@@ -1788,6 +1788,7 @@ void serial8250_tx_chars(struct uart_8250_port *up)
 	struct uart_port *port = &up->port;
 	struct circ_buf *xmit = &port->state->xmit;
 	int count;
+	int res;
 
 	if (port->x_char) {
 		serial_out(up, UART_TX, port->x_char);
@@ -1805,6 +1806,15 @@ void serial8250_tx_chars(struct uart_8250_port *up)
 	}
 
 	count = up->tx_loadsz;
+	if(up->port.rs485.flags & SER_RS485_ENABLED) {
+		res = (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 0 : 1;
+		if (gpio_get_value(up->port.rs485.rts_gpio) != res) {
+			if (port->rs485.delay_rts_before_send > 0)
+                mdelay(port->rs485.delay_rts_before_send);
+			gpio_set_value(up->port.rs485.rts_gpio, res);
+		}
+	}
+
 	do {
 		serial_out(up, UART_TX, xmit->buf[xmit->tail]);
 		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -1828,8 +1838,17 @@ void serial8250_tx_chars(struct uart_8250_port *up)
 	 * HW can go idle. So we get here once again with empty FIFO and disable
 	 * the interrupt and RPM in __stop_tx()
 	 */
-	if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
+	if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)){
 		__stop_tx(up);
+		if(up->port.rs485.flags & SER_RS485_ENABLED) {
+			res = (up->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
+			if (gpio_get_value(up->port.rs485.rts_gpio) != res) {
+				if (port->rs485.delay_rts_after_send > 0)
+					mdelay(port->rs485.delay_rts_after_send);
+				gpio_set_value(up->port.rs485.rts_gpio, res);
+			}
+		}
+	}
 }
 EXPORT_SYMBOL_GPL(serial8250_tx_chars);
 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值