Allwinner T113 UART转485

1.主题

UART转485

2.问题背景

产品:T113
硬件:T113
软件:linux+ uart
其他:uart转485功能实现

3.问题描述

3.1复现步骤

3.2具体表现

4.问题分析

5.根本原因

6.解决办法

如下是T113中uart转485patch可以参考附件,dts的配置如下:

	uart1: uart@05000400 {
		status = "okay";
		uart1_type = <2>;
		uart1_rs485=<1>;
		uart1_485fl=<0>;
		uart1_485oe=<&pio PG 1 1 0xffffffff 0xffffffff 0>;
	};

patch如下:

diff --git a/arch/arm/boot/dts/sun8iw20p1.dtsi b/arch/arm/boot/dts/sun8iw20p1.dtsi
index 6e51b72..4d3fb29 100644
--- a/arch/arm/boot/dts/sun8iw20p1.dtsi
+++ b/arch/arm/boot/dts/sun8iw20p1.dtsi
@@ -613,8 +613,11 @@
 			clock-names = "uart1";
 			resets = <&ccu RST_BUS_UART1>;
 			uart1_port = <1>;
-			uart1_type = <4>;
+			uart1_type = <2>;
 			status = "okay";
+			uart1_rs485=<1>;
+			uart1_485fl=<0>;
+			uart1_485oe=<&pio PG 1 1 0xffffffff 0xffffffff 0>;			
 		};
 
 		uart2: uart@2500800 {
diff --git a/drivers/tty/serial/sunxi-uart.c b/drivers/tty/serial/sunxi-uart.c
index 395b1bd..f013ad2 100644
--- a/drivers/tty/serial/sunxi-uart.c
+++ b/drivers/tty/serial/sunxi-uart.c
@@ -43,6 +43,10 @@
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/of_address.h>
+
+#include <linux/delay.h>
+#include <uapi/linux/sched/types.h>
+#include <linux/of_gpio.h>
 #include "sunxi-uart.h"
 
 #if defined(CONFIG_SERIAL_SUNXI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
@@ -191,6 +195,28 @@ static inline void sw_uart_disable_ier_thri(struct uart_port *port)
 	}
 }
 
+
+/********************************************************************/
+/* cet,liudachuan: rs485 delayed rts release control, not used now */
+# if 0
+static enum hrtimer_restart uart_set_rs485(struct hrtimer *u_timer)
+{
+	struct sw_uart_port *sw_uport = container_of(u_timer, struct sw_uart_port, uart_timer);
+	if(sw_uport == NULL)
+	{
+		SERIAL_DBG("faile to get sw_uport\n", __func__);
+		return HRTIMER_NORESTART;
+	}
+	
+	if(sw_uport->rs485_en){
+		gpio_direction_output(sw_uport->rs485oe_io.gpio, sw_uport->rs485_fl);
+	}
+
+	return HRTIMER_NORESTART;
+}
+#endif
+/***********************************************************************************/
+
 static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)
 {
 	unsigned char ch = 0;
@@ -272,23 +298,66 @@ static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned in
 	return lsr;
 }
 
