mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
serial: max310x: don't power down the port mid-transmission on close
max310x_shutdown() powers the port down regardless of what the
transmitter is doing. The power-off stops the UART clock mid-character,
truncating the final frame, and on the auto-RTS path it freezes the
RTS output at its current level: a close() with data still queued
leaves the RS485 transceiver asserted until the next open, which also
emits the interrupted character corrupted.
Waiting for the data to drain is no better: the tty layer's
wait-until-sent is bounded by closing_wait, configurable to none, and
a hangup arrives with no wait at all, so draining a full FIFO blocks
close() in uninterruptible sleep for seconds at low baud rates - and
forever when CTS flow control blocks the FIFO.
Instead, set MODE1 TxDisabl: the character in flight completes and the
transmitter ceases with TX_ at idle. Give that character one character
time (the chip has no transmitter-idle status), then reset the FIFOs
so the auto-RTS engine sees the transmitter empty and releases RTS
within the configured after-send hold; wait that hold plus one bit
time before powering off. Abandoned data was explicitly not waited
for, and startup() resets the FIFOs and TxDisabl on open anyway.
Measured on a MAX14830: a truncating close() at 50 baud takes 0.23 s,
RTS releases one character plus the hold after the last stop bit on
both polarities, and the reopen corruption is gone.
Fixes: f65444187a ("serial: New serial driver MAX310X")
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-5-6ba881f81e1f@vaisala.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
7784d6aee5
commit
c3ad42ccd3
|
|
@ -300,6 +300,7 @@ struct max310x_one {
|
|||
struct work_struct md_work;
|
||||
struct work_struct rs_work;
|
||||
struct regmap *regmap;
|
||||
unsigned int char_time_us;
|
||||
unsigned int baud;
|
||||
bool tx_break; /* break_ctl() owns the transceiver */
|
||||
|
||||
|
|
@ -1010,6 +1011,7 @@ static void max310x_set_termios(struct uart_port *port,
|
|||
struct ktermios *termios,
|
||||
const struct ktermios *old)
|
||||
{
|
||||
unsigned int frame_bits = tty_get_frame_size(termios->c_cflag);
|
||||
unsigned int lcr = 0, flow = 0;
|
||||
int baud;
|
||||
|
||||
|
|
@ -1122,10 +1124,13 @@ static void max310x_set_termios(struct uart_port *port,
|
|||
uart_update_timeout(port, termios->c_cflag, baud);
|
||||
|
||||
/*
|
||||
* Cache the new baud rate and reprogram the RS485 RTS delays, whose
|
||||
* millisecond-to-bit-time conversion depends on it.
|
||||
* Cache the new baud rate and the time it takes to clock out one
|
||||
* character before reprogramming the RS485 RTS delays: the
|
||||
* millisecond-to-bit-time conversion divides by the baud rate.
|
||||
*/
|
||||
to_max310x_port(port)->baud = baud;
|
||||
to_max310x_port(port)->char_time_us =
|
||||
DIV_ROUND_UP(USEC_PER_SEC * frame_bits, baud);
|
||||
max310x_set_rts_ctl_params(to_max310x_port(port));
|
||||
}
|
||||
|
||||
|
|
@ -1182,9 +1187,10 @@ static int max310x_startup(struct uart_port *port)
|
|||
one->tx_break = false;
|
||||
max310x_port_update(port, MAX310X_LCR_REG, MAX310X_LCR_TXBREAK_BIT, 0);
|
||||
|
||||
/* Configure MODE1 register */
|
||||
/* Configure MODE1: re-enable the transmitter shutdown() stopped */
|
||||
max310x_port_update(port, MAX310X_MODE1_REG,
|
||||
MAX310X_MODE1_TRNSCVCTRL_BIT, 0);
|
||||
MAX310X_MODE1_TRNSCVCTRL_BIT |
|
||||
MAX310X_MODE1_TXDIS_BIT, 0);
|
||||
|
||||
/* Configure MODE2 register & Reset FIFOs*/
|
||||
val = MAX310X_MODE2_RXEMPTINV_BIT | MAX310X_MODE2_FIFORST_BIT;
|
||||
|
|
@ -1221,9 +1227,39 @@ static int max310x_startup(struct uart_port *port)
|
|||
|
||||
static void max310x_shutdown(struct uart_port *port)
|
||||
{
|
||||
struct max310x_one *one = to_max310x_port(port);
|
||||
|
||||
/* Disable all interrupts */
|
||||
max310x_port_write(port, MAX310X_IRQEN_REG, 0);
|
||||
|
||||
/*
|
||||
* Stop the transmitter: the character in flight completes, data
|
||||
* still queued is abandoned (the next startup() resets the FIFOs).
|
||||
*/
|
||||
max310x_port_update(port, MAX310X_MODE1_REG,
|
||||
MAX310X_MODE1_TXDIS_BIT, MAX310X_MODE1_TXDIS_BIT);
|
||||
|
||||
/*
|
||||
* Let the character in flight finish (the chip has no
|
||||
* transmitter-idle status), then empty the FIFO: auto-RTS releases
|
||||
* only once the transmitter is empty, and the power-off below
|
||||
* would freeze an asserted pin until the next open.
|
||||
*/
|
||||
fsleep(one->char_time_us);
|
||||
max310x_port_update(port, MAX310X_MODE2_REG,
|
||||
MAX310X_MODE2_FIFORST_BIT,
|
||||
MAX310X_MODE2_FIFORST_BIT);
|
||||
max310x_port_update(port, MAX310X_MODE2_REG,
|
||||
MAX310X_MODE2_FIFORST_BIT, 0);
|
||||
if (port->rs485.flags & SER_RS485_ENABLED && one->baud) {
|
||||
unsigned int hold = DIV_ROUND_UP(one->baud *
|
||||
port->rs485.delay_rts_after_send,
|
||||
MSEC_PER_SEC);
|
||||
|
||||
fsleep(max(DIV_ROUND_UP((hold + 1) * USEC_PER_SEC,
|
||||
one->baud), 100U));
|
||||
}
|
||||
|
||||
max310x_power(port, 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user