我有一个 UART,在 Linux 启动日志中显示为:
AMDI0020:01: ttyS5 at MMIO 0xfedca000 (irq = 4, base_baud = 3000000) is a 16550A
我想启用此 UART 端口的 Linux 内核启动日志。为此,我添加内核启动参数:
console=uart,mmio32,0xfedca000,115200n8
结果,由于某种原因,日志被分成两部分(我什至用示波器检查了这一点):
- 启动日志第一部分的速度为 3000000
- 启动日志的第二部分的速度为 115200
据推测,分裂恰好发生在 ttyS5 初始化时。
我认为写作的全部意义
console=uart,mmio32,0xfedca000,115200n8
代替
console=ttyS5,115200n8
是在实际驱动程序初始化之前获得工作的 UART。
但由于某种原因,该uart,mmio32,0xfedca000,115200n8
参数一开始并没有设置速度。
是否可以将早期内核启动日志的串行速度设置为 MMIO UART?
以防万一我的操作系统是:
~$ 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
答案1
就我而言,问题是由非标准 UART 时钟引起的。
earlycon
驱动程序将 UART 时钟设置为该值BASE_BAUD * 16
static int __init register_earlycon(char *buf, const struct earlycon_id *match)
{
...
port->uartclk = BASE_BAUD * 16;
...
}
对于 x86 archBASE_BAUD
在文件中定义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)
但在我的例子中,AMDI0020
UART 不使用标准 1.8432MHz 时钟,而是使用 48MHz 时钟。
这就是为什么base_baud = 3000000
打印反对标准 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
我看不出有任何可能在驱动程序中设置不同的时钟。但我可以通过设置不同的较慢波特率来作弊:
115 200 * (1 843 200 / 48 000 000) = 4 423,68
此设置在早期引导阶段给了我正确的输出:
console=uart,mmio32,0xfedca000,4423
尽管该方法并不理想,因为由于波特率不正确,后续内核启动阶段将无法正确打印(一旦AMDI0020
驱动程序接管(https://github.com/torvalds/linux/blob/master/drivers/tty/serial/8250/8250_dw.c))。
如果(且仅当!)该 UART 已在 BIOS/GRUB 阶段设置为正确的速度,则可以省略波特率设置
console=uart,mmio32,0xfedca000
这样所有的内核日志都会被正确打印。
但正确的解决办法当然是给earlycon
驱动程序添加必要的支持。
目前AMDI0020
从该驱动程序获取其时钟: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) },
...
}
由于是earlycon
在 ACPI 解析之前设置的,因此解决该问题的唯一可能方法似乎是添加为控制台传递自定义时钟的可能性,如下所示:
console=uart,mmio32,0xfedca000,115200,48000000