[PATCH v2] serial: imx: Prevent RTS line toggle delays for RS485

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When using RS485 in half duplex mode the RTS line is typically used to
toggle receive/transmit on the transceiver IC. As some protocols (OSDP
f.ex.) specify that the slave should respond within some short time period,
it is important that the RTS line toggles the transceiver from transmit
mode to receive mode within that time.

This change puts a pm_qos requirement on CPU_DMA_LATENCY when in RS485 tx
mode so that CPU wake times won't cause to long delays.

Signed-off-by: Einar Vading <einarv@xxxxxxxx>
---
Changes in v2:
  - Uses a workqueue to avoid setting pm_qos from hard IRQ.

 drivers/tty/serial/imx.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index f370c1cf4f27..5967f98f1686 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -30,6 +30,8 @@
 #include <linux/of_device.h>
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
+#include <linux/pm_qos.h>
+#include <linux/workqueue.h>
 
 #include <asm/irq.h>
 #include <linux/platform_data/serial-imx.h>
@@ -225,6 +227,9 @@ struct imx_port {
 	unsigned int		dma_tx_nents;
 	unsigned int            saved_reg[10];
 	bool			context_saved;
+	u32			latency;
+	struct pm_qos_request	pm_qos_req;
+	struct work_struct	qos_work;
 };
 
 struct imx_port_ucrs {
@@ -457,6 +462,8 @@ static void imx_uart_stop_tx(struct uart_port *port)
 		ucr4 = imx_uart_readl(sport, UCR4);
 		ucr4 &= ~UCR4_TCEN;
 		imx_uart_writel(sport, ucr4, UCR4);
+		sport->latency = PM_QOS_DEFAULT_VALUE;
+		schedule_work(&sport->qos_work);
 	}
 }
 
@@ -657,6 +664,9 @@ static void imx_uart_start_tx(struct uart_port *port)
 	if (port->rs485.flags & SER_RS485_ENABLED) {
 		u32 ucr2;
 
+		sport->latency = 0;
+		schedule_work(&sport->qos_work);
+
 		ucr2 = imx_uart_readl(sport, UCR2);
 		if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
 			imx_uart_rts_active(sport, &ucr2);
@@ -1852,6 +1862,14 @@ static int imx_uart_rs485_config(struct uart_port *port,
 	return 0;
 }
 
+static void imx_qos_work(struct work_struct *work)
+{
+	struct imx_port *sport;
+
+	sport = container_of(work, struct imx_port, qos_work);
+	pm_qos_update_request(&sport->pm_qos_req, sport->latency);
+}
+
 static const struct uart_ops imx_uart_pops = {
 	.tx_empty	= imx_uart_tx_empty,
 	.set_mctrl	= imx_uart_set_mctrl,
@@ -1868,6 +1886,7 @@ static const struct uart_ops imx_uart_pops = {
 	.type		= imx_uart_type,
 	.config_port	= imx_uart_config_port,
 	.verify_port	= imx_uart_verify_port,
+
 #if defined(CONFIG_CONSOLE_POLL)
 	.poll_init      = imx_uart_poll_init,
 	.poll_get_char  = imx_uart_poll_get_char,
@@ -2335,6 +2354,10 @@ static int imx_uart_probe(struct platform_device *pdev)
 		}
 	}
 
+	pm_qos_add_request(&sport->pm_qos_req, PM_QOS_CPU_DMA_LATENCY,
+			PM_QOS_DEFAULT_VALUE);
+	INIT_WORK(&sport->qos_work, imx_qos_work);
+
 	imx_uart_ports[sport->port.line] = sport;
 
 	platform_set_drvdata(pdev, sport);
@@ -2346,6 +2369,8 @@ static int imx_uart_remove(struct platform_device *pdev)
 {
 	struct imx_port *sport = platform_get_drvdata(pdev);
 
+	pm_qos_remove_request(&sport->pm_qos_req);
+
 	return uart_remove_one_port(&imx_uart_uart_driver, &sport->port);
 }
 
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux