uboot下UCLASS框架详解---结合项目工作中spi master和flash驱动开发

本文详细介绍了U-Boot中的设备模型(DeviceModel,DM)架构,特别是针对UCLASS、udevice、uclass_driver和driver的概念及关系。文章讲解了如何通过编写spimaster驱动来识别spinorflash设备,完成norflash的读写操作。此外,还涵盖了uboot代码的解析,包括DM的初始化、spinorflash设备的识别、设备树内容以及配置选项。最后,提供了SPI协议、SPI网卡驱动移植和设备树内容的相关链接。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

一、综述

本文通过如何通过编写特定板子的spi master驱动从而识别到spi norflash设备,完成norflash设备的读写。

二、UCLASS架构解析

2.1 uclass

uclass可以理解为一些具有相同属性的udevice对外操作的接口,uclass的驱动是uclass_driver,主要为上层提供接口。
udevice的是指具体设备的抽象,对应驱动是driver,driver主要负责和硬件通信,为uclass提供实际的操作集。
udevice找到对应的uclass的方式主要是通过:udevice对应的driver的id和uclass对应的uclass_driver的id是否匹配。
udevice会和uclass绑定。driver会和udevice绑定。uclass_driver会和uclass绑定。
在这里插入图片描述
uclass和udevice都是动态生成的。在解析fdt中的设备的时候,会动态生成udevice。
然后找到udevice对应的driver,通过driver中的uclass id得到uclass_driver id。从uclass链表中查找对应的uclass是否已经生成,没有生成的话则动态生成uclass。

2.2 udevice

连接到对应uclass中
也就是会连接到uclass->dev_head中
连接到父设备的子设备链表中
也就是会连接到udevice->child_head中,并且最终的根设备是gd->dm_root这个根设备。

struct uclass {
   
   
    void *priv;                             //uclass的私有数据
    struct uclass_driver *uc_drv;           //uclass类的操作函数集合
    struct list_head dev_head;              //该uclass的所有设备
    struct list_head sibling_node;          //下一个uclass的节点
};

2.3 uclass driver

主要函数:

struct uclass_driver {
   
   
    const char *name; // 该uclass_driver的命令
    enum uclass_id id; // 对应的uclass 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分配多少私有数据
    int per_device_auto_alloc_size; //
    int per_device_platdata_auto_alloc_size; //
    int per_child_auto_alloc_size; //
    int per_child_platdata_auto_alloc_size;  //
    const void *ops; //操作集合
    uint32_t flags;   // 标识为
};

spi-uclass驱动:

UCLASS_DRIVER(spi) = {
   
   
	.id		= UCLASS_SPI,
	.name		= "spi",
	.flags		= DM_UC_FLAG_SEQ_ALIAS,
#if CONFIG_IS_ENABLED(OF_REAL)
	.post_bind	= dm_scan_fdt_dev,
#endif
	.post_probe	= spi_post_probe,
	.child_pre_probe = spi_child_pre_probe,
	.per_device_auto	= sizeof(struct dm_spi_bus),
	.per_child_auto	= sizeof(struct spi_slave),
	.per_child_plat_auto	= sizeof(struct dm_spi_slave_plat),
#if CONFIG_IS_ENABLED(OF_REAL)
	.child_post_bind = spi_child_post_bind,
#endif
};

//存放在段._u_boot_list_2_uclass_2_spi中,也就是section段的内容可以在uboot.map可以查看
在这里插入图片描述

想要获取uclass_driver需要先获取uclass_driver table。

struct uclass_driver *uclass =
        ll_entry_start(struct uclass_driver, uclass); 
// 会根据.u_boot_list_2_uclass_1的段地址来得到uclass_driver table的地址

    const int n_ents = ll_entry_count(struct uclass_driver, uclass);
// 获得uclass_driver table的长度

struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
// 从uclass_driver table中获取uclass id为id的uclass_driver。

2.4 driver

主要函数:

struct driver {
   
   
    char *name;    // 驱动名
    enum uclass_id id;  // 对应的uclass id
    const struct udevice_id *of_match;    // compatible字符串的匹配表,用于和device tree里面的设备节点匹配
    int (*bind)(struct udevice *dev);   // 用于绑定目标设备到该driver中
    int (*probe)(struct udevice *dev);   // 用于probe目标设备,激活
    int (*remove)(struct udevice *dev); // 用于remove目标设备。禁用
    int (*unbind)(struct udevice *dev); // 用于解绑目标设备到该driver中
    int (*ofdata_to_platdata)(struct udevice *dev); // 在probe之前,解析对应udevice的dts节点,转化成udevice的平台数据
    int (*child_post_bind)(struct udevice *dev); // 如果目标设备的一个子设备被绑定之后,调用
    int (*child_pre_probe)(struct udevice *dev); // 在目标设备的一个子设备被probe之前,调用
    int (*child_post_remove)(struct udevice *dev); // 在目标设备的一个子设备被remove之后,调用
    int priv_auto_alloc_size; //需要分配多少空间作为其udevice的私有数据
    int platdata_auto_alloc_size; //需要分配多少空间作为其udevice的平台数据
    int per_child_auto_alloc_size;  // 对于目标设备的每个

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值