U-boot驱动模型
1. 基本概念
1.1 全局数据global_data
某些情况下,u-boot在某些只读存储器上运行,如ROM,NorFlash等;在其重定位到RAM之前,无法写入数据或者通过全局变量传递数据,而global_data(也称GD)则可以解决这个问题
简单来说,u-boot把GD放在RAM区,使用它来存储全局数据, 以解决上述场景中无法使用全局变量的问题
GD数据结构
// include/asm-generic/global_data.h
typedef struct global_data {
bd_t *bd; // 保存开发板的相关参数
unsigned long env_addr; // 环境变量地址
unsigned long ram_top; // RAM空间的顶端地址
unsigned long relocaddr; // u-boot重定位后的地址
phys_size_t ram_size; // 物理ram的size
unsigned long irq_sp; // 中断的栈地址
unsigned long start_addr_sp; // stack地址
unsigned long reloc_off; // uboot的relocation的偏移
struct global_data *new_gd; // 重定位后的GD结构体
const void *fdt_blob; // dtb地址
void *new_fdt; // 重定位后dtb地址
unsigned long fdt_size; // dtb的长度
struct udevice *cur_serial_dev; // 当前使用串口设备
......
} gd_t;
初始化GD
// crt0.S
// 设置C运行时环境
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) // 设置栈顶SP指针,只是预设,并不是最终的栈顶地址
bic sp, sp, #7 // 8byte对齐
mov r0, sp // SP放入r0
bl board_init_f_alloc_reserve // 参数为r0,返回后,r0中存放的是GD的地址
mov sp, r0 // r0放入sp中
mov r9, r0 // r0放入r9中,即r0和r9存放的都是GD的地址
bl board_init_f_init_reserve // 对GD初始化,r0为参数
// 给GD分配空间,传入的是r0,即栈顶地址
ulong board_init_f_alloc_reserve(ulong top)
{
// 自顶向下分配CONFIG_SYS_MALLOC_F_LEN大小内存, 用于在relocation前用于给malloc函数提供内存池(给堆用的)
top -= CONFIG_SYS_MALLOC_F_LEN;
// 继续向下分配sizeof(struct global_data)大小的内存给GD使用,向下16byte对齐
top = rounddown(top-sizeof(struct global_data), 16);
// 返回GD地址
return top;
}
// 初始化GD分配的空间, 即清空global_data区域
// 传入的参数为GD的基地址
void board_init_f_init_reserve(ulong base)
{
// 清零
memset(gd_ptr, '\0', sizeof(*gd));
// 获取了early malloc的内存池的地址(给堆使用)
base += roundup(sizeof(struct global_data), 16);
// 写入到gd->malloc_base中
gd->malloc_base = base;
// 获取early malloc的内存池的末尾地址
base += CONFIG_SYS_MALLOC_F_LEN;
}
使用GD
根据上面可得GD的基地址存放在r9中,需要GD的时候,直接从r9寄存器中取的其地址即可
// arch/arm/include/asm/global_data.h
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
// common/board_r.c
DECLARE_GLOBAL_DATA_PTR
static int initr_reloc(void)
{
// 直接使用gd变量即可
gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;
}
1.2 驱动模型引入
在U-boot中引入驱动模型(driver model),为驱动的定义和范文接口提供统一的方法,提高驱动间的兼容性以及访问的标准性,u-boot中的驱动模型(DM)和kernel中的设备驱动模型类似,但是也有所区别
通过宏定义CONFIG_DM使能驱动模型,对应的实际驱动设备则需要通过使能CONFIG_DM_SERIAL来使能;后面以serial驱动为例
1.2.1 uclass/udevice/drivers三者之间的关联
uclass可以理解为具有相同属性的device对外操作的接口, 它与上层接口直接通讯,其驱动为uclass_driver,给上层提供接口
udevice对具体设备的抽象,对应的驱动是driver; driver负责和硬件通讯,为uclass提供实际的操作集
udevice如何和uclass绑定:udevice对应的driver_id和uclass对应的uclass_driver_id是否匹配
hardware对应的driver绑定对应的udevice,udevice绑定uclass,uclass有其对应的uclass_driver
uclass和udevice是动态生成的
- udevice在解析fdt中的设备的时候自动生成,然后udevice找到对应的driver
- driver中保存了uclass_id, 根据它找到uclass_driver_id
- 从uclass链表中查找对应的uclass是否已经生成,若没有生成,则动态生成
- 重点是解析设备树,生成udevice, 并找到对应的driver

