devicetree下的earlycon的初始化

本文介绍如何通过Device Tree配置Earlycon参数,并详细解析了Linux内核中early_init_dt_scan_chosen_stdout函数的工作流程,包括从Device Tree中读取stdout-path属性、匹配相应的串口设备以及earlycon的初始化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在ACPI的时候可以通过spcr表来制定earlycon的参数,在devcetree的时候可以通过在chosen这个节点下通过stdout-path来制定earlycon

/ {
    chosen {
        stdout-path = "/serial@f00:115200";
    };

    serial@f00 {
        compatible = "vendor,some-uart";
        reg = <0xf00 0x10>;
    };
};
而解析的stdout-path在code在drivers/fdt.c 中,可见要定义CONFIG_SERIAL_EARLYCON 这个宏才会使能
下面的code
#ifdef CONFIG_SERIAL_EARLYCON

int __init early_init_dt_scan_chosen_stdout(void)
{
    int offset;
    const char *p, *q, *options = NULL;
    int l;
    const struct earlycon_id *match;
    const void *fdt = initial_boot_params;
// 从devicetree中找到chosen节点
    offset = fdt_path_offset(fdt, "/chosen");
    if (offset < 0)
        offset = fdt_path_offset(fdt, "/chosen@0");
    if (offset < 0)
        return -ENOENT;
//从chosen节点下找到stdout-path
    p = fdt_getprop(fdt, offset, "stdout-path", &l);
    if (!p)
        p = fdt_getprop(fdt, offset, "linux,stdout-path", &l);
    if (!p || !l)
        return -ENOENT;

    q = strchrnul(p, ':');
    if (*q != '\0')
        options = q + 1;
    l = q - p;

    /* Get the node specified by stdout-path */
    offset = fdt_path_offset_namelen(fdt, p, l);
    if (offset < 0) {
        pr_warn("earlycon: stdout-path %.*s not found\n", l, p);
        return 0;
    }
//从__earlycon_table 到 __earlycon_table_end 之间找是否有匹配的,本例中匹配的就是”vendor,some-uart“
    for (match = __earlycon_table; match < __earlycon_table_end; match++) {
        if (!match->compatible[0])
            continue;

        if (fdt_node_check_compatible(fdt, offset, match->compatible))
            continue;
//如果找到的话,就调用of_setup_earlycon
        of_setup_earlycon(match, offset, options);
        return 0;
    }
    return -ENODEV;
}
#endif

#ifdef CONFIG_OF_EARLY_FLATTREE

int __init of_setup_earlycon(const struct earlycon_id *match,
                 unsigned long node,
                 const char *options)
{
    int err;
    struct uart_port *port = &early_console_dev.port;
    const __be32 *val;
    bool big_endian;
    u64 addr;

    spin_lock_init(&port->lock);
    port->iotype = UPIO_MEM;
    addr = of_flat_dt_translate_address(node);
    if (addr == OF_BAD_ADDR) {
        pr_warn("[%s] bad address\n", match->name);
        return -ENXIO;
    }
    port->mapbase = addr;
    port->uartclk = BASE_BAUD * 16;
    port->membase = earlycon_map(port->mapbase, SZ_4K);

    val = of_get_flat_dt_prop(node, "reg-offset", NULL);
    if (val)
        port->mapbase += be32_to_cpu(*val);
    val = of_get_flat_dt_prop(node, "reg-shift", NULL);
    if (val)
        port->regshift = be32_to_cpu(*val);
    big_endian = of_get_flat_dt_prop(node, "big-endian", NULL) != NULL ||
        (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) &&
         of_get_flat_dt_prop(node, "native-endian", NULL) != NULL);
    val = of_get_flat_dt_prop(node, "reg-io-width", NULL);
    if (val) {
        switch (be32_to_cpu(*val)) {
        case 1:
            port->iotype = UPIO_MEM;
            break;
        case 2:
            port->iotype = UPIO_MEM16;
            break;
        case 4:
            port->iotype = (big_endian) ? UPIO_MEM32BE : UPIO_MEM32;
            break;
        default:
            pr_warn("[%s] unsupported reg-io-width\n", match->name);
            return -EINVAL;
        }
    }

    if (options) {
        strlcpy(early_console_dev.options, options,
            sizeof(early_console_dev.options));
    }
//其面都是找参数,从这里到最后就是真正的earlycon的初始化,这部分和ACPI的方式就一模一样了
    earlycon_init(&early_console_dev, match->name);
    err = match->setup(&early_console_dev, options);
    if (err < 0)
        return err;
    if (!early_console_dev.con->write)
        return -ENODEV;


    register_console(early_console_dev.con);
    return 0;
}

#endif /* CONFIG_OF_EARLY_FLATTREE */


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值