summaryrefslogtreecommitdiff
path: root/tools/lib/python
diff options
context:
space:
mode:
authorTapio Reijonen <tapio.reijonen@vaisala.com>2026-10-08 09:48:31 +0000
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-10-13 15:19:41 +0200
commit33632215e1bbc6109173b761e0c9bc5c4fcdbeee (patch)
tree9a132a430ecc649d3f8057d516a3497d253baff6 /tools/lib/python
parentc06fcc258db0c0c7f607e64a7ce6a157bb2414b1 (diff)
serial: max310x: drive RTS in software when hardware delays are too short
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. A start_tx() landing while the envelope is already in its send phase only pumps the new data: rewinding it to the before-send phase would insert a spurious setup delay mid-stream. 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(). The interlock also fences the adoption path below, so a reconfigure racing a teardown cannot re-enable the cancelled timer, and an RS485 disable flushes a queued rts_work before the pin is settled: one already past its tx_state read would re-assert RTS after the settle. 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() gates first - the interlock is set and the interrupts are masked before any wait, so nothing can start a new envelope or requeue tx_work behind the cancels - then honours a running after-send hold, bounded by the after-send delay plus two character times (the transmitter is already stopped by this point), 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. A hold that did not complete within the bound is settled by the final release below regardless. break_ctl() on the software path applies the configured RTS polarity itself. A reconfigure can also move the port off the hardware path while the chip's auto-RTS still owns a transmission in flight - a termios change or TIOCSRS485 pushing a delay above what the hardware can time at the new rate. Such transfers are invisible to tx_state, so the idle settle would release RTS and the MODE1 write would disable auto-RTS mid-transfer, shifting the rest of the data out with the transceiver released and no error reported anywhere. Adopt the transfer instead when the chip TX FIFO or the xmit buffer is not empty: enter MAX310X_TX_SEND and drive RTS manually before auto-RTS is disabled, so the pin is never released across the handover, and the normal drain path arms the after-send hold - or the adoption arms it directly if the FIFO drained while this raced the TX-empty worker. A transfer whose last character has already left the FIFO for the transmit shift register is still invisible and keeps the old behavior: at most one character of early release. The reverse move strands state instead: a reconfigure onto the hardware path mid-envelope left tx_state set, because nothing reset it in that direction and the TX-empty hold arming only ran while the software path was selected. A later reconfigure back off the hardware path then trusted the stale tx_state, skipped both the adoption and the idle settle, and disabled auto-RTS with the RTS register released - the rest of the transfer in flight shifted out with the transceiver off, again with no error reported (hit twice in 79 in-flight reconfigures under a multi-process stress test). The TX-empty unwind now runs regardless of the selected path - it is a no-op outside an envelope - and the reconfigure asserts RTS for any transfer it believes exists, stale or live, before auto-RTS is disabled. The hold is armed only once no transmittable data remains, so that assert cannot cut a burst short when it lands in a FIFO refill window; output that is stopped still releases through the hold. rts_work also derives the level from tx_break: a break can begin while the previous envelope's after-send hold is still armed - TIOCSBRK waits for the output to drain, which lands the break exactly there - and the expiring hold would otherwise release the transceiver mid-break. With break ownership in the derivation the hold expiry becomes an idempotent re-assert, and break-off restores the idle state as it already does. shutdown() gives its final RTS release one bit time, and at least 100 us, to reach the pin before powering the port down. The power-off stops the UART channel clock, and the RTS output stage is clocked by it: a release written immediately before the clock stops reaches the register but never moves the pin, which stays asserted until the next startup restarts the clock - a close racing the after-send hold expiry left the transceiver driving the bus, with the register reading released and the pin high. The measured propagation is about one tick of the 16x oversampling clock - below 50 us at 1200 baud - so one bit time keeps a 16x margin and scales with the clock the output stage runs on; the floor covers rates where a bit is shorter than the measured bound. Handing the pin to the chip's auto-RTS engine instead was tried and rejected on the wire: the pin level is RTSINVERT xor the selected source, so for an active-low RTS every single-register step of that handover drives the pin to the asserted level, and the same clock stop can freeze it there. Fixes: 55367c620aed ("serial: max310x: Add support for RS-485 mode") Assisted-by: Claude:claude-fable-5 Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com> Link: https://patch.msgid.link/20261008-max310x-rs485-sw-delay-v8-8-6ba881f81e1f@vaisala.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'tools/lib/python')
0 files changed, 0 insertions, 0 deletions