Skip to content

Commit 0ba9aee

Browse files
nbuchwitzpelwell
authored andcommitted
serial: pl011: Add configurable bounded polling for RS485 TX drain
The hrtimer-based TX drain introduced in commit 2c1fd53 ("serial: amba-pl011: Fix RTS handling in RS485 mode") can cause RX errors in fast request/response RS485 protocols. The hrtimer callback latency (often 50-100µs per iteration) delays RX re-enable, causing the first bytes of slave responses to be lost. Add a configurable bounded polling mode that spins for TX completion with a timeout, falling back to hrtimer only if the timeout is exceeded. This is controlled by two new device tree properties: rs485-tx-drain-poll - enable bounded polling mode rs485-tx-drain-timeout-us - explicit timeout in microseconds (optional) When rs485-tx-drain-poll is set without an explicit timeout, the timeout is auto-calculated as (fifo_size + 1) * character_time, accounting for the FIFO depth plus one character in the shift register. This adapts automatically to baud rate changes. The default behavior (no DT properties) remains unchanged, preserving the pure hrtimer approach for systems where shared IRQ latency is the primary concern. Signed-off-by: Nicolai Buchwitz <n.buchwitz@kunbus.com>
1 parent 1e70af9 commit 0ba9aee

1 file changed

Lines changed: 38 additions & 0 deletions

File tree

drivers/tty/serial/amba-pl011.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ struct uart_amba_port {
286286
enum pl011_rs485_tx_state rs485_tx_state;
287287
struct hrtimer trigger_start_tx;
288288
struct hrtimer trigger_stop_tx;
289+
bool rs485_tx_drain_poll; /* use bounded polling */
290+
unsigned int rs485_tx_drain_timeout_us; /* explicit override, 0 = auto */
289291
#ifdef CONFIG_DMA_ENGINE
290292
/* DMA stuff */
291293
unsigned int dmacr; /* dma control reg */
@@ -1288,6 +1290,19 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
12881290
#define pl011_dma_flush_buffer NULL
12891291
#endif
12901292

1293+
static unsigned int pl011_rs485_tx_drain_timeout(struct uart_amba_port *uap)
1294+
{
1295+
if (uap->rs485_tx_drain_timeout_us > 0)
1296+
return uap->rs485_tx_drain_timeout_us;
1297+
1298+
/*
1299+
* Auto-calculate based on FIFO depth plus one character
1300+
* in the shift register.
1301+
*/
1302+
return div_u64(ktime_to_ns(uap->rs485_tx_drain_interval) *
1303+
(uap->fifosize + 1), NSEC_PER_USEC);
1304+
}
1305+
12911306
static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
12921307
{
12931308
struct uart_port *port = &uap->port;
@@ -1297,6 +1312,19 @@ static void pl011_rs485_tx_stop(struct uart_amba_port *uap)
12971312
uap->rs485_tx_state = WAIT_AFTER_SEND;
12981313

12991314
if (uap->rs485_tx_state == WAIT_AFTER_SEND) {
1315+
if (uap->rs485_tx_drain_poll) {
1316+
/* Bounded spin with auto-calculated or explicit timeout */
1317+
unsigned int timeout_us = pl011_rs485_tx_drain_timeout(uap);
1318+
ktime_t deadline = ktime_add_us(ktime_get(), timeout_us);
1319+
1320+
while (!pl011_tx_empty(port)) {
1321+
if (ktime_after(ktime_get(), deadline)) {
1322+
/* Timeout - fall back to hrtimer */
1323+
break;
1324+
}
1325+
cpu_relax();
1326+
}
1327+
}
13001328
/* Schedule hrtimer if tx queue not empty */
13011329
if (!pl011_tx_empty(port)) {
13021330
hrtimer_start(&uap->trigger_stop_tx,
@@ -2904,6 +2932,11 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
29042932
uap->trigger_start_tx.function = pl011_trigger_start_tx;
29052933
uap->trigger_stop_tx.function = pl011_trigger_stop_tx;
29062934

2935+
uap->rs485_tx_drain_poll = device_property_read_bool(&dev->dev,
2936+
"rs485-tx-drain-poll");
2937+
device_property_read_u32(&dev->dev, "rs485-tx-drain-timeout-us",
2938+
&uap->rs485_tx_drain_timeout_us);
2939+
29072940
ret = pl011_setup_port(&dev->dev, uap, &dev->res, portnr);
29082941
if (ret)
29092942
return ret;
@@ -3103,6 +3136,11 @@ static int pl011_axi_probe(struct platform_device *pdev)
31033136
uap->trigger_start_tx.function = pl011_trigger_start_tx;
31043137
uap->trigger_stop_tx.function = pl011_trigger_stop_tx;
31053138

3139+
uap->rs485_tx_drain_poll = device_property_read_bool(&pdev->dev,
3140+
"rs485-tx-drain-poll");
3141+
device_property_read_u32(&pdev->dev, "rs485-tx-drain-timeout-us",
3142+
&uap->rs485_tx_drain_timeout_us);
3143+
31063144
ret = pl011_setup_port(&pdev->dev, uap, r, portnr);
31073145
if (ret)
31083146
return ret;

0 commit comments

Comments
 (0)