U-Boot 串口初始化流程

本文详细描述了U-Boot在ZYNQ7000ZC702KitRev1.1开发板上的初始化过程,重点讲解了串口初始化步骤,包括设置全局变量、调用相关函数以及标志位的管理,确保控制台和设备驱动的正确初始化。
摘要由CSDN通过智能技术生成

环境

U-Boot版本:官方2023.04
开发板:ZYNQ 7000 ZC702Kit Rev1.1

初始化过程

1. 前期工作

从汇编代码启动后一路配置到 board_init_f,首先对全局变量的两个参数清零,然后开始执行 init_sequence_f 列出的有效函数开始一系列功能初始化。

// crt0.S
mov	r0, #0
bl	board_init_f

// board_f.c
void board_init_f(ulong boot_flags)
{
	gd->flags = boot_flags;
	gd->have_console = 0;

	if (initcall_run_list(init_sequence_f))
		hang();
} 

2 _f 阶段串口初始化过程

完成串口初始化主要有三个函数 init_baud_rate,serial_init,console_init_f
在此之前已经从列表顺序里执行了一些初始化,最重要的是 initf_dm 完成了设备驱动的初始化。

2.1 init_baud_rate
static int init_baud_rate(void)
{
	gd->baudrate = env_get_ulong("baudrate", 10, CONFIG_BAUDRATE);
	return 0;
}

在全局变量中设置串口的波特率。

2.2 serial_init
int serial_init(void)
{
#if CONFIG_IS_ENABLED(SERIAL_PRESENT)
	serial_find_console_or_panic();
	gd->flags |= GD_FLG_SERIAL_READY;
	serial_setbrg();
#endif

	return 0;
}

static void serial_find_console_or_panic(void)
{
    ...
    if (!serial_check_stdout(blob, &dev)) {
        gd->cur_serial_dev = dev;
        return;
    }
    ...
}

先执行 serial_find_console_or_panic 根据设备树定义的输出设备从 uclass 列表里查找对应设备,在全局变量中保存查找到的设备。
修改全局变量的 flag 参数标记串口设备已经准备完成,并且将串口波特率设置为全局变量中的 baudrate 设定值。

2.3 console_init_f
// .config
# CONFIG_PRE_CONSOLE_BUFFER is not set
# CONFIG_SILENT_CONSOLE is not set

// console.c
#if CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER)
...
#else
static inline void pre_console_putc(const char c) {}
static inline void pre_console_puts(const char *s) {}
static inline void print_pre_console_buffer(int flushpoint) {}
#endif

static bool console_update_silent(void)
{
	unsigned long flags = gd->flags;

	if (!IS_ENABLED(CONFIG_SILENT_CONSOLE))
		return false;
    ...
}

int console_init_f(void)
{
	gd->have_console = 1;

	console_update_silent();
    
    print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);

	return 0;
}

修改全局变量的 have_console 参数,标记控制台可用。
console_update_silent 没有执行有效功能,因为 CONFIG_SILENT_CONSOLE 未使能。
print_pre_console_buffer 也没有执行有效功能,因为 PRE_CONSOLE_BUFFER 未使能情况下所有的 pre_console 功能都是空的。
所以 console_init_f 实际只是标记控制台的使能。

2.4 _f 阶段串口初始化后的打印
void serial_putc(char ch)
{
	if (gd->cur_serial_dev)
		_serial_putc(gd->cur_serial_dev, ch);
}

void putc(const char c)
{
	if (!gd)
		return;
    ...
	if (!gd->have_console)
		return pre_console_putc(c);

	if (gd->flags & GD_FLG_DEVINIT) {
		/* Send to the standard output */
		fputc(stdout, c);
	} else {
		/* Send directly to the handler */
		pre_console_putc(c);
		serial_putc(c);
	}
}
void serial_puts(const char *str)
{
	if (gd->cur_serial_dev)
		_serial_puts(gd->cur_serial_dev, str);
}

