[uboot] uboot流程系列:
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
[project X] tiny210(s5pv210)从存储设备加载代码到DDR
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl编译流程
[uboot] (第三章)uboot流程——uboot-spl代码流程
[uboot] (第四章)uboot流程——uboot编译流程
[uboot] (第五章)uboot流程——uboot启动流程
[uboot] (番外篇)global_data介绍
[uboot] (番外篇)uboot relocation介绍
[uboot] (番外篇)uboot 驱动模型
建议先看《[uboot] (番外篇)uboot 驱动模型》
=============================================================================================================
一、uboot serial框架
1、serial模块驱动模型
在《[uboot] (番外篇)uboot 驱动模型》中我们已经介绍uboot的驱动模型,uboot DM。
在uboot中,serial模块也使用了对应的驱动模型。
其框架图如下:
我们在《[uboot] (番外篇)uboot 驱动模型》已经说明过了,这里再简单解释一下:
- serial core为serial模块向外提供接口,但是也是在serial-uclass中实现
- serial uclass是serial设备的集合抽象,为serial设备提供统一的操作接口,serial uclass driver则是其对应的驱动
- serial udevice是serial设备的具体抽象,代表了一个serial设备对象,serial driver则是其对应的驱动
2、serial DM实现
在《[uboot] (番外篇)uboot 驱动模型》中,我们已经知道了uclass和udevice由uboot动态生成,但是我们需要在dtsi中添加相应的设备信息,以及添加相应的uclass driver和udevice driver.
以tiny210为例,如下:
- dts中的设备信息
/{
aliases {
console = "/serial@e2900000";
};
serial@e2900000 {
compatible = "samsung,exynos4210-uart";
reg = <0xe2900000 0x100>;
interrupts = <0 51 0>;
id = <0>;
};
};
这里,有些人或许会有疑问,在relocate之前就需要打印串口数据,就需要使用到这个节点了,为什么不需要加上“u-boot,dm-pre-reloc”属性?
确实是可以加,但是不加也没事,因为console中已经指定了串口节点的路径,在relocate之前的串口初始化过程中,在设备链表上找不到对应串口设备的话,会强制绑定console指定的串口节点的设备。
- uclass driver
driver/serial/serial-uclass.c
UCLASS_DRIVER(serial) = {
.id = UCLASS_SERIAL,
.name = "serial",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = serial_post_probe,
.pre_remove = serial_pre_remove,
.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};
- udevice driver
driver/serial/serial_s5p.c
static const struct udevice_id s5p_serial_ids[] = {
{ .compatible = "samsung,exynos4210-uart" }, // 必须和dts匹配
{ }
};
// s5p serial driver 提供了如下操作集
static const struct dm_serial_ops s5p_serial_ops = {
.putc = s5p_serial_putc,
.pending = s5p_serial_pending,
.getc = s5p_serial_getc,
.setbrg = s5p_serial_setbrg,
};
U_BOOT_DRIVER(serial_s5p) = {
.name = "serial_s5p",
.id = UCLASS_SERIAL,
.of_match = s5p_serial_ids,
.ofdata_to_platdata = s5p_serial_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct s5p_serial_platdata),
.probe = s5p_serial_probe,
.ops = &s5p_serial_ops,
.flags = DM_FLAG_PRE_RELOC,
};
注意,serial uclass driver的id和serial driver的id是一致的,都是UCLASS_SERIAL。
具体也在《[uboot] (番外篇)uboot 驱动模型》分析过了,这里也不多说了。
关于tiny210的串口驱动的实现,在第四节中再学习。
3、serial core提供的接口
serial core会利用serial uclass找到对应的设备及其操作集,向上层提供接口。
如下:
driver/serial/serial-uclass.c
- void serial_putc(char ch)
往gd->cur_serial_dev指定的串口设备输出一个字符。
void serial_putc(char ch)
{
if (gd->cur_serial_dev)
_serial_putc(gd->cur_serial_dev, ch); // 将gd->cur_serial_dev指定的udevice作为参数传入
}
static void _serial_putc(struct udevice *dev, char ch)
{
struct dm_serial_ops *ops = serial_get_ops(dev); // 获取对应udevice的操作集(driver->ops)
int err;
if (ch == '\n')
_serial_putc(dev, '\r');
do {
err = ops->putc(dev, ch); // 调用udevice的操作集(driver->ops)中的putc函数,这里真正向硬件串口设备进行输出。
} while (err == -EAGAIN);
}
void serial_puts(const char *str)
往gd->cur_serial_dev指定的串口设备输出字符串。其方法与serial_putc类似,自己参考代码。int serial_getc(void)
从gd->cur_serial_dev指定的串口设备获取一个字符。其方法与serial_putc类似,自己参考代码。int serial_tstc(void)
判断gd->cur_serial_dev指定的串口设备是否有数据在等待。其方法与serial_putc类似,自己参考代码。void serial_setbrg(void)
设置gd->cur_serial_dev指定的串口设备的波特率。其方法与serial_putc类似,自己参考代码。void serial_initialize(void) & int serial_init(void)
串口初始化。这里重点说明
void serial_initialize(void)
{
serial_init();
}
// 可以看出serial_initialize和serial_init是一样的。
int serial_init(void)
{
serial_find_console_or_panic(); //调用serial_find_console_or_panic来查找console指定的设备
gd->flags |= GD_FLG_SERIAL_READY;
return 0;
}
static