[PATCH 2/2] serial: sc16is7xx: set TX FIFO trigger level to half FIFO to prevent underruns

From: Paul Mbewe

Date: Tue Jun 23 2026 - 07:25:08 EST


The THRI interrupt (IER[1]) fires when the TX FIFO free space reaches the
configured threshold. With the reset default (TLR=0), the chip falls back
to the FCR TX trigger of 8 free spaces (FCR[5:4]=00), causing THRI to
assert after every 8 bytes drain from the FIFO.

At 115200 baud 8N1, 8 bytes drain in 694 us. On slow single-core SPI
hosts, the combined latency of an SPI IIR read, TXLVL read, and 8-byte
THR write per interrupt, plus kthread scheduling jitter, can exceed this
window on a loaded system. When the kthread cannot refill the FIFO within
694 us, the FIFO empties and produces an idle gap on the TX line.

This violates the Modbus RTU specification, which treats any intra-frame
silence longer than 1.5 character times (~130 us at 115200 baud) as a
frame boundary, causing receivers to fragment frames and report CRC errors.
Oscilloscope measurements confirmed a 757 us inter-burst gap during
continuous transmission without this fix.

Setting the TX trigger to 32 free spaces (half FIFO) via TLR[3:0]=8
widens the refill window to 2778 us at 115200 baud, reducing THRI events
per 256-byte frame from ~32 to ~8 and eliminating the underrun.

Only TLR[3:0] is written; TLR[7:4] is left at zero, so the RX trigger
retains its FCR default. Only TX interrupt timing is affected.

While increasing the SPI clock would also reduce per-round-trip latency,
the driver should work correctly regardless of SPI speed. The fix belongs
in the driver.

Tested on i.MX6ULL (ARM Cortex-A7, single-core) with SC16IS752IBS over
SPI at 1 MHz, 115200 baud 8N1, 256-byte Modbus RTU frames under production
load:

IRQ thread (irq/134-spi2.0): ~15-17% -> ~5% (~67% reduction)
sys CPU: ~51-61% -> ~19-28% (~55% reduction)
load average: ~2.0-2.2 -> ~0.65-1.3

No mid-frame gaps observed after fix. Without fix, oscilloscope confirmed
757 us inter-burst gaps causing Modbus frame fragmentation.

Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Tobias Gannert <tobias.gannert@xxxxxxxxxxxxxx>
Tested-by: Tobias Gannert <tobias.gannert@xxxxxxxxxxxxxx>
Reviewed-by: Joachim Knorr <joachim.knorr@xxxxxxxxxxxxxx>
Signed-off-by: Paul Mbewe <paultyson.mbewe@xxxxxxxxxxxxxx>
---
drivers/tty/serial/sc16is7xx.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index 395a219280be..476e0dd3fa7f 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1196,6 +1196,16 @@ static int sc16is7xx_startup(struct uart_port *port)
SC16IS7XX_TCR_RX_RESUME(24) |
SC16IS7XX_TCR_RX_HALT(48));

+ /*
+ * Set TX FIFO trigger level to 32 spaces (half FIFO) via TLR. The reset
+ * default (TLR=0) falls back to the FCR TX trigger of 8 free spaces,
+ * requiring ~8 SPI round-trips per 64-byte FIFO load. On slow single-core
+ * SPI hosts, this accumulated latency can cause a TX FIFO underrun gap
+ * between bursts.
+ */
+ sc16is7xx_port_write(port, SC16IS7XX_TLR_REG,
+ SC16IS7XX_TLR_TX_TRIGGER(32));
+
/* Disable TCR/TLR access */
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG, SC16IS7XX_MCR_TCRTLR_BIT, 0);

--
2.43.0