1.2.2 全局数据GD中和DM相关部分
typedef struct global_data {
// dts中的根节点,第一个创建的udevice
struct udevice *dm_root;
// relocation之前的根设备
struct udevice *dm_root_f;
// uclass的链表, 挂的是有udevice的uclass
struct list_head uclass_root;
} gd_t;
数据结构
2. uclass_id
每种uclass有对应的ID号,定义在其uclass_driver中,这个和ID号和其对应的udevice中的driver中的uclass_id一样
// include/dm/uclass-id.h
enum uclass_id {
UCLASS_ROOT = 0,
UCLASS_DEMO,
UCLASS_CLK,
UCLASS_PINCTRL,
UCLASS_SERIAL,
}
3. uclass
3.1 数据结构
struct uclass {
// uclass的私有数据指针
void *priv;
// 对应的uclass driver
struct uclass_driver *uc_drv;
// 链表头,挂接它所有相关联的udevice
struct list_head dev_head;
// 链表节点,用于把uclass连接到uclass_root链表上
struct list_head sibling_node;
};
3.2 生成
有对应uclass driver并且下面挂有udevice的uclass才会被uboot自动生成
3.3 存放位置
所有生成的都会挂接到gd->uclass_root链表上
3.4 获取对应的API
// 根据uclass_id遍历gd->ulcass_root链表
int uclass_get(enum uclass_id key, struct uclass **ucp);
4. uclass_driver
4.1 数据结构
// include/dm/uclass.h,每个uclass都有对应的uclass_driver
struct uclass_driver {
// 该uclass_driver的名字
const char *name;
// 对应的uclass id
enum uclass_id id;
/* 以下函数指针主要是调用时机的区别 */
int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用
int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用
int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用
int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用
int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用
int (*child_post_bind)(struct udevice *dev); // 在该uclass对应的某个udevice的某个设备被绑定到该udevice之后调用
int (*child_pre_probe)(struct udevice *dev); // 在该uclass对应的某个udevice的某个设备进行probe之前调用
int (*init)(struct uclass *class); // 安装该uclass的时候调用
int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用
int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据
const void *ops; //操作集合
};
4.2 生成
- 通过UCLASS_DRIVER定义uclass_driver
// 以serial-uclass为例
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),
};
- UCLASS_DRIVER宏定义
#define UCLASS_DRIVER(__name) \
ll_entry_declare(struct uclass_driver, __name, uclass)
#define ll_entry_declare(_type, _name, _list) \
_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \
__attribute__((unused, \
section(".u_boot_list_2_"#_list"_2_"#_name)))
- 最终会得到如下结构体,并且存放在.u_boot_list_2_uclass_2_serial段中
struct uclass_driver _u_boot_list_2_uclass_2_serial = {
.id = UCLASS_SERIAL, // 设置对应的uclass id
.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),
}
4.3 存放位置
通过查看u-boot.map可以得到:
.u_boot_list_2_uclass_1
0x23e368e0 0x0 drivers/built-in.o
.u_boot_list_2_uclass_2_gpio
0x23e368e0 0x48 drivers/gpio/built-in.o
0x23e368e0 _u_boot_list_2_uclass_2_gpio // gpio uclass driver的符号
.u_boot_list_2_uclass_2_root
0x23e36928 0x48 drivers/built-in.o
0x23e36928 _u_boot_list_2_uclass_2_r

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



