[PATCH v3 1/3] serial: sb1250-duart: Fix console message clobbering at channel resets
"Maciej W. Rozycki" <[email protected]> Thu, 6 Aug 2026 11:54:16 +0100 (BST)
| Newsgroups | org.kernel.vger.linux-serial,org.kernel.vger.linux-kernel,org.kernel.vger.linux-mips |
|---|---|
| Message-ID | <[email protected]> |
Ensure any characters outstanding have been sent before issuing channel
resets so as to prevent messages issued to the bootconsole from getting
clobbered.
Contrary to device documentation at the time the transmitter empty bit
is set only the transmit FIFO has been drained and there is still data
outstanding in the transmitter shift register, so wait an extra amount
of time for that register to drain too. This also prevents subsequent
messages produced to the console from getting clobbered, owing to what
seems a transmitter synchronisation issue.
When called from sbd_serial_console_init() it is too early for fsleep()
to work and even before lpj has been calculated, and therefore neither
udelay() works. Therefore delay by hand, observing that roughly 170
iterations over an Input Port Register read are sufficient for the
transmitter to drain with the BCM1250 SoC clocked at 800MHz, so using
2048 iterations should give enough margin including in particular for
the faster BCM1480 SoC clocked at 1GHz.
Fixes: b45d52797432 ("sb1250-duart.c: SB1250 DUART serial support")
Signed-off-by: Maciej W. Rozycki <[email protected]>
Cc: [email protected] # v2.6.23+
---
Changes from v2 (1/6),
<https://lore.kernel.org/r/[email protected]/>:
- In the early-boot case delay by hand rather than via udelay() and switch
to fsleep() for the late execution case.
- Update the Fixes: tag now that the delay has been sanitised and is more
than just a placeholder.
Changes from v1 (1/4),
<https://lore.kernel.org/r/[email protected]/>:
- Sanitise the change heading.
---
drivers/tty/serial/sb1250-duart.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
linux-serial-sb1250-duart-reset-drain.diff
Index: linux-swarm64/drivers/tty/serial/sb1250-duart.c
===================================================================
--- linux-swarm64.orig/drivers/tty/serial/sb1250-duart.c
+++ linux-swarm64/drivers/tty/serial/sb1250-duart.c
@@ -516,6 +516,33 @@ static void sbd_init_port(struct sbd_por
if (sport->initialised)
return;
+ /*
+ * Contrary to documentation, which says that the transmitter
+ * empty bit is set when "there are no characters to send and
+ * the transmitter is idle," the bit is already set by hardware
+ * once the transmit FIFO has been drained only and while the
+ * transmitter shift register still holds data being supplied
+ * to the line. Consequently issuing a transmitter reset at
+ * this point causes the final character outstanding to be lost.
+ *
+ * Moreover, resetting the transmitter while transmission is
+ * in progress appears to make the transmitter go out of sync
+ * and subsequent characters issued after the transmitter has
+ * been reprogrammed and re-enabled are sent corrupted or with
+ * their bit patterns shifted.
+ *
+ * So once the transmitter empty bit has been set wait an extra
+ * amount of time, sufficient for the transmitter shift register
+ * to drain at 115200bps, which is the baud rate setting used by
+ * a standard CFE firmware compilation.
+ */
+ sbd_line_drain(sport);
+ if (IS_ENABLED(CONFIG_SERIAL_SB1250_DUART_CONSOLE) &&
+ system_state < SYSTEM_SCHEDULING)
+ for (int i = 0; i < 2048; i++)
+ read_sbdshr(sport, R_DUART_IN_PORT);
+ else
+ fsleep(100);
/* There is no DUART reset feature, so just set some sane defaults. */
write_sbdchn(sport, R_DUART_CMD, V_DUART_MISC_CMD_RESET_TX);