libplatsupport,imx: improve CR LF handling
Ensure either CR LF is sent or nothing at all, but never send just a
CR in case the FIFO is full. Add comments about FIFO behavior.
Signed-off-by: Axel Heider <axelheider@gmx.de>
diff --git a/libplatsupport/src/mach/imx/serial/serial.c b/libplatsupport/src/mach/imx/serial/serial.c
index a0cf490..f8f3778 100644
--- a/libplatsupport/src/mach/imx/serial/serial.c
+++ b/libplatsupport/src/mach/imx/serial/serial.c
@@ -103,20 +103,50 @@
return c;
}
+static int internal_is_tx_fifo_busy(
+ imx_uart_regs_t *regs)
+{
+ /* check the TXFE (transmit buffer FIFO empty) flag, which is cleared
+ * automatically when data is written to the TxFIFO. Even though the flag
+ * is set, the actual data transmission via the UART's 32 byte FIFO buffer
+ * might still be in progress.
+ */
+ return (0 == (regs->sr2 & UART_SR2_TXFIFO_EMPTY));
+}
+
int uart_putchar(
ps_chardevice_t *d,
int c)
{
imx_uart_regs_t *regs = imx_uart_get_priv(d);
- if (regs->sr2 & UART_SR2_TXFIFO_EMPTY) {
- if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) {
- uart_putchar(d, '\r');
- }
- regs->txd = c;
- return c;
- } else {
+
+ if (internal_is_tx_fifo_busy(regs)) {
return -1;
}
+
+ if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) {
+ /* write CR first */
+ regs->txd = '\r';
+ /* if we transform a '\n' (LF) into '\r\n' (CR+LF) this shall become an
+ * atom, ie we don't want CR to be sent and then fail at sending LF
+ * because the TX FIFO is full. Basically there are two options:
+ * - check if the FIFO can hold CR+LF and either send both or none
+ * - send CR, then block until the FIFO has space and send LF.
+ * Assuming that if SERIAL_AUTO_CR is set, it's likely this is a serial
+ * console for logging, so blocking seems acceptable in this special
+ * case. The IMX6's TX FIFO size is 32 byte and TXFIFO_EMPTY is cleared
+ * automatically as soon as data is written from regs->txd into the
+ * FIFO. Thus the worst case blocking is roughly the time it takes to
+ * send 1 byte to have room in the FIFO again. At 115200 baud with 8N1
+ * this takes 10 bit-times, which is 10/115200 = 86,8 usec.
+ */
+ while (internal_is_tx_fifo_busy(regs)) {
+ /* busy loop */
+ }
+ }
+
+ regs->txd = c;
+ return c;
}
static void uart_handle_irq(ps_chardevice_t *d UNUSED)