void puts(const char *s)
{
	if (!gd)
		return;
    ...
	if (!gd->have_console)
		return pre_console_puts(s);

	if (gd->flags & GD_FLG_DEVINIT) {
		/* Send to the standard output */
		fputs(stdout, s);
	} else {
		/* Send directly to the handler */
		pre_console_puts(s);
		serial_puts(s);
	}
}
当前位置 `GD_FLG_DEVINIT` 还没有被置位,所以实际会执行 `serial_putc` 和 `serial_puts`;通过全局变量保存的串口设备完成打印。

3 _r 阶段串口初始化过程

board_init_f 之后进入 board_init_r,清除前面置位的 GD_FLG_SERIAL_READY 标记,开始执行 init_sequence_r 列表里的有效函数。这段过程跟串口有关的函数包括 stdio_init_tables,serial_initialize,stdio_add_devices,console_init_r

void board_init_r(gd_t *new_gd, ulong dest_addr)
{
	gd->flags &= ~(GD_FLG_SERIAL_READY | GD_FLG_LOG_READY);
    ...
	if (initcall_run_list(init_sequence_r))
		hang();

	/* NOTREACHED - run_main_loop() does not return */
	hang();
}
3.1 stdio_init_tables
int stdio_init_tables(void)
{
    ...
	/* Initialize the list */
	INIT_LIST_HEAD(&devs.list);

	return 0;
}

初始化标准设备列表。

3.2 serial_initialize
int serial_initialize(void)
{
    ...
	return serial_init();
}

再次初始化串口,全局变量置位 GD_FLG_SERIAL_READY 标记。

3.3 stdio_add_devices
int stdio_add_devices(void)
{
	...
	drv_system_init();
	...
}

创建一个标准设备并且注册到标准设备链表,新注册的标准设备数据流依旧是通过全局变量保存的串口设备实现。

3.4 console_init_r
int console_init_r(void)
{
    ...
	/* Scan devices looking for input and output devices */
	list_for_each(pos, list) {
		dev = list_entry(pos, struct stdio_dev, list);

		if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
			inputdev = dev;
		}
		if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
			outputdev = dev;
		}
		if(inputdev && outputdev)
			break;
	}

	/* Initializes output console first */
	if (outputdev != NULL) {
		console_setfile_and_devices(stdout, outputdev);
		console_setfile_and_devices(stderr, outputdev);
	}

	/* Initializes input console */
	if (inputdev != NULL)
		console_setfile_and_devices(stdin, inputdev);

	if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET))
		stdio_print_current_devices();

	/* Setting environment variables */
	for (i = 0; i < MAX_FILES; i++) {
		env_set(stdio_names[i], stdio_devices[i]->name);
	}

	gd->flags |= GD_FLG_DEVINIT;	/* device initialization completed */

	print_pre_console_buffer(flushpoint);
	return 0;
}

查找串口设备与标准输入输出设备绑定,stdio_print_current_devices 在控制台打印标准输入输出设备信息,最后全局变量置位 GD_FLG_DEVINIT 标记初始化彻底完成。

3.5 _f 阶段串口初始化后的打印
void putc(const char c)
{
	if (!gd)
		return;
    ...
	if (!gd->have_console)
		return pre_console_putc(c);

	if (gd->flags & GD_FLG_DEVINIT) {
		/* Send to the standard output */
		fputc(stdout, c);
	} else {
		/* Send directly to the handler */
		pre_console_putc(c);
		serial_putc(c);
	}
}

void puts(const char *s)
{
	if (!gd)
		return;
    ...
	if (!gd->have_console)
		return pre_console_puts(s);

	if (gd->flags & GD_FLG_DEVINIT) {
		/* Send to the standard output */
		fputs(stdout, s);
	} else {
		/* Send directly to the handler */
		pre_console_puts(s);
		serial_puts(s);
	}
}

全局变量置位了 GD_FLG_DEVINIT 标记,所以实际会执行 fputcfputs;通过设置的标准设备输出,实质依旧是定义的串口设备。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怦然心动如往昔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值