]> git.karo-electronics.de Git - linux-beck.git/commitdiff
powerpc/udbg: Fix lost byte during console handover; change LFCR to CRLF
authorAndrew Klossner <andrew@cesa.opbu.xerox.com>
Mon, 9 Mar 2009 07:52:41 +0000 (07:52 +0000)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Wed, 11 Mar 2009 06:11:34 +0000 (17:11 +1100)
When the console is on a serial port to be driven by serial8250, a
character can be lost from the end of the first line in the two-line
sequence

serial8250.0: ttyS0 at MMIO 0xe0004500 (irq = 42) is a 16550A
console handover: boot [udbg0] -> real [ttyS0]

This happens because udbg_puts or udbg_write stuff the last byte of
the line into the Tx FIFO and return, whereupon the serial8250
initialization code immediately empties that FIFO.  The fix: udbg_puts
and udbg_write now wait for the Tx FIFO to clear before returning.
This delays the system by one additional serial frame time for each
line written by udbg, but the effect is not noticeable, a cumulative
17 milliseconds for 200 lines of early printk output at 115200 baud.

Also, the routines in udbg_16550.c now emit CRLF instead of LFCR.
Linux makes a point of emitting CRLF because, when serial output is
captured to a file, LFCR sequences can confuse text editors.  See
http://lkml.org/lkml/2006/2/4/50 for some history.

Signed-off-by: Andrew Klossner <andrew@cesa.opbu.xerox.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/include/asm/udbg.h
arch/powerpc/kernel/udbg.c
arch/powerpc/kernel/udbg_16550.c

index 6418ceea44b70f27d69027b3a30ee85d8bd4b3fd..cd21e5e6b04f975e09c7ae7d4feb32a862940501 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 
 extern void (*udbg_putc)(char c);
+extern void (*udbg_flush)(void);
 extern int (*udbg_getc)(void);
 extern int (*udbg_getc_poll)(void);
 
index 7d6c9bb8c77fe4f6f220b81d82b282f70e26b83f..fc9af47e21285f7a10872849c88930d0218bcb44 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/udbg.h>
 
 void (*udbg_putc)(char c);
+void (*udbg_flush)(void);
 int (*udbg_getc)(void);
 int (*udbg_getc_poll)(void);
 
@@ -76,6 +77,9 @@ void udbg_puts(const char *s)
                        while ((c = *s++) != '\0')
                                udbg_putc(c);
                }
+
+               if (udbg_flush)
+                       udbg_flush();
        }
 #if 0
        else {
@@ -98,6 +102,9 @@ int udbg_write(const char *s, int n)
                }
        }
 
+       if (udbg_flush)
+               udbg_flush();
+
        return n - remain;
 }
 
index 7b7da8cfd5e862364feb391e4a3cc68447cfdec2..0362a891e54ee3736707ae0b4066237fd529fadd 100644 (file)
@@ -48,14 +48,21 @@ struct NS16550 {
 
 static struct NS16550 __iomem *udbg_comport;
 
-static void udbg_550_putc(char c)
+static void udbg_550_flush(void)
 {
        if (udbg_comport) {
                while ((in_8(&udbg_comport->lsr) & LSR_THRE) == 0)
                        /* wait for idle */;
-               out_8(&udbg_comport->thr, c);
+       }
+}
+
+static void udbg_550_putc(char c)
+{
+       if (udbg_comport) {
                if (c == '\n')
                        udbg_550_putc('\r');
+               udbg_550_flush();
+               out_8(&udbg_comport->thr, c);
        }
 }
 
@@ -108,6 +115,7 @@ void udbg_init_uart(void __iomem *comport, unsigned int speed,
                /* Clear & enable FIFOs */
                out_8(&udbg_comport->fcr ,0x07);
                udbg_putc = udbg_550_putc;
+               udbg_flush = udbg_550_flush;
                udbg_getc = udbg_550_getc;
                udbg_getc_poll = udbg_550_getc_poll;
        }
@@ -149,14 +157,21 @@ unsigned int udbg_probe_uart_speed(void __iomem *comport, unsigned int clock)
 }
 
 #ifdef CONFIG_PPC_MAPLE
-void udbg_maple_real_putc(char c)
+void udbg_maple_real_flush(void)
 {
        if (udbg_comport) {
                while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
                        /* wait for idle */;
-               real_writeb(c, &udbg_comport->thr); eieio();
+       }
+}
+
+void udbg_maple_real_putc(char c)
+{
+       if (udbg_comport) {
                if (c == '\n')
                        udbg_maple_real_putc('\r');
+               udbg_maple_real_flush();
+               real_writeb(c, &udbg_comport->thr); eieio();
        }
 }
 
@@ -165,20 +180,28 @@ void __init udbg_init_maple_realmode(void)
        udbg_comport = (struct NS16550 __iomem *)0xf40003f8;
 
        udbg_putc = udbg_maple_real_putc;
+       udbg_flush = udbg_maple_real_flush;
        udbg_getc = NULL;
        udbg_getc_poll = NULL;
 }
 #endif /* CONFIG_PPC_MAPLE */
 
 #ifdef CONFIG_PPC_PASEMI
-void udbg_pas_real_putc(char c)
+void udbg_pas_real_flush(void)
 {
        if (udbg_comport) {
                while ((real_205_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
                        /* wait for idle */;
-               real_205_writeb(c, &udbg_comport->thr); eieio();
+       }
+}
+
+void udbg_pas_real_putc(char c)
+{
+       if (udbg_comport) {
                if (c == '\n')
                        udbg_pas_real_putc('\r');
+               udbg_pas_real_flush();
+               real_205_writeb(c, &udbg_comport->thr); eieio();
        }
 }
 
@@ -187,6 +210,7 @@ void udbg_init_pas_realmode(void)
        udbg_comport = (struct NS16550 __iomem *)0xfcff03f8UL;
 
        udbg_putc = udbg_pas_real_putc;
+       udbg_flush = udbg_pas_real_flush;
        udbg_getc = NULL;
        udbg_getc_poll = NULL;
 }
@@ -195,14 +219,21 @@ void udbg_init_pas_realmode(void)
 #ifdef CONFIG_PPC_EARLY_DEBUG_44x
 #include <platforms/44x/44x.h>
 
-static void udbg_44x_as1_putc(char c)
+static int udbg_44x_as1_flush(void)
 {
        if (udbg_comport) {
                while ((as1_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
                        /* wait for idle */;
-               as1_writeb(c, &udbg_comport->thr); eieio();
+       }
+}
+
+static void udbg_44x_as1_putc(char c)
+{
+       if (udbg_comport) {
                if (c == '\n')
                        udbg_44x_as1_putc('\r');
+               udbg_44x_as1_flush();
+               as1_writeb(c, &udbg_comport->thr); eieio();
        }
 }
 
@@ -222,19 +253,27 @@ void __init udbg_init_44x_as1(void)
                (struct NS16550 __iomem *)PPC44x_EARLY_DEBUG_VIRTADDR;
 
        udbg_putc = udbg_44x_as1_putc;
+       udbg_flush = udbg_44x_as1_flush;
        udbg_getc = udbg_44x_as1_getc;
 }
 #endif /* CONFIG_PPC_EARLY_DEBUG_44x */
 
 #ifdef CONFIG_PPC_EARLY_DEBUG_40x
-static void udbg_40x_real_putc(char c)
+static void udbg_40x_real_flush(void)
 {
        if (udbg_comport) {
                while ((real_readb(&udbg_comport->lsr) & LSR_THRE) == 0)
                        /* wait for idle */;
-               real_writeb(c, &udbg_comport->thr); eieio();
+       }
+}
+
+static void udbg_40x_real_putc(char c)
+{
+       if (udbg_comport) {
                if (c == '\n')
                        udbg_40x_real_putc('\r');
+               udbg_40x_real_flush();
+               real_writeb(c, &udbg_comport->thr); eieio();
        }
 }
 
@@ -254,6 +293,7 @@ void __init udbg_init_40x_realmode(void)
                CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR;
 
        udbg_putc = udbg_40x_real_putc;
+       udbg_flush = udbg_40x_real_flush;
        udbg_getc = udbg_40x_real_getc;
        udbg_getc_poll = NULL;
 }