Supporting early console on a SERIAL_8250_RT288X platform
Robin Murphy
robin.murphy at arm.com
Fri Apr 7 08:18:40 EDT 2017
On 07/04/17 12:53, Mason wrote:
> Hello,
>
> Some drivers hang or panic before the normal console is available.
> In these situations, "earlyprintk" is a life-saver.
>
> My problem with earlyprintk is that the UART address needs to be
> hard-coded in the kernel binary, which means I can't use the same
> binary for two chips with different UART addresses, right?
>
> As far as I understand, "earlycon" helps in that situation, because
> it looks up the UART address at run-time in the device tree.
>
>
> QUESTION #1
>
> Is that the only difference between earlyprintk and earlycon?
> (Probably not, see below)
>
>
> I hacked kernel v4.9.20 to add early console support on my platform.
> The patch is provided below, I'd like to hear comments on what needs
> fixing... because something is not right. If I call panic() in one of
> my driver's probe function, I don't see the output of the panic()
> call, which makes the whole exercise rather pointless.
>
> This is what I see:
>
> [ 0.000000] Booting Linux on physical CPU 0x0
> [ 0.000000] Linux version 4.9.20-1-rc3 (gcc version 5.3.1 20160113 (Linaro GCC 5.3-2016.02) ) #4 SMP PREEMPT Fri Apr 7 12:57
> [ 0.000000] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c5387d
> [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
> [ 0.000000] OF: fdt:Machine model: Sigma Designs SMP8758 Vantage-1172 Rev E1
> [ 0.000000] ENTER param_setup_earlycon
> [ 0.000000] ENTER early_init_dt_scan_chosen_stdout
> [ 0.000000] early_init_dt_scan_chosen_stdout: scan __earlycon_table
> [ 0.000000] name=palmchip compatible=ralink,rt2880-uart setup=early_palmchip_setup
> [ 0.000000] ENTER of_setup_earlycon
> [ 0.000000] earlycon: palmchip0 at MMIO 0x00010700 (options '115200n8')
> [ 0.000000] bootconsole [palmchip0] enabled
> [ 0.000000] Memory policy: Data cache writealloc
> [ 0.000000] percpu: Embedded 14 pages/cpu @cfdd6000 s24704 r8192 d24448 u57344
> [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024
> [ 0.000000] Kernel command line: mem=256M earlycon
> [ 0.000000] PID hash table entries: 1024 (order: 0, 4096 bytes)
> [ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
> [ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
> [ 0.000000] Memory: 248064K/262144K available (4096K kernel code, 147K rwdata, 892K rodata, 6144K init, 233K bss, 14080K reserved, 0K cma-reserved, 0K highmem)
> [ 0.000000] Virtual kernel memory layout:
> [ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB)
> [ 0.000000] fixmap : 0xffc00000 - 0xfff00000 (3072 kB)
> [ 0.000000] vmalloc : 0xd0800000 - 0xff800000 ( 752 MB)
> [ 0.000000] lowmem : 0xc0000000 - 0xd0000000 ( 256 MB)
> [ 0.000000] pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB)
> [ 0.000000] modules : 0xbf000000 - 0xbfe00000 ( 14 MB)
> [ 0.000000] .text : 0xc0008000 - 0xc0500000 (5088 kB)
> [ 0.000000] .init : 0xc0600000 - 0xc0c00000 (6144 kB)
> [ 0.000000] .data : 0xc0c00000 - 0xc0c24c60 ( 148 kB)
> [ 0.000000] .bss : 0xc0c24c60 - 0xc0c5f140 ( 234 kB)
> [ 0.000000] Preemptible hierarchical RCU implementation.
> [ 0.000000] Build-time adjustment of leaf fanout to 32.
> [ 0.000000] RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2.
> [ 0.000000] RCU: Adjusting geometry for rcu_fanout_leaf=32, nr_cpu_ids=2
> [ 0.000000] NR_IRQS:16 nr_irqs:16 16
> [ 0.000000] L2C-310 enabling early BRESP for Cortex-A9
> [ 0.000000] L2C-310 ID prefetch enabled, offset 4 lines
> [ 0.000000] L2C-310 dynamic clock gating enabled, standby mode enabled
> [ 0.000000] L2C-310 cache controller enabled, 8 ways, 512 kB
> [ 0.000000] L2C-310: CACHE_ID 0x410000c8, AUX_CTRL 0x72860401
> [ 0.000000] clocksource: tango-xtal: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 70787423951 ns
> [ 0.000003] sched_clock: 32 bits at 27MHz, resolution 37ns, wraps every 79536431085ns
> [ 0.007873] Switching to timer-based delay loop, resolution 37ns
> [ 0.014146] Console: colour dummy device 80x30
> [ 0.018616] console [tty0] enabled
> [ 0.022039] bootconsole [palmchip0] disabled
>
> And it just hangs there...
>
> If I remove the panic() call, it boots normally, like this:
>
> ...
> [ 0.014147] Console: colour dummy device 80x30
> [ 0.018616] console [tty0] enabled
> [ 0.022039] bootconsole [palmchip0] disabled
> [ 0.026345] Calibrating delay loop (skipped), value calculated using timer frequency.. 54.25 BogoMIPS (lpj=90000)
> [ 0.026364] pid_max: default: 32768 minimum: 301
> ...
>
> Did I implement something wrong?
> Why am I not seeing the output of the panic() call, like I do
> with earlyprintk?
> Since tty0 is enabled, why isn't it picking up where palmchip0
> left off?
As far as I'm aware, tty0 is a purely virtual console: look further down
still and you'll probably see a second switch over from tty0 to ttyS0,
which will be when the buffered output *actually* starts coming out
again. That's certainly what happens for me, albeit with
"pl11"->"tty0"->"ttyAMA0", (and complete with not realising ttyAMA0 is
still the same port and reprinting the entire log buffer from the top).
I believe this is something to do with how the DT stdout-path property
is implemented, because with an explicit "console=ttyAMA0" (or ttyS0 in
your case) on the command line instead of relying on stdout-path, the
boot console does switch directly to the real UART without the
intermediate tty0 blind spot.
Robin.
>
>
> Earlier discussion:
> https://www.spinics.net/lists/linux-serial/msg16227.html
>
>
> This is the patch I'm using:
>
> diff --git a/drivers/tty/serial/8250/8250_early.c b/drivers/tty/serial/8250/8250_early.c
> index 85a12f032402..2adabe788c17 100644
> --- a/drivers/tty/serial/8250/8250_early.c
> +++ b/drivers/tty/serial/8250/8250_early.c
> @@ -172,3 +172,52 @@ static int __init early_omap8250_setup(struct earlycon_device *device,
> OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
>
> #endif
> +
> +#ifdef CONFIG_SERIAL_8250_RT288X
> +
> +extern unsigned int au_serial_in(struct uart_port *p, int offset);
> +extern void au_serial_out(struct uart_port *p, int offset, int value);
> +
> +/*** Copy serial_putc with s/serial8250_early/au_serial/g ***/
> +
> +static void __init palmchip_putc(struct uart_port *port, int c)
> +{
> + unsigned int status;
> +
> + au_serial_out(port, UART_TX, c);
> +
> + for (;;) {
> + status = au_serial_in(port, UART_LSR);
> + if ((status & BOTH_EMPTY) == BOTH_EMPTY)
> + break;
> + cpu_relax();
> + }
> +}
> +
> +/*** Copy early_serial8250_write with s/serial_putc/palmchip_putc/g ***/
> +
> +static void __init early_palmchip_write(struct console *console,
> + const char *s, unsigned int count)
> +{
> + struct earlycon_device *device = console->data;
> + struct uart_port *port = &device->port;
> +
> + //extern void printascii(const char *); printascii("EARLY: ");
> + uart_console_write(port, s, count, palmchip_putc);
> +}
> +
> +static int __init early_palmchip_setup(struct earlycon_device *device,
> + const char *options)
> +{
> + struct uart_port *port = &device->port;
> +
> + if (!(device->port.membase || device->port.iobase))
> + return -ENODEV;
> +
> + port->regshift = 2; /* Hard coding the value doesn't feel right */
> + device->con->write = early_palmchip_write;
> + return 0;
> +}
> +OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_palmchip_setup);
> +
> +#endif
> diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> index 080d5a59d0a7..7eb60146cb3f 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -313,7 +313,7 @@ static void default_serial_dl_write(struct uart_8250_port *up, int value)
> -1, /* UART_SCR (unmapped) */
> };
>
> -static unsigned int au_serial_in(struct uart_port *p, int offset)
> +/*static*/ unsigned int au_serial_in(struct uart_port *p, int offset)
> {
> if (offset >= ARRAY_SIZE(au_io_in_map))
> return UINT_MAX;
> @@ -323,7 +323,7 @@ static unsigned int au_serial_in(struct uart_port *p, int offset)
> return __raw_readl(p->membase + (offset << p->regshift));
> }
>
> -static void au_serial_out(struct uart_port *p, int offset, int value)
> +/*static*/ void au_serial_out(struct uart_port *p, int offset, int value)
> {
> if (offset >= ARRAY_SIZE(au_io_out_map))
> return;
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
More information about the linux-arm-kernel
mailing list