2

I have a UART that is displayed in a Linux boot log as:

AMDI0020:01: ttyS5 at MMIO 0xfedca000 (irq = 4, base_baud = 3000000) is a 16550A

I want to enable Linux kernel boot log to this UART port. To do this I add kernel boot parameter:

console=uart,mmio32,0xfedca000,115200n8

In result for some reason the log is getting split in two parts (I've even checked this with the oscilloscope):

  • first part of the boot log goes at the speed of 3000000
  • second part of the boot log goes at the speed of 115200

Presumably the split is happening exactly at the point of ttyS5 initialization.

I thought that the whole point of writing

console=uart,mmio32,0xfedca000,115200n8

instead of

console=ttyS5,115200n8

is to get working UART before the actual driver initialization.

But for some reason the uart,mmio32,0xfedca000,115200n8 parameter doesn't set speed at the beginning.

Is it possible to set serial speed for an early kernel boot log to a MMIO UART?

Just in case my OS is:

~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:        18.04
Codename:       bionic
~$ uname -a
Linux ermak-Diesel 5.4.0-65-generic #73~18.04.1-Ubuntu SMP Tue Jan 19 09:02:24 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
4
  • Maybe you need to use the earlyprintk option - check it out in this. You might need to add arch support this. Commented Feb 17, 2021 at 5:57
  • @MurrayJensen My arch is x86, so earlyprintk should be supported. But it doesn't do anything for me. I've tried earlyprintk=serial,0xfedca000,115200 and earlyprintk=serial,ttyS5,115200. Also, from the source code it seems like earlyprintk supports only legacy I/O Com ports (elixir.bootlin.com/linux/latest/source/arch/x86/kernel/…)
    – kostr22
    Commented Feb 17, 2021 at 7:47
  • That's what I meant by adding arch support for earlyprintk on your serial hardware - it shouldn't be too hard, just hack some code for your hardware into the file you linked above, using the other code as an example. Commented Feb 17, 2021 at 13:47
  • 1
    Did you find anything more about this? In my experience if I omit the baud rate, and then configure GRUB_SERIAL_COMMAND to set it, I will see the early boot console, otherwise I don't see it (perhaps it is being sent at a very high baud rate as you mentioned, but I can't monitor it).
    – rgov
    Commented Mar 2, 2021 at 0:02

1 Answer 1

1

In my case the problem was caused by non-standard UART clock.

earlycon driver sets uart clock to the value BASE_BAUD * 16

https://github.com/torvalds/linux/blob/2a987e65025e2b79c6d453b78cb5985ac6e5eb26/drivers/tty/serial/earlycon.c#L142

static int __init register_earlycon(char *buf, const struct earlycon_id *match)
{
 ...
 port->uartclk = BASE_BAUD * 16;
 ...
}

For the x86 arch BASE_BAUD is defined in the file https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/serial.h

/*
 * This assumes you have a 1.8432 MHz clock for your UART.
 *
 * It'd be nice if someone built a serial card with a 24.576 MHz
 * clock, since the 16550A is capable of handling a top speed of 1.5
 * megabits/second; but this requires a faster clock.
 */
#define BASE_BAUD (1843200/16)

But in my case AMDI0020 UART doesn't use standard 1.8432MHz clock, but 48MHz clock.

This is why base_baud = 3000000 was printed opposed to standard 115200:

 1 843 200 / 16 =   115 200
48 000 000 / 16 = 3 000 000
AMDI0020:01: ttyS5 at MMIO 0xfedca000 (irq = 4, base_baud = 3000000) is a 16550A

I don't see any possibility to set different clock in the driver. But I was able to cheat with setting different slower baudrate:

115 200 * (1 843 200 / 48 000 000) = 4 423,68

This setting gave me correct output at the early bootstage:

console=uart,mmio32,0xfedca000,4423

Although the method is not ideal, as because of this incorrect baudrate later kernel boot stages wouldn't be printed correctly (once AMDI0020 driver will take over (https://github.com/torvalds/linux/blob/master/drivers/tty/serial/8250/8250_dw.c)).

If (and only if!) this UART was already set to the correct speed at BIOS/GRUB stage, it is possible to omit baudrate setting

console=uart,mmio32,0xfedca000

This way all the kernel log would be printed correctly.

But the correct solution is of course to add necessary support to the earlycon driver.

Currently AMDI0020 get its clock from this driver: https://github.com/torvalds/linux/blob/master/drivers/acpi/acpi_apd.c

static const struct apd_device_desc cz_uart_desc = {
    .setup = acpi_apd_setup,
    .fixed_clk_rate = 48000000,
    .properties = uart_properties,
};

...

static const struct acpi_device_id acpi_apd_device_ids[] = {
  ...
  { "AMDI0020", APD_ADDR(cz_uart_desc) },
  ...
}

Since the earlycon is set up before ACPI parsing, it looks like the only possible way to solve the problem is to add a possibility to pass custom clock for the console in a way something like:

console=uart,mmio32,0xfedca000,115200,48000000

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .