From: Frank Li <Frank.li@oss.nxp.com>
To: Otavio Salvador <otavio@ossystems.com.br>
Cc: gregkh@linuxfoundation.org, jirislaby@kernel.org,
Frank.Li@nxp.com, s.hauer@pengutronix.de, kernel@pengutronix.de,
festevam@gmail.com, linux-serial@vger.kernel.org,
imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org,
"Rodrigo M. Duarte" <rodrigo.duarte@ossystems.com.br>
Subject: Re: [PATCH] serial: imx: keep the CPU out of deep idle while an RS485 port is open
Date: Thu, 15 Oct 2026 10:24:49 -0500 [thread overview]
Message-ID: <atDwQfCyI6yUpAWK@SMW015318> (raw)
In-Reply-To: <20261015144627.2461915-1-otavio@ossystems.com.br>
On Thu, Oct 15, 2026 at 11:46:27AM -0300, Otavio Salvador wrote:
> [You don't often get email from otavio@ossystems.com.br. 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@ossystems.com.br>
> Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> 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
>
>
prev parent reply other threads:[~2026-10-15 15:25 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 14:46 [PATCH] serial: imx: keep the CPU out of deep idle while an RS485 port is open Otavio Salvador
2026-10-15 15:24 ` Frank Li [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=atDwQfCyI6yUpAWK@SMW015318 \
--to=frank.li@oss.nxp.com \
--cc=Frank.Li@nxp.com \
--cc=festevam@gmail.com \
--cc=gregkh@linuxfoundation.org \
--cc=imx@lists.linux.dev \
--cc=jirislaby@kernel.org \
--cc=kernel@pengutronix.de \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=otavio@ossystems.com.br \
--cc=rodrigo.duarte@ossystems.com.br \
--cc=s.hauer@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox