rk36566 uboot - dm 模型数据结构与常见接口

文章目录

  • 一、数据结构
    • 1、udevice
    • 2、driver
    • 3、uclass
    • 4、uclass\_driver
    • 5、 总结
    • 6、device\_probe
  • 二、常用接口
    • 1、udevice 创建接口
      • 1) device_bind_with_driver_data
      • 2) device_bind
      • 3) device_bind_by_name
    • 2、uclass 操作函数
      • 1) uclass_get
      • 2) uclass_get_name
      • 3) uclass_find_device
      • 4) uclass_find_first_device
      • 5) uclass_find_first_device
      • 6) uclass_find_device_by_name
      • 7) uclass_find_device_by_seq
      • 8) uclass_find_device_by_ofnode
    • 3、device_probe 封装接口
      • 1) uclass_get_device
      • 2) uclass_get_device_by_name
      • 3) uclass_get_device_by_seq
      • 4) uclass_get_device_by_ofnode
      • 6) uclass_first_device
      • 7) uclass_next_device

作者: baron

一、数据结构

1、udevice

  用于描述具体的硬件设备, 在当前的 dm 模型中, 在 uboot 启动的时候扫描 dts 自动创建. 详情参考 dts 加载和 dm 模型的本质. 其中需要关注的数据结构有 priv 常用于设置设备硬件私有数据结构. 通过接口void *dev_get_priv(struct udevice *dev)返回.

struct udevice {  const struct driver *driver;   // 在 device_bind_common 中连接对应的 drvconst char *name;              // 自动创建时, 由匹配到的 drv->name 设置. 也可以通过参数传入设置.void *platdata;                // 在 device_bind_common 由 drv->platdata_auto_alloc_size 指定void *parent_platdata;         // 在 device_bind_common 由 parent->driver->per_child_platdata_auto_alloc_size;void *uclass_platdata;         // 在 device_bind_common 由 uc->uc_drv->per_device_platdata_auto_alloc_size; 指定ofnode node;ulong driver_data;              // 由  device_bind_common 传入的参数设置struct udevice *parent;         // 在 device_bind_common 设置, 连接父设备void *priv;                     // 在 device_probe 中设置,由 dev->drv->priv_auto_alloc_size 决定大小struct uclass *uclass;          // 一般在 device_bind_common 中由 void *uclass_priv;              // 在 device_probe 中设置,由 dev->uclass->uc_drv->per_device_auto_alloc_size // 在 device_probe 中设置, 由 dev->parent->driver->per_child_auto_alloc_size 指定大小. // 如果不存在则由 dev->parent->uclass->uc_drv->per_child_auto_alloc_size 指定大小void *parent_priv;   struct list_head uclass_node;  // 在 device_bind_common 中的 uclass_bind_device 设置, 连接到 uc->dev_headstruct list_head child_head;   // 在子设备创建时设置, 连接子设备的 sibling_nodestruct list_head sibling_node; // 在 device_bind_common 中设置, 连接到 parent->child_headuint32_t flags;          int req_seq;int seq;                       //  // 在 device_probe 中设置, 表示 device 是第几个被注册, 由于他的唯一性, 因此也可以通过 seq 查找设备.
#ifdef CONFIG_DEVRESstruct list_head devres_head;
#endif
};

2、driver

  对应的 udevice 的驱动, 其中 probe 用于驱动的初始化. 已经 probe 就表示该设备已就绪可以使用. ofdata_to_platdata 接口在 probe 之前调用, 用于解析设备树. ops 则用于创建 drv 真的的操作接口.

struct driver {char *name;enum uclass_id id;const struct udevice_id *of_match;int (*bind)(struct udevice *dev);               // 第三个在 device_bind_common 中被调用, 这个接口比较常用int (*probe)(struct udevice *dev);              // 在 device_probe 中第 5 个被调用int (*remove)(struct udevice *dev);      int (*unbind)(struct udevice *dev);int (*ofdata_to_platdata)(struct udevice *dev); // 在 device_probe 中第 4 个被调用int (*child_post_bind)(struct udevice *dev);    // 在子设备的 device_bind_common 中第四个被调用int (*child_pre_probe)(struct udevice *dev);    // 在 device_probe 中第 3 个被调用int (*child_post_remove)(struct udevice *dev);int priv_auto_alloc_size;       // 在 device_probe 中指定  dev->priv 的大小int platdata_auto_alloc_size;   // 在 device_bind_common 中指定 dev->platdata 的大小int per_child_auto_alloc_size;  // 在 device_probe 中指定 child_dev->parent_priv 的大小int per_child_platdata_auto_alloc_size;const void *ops;    /* driver-specific operations */uint32_t flags;
};

drv 需要手动创建, 通过 U_BOOT_DRIVER 创建.

#define U_BOOT_DRIVER(__name)                       \ll_entry_declare(struct driver, __name, driver)#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)))

  展开后得到.

struct driver _u_boot_list_2_driver_2___name __aligned(4) __attribute__((unused, section(".u_boot_list_2_driver_2___name")));

  因此该宏将对应的 drv 编译到指定的段 u_boot_list_2_driver_2_中. 可以通过以下接口获取对应的 drv.

struct driver *lists_driver_lookup_name(const char *name)
{struct driver *drv = ll_entry_start(struct driver, driver);const int n_ents = ll_entry_count(struct driver, driver);struct driver *entry;// 遍历所有的 drv 返回对应 name 的 drvfor (entry = drv; entry != drv + n_ents; entry++) {if (!strcmp(name, entry->name))return entry;}/* Not found */return NULL;
}

3、uclass

  在创建 udevice 时自动创建, 管理一类设备, 即同类的 udevice 由 uclass 进行统一管理. 每一个 uclalss 都有一个唯一的 uc_drv->uclass_id 进行描述.

struct uclass {void *priv;                        // 在 device_bind_common 由 uc_drv->priv_auto_alloc_size 指定struct uclass_driver *uc_drv;      // 对应的 uclass drvstruct list_head dev_head;         // 用于连接所属的 udevicestruct list_head sibling_node;     // 连接到 gd->uclass_root
};

4、uclass_driver

  给出该类设备的统一接口. post_probe 接口常用来设置该类设备共有属性, 例如 i2c 的速率. post_bind 接口则常设置为 dm_scan_fdt_dev 用于扫描并创建其下的子设备 udevice. 和 driver 类似通过 UCLASS_DRIVER创建

struct uclass_driver {const char *name;enum uclass_id id;                                 // 所属的 uclassint (*post_bind)(struct udevice *dev);             // 第五个在 device_bind_common 中被调用int (*pre_unbind)(struct udevice *dev);int (*pre_probe)(struct udevice *dev);             // 在 device_probe 中第 1 个被调用int (*post_probe)(struct udevice *dev);            // 在 device_probe 中第 6 个被调用int (*pre_remove)(struct udevice *dev);int (*child_post_bind)(struct udevice *dev);       // 第二个在 device_bind_common 中被调用int (*child_pre_probe)(struct udevice *dev);       // 在 device_probe 中第 2 个被调用 int (*init)(struct uclass *class);                 // 第一个在 device_bind_common 中被调用int (*destroy)(struct uclass *class);int priv_auto_alloc_size;int per_device_auto_alloc_size;                    // 在 device_probe 中为 dev->uclass_priv 分配 dev->uclass->uc_drv->per_device_auto_alloc_sizeint per_device_platdata_auto_alloc_size;           // 在 device_bind_common 中指 child_dev->uclass_platdata 的大小int per_child_auto_alloc_size;                     // 在 device_probe 中如果 dev->parent->driver->per_child_auto_alloc_size 不存在则由它指定 child_dev->uclass_priv 大小.int per_child_platdata_auto_alloc_size;            // 在 device_bind_common 中指定 child_dev->parent_platdata 的大小const void *ops;                                  // uint32_t flags;
};

5、 总结

  udevice, driver, uclass, uclass_driver 他们四为位一体, 在 uboot 中扫描 dts 自动创建. 以 i2c 为例进行说明. 下图展示了 rk3566 i2c 的组织架构.

在这里插入图片描述

  第一个阶段通过扫描 dts 创建了 i2c0 控制器 i2c2: i2c@fe5b0000的 udevice 然后以及挂在该子设备 pmic 和 rk817_fg 对应的 device. 需要注意第一个阶段只会创建 device 并不会对硬件进行初始化(probe).

   第二阶段, 即调用 probe 初始化硬件, 注意和 linux 内核自动 probe 不同. uboot 的设计理念是, 即用即初始化, 不用不初始化. 因此 porbe 是手动调用的. 需要初始化硬件的时候手动调用 probe 函数进行初始化. 在初始化硬件(probe)的时候会检测其父设备的硬件是否已经初始化(probe), 如果父设备没有准备好则先初始化父设备. 如上图所示, 在 probe pmic 的时候会检测 i2c0 是否已经 probe, i2c0 没有 probe 则先调用 i2c0 的 probe 初始化 i2c0, 再调用 i2c class 提供的dm_i2c_read dm_i2c_write等统一接口在 pmic 的 probe 中初始化 pmic. 核心接口为 device_probe.

6、device_probe

  1. 检查标志位 DM_FLAG_ACTIVATED 判断是否已经完成 probe, 如果已经 probe 则直接返回.
  2. 为 dev 分配一些列空间如下.
dev->priv ==> drv->priv_auto_alloc_size
dev->uclass_priv ==> dev->uclass->uc_drv->per_device_auto_alloc_size
dev->parent_priv ==> dev->parent->driver->per_child_auto_alloc_size==> dev->parent->uclass->uc_drv->per_child_auto_alloc_size // 如果前面的不存在则使用这个
  1. 调用父设备的 probe 函数, 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq 设置 dev->seq 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推. 因此可以通过 seq 判断 dev 的初始化顺序.

  2. 进行一系列回调

回调 dev->uclass->uc_drv->pre_probe(dev); -->
回调 dev->parent->uclass->uc_drv->child_pre_probe(dev); -->
回调 dev->parent->driver->child_pre_probe(dev); -->
回调 dev->drv->ofdata_to_platdata(dev); -->         // 常用接口用于解析 dts.
回调 dev->drv->probe(dev); -->                      // 常用接口用于初始化硬件的 probe 接口.
回调 dev->uclass->uc_drv->post_probe(dev); -->      // 在这里设置共有的硬件特性
  1. 调用 pinctrl 设置 default 的 pin 脚状态.
int device_probe(struct udevice *dev)
{const struct driver *drv;int size = 0;int ret;int seq;if (!dev)return -EINVAL;// 检查是否已经完成 probeif (dev->flags & DM_FLAG_ACTIVATED)return 0;// 获取 drvdrv = dev->driver;assert(drv);// 为 dev->priv 分配空间 drv->priv_auto_alloc_sizeif (drv->priv_auto_alloc_size && !dev->priv) {dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags);if (!dev->priv) {ret = -ENOMEM;goto fail;}}// 为 dev->uclass_priv 分配空间 dev->uclass->uc_drv->per_device_auto_alloc_sizesize = dev->uclass->uc_drv->per_device_auto_alloc_size;if (size && !dev->uclass_priv) {dev->uclass_priv = calloc(1, size);if (!dev->uclass_priv) {ret = -ENOMEM;goto fail;}}//  为 dev->parent_priv 分配空间 dev->parent->driver->per_child_auto_alloc_size// 如果没有 per_child_auto_alloc_size 则根据 dev->parent->uclass->uc_drv->per_child_auto_alloc_size 分配空间if (dev->parent) {size = dev->parent->driver->per_child_auto_alloc_size;if (!size) {size = dev->parent->uclass->uc_drv->per_child_auto_alloc_size;}if (size && !dev->parent_priv) {dev->parent_priv = alloc_priv(size, drv->flags);if (!dev->parent_priv) {ret = -ENOMEM;goto fail;}}// 调用父设备的 probe 函数ret = device_probe(dev->parent);if (ret)goto fail;if (dev->flags & DM_FLAG_ACTIVATED)return 0;}// 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq// 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推// 可以通过 seq 判断 dev 的初始化顺序.seq = uclass_resolve_seq(dev);if (seq < 0) {ret = seq;goto fail;}dev->seq = seq;// 设置标志位dev->flags |= DM_FLAG_ACTIVATED;// 回调 pinctrl 设置 pin 脚状态if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)pinctrl_select_state(dev, "default");// 回调 dev->uclass->uc_drv->pre_probe(dev);// 回调 dev->parent->uclass->uc_drv->child_pre_probe(dev);ret = uclass_pre_probe_device(dev);if (ret)goto fail;// 回调  dev->parent->driver->child_pre_probe(dev);if (dev->parent && dev->parent->driver->child_pre_probe) {ret = dev->parent->driver->child_pre_probe(dev);if (ret)goto fail;}// 回调 dev->drv->ofdata_to_platdata(dev);if (drv->ofdata_to_platdata && dev_has_of_node(dev)) {ret = drv->ofdata_to_platdata(dev);if (ret)goto fail;}// 回调 dev->drv->probe(dev);if (drv->probe) {ret = drv->probe(dev);if (ret) {dev->flags &= ~DM_FLAG_ACTIVATED;goto fail;}}// 回调 dev->uclass->uc_drv->post_probe(dev);ret = uclass_post_probe_device(dev);if (ret)goto fail_uclass;// 回调 pinctrl 再次设置 pin 脚状态if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)pinctrl_select_state(dev, "default");return 0;
fail_uclass:if (device_remove(dev, DM_REMOVE_NORMAL)) {dm_warn("%s: Device '%s' failed to remove on error path\n",__func__, dev->name);}
fail:dev->flags &= ~DM_FLAG_ACTIVATED;dev->seq = -1;device_free(dev);return ret;
}

二、常用接口

1、udevice 创建接口

1) device_bind_with_driver_data

设用设备节点 node 创建并返回一个 udevice

// parent: 父设备
// driver: 创建设备的 drv
// name: 设备名称, 一般设置的和 drv name 相同
// driver_data: drv 的私有数据
// node: 该设备对应的 dts 节点, 可以为 null
// devp: 返回创建的 udevice
int device_bind_with_driver_data(struct udevice *parent,const struct driver *drv, const char *name, ulong driver_data, ofnode node, struct udevice **devp)

2) device_bind

使用设备树偏移地址 of_offset 创建 udevice

// parent: 父设备
// driver: 创建设备的 drv
// name: 设备名称, 一般设置的和 drv name 相同
// platdata: 设置 dev->platedata
// of_offset: 设备节点的偏移, 可以为 -1
// devp: 返回创建的 udevice
int device_bind(struct udevice *parent, const struct driver *drv,const char *name, void *platdata, int of_offset, struct udevice **devp);

3) device_bind_by_name

使用 driver_info 创建 udevice, 不使用设备树.

// parent: 父设备
// pre_reloc_only: 是否已经重定位
// driver_info: udevice 设备描述结构
// devp: 返回创建的 udevice
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp);

2、uclass 操作函数

1) uclass_get

通过 uclass_id 返回对应的 uclass, 没有则创建一个 uclass 返回.

int uclass_get(enum uclass_id id, struct uclass **ucp);

2) uclass_get_name

通过 uclass_id 返回 uclass 的 name

const char *uclass_get_name(enum uclass_id id);

3) uclass_find_device

返回对应 uclass_id 的 uclass 对应的设备链表上的第 index 个 udevice

int uclass_find_device(enum uclass_id id, int index, struct udevice **devp);

4) uclass_find_first_device

返回对应 uclass_id 的 uclass 对应的设备链表上的第一个 udevice

int uclass_find_first_device(enum uclass_id id, struct udevice **devp);

5) uclass_find_first_device

返回所属 uclass 链表的下一个 udevice

int uclass_find_next_device(struct udevice **devp);

6) uclass_find_device_by_name

返回对应 uclass_id 的 uclass 的设备链表上对应 name 的 udevice

int uclass_find_device_by_name(enum uclass_id id, const char *name, struct udevice **devp);

7) uclass_find_device_by_seq

uclass_id 的 uclass 的设备链表上通过 seq 查找 udevice

int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq, bool find_req_seq, struct udevice **devp)

8) uclass_find_device_by_ofnode

uclass_id 的 uclass 的设备链表查找对应 node 的 device

int uclass_find_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp

3、device_probe 封装接口

1) uclass_get_device

返回 uclass_id 的 uclass 的设备链表上第 index 个 udevice 并进行 device_probe

int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);

2) uclass_get_device_by_name

返回 uclass_id 的 uclass 的设备链表上对应 name 的 udevice 并进行 device_probe

int uclass_get_device_by_name(enum uclass_id id, const char *name, struct udevice **devp);

3) uclass_get_device_by_seq

返回 uclass_id 的 uclass 的设备链表上对应 seq 的 udevice 并进行 device_probe

int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);

4) uclass_get_device_by_ofnode

返回 uclass_id 的 uclass 的设备链表上对应设备节点 node 的 udevice 并进行 device_probe

int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node, struct udevice **devp);

6) uclass_first_device

返回 uclass_id 的 uclass 的设备链表上第一个 udevice 并进行 device_probe

int uclass_first_device(enum uclass_id id, struct udevice **devp);

7) uclass_next_device

返回所属 uclass 链表上的下一个 udevice 并进行 device_probe

int uclass_next_device(struct udevice **devp);

简单总结: 如果只需要返回某个 udevice 则使用带 find 的接口, 如果需要返回并且 probe 则使用带 get 的接口.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/540002.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

普发Pfeiffer TPG256A MaxiGauge 真空计控制器接口通讯针脚等详情见图目录

普发Pfeiffer TPG256A MaxiGauge 真空计控制器接口通讯针脚等详情见图目录

Java两周半速成之路(第十五天)

一.多线程 1.线程的状态转换图 2. 线程组 2.1概述&#xff1a; Java中使用ThreadGroup来表示线程组&#xff0c;它可以对一批线程进行分类管理&#xff0c;Java允许程序直接对线程组进行控制。 2.2构造方法 ThreadGroup(String name) 构造一个新的线程组 2.3成…

Day34:安全开发-JavaEE应用反射机制攻击链类对象成员变量方法构造方法

目录 Java-反射-Class对象类获取 Java-反射-Field成员变量类获取 Java-反射-Method成员方法类获取 Java-反射-Constructor构造方法类获取 Java-反射-不安全命令执行&反序列化链构造 思维导图 Java知识点 功能&#xff1a;数据库操作&#xff0c;文件操作&#xff0c;…

【Vue2】组件通信

父子通信 父 -> 子 子 -> 父 props 校验 props: {校验的属性名: {type: 类型, // Number String Boolean ...required: true, // 是否必填default: 默认值, // 默认值validator (value) {// 自定义校验逻辑return 是否通过校验}} },data 的数据是自己的 → 随便改pr…

数据和类型转换

文章目录 数据类型数字类型数字操作NaNJavaScript算术运算符的执行顺序 字符串类型&#xff08;string&#xff09;字符串拼接模板字符串 未定义类型&#xff08;undefined&#xff09;布尔类型&#xff08;boolean&#xff09;null&#xff08;空类型&#xff09; 类型转换显式…

Yolo系列算法-理论部分-YOLOv3

0. 写在前面 YOLO系列博客&#xff0c;紧接上一篇Yolo系列算法-理论部分-YOLOv2-CSDN博客 1. YOLOv3-定型之作 2018年&#xff0c;Redmon团队推出YOLOv3的网络模型&#xff0c;将骨干网络&#xff08;backbone&#xff09;由darknet-19替换成darknet-53网络&#xff0c;加入特…

精品基于Uniapp+ssm模拟考试系统小程序考试试题试卷

《[含文档PPT源码等]精品微信小程序基于Uniappssm模拟考试系统小程序》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;Java 后台框架&#xff1a;ssm 安卓框架&a…

守护健康,从营养开始 —— 帕金森患者的饮食秘籍

亲爱的读者朋友们&#xff0c;您是否知道&#xff0c;在对抗帕金森病的道路上&#xff0c;正确的饮食和营养补充可以成为我们的有力盟友&#xff1f;今天&#xff0c;就让我们一起探索那些能够帮助帕金森患者改善症状、提高生活质量的营养素&#xff0c;开启健康生活的新篇章。…

《父母的觉醒》父母不是在培养一个“迷你版”的自己

简介 作者为美国哥伦比亚大学心理学博士沙法丽萨巴瑞。作者也写了《家庭的觉醒》。 作者的核心观点&#xff1a; 我们必须认识到&#xff0c;我们不是在培养一个“迷你版”的自己&#xff0c;而是在塑造一个具有独立特征的灵魂。正因为如此&#xff0c;我们必须铆足精神&#…

ETH共识升级之路

简介 根据我们之前的介绍&#xff0c;了解到ETH网络的共识方式&#xff0c;已经从 PoW 切换到了 PoS&#xff0c;今天我们就回顾下升级之路&#xff0c;以及升级带来的影响 最早的共识机制 PoW 以太坊创建之初采用了类似比特币的工作量证明机制&#xff0c;即矿工通过计算哈希函…

JAVA实战开源项目:计算机机房作业管理系统(Vue+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 登录注册模块2.2 课程管理模块2.3 课时管理模块2.4 学生作业模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 课程表3.2.2 课时表3.2.3 学生作业表 四、系统展示五、核心代码5.1 查询课程数据5.2 新增课时5.3 提交作…

【消息队列开发】 实现消息删除逻辑

文章目录 &#x1f343;前言&#x1f332;实现步骤&#x1f6a9;检验参数的合法性&#x1f6a9;读取Message数据&#x1f6a9;二进制转为message&#x1f6a9;isValid 设置为无效&#x1f6a9;写入文件&#x1f6a9;更新统计文件&#x1f6a9;特别注意&#x1f6a9;完整代码 ⭕…