+
+
+/******************************************************************************/
+/* cet,liudachuan: add sw-rs485 support */
+#define BITS_PER_SERIAL_BYTE	10
+#define NANOSECOND_PER_USECONDS	1000
+#define USECONDS_PER_SECOND		1000000
+static void sw_uart_rs485_cal_tout(struct uart_port *port, unsigned int lcr)
+{
+	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
+	unsigned int bits_per_byte;
+	if (lcr & SUNXI_UART_LCR_PARITY) /*有奇偶的时候,byte=start+8+parity=10*/
+		bits_per_byte = 10;
+	else    /*无奇偶的时候,byte=start+8=9*/
+		bits_per_byte = 9;
+
+	if (sw_uport->baud != 0) {
+		sw_uport->tout = 
+			(bits_per_byte * USECONDS_PER_SECOND) / (sw_uport->baud) * NANOSECOND_PER_USECONDS;
+	}
+	else {
+		sw_uport->tout = 0;
+	}
+}
+
 static void sw_uart_stop_tx(struct uart_port *port)
 {
-#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
 	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
+
+#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
 	struct sw_uart_dma *uart_dma = sw_uport->dma;
 
 	if (uart_dma->use_dma & TX_DMA)
 		sw_uart_stop_dma_tx(sw_uport);
 #endif
 	sw_uart_disable_ier_thri(port);
+	/* cet,liudachuan: delay 1 byte transmit time then turn off rs485 tx-en */
+	if (sw_uport->rs485_en) {
+	   	// hrtimer_start(&sw_uport->uart_timer, sw_uport->tout, HRTIMER_MODE_REL);
+		unsigned char lsr = 0;
+		while ((lsr & SUNXI_UART_LSR_BOTH_EMPTY) != SUNXI_UART_LSR_BOTH_EMPTY) {
+			// cpu_relax();
+			udelay(10);
+			lsr = serial_in(port, SUNXI_UART_LSR);
+		};
+		gpio_direction_output(sw_uport->rs485oe_io.gpio, sw_uport->rs485_fl);
+	}
 }
 
 static void sw_uart_start_tx(struct uart_port *port)
 {
-#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
 	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
+	/* cet,liudachuan: rs485 rts = 1 */
+	// serial_out(port, sw_uport->mcr & (~SUNXI_UART_MCR_RTS), SUNXI_UART_MCR);
+	if(sw_uport->rs485_en){
+		// udelay(20);
+		gpio_direction_output(sw_uport->rs485oe_io.gpio, !sw_uport->rs485_fl);
+	}
 
+#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
 	if (!((sw_uport->dma->use_dma & TX_DMA) && sw_uport->dma->tx_dma_used))
 #endif
 		sw_uart_enable_ier_thri(port);
@@ -342,8 +411,14 @@ static void sw_uart_handle_tx(struct sw_uart_port *sw_uport)
 		uart_write_wakeup(&sw_uport->port);
 		spin_lock(&sw_uport->port.lock);
 	}
-	if (uart_circ_empty(xmit))
-		sw_uart_stop_tx(&sw_uport->port);
+
+	/* cet,liudachuan: add sw-rs485 support */
+	// if (uart_circ_empty(xmit))
+	//     sw_uart_stop_tx(&sw_uport->port);
+	if (!sw_uport->rs485_en) {
+		if (uart_circ_empty(xmit))
+			sw_uart_stop_tx(&sw_uport->port);
+	}
 }
 
 static unsigned int sw_uart_modem_status(struct sw_uart_port *sw_uport)
