From: Tapio Reijonen <tapio.reijonen@vaisala.com>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
Jiri Slaby <jirislaby@kernel.org>
Cc: linux-kernel@vger.kernel.org, linux-serial@vger.kernel.org,
Hugo Villeneuve <hvilleneuve@dimonoff.com>,
Tapio Reijonen <tapio.reijonen@kolumbus.fi>,
Tapio Reijonen <tapio.reijonen@vaisala.com>
Subject: [PATCH v5 7/8] serial: max310x: drive RTS in software when hardware delays are too short
Date: Tue, 29 Sep 2026 09:38:01 +0000 [thread overview]
Message-ID: <20260929-max310x-rs485-sw-delay-v5-7-ae46afa583f2@vaisala.com> (raw)
In-Reply-To: <20260929-max310x-rs485-sw-delay-v5-0-ae46afa583f2@vaisala.com>
The chip's auto-RTS path can express at most 15 bit-times of RTS setup
and hold delay - a fraction of a millisecond at typical baud rates -
while the uapi expresses the delays in milliseconds up to the UART
core's RS485_MAX_RTS_DELAY. Requests beyond the field were rejected
with -ERANGE, which makes the core silently wipe port->rs485 and
disable RS485; a device tree asking for a 20 ms setup delay ends up
with no RS485 at all and an unusable bus.
Add a software-driven RTS path that takes over whenever the hardware
cannot represent the requested timing, and clamp the delays to
RS485_MAX_RTS_DELAY instead of rejecting them.
max310x_set_rts_ctl_params() picks the path: software if either delay
exceeds what 15 bit-times encode at the current baud rate, or if the
requested RTS polarity combination cannot be produced by the auto-RTS
engine; hardware otherwise, as before.
On the software path the RTS envelope is driven by a single hrtimer,
re-used for the before- and after-send phases (tracked in tx_state),
plus a single rts_work that toggles RTS. start_tx() begins the
envelope; rts_work asserts RTS and only then arms the before-send
timer, so data is never shifted before RTS is on the wire. The timer
expiry kicks tx_work; once the chip FIFO drains, the same timer is
re-armed for one character (the byte still in the shift register)
plus the after-send delay, after which rts_work releases RTS. One
timer and one work keep the phases mutually exclusive and the RTS
toggles ordered.
Teardown is interlocked: shutdown() and an RS485-disabling
TIOCSRS485 set tx_teardown under port->lock before cancelling the
timer and works, and start_tx() checks it on entry and again after
the hrtimer_try_to_cancel(-1) path retakes the dropped lock -
otherwise a write racing the teardown could re-arm the timer or
queue rts_work against a port being shut down, leaving the
transceiver driving the bus after close. The rs485-disable path
additionally kicks tx_work afterwards, since a racing write may have
queued data with no envelope left to pump it, and shutdown() now also
cancels tx_work, which was previously cancelled only in remove().
set_rts_ctl_params() publishes sw_rts_during_tx with a single store
and settles the RTS idle level only while tx_state is off, because
serial_core calls set_termios() without port->lock and rs485_config()
schedules a reconfigure on every TIOCSRS485 - either could otherwise
release the transceiver mid-envelope. The settle also re-checks
tx_state after its write and requeues rts_work if an envelope started
meanwhile: the state read and the register write are not atomic, and
rts_work re-derives the level from tx_state, so this converges without
locking. shutdown() waits out an
in-flight envelope (bounded) before powering down, and cancels the
timer and works unconditionally: a TIOCSRS485 can clear
sw_rts_during_tx while an envelope is still in flight, and neither
may outlive the port. break_ctl() on the software path applies the
configured RTS polarity itself.
Fixes: 55367c620aed ("serial: max310x: Add support for RS-485 mode")
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
---
drivers/tty/serial/max310x.c | 386 ++++++++++++++++++++++++++++++++++++++-----
1 file changed, 341 insertions(+), 45 deletions(-)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 8f8817b127c891f18af52bd3d24efb269db78211..6ad27eb8a32586ec5719773e6eadf1249155bf19 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/driver.h>
+#include <linux/hrtimer.h>
#include <linux/i2c.h>
#include <linux/kconfig.h>
#include <linux/module.h>
@@ -297,15 +298,29 @@ struct max310x_devtype {
u8 power_bit; /* Bit for sleep or power-off mode (active high). */
};
+/* Software-timed RS485 RTS envelope phase */
+enum max310x_tx_state {
+ MAX310X_TX_OFF, /* idle, RTS released */
+ MAX310X_TX_WAIT_BEFORE_SEND, /* RTS asserted, before-send delay */
+ MAX310X_TX_SEND, /* data in flight, awaiting TX-empty */
+ MAX310X_TX_WAIT_AFTER_SEND, /* data drained, after-send hold */
+};
+
struct max310x_one {
struct uart_port port;
struct work_struct tx_work;
struct work_struct md_work;
struct work_struct rs_work;
+ struct work_struct rts_work;
+ struct hrtimer tx_delay_tmr;
struct regmap *regmap;
unsigned int one_char_duration_us;
unsigned int baud;
+ bool sw_rts_during_tx;
+ bool cancel_tx_delay_tmr;
+ bool tx_teardown; /* envelope being torn down */
bool tx_break; /* break_ctl() owns the transceiver */
+ enum max310x_tx_state tx_state;
u8 rx_buf[MAX310X_FIFO_SIZE];
};
@@ -696,6 +711,36 @@ static void max310x_rts_ctl(struct uart_port *port, bool rts_state)
rts_state ? MAX310X_LCR_RTS_BIT : 0);
}
+/* RTS level for the transmitting or the idle phase of an RS485 envelope */
+static bool max310x_rts_level(struct uart_port *port, bool active)
+{
+ return active ? (port->rs485.flags & SER_RS485_RTS_ON_SEND) :
+ (port->rs485.flags & SER_RS485_RTS_AFTER_SEND);
+}
+
+/*
+ * Drive the RS485 RTS line to match the current tx_state. This is the only
+ * place that touches RTS, and it reads tx_state rather than a fixed
+ * assert/deassert intent, so a newer assert is never clobbered by a stale
+ * release. It also arms the before-send timer once the RTS edge is on the wire,
+ * so data is never shifted before RTS is asserted.
+ */
+static void max310x_rts_work_proc(struct work_struct *ws)
+{
+ struct max310x_one *one = container_of(ws, struct max310x_one, rts_work);
+ struct uart_port *port = &one->port;
+ bool rts_on = READ_ONCE(one->tx_state) != MAX310X_TX_OFF;
+
+ max310x_rts_ctl(port, max310x_rts_level(port, rts_on));
+
+ guard(spinlock_irqsave)(&port->lock);
+ if (READ_ONCE(one->tx_state) == MAX310X_TX_WAIT_BEFORE_SEND &&
+ !one->cancel_tx_delay_tmr && !hrtimer_active(&one->tx_delay_tmr))
+ hrtimer_start(&one->tx_delay_tmr,
+ ms_to_ktime(port->rs485.delay_rts_before_send),
+ HRTIMER_MODE_REL);
+}
+
static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
{
struct max310x_one *one = to_max310x_port(port);
@@ -792,6 +837,68 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
tty_flip_buffer_push(&port->state->port);
}
+static enum hrtimer_restart max310x_tmr_tx(struct hrtimer *timer)
+{
+ struct max310x_one *one = container_of(timer, struct max310x_one,
+ tx_delay_tmr);
+
+ guard(spinlock_irqsave)(&one->port.lock);
+ if (!one->cancel_tx_delay_tmr) {
+ if (READ_ONCE(one->tx_state) == MAX310X_TX_WAIT_AFTER_SEND) {
+ /* After-send hold elapsed: drop RTS via the rts worker. */
+ WRITE_ONCE(one->tx_state, MAX310X_TX_OFF);
+ schedule_work(&one->rts_work);
+ } else {
+ WRITE_ONCE(one->tx_state, MAX310X_TX_SEND);
+ schedule_work(&one->tx_work);
+ }
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static void max310x_delayed_stop_tx(struct uart_port *port)
+{
+ struct max310x_one *one = to_max310x_port(port);
+ unsigned int txlvl;
+
+ if (READ_ONCE(one->tx_state) == MAX310X_TX_OFF)
+ return;
+
+ /*
+ * The kfifo can be empty while the chip TX FIFO is still draining, so arm
+ * the after-send hold only once the chip FIFO is empty too - the TX-empty
+ * interrupt re-invokes us then. Otherwise the hold starts early and RTS
+ * drops mid-character, clipping the last byte(s).
+ */
+ txlvl = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
+ if (txlvl)
+ return;
+
+ /*
+ * Runs from tx_work without port->lock, so re-check the state under it:
+ * shutdown() may have cancelled the envelope meanwhile. Only
+ * MAX310X_TX_SEND may arm the hold.
+ */
+ guard(spinlock_irqsave)(&one->port.lock);
+ if (one->cancel_tx_delay_tmr ||
+ READ_ONCE(one->tx_state) != MAX310X_TX_SEND)
+ return;
+
+ if (!hrtimer_active(&one->tx_delay_tmr)) {
+ /*
+ * Add one character for the byte still in the shift register -
+ * TX-empty fires as it enters, not as it leaves.
+ */
+ ktime_t delay = us_to_ktime(one->one_char_duration_us +
+ port->rs485.delay_rts_after_send *
+ USEC_PER_MSEC);
+
+ WRITE_ONCE(one->tx_state, MAX310X_TX_WAIT_AFTER_SEND);
+ hrtimer_start(&one->tx_delay_tmr, delay, HRTIMER_MODE_REL);
+ }
+}
+
static void max310x_handle_tx(struct uart_port *port)
{
struct tty_port *tport = &port->state->port;
@@ -803,8 +910,13 @@ static void max310x_handle_tx(struct uart_port *port)
return;
}
- if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))
+ if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port)) {
+ struct max310x_one *one = to_max310x_port(port);
+
+ if (READ_ONCE(one->sw_rts_during_tx))
+ max310x_delayed_stop_tx(port);
return;
+ }
/*
* It's a circ buffer -- wrap around.
@@ -829,11 +941,58 @@ static void max310x_handle_tx(struct uart_port *port)
uart_write_wakeup(port);
}
+/*
+ * Begin a software-timed RTS envelope: set the before-send phase and queue the
+ * rts worker to assert RTS. tx_state is set synchronously here (start_tx() holds
+ * port.lock) so close()/shutdown can see an envelope is in flight; rts_work then
+ * asserts RTS and arms the before-send timer (see there).
+ */
+static void max310x_delayed_start_tx(struct uart_port *port)
+{
+ struct max310x_one *one = to_max310x_port(port);
+
+ WRITE_ONCE(one->tx_state, MAX310X_TX_WAIT_BEFORE_SEND);
+ one->cancel_tx_delay_tmr = false;
+ schedule_work(&one->rts_work);
+}
+
+/* called with port.lock taken and irqs off */
static void max310x_start_tx(struct uart_port *port)
{
struct max310x_one *one = to_max310x_port(port);
- schedule_work(&one->tx_work);
+ /* A teardown is in progress; nothing may start an envelope or TX. */
+ if (one->tx_teardown)
+ return;
+
+ if (READ_ONCE(one->sw_rts_during_tx)) {
+ /*
+ * The before- and after-send phases share one delay timer. If an
+ * after-send release is pending, cancel it before starting a new
+ * TX so the just-asserted RTS is not yanked; re-arming the timer
+ * for the before-send phase then supersedes the release.
+ */
+ int res = 0;
+
+ if (READ_ONCE(one->tx_state) == MAX310X_TX_WAIT_AFTER_SEND)
+ res = hrtimer_try_to_cancel(&one->tx_delay_tmr);
+ if (unlikely(res == -1)) {
+ one->cancel_tx_delay_tmr = true;
+ uart_port_unlock(port);
+ hrtimer_cancel(&one->tx_delay_tmr);
+ uart_port_lock(port);
+ /*
+ * The lock was dropped: a teardown may have run to
+ * completion meanwhile. Re-check before starting.
+ */
+ if (one->tx_teardown)
+ return;
+ }
+
+ max310x_delayed_start_tx(port);
+ } else {
+ schedule_work(&one->tx_work);
+ }
}
static irqreturn_t max310x_port_irq(struct max310x_port *s, int portno)
@@ -946,36 +1105,50 @@ static void max310x_set_mctrl(struct uart_port *port, unsigned int mctrl)
}
/*
- * Program the chip's RS485 RTS timing. The HDPIXDELAY setup and hold fields
- * count bit-times, four bits per direction, while the uapi expresses the
- * delays in milliseconds: convert at the current baud rate, rounding up, and
- * cap at the field maximum.
+ * Pick hardware or software RTS timing for the current port. The chip can
+ * deliver up to 15 bit-times of setup/hold delay via HDPIXDELAY; anything
+ * longer (or any RTS polarity the chip cannot produce automatically) must
+ * be driven by software via tx_delay_tmr and rts_work.
*/
static void max310x_set_rts_ctl_params(struct max310x_one *one)
{
const unsigned int max_bit_dly = 15;
struct uart_port *port = &one->port;
+ u64 max_hw_delay_ns = 0;
unsigned int setup = 0, hold = 0;
u8 mode1 = 0, irda = 0;
+ bool sw_rts = false;
- if (port->rs485.flags & SER_RS485_ENABLED) {
- /* Convert milliseconds to bit-times, rounding up. */
- setup = DIV_ROUND_UP(one->baud * port->rs485.delay_rts_before_send,
- MSEC_PER_SEC);
- hold = DIV_ROUND_UP(one->baud * port->rs485.delay_rts_after_send,
- MSEC_PER_SEC);
- setup = min(setup, max_bit_dly);
- hold = min(hold, max_bit_dly);
-
- mode1 = MAX310X_MODE1_TRNSCVCTRL_BIT;
- /*
- * The auto-RTS engine asserts RTS high on send; for an
- * active-low RTS let IRDA.RTSINVERT invert the output stage.
- */
- if (!(port->rs485.flags & SER_RS485_RTS_ON_SEND))
- irda = MAX310X_IRDA_RTSINVERT_BIT;
+ if (!(port->rs485.flags & SER_RS485_ENABLED))
+ goto out;
+
+ if (one->baud)
+ max_hw_delay_ns = NSEC_PER_SEC / one->baud * max_bit_dly;
+
+ if ((u64)port->rs485.delay_rts_before_send * NSEC_PER_MSEC > max_hw_delay_ns ||
+ (u64)port->rs485.delay_rts_after_send * NSEC_PER_MSEC > max_hw_delay_ns ||
+ !!(port->rs485.flags & SER_RS485_RTS_ON_SEND) ==
+ !!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
+ sw_rts = true;
+
+ if (sw_rts) {
+ setup = 0;
+ hold = 0;
+ goto out;
}
+ /* Convert milliseconds to bit-times, rounding up. */
+ setup = DIV_ROUND_UP(one->baud * port->rs485.delay_rts_before_send,
+ MSEC_PER_SEC);
+ hold = DIV_ROUND_UP(one->baud * port->rs485.delay_rts_after_send,
+ MSEC_PER_SEC);
+ setup = min(setup, max_bit_dly);
+ hold = min(hold, max_bit_dly);
+
+out:
+ /* Assign once; a transient false would be seen by other readers. */
+ WRITE_ONCE(one->sw_rts_during_tx, sw_rts);
+
max310x_port_write(port, MAX310X_HDPIXDELAY_REG,
MAX310X_HDPIXDELAY_SETUP(setup) |
MAX310X_HDPIXDELAY_HOLD(hold));
@@ -983,12 +1156,48 @@ static void max310x_set_rts_ctl_params(struct max310x_one *one)
/*
* A break owns the transceiver: break_ctl() disabled auto-RTS and
* drives RTS manually, and restores it from the current
- * configuration when the break ends. Touching MODE1 here would
+ * configuration when the break ends. Touching them here would
* release the transceiver mid-break.
*/
if (one->tx_break)
return;
+ if (port->rs485.flags & SER_RS485_ENABLED) {
+ if (sw_rts) {
+ /*
+ * Only settle RTS at idle when no transmission owns it.
+ * A reconfigure while one is in flight - rs_work runs
+ * on every TIOCSRS485 - would otherwise release the
+ * transceiver mid-character.
+ */
+ if (READ_ONCE(one->tx_state) == MAX310X_TX_OFF) {
+ max310x_rts_ctl(port,
+ max310x_rts_level(port, false));
+ /*
+ * serial_core calls set_termios() without
+ * port->lock, so an envelope may have started
+ * while the idle level was written and the
+ * settle can land after its RTS assert.
+ * rts_work re-derives the level from
+ * tx_state; requeue it to converge.
+ */
+ if (READ_ONCE(one->tx_state) != MAX310X_TX_OFF)
+ schedule_work(&one->rts_work);
+ }
+ } else {
+ mode1 = MAX310X_MODE1_TRNSCVCTRL_BIT;
+ /*
+ * The auto-RTS engine asserts RTS high on send; for an
+ * active-low RTS let IRDA.RTSINVERT invert the output
+ * stage.
+ */
+ if (!(port->rs485.flags & SER_RS485_RTS_ON_SEND))
+ irda = MAX310X_IRDA_RTSINVERT_BIT;
+ }
+ } else {
+ max310x_rts_ctl(port, 0);
+ }
+
max310x_port_update(port, MAX310X_MODE1_REG,
MAX310X_MODE1_TRNSCVCTRL_BIT, mode1);
max310x_port_update(port, MAX310X_IRDA_REG,
@@ -1017,18 +1226,24 @@ static void max310x_break_ctl(struct uart_port *port, int break_state)
* is driven as it is.
*/
if (break_state) {
- max310x_port_update(port, MAX310X_MODE1_REG,
- MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
- max310x_rts_ctl(port, 1);
+ if (READ_ONCE(one->sw_rts_during_tx)) {
+ max310x_rts_ctl(port, max310x_rts_level(port, 1));
+ } else {
+ max310x_port_update(port, MAX310X_MODE1_REG,
+ MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
+ max310x_rts_ctl(port, 1);
+ }
} else {
/*
* Reapply the current configuration: a reconfigure that
* arrived during the break was deferred by the tx_break
- * guard. Then release the manual RTS - auto-RTS owns the
- * pin again.
+ * guard. On the software path the helper also settles the
+ * idle level; on the hardware path release the manual RTS -
+ * auto-RTS owns the pin again.
*/
max310x_set_rts_ctl_params(one);
- max310x_rts_ctl(port, 0);
+ if (!READ_ONCE(one->sw_rts_during_tx))
+ max310x_rts_ctl(port, 0);
}
}
@@ -1071,9 +1286,10 @@ static void max310x_set_termios(struct uart_port *port,
lcr |= MAX310X_LCR_STOPLEN_BIT; /* 2 stops */
/*
- * Update LCR register. Leave the TX break bit alone: it is driven by
- * break_ctl(), and an absolute write here would end a break in
- * progress.
+ * Update LCR register. Leave the TX break and RTS bits alone: they are
+ * driven by break_ctl() and by the software-timed RS485 RTS, and an
+ * absolute write here would end a break in progress or release the
+ * transceiver mid-character.
*/
max310x_port_update(port, MAX310X_LCR_REG, MAX310X_LCR_TERMIOS_MASK, lcr);
@@ -1181,14 +1397,37 @@ static void max310x_rs_proc(struct work_struct *ws)
MAX310X_MODE2_ECHOSUPR_BIT, mode2);
}
+/* called with port.lock taken and irqs off */
static int max310x_rs485_config(struct uart_port *port, struct ktermios *termios,
struct serial_rs485 *rs485)
{
struct max310x_one *one = to_max310x_port(port);
- if ((rs485->delay_rts_before_send > 0x0f) ||
- (rs485->delay_rts_after_send > 0x0f))
- return -ERANGE;
+ rs485->delay_rts_before_send = min(rs485->delay_rts_before_send, 100U);
+ rs485->delay_rts_after_send = min(rs485->delay_rts_after_send, 100U);
+
+ /*
+ * Make sure no SW-timed RTS toggle survives an RS485 disable, even
+ * if the delay timer happens to be running right now.
+ */
+ if (!(rs485->flags & SER_RS485_ENABLED)) {
+ one->tx_teardown = true;
+ one->cancel_tx_delay_tmr = true;
+ if (hrtimer_try_to_cancel(&one->tx_delay_tmr) == -1) {
+ uart_port_unlock(port);
+ hrtimer_cancel(&one->tx_delay_tmr);
+ uart_port_lock(port);
+ }
+ WRITE_ONCE(one->tx_state, MAX310X_TX_OFF);
+ one->tx_teardown = false;
+ /*
+ * The port stays alive, and a write that raced the teardown
+ * may have left data queued with no envelope left to pump it.
+ * Kick tx_work; RS485 is disabled, so the plain path is right.
+ */
+ if (!kfifo_is_empty(&port->state->port.xmit_fifo))
+ schedule_work(&one->tx_work);
+ }
port->rs485 = *rs485;
@@ -1222,7 +1461,15 @@ static int max310x_startup(struct uart_port *port)
max310x_port_update(port, MAX310X_MODE2_REG,
MAX310X_MODE2_FIFORST_BIT, 0);
- /* Configure the RS485 RTS timing and the RS485/RS232 mode bits. */
+ one->tx_teardown = false;
+
+ /*
+ * Configure the RS485 RTS timing (HW auto-RTS vs software-driven) and
+ * the RS485/RS232 mode bits. Don't hardcode HW auto-RTS here - let
+ * max310x_set_rts_ctl_params() pick HW or SW per the configured
+ * delays, otherwise the chip's auto-RTS would override the software
+ * RTS hold and the after-send delay is lost.
+ */
max310x_set_rts_ctl_params(one);
if (port->rs485.flags & SER_RS485_ENABLED &&
@@ -1252,22 +1499,64 @@ static int max310x_startup(struct uart_port *port)
static void max310x_shutdown(struct uart_port *port)
{
struct max310x_one *one = to_max310x_port(port);
- unsigned int loops = port->fifosize + 1;
+
+ if (READ_ONCE(one->sw_rts_during_tx)) {
+ /*
+ * Drain any in-flight software-timed RTS envelope before the
+ * port is powered down, so the last character and its
+ * after-send hold complete - close() can reach shutdown with
+ * data still queued and a before-send delay pending. The loop
+ * ends when the envelope does (tx_state == OFF); the bound is
+ * just a worst-case safety cap.
+ */
+ unsigned int loops = port->rs485.delay_rts_before_send +
+ port->rs485.delay_rts_after_send +
+ DIV_ROUND_UP_ULL((u64)(kfifo_len(&port->state->port.xmit_fifo) +
+ port->fifosize) *
+ one->one_char_duration_us,
+ USEC_PER_MSEC);
+
+ while (READ_ONCE(one->tx_state) != MAX310X_TX_OFF && loops-- > 0)
+ fsleep(USEC_PER_MSEC);
+ } else {
+ /*
+ * The tty layer waits for tx_empty() before close(), but
+ * tx_empty() only reflects the chip TX FIFO - the last
+ * character may still be in the transmit shift register. Let
+ * the FIFO drain and the final character clock out before the
+ * port is powered down, otherwise close() truncates the last
+ * byte on the wire.
+ */
+ unsigned int loops = port->fifosize + 1;
+
+ while (!max310x_tx_empty(port) && loops-- > 0)
+ fsleep(one->one_char_duration_us);
+ fsleep(one->one_char_duration_us);
+ }
/*
- * The tty layer waits for tx_empty() before close(), but tx_empty()
- * only reflects the chip TX FIFO - the last character may still be in
- * the transmit shift register. Let the FIFO drain and the final
- * character clock out before the port is powered down, otherwise
- * close() truncates the last byte on the wire.
+ * Set the teardown interlock under port->lock, so a concurrent
+ * start_tx() either sees it or happens-before the cancellations
+ * below. Then cancel unconditionally: the SW/HW decision is
+ * recomputed on every reconfigure, so a TIOCSRS485 can clear
+ * sw_rts_during_tx while an envelope is still in flight, and
+ * neither the timer nor the works may outlive the port.
*/
- while (!max310x_tx_empty(port) && loops-- > 0)
- fsleep(one->one_char_duration_us);
- fsleep(one->one_char_duration_us);
+ scoped_guard(spinlock_irqsave, &port->lock) {
+ one->tx_teardown = true;
+ one->cancel_tx_delay_tmr = true;
+ }
+ cancel_work_sync(&one->tx_work);
+ hrtimer_cancel(&one->tx_delay_tmr);
+ cancel_work_sync(&one->rts_work);
+ WRITE_ONCE(one->tx_state, MAX310X_TX_OFF);
/* Disable all interrupts */
max310x_port_write(port, MAX310X_IRQEN_REG, 0);
+ if (READ_ONCE(one->sw_rts_during_tx))
+ max310x_rts_ctl(port, max310x_rts_level(port, false));
+
max310x_power(port, 0);
}
@@ -1557,6 +1846,11 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
INIT_WORK(&s->p[i].md_work, max310x_md_proc);
/* Initialize queue for changing RS485 mode */
INIT_WORK(&s->p[i].rs_work, max310x_rs_proc);
+ /* Initialize queue for software-driven RTS toggling */
+ INIT_WORK(&s->p[i].rts_work, max310x_rts_work_proc);
+ hrtimer_setup(&s->p[i].tx_delay_tmr, max310x_tmr_tx,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ s->p[i].tx_state = MAX310X_TX_OFF;
}
#ifdef CONFIG_GPIOLIB
@@ -1667,6 +1961,8 @@ static void max310x_remove(struct device *dev)
int i;
for (i = 0; i < s->devtype->nr; i++) {
+ hrtimer_cancel(&s->p[i].tx_delay_tmr);
+ cancel_work_sync(&s->p[i].rts_work);
cancel_work_sync(&s->p[i].tx_work);
cancel_work_sync(&s->p[i].md_work);
cancel_work_sync(&s->p[i].rs_work);
--
2.47.3
next prev parent reply other threads:[~2026-09-29 9:39 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-29 9:37 [PATCH v5 0/8] serial: max310x: RS485 delay and RTS fixes, software-timed delays Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 1/8] serial: max310x: don't clobber the TX break bit in set_termios Tapio Reijonen
2026-09-29 13:40 ` Hugo Villeneuve
2026-10-02 7:25 ` Tapio Reijonen
2026-10-02 15:03 ` Hugo Villeneuve
2026-10-04 10:45 ` Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 2/8] serial: max310x: assert the transceiver during a break Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 3/8] serial: max310x: convert RS485 delays from milliseconds to bit-times Tapio Reijonen
2026-09-29 13:54 ` Hugo Villeneuve
2026-10-01 19:11 ` Hugo Villeneuve
2026-10-02 7:27 ` Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 4/8] serial: max310x: wait for TX to drain before powering down in shutdown Tapio Reijonen
2026-10-01 20:00 ` Hugo Villeneuve
2026-10-02 7:28 ` Tapio Reijonen
2026-10-02 15:01 ` Hugo Villeneuve
2026-10-04 10:56 ` Tapio Reijonen
2026-09-29 9:37 ` [PATCH v5 5/8] serial: max310x: support active-low RTS on the hardware path Tapio Reijonen
2026-09-29 9:38 ` [PATCH v5 6/8] serial: max310x: schedule tx_work directly from the IRQ handler Tapio Reijonen
2026-09-29 9:38 ` Tapio Reijonen [this message]
2026-09-29 9:38 ` [PATCH v5 8/8] serial: max310x: don't transmit while an RS485 reconfigure is pending Tapio Reijonen
2026-10-01 8:35 ` [PATCH v5 0/8] serial: max310x: RS485 delay and RTS fixes, software-timed delays Greg Kroah-Hartman
2026-10-01 9:10 ` Tapio Reijonen
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=20260929-max310x-rs485-sw-delay-v5-7-ae46afa583f2@vaisala.com \
--to=tapio.reijonen@vaisala.com \
--cc=gregkh@linuxfoundation.org \
--cc=hvilleneuve@dimonoff.com \
--cc=jirislaby@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=tapio.reijonen@kolumbus.fi \
/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
all inboxes | Powered by JetHome®