Re: [PATCH] serial: imx: keep the CPU out of deep idle while an RS485 port is open
From: Frank Li
Date: Thu Oct 15 2026 - 13:30:53 EST
On Thu, Oct 15, 2026 at 11:46:27AM -0300, Otavio Salvador wrote:
> [You don't often get email from otavio@xxxxxxxxxxxxxxxx. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> In RS485 mode the driver releases the transmitter (RTS/DE) from the TX
> complete interrupt. On i.MX6UL/ULL the imx6sx cpuidle driver offers an
> "ARM power off" state with a declared exit latency of 300us, measured at
> up to 2ms. When the CPU enters it while the last character is shifted
> out, DE is released that much later. Modbus RTU slaves may answer after
> 3.5 character times (1.75ms at >=19200 baud), so the head of their reply
> collides with our still-driven transmitter and is lost.
>
> Add a CPU latency QoS request of 100us while an RS485 port is open. It
> excludes the power-off state but keeps WFI and WAIT (50us) available, so
> cpuidle can stay enabled and the constraint only applies while the port
> is in use.
>
> The request is added on startup and removed on shutdown, and its value
> follows the RS485 state in imx_uart_rs485_config(), so RS485 enabled
> through TIOCSRS485 after the port is opened, as libmodbus does, is
> covered as well.
>
> Tested-by: Rodrigo M. Duarte <rodrigo.duarte@xxxxxxxxxxxxxxxx>
> Signed-off-by: Otavio Salvador <otavio@xxxxxxxxxxxxxxxx>
> ---
Reviewed-by: Frank Li <Frank.Li@xxxxxxx>
> drivers/tty/serial/imx.c | 28 ++++++++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 986fefb45c632..be05ed94bf0c4 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -24,6 +24,7 @@
> #include <linux/ktime.h>
> #include <linux/mutex.h>
> #include <linux/pinctrl/consumer.h>
> +#include <linux/pm_qos.h>
> #include <linux/rational.h>
> #include <linux/slab.h>
> #include <linux/of.h>
> @@ -237,6 +238,7 @@ struct imx_port {
> struct hrtimer trigger_start_tx;
> struct hrtimer trigger_stop_tx;
> unsigned int rxtl;
> + struct pm_qos_request rs485_pm_qos;
> };
>
> struct imx_port_ucrs {
> @@ -339,6 +341,22 @@ static void start_hrtimer_ms(struct hrtimer *hrt, unsigned long msec)
> hrtimer_start(hrt, ms_to_ktime(msec), HRTIMER_MODE_REL);
> }
>
> +/*
> + * In RS485 mode the transmitter is only released from the TX complete
> + * interrupt. If the CPU sits in a deep idle state at that point, the wakeup
> + * delays the release long enough to collide with fast responders on the
> + * bus. The i.MX6UL/SX "ARM power off" state has a 300us exit latency and was
> + * measured at up to 2ms, so keep the CPU out of states slower than this while
> + * an RS485 port is open.
> + */
> +#define IMX_UART_RS485_CPU_LATENCY_US 100
> +
> +static s32 imx_uart_rs485_cpu_latency(u32 rs485_flags)
> +{
> + return rs485_flags & SER_RS485_ENABLED ?
> + IMX_UART_RS485_CPU_LATENCY_US : PM_QOS_DEFAULT_VALUE;
> +}
> +
> /* called with port.lock taken and irqs off */
> static void imx_uart_soft_reset(struct imx_port *sport)
> {
> @@ -1585,6 +1603,9 @@ static int imx_uart_startup(struct uart_port *port)
>
> uart_port_unlock_irqrestore(&sport->port, flags);
>
> + cpu_latency_qos_add_request(&sport->rs485_pm_qos,
> + imx_uart_rs485_cpu_latency(port->rs485.flags));
> +
> return 0;
> }
>
> @@ -1710,6 +1731,8 @@ static void imx_uart_shutdown(struct uart_port *port)
>
> clk_disable_unprepare(sport->clk_per);
> clk_disable_unprepare(sport->clk_ipg);
> +
> + cpu_latency_qos_remove_request(&sport->rs485_pm_qos);
> }
>
> /* called with port.lock taken and irqs off */
> @@ -2053,6 +2076,11 @@ static int imx_uart_rs485_config(struct uart_port *port, struct ktermios *termio
> imx_uart_start_rx(port);
> }
>
> + /* The request only exists while the port is open */
> + if (cpu_latency_qos_request_active(&sport->rs485_pm_qos))
> + cpu_latency_qos_update_request(&sport->rs485_pm_qos,
> + imx_uart_rs485_cpu_latency(rs485conf->flags));
> +
> return 0;
> }
>
> --
> 2.55.0
>
>