@@ -354,7 +429,7 @@ static unsigned int sw_uart_modem_status(struct sw_uart_port *sw_uport)
 	sw_uport->msr_saved_flags = 0;
 
 	if (status & SUNXI_UART_MSR_ANY_DELTA && sw_uport->ier & SUNXI_UART_IER_MSI &&
-	    sw_uport->port.state != NULL) {
+		sw_uport->port.state != NULL) {
 		if (status & SUNXI_UART_MSR_TERI)
 			sw_uport->port.icount.rng++;
 		if (status & SUNXI_UART_MSR_DDSR)
@@ -724,6 +799,25 @@ static enum hrtimer_restart sw_uart_report_dma_rx(struct hrtimer *rx_hrtimer)
 
 #endif
 
+
+
+/**
+ * 串口中断实测14-135us,数据发完后gic_handle_irq两次进入中断,第二次才真正进入串口中断,导致整个延迟变大
+ * 通过提高中断线程优先级为4以上,可以解决,时间缩短到(-5-35us),这里优先级设置为1
+ * cet,ljt 2021.10
+*/
+static inline void sw_uart_irq_set_pri(struct sw_uart_port *sw_uport)
+{
+    struct sched_param param = { .sched_priority = MAX_USER_RT_PRIO - 2 };
+	if (sw_uport->irq_pri_promoted)
+		return;
+	if (sched_setscheduler(current, SCHED_FIFO, &param) < 0)
+	{
+		SERIAL_MSG("sched_setscheduler set error\n");
+	}
+	sw_uport->irq_pri_promoted = 1;
+}
+
 static irqreturn_t sw_uart_irq(int irq, void *dev_id)
 {
 	struct uart_port *port = dev_id;
@@ -731,6 +825,9 @@ static irqreturn_t sw_uart_irq(int irq, void *dev_id)
 	unsigned int iir = 0, lsr = 0;
 	unsigned long flags;
 
+	/* if we hasn't promote the irq priority, set priority to 97 */
+	//sw_uart_irq_set_pri(sw_uport);
+
 	spin_lock_irqsave(&port->lock, flags);
 
 	iir = serial_in(port, SUNXI_UART_IIR) & SUNXI_UART_IIR_IID_MASK;
@@ -1072,7 +1169,7 @@ static void sw_uart_flush_buffer(struct uart_port *port)
 }
 
 static void sw_uart_set_termios(struct uart_port *port, struct ktermios *termios,
-			    struct ktermios *old)
+				struct ktermios *old)
 {
 	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
 	unsigned long flags;
@@ -1119,6 +1216,12 @@ static void sw_uart_set_termios(struct uart_port *port, struct ktermios *termios
 	dlh = quot >> 8;
 	SERIAL_DBG("set baudrate %d, quot %d\n", baud, quot);
 
+	/* cet,liudachuan: add sw rs485 support */
+	sw_uport->baud = baud;
+	if (sw_uport->rs485_en) {
+		sw_uart_rs485_cal_tout(port, lcr);
+	}
+
 	spin_lock_irqsave(&port->lock, flags);
 	uart_update_timeout(port, termios->c_cflag, baud);
 
@@ -1379,7 +1482,7 @@ static int sw_uart_ioctl(struct uart_port *port, unsigned int cmd,
 }
 
 static void sw_uart_pm(struct uart_port *port, unsigned int state,
-		      unsigned int oldstate)
+			  unsigned int oldstate)
 {
 #if IS_ENABLED(CONFIG_EVB_PLATFORM)
 	int ret;
@@ -1727,7 +1830,7 @@ static void sw_console_putchar(struct uart_port *port, int c)
 }
 
 static void sw_console_write(struct console *co, const char *s,
-			      unsigned int count)
+				  unsigned int count)
 {
 	struct uart_port *port = NULL;
 	struct sw_uart_port *sw_uport;
@@ -1909,6 +2012,9 @@ static int sw_uart_probe(struct platform_device *pdev)
 	char uart_para[16] = {0};
 	const char *uart_string;
 	int ret = -1;
+	int rs485_en = 0;         //2020.09.24 2¨??? ¨?¨a?¨?RS485????
+	int rs485_fl = 0;
+
 	struct device_node *apk_np = of_find_node_by_name(NULL, "auto_print");
 	const char *apk_sta = NULL;
 #if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
@@ -2052,6 +2158,45 @@ static int sw_uart_probe(struct platform_device *pdev)
 	else
 		sw_uport->card_print = false;
 
+	/* cet,liudachuan: add sw-rs485 support */
+	/************ Modify by wangshunfan @2021.01.11 for this part *************/
+	/* Get rs485 enable flag */
+	snprintf(uart_para, sizeof(uart_para), "uart%d_rs485", pdev->id);
+	ret = of_property_read_u32(np, uart_para, &rs485_en);
+	if (ret) {
+		SERIAL_MSG("error to get uart%d_rs485\n", pdev->id);
+			rs485_en = 0;
+	}
+	sw_uport->rs485_en = rs485_en;
+
+	//SERIAL_DBG("caid rs485_en = %d port = %d", rs485_en, pdev->id);
+	if (sw_uport->rs485_en) {
+		snprintf(uart_para, sizeof(uart_para), "uart%d_485fl", pdev->id);
+		ret = of_property_read_u32(np, uart_para, &rs485_fl);
+		if (ret) {
+			SERIAL_MSG("error to get tuart%d_rs485\n", pdev->id);
+				rs485_fl = 0;
+		}
+		sw_uport->rs485_fl = rs485_fl;
+
+		/* Get rs485 enable control pin, request and init gpio */
+		snprintf(uart_para, sizeof(uart_para), "uart%d_485oe", pdev->id);
+		sw_uport->rs485oe_io.gpio = of_get_named_gpio_flags(np, uart_para, 0,
+			(enum of_gpio_flags *)(&(sw_uport->rs485oe_io)));
+		if (gpio_is_valid(sw_uport->rs485oe_io.gpio)){
+			gpio_request(sw_uport->rs485oe_io.gpio, NULL);
+			gpio_direction_output(sw_uport->rs485oe_io.gpio, sw_uport->rs485_fl);
+		}
+
+		/* Init hrtimer for rs485 and set timeout callback function */
+		// hrtimer_init(&sw_uport->uart_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+		// sw_uport->uart_timer.function = uart_set_rs485;
+
+		sw_uport->irq_pri_promoted = 0;
+		SERIAL_MSG("uart%d rs485 enabled\n", pdev->id);
+	}
+	/**************** end of modify by wangshunfan for this part ****************/
+
 	pdata->used = 1;
 	port->iotype = UPIO_MEM;
 	port->type = PORT_SUNXI;
@@ -2129,7 +2274,14 @@ static int sw_uart_remove(struct platform_device *pdev)
 	struct sw_uart_port *sw_uport = platform_get_drvdata(pdev);
 
 	SERIAL_DBG("release uart%d port\n", sw_uport->id);
-#if IS_ENABLED(CONFIG_SERIAL_SUNXI_DMA)
+
+	/* cet,liudachuan: add sw-rs485 support */
+	if (sw_uport->rs485_en) {
+		gpio_free(sw_uport->rs485oe_io.gpio);	/* free gpio of rs485 */
+		// hrtimer_cancel(&sw_uport->uart_timer);	/* canel hrtimer      */
+	}
+
+#ifdef CONFIG_SERIAL_SUNXI_DMA
 	sw_uart_release_dma_tx(sw_uport);
 	sw_uart_release_dma_rx(sw_uport);
 #endif
diff --git a/drivers/tty/serial/sunxi-uart.h b/drivers/tty/serial/sunxi-uart.h
index 565acbf..ab096d6 100644
--- a/drivers/tty/serial/sunxi-uart.h
+++ b/drivers/tty/serial/sunxi-uart.h
@@ -22,6 +22,8 @@
 #include <linux/dmaengine.h>
 #include <linux/reset.h>
 //include <linux/serial_core.h>
+#include <linux/ktime.h>
+#include <linux/sunxi-gpio.h>
 
 /* SUNXI UART PORT definition*/
 #define PORT_MAX_USED	PORT_LINFLEXUART  /* see include/uapi/linux/serial_core.h */
@@ -106,6 +108,16 @@ struct sw_uart_port {
 	struct serial_rs485 rs485conf;
 	bool card_print;
 	bool throttled;
+
+    /* cet,liudachuan: add rs485 support(not hardware's rs485) */
+    struct gpio_config rs485oe_io;
+    unsigned int rs485_en;
+    unsigned int rs485_fl;
+    struct hrtimer uart_timer;
+    unsigned int baud;
+    int irq_pri_promoted;
+    /* Added by wangshunfan @2021.01.11 for hrtimer timeout */
+	ktime_t tout;
 };
 
 /* register offset define */
@@ -191,6 +203,8 @@ struct sw_uart_port {
 #define SUNXI_UART_LSR_OE         (BIT(1))
 #define SUNXI_UART_LSR_DR         (BIT(0))
 #define SUNXI_UART_LSR_BRK_ERROR_BITS 0x1E /* BI, FE, PE, OE bits */
+/* cet,liudachuan: add BOTH_EMPTY macro */
+#define SUNXI_UART_LSR_BOTH_EMPTY   (SUNXI_UART_LSR_TEMT | SUNXI_UART_LSR_THRE)
 /* Modem Status Register */
 #define SUNXI_UART_MSR_DCD        (BIT(7))
 #define SUNXI_UART_MSR_RI         (BIT(6))
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值