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