马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
设备树写好了,内核到底怎么用
上一章讲完设备树的语法和怎么编译,这一章接上:内核拿到 .dtb 之后做了什么、哪些节点会变成 platform_device、driver 和 device 怎么配对、以及操作设备树要调哪些 API。
先把流程记住:设备树是给内核和驱动用的中介。板级信息原本写在 C 代码里,现在搬到 .dts 里描述,内核启动时解析成内存中的结构体,再按规则生成平台设备,最后由总线模型完成匹配。
一、内核对设备树的处理流程
1.1 每个节点都会有一个 device_node
内核启动时(setup_machine_fdt → of_flat_dt_match 这条链路上)会把 dtb 里每一个节点解析出来,逐一申请 struct device_node 并挂进全局的 tree。节点之间的父子关系、compatible、reg、status 这些属性都存在这个结构体的属性链表里。
也就是说,在内核视角里,设备树首先是一棵对象树,还不是设备。要变成真正的 device 还得过第二关。
1.2 哪些节点会变成 platform_device
不是所有节点都会生成 platform_device,判断规则很清楚:
- 没有
compatible 属性的根节点会生成一个 platform_device。
- 有
compatible 属性、且不是以下几种特殊节点的节点才会转换。
被排除的特殊节点包括:
Chosen、aliases、labels 这类不描述设备的节点;
- 挂在某些特殊总线(如 i2c、spi)下的子节点——它们走自己的总线模型,生成的是 i2c_client / spi_device,不走 platform 总线。
1.3 转换时机与位置
转换发生在内核初始化阶段,具体在 platform_bus_init() / of_platform_populate() 里遍历整棵树,按上面规则为每个 eligible 节点填一个 struct platform_device:节点的 reg 变成 resource,name 取 compatible[0],然后 platform_device_register() 挂到 platform 总线上。
1.4 platform_device 怎么和 platform_driver 配对
内核里一个驱动想被匹配上,依次过四关——按顺序比较,命中即配对:
- 先比较是否强制选择某个 driver:设备树里若写了
force_driver(或驱动用 .id_table 指定),优先按指定 driver 匹配。
- 再比较设备树信息:取 device 的
of_device_id 表里的 .compatible 字符串,和节点的 compatible 属性比对。驱动里要定义 static const struct of_device_id xxx_of_match[] 并用 of_match_table 指过去。
- 接下比较 platform_device_id:如果节点带
platform_data 或者驱动提供 id_table,用 id 字段匹配。
- 最后比较:都用上后如果还是对不上,再比
name(老式匹配路径,现在的 DTS 驱动基本不走这条)。
匹配成功的 callback 就是驱动里的 .probe,此时 device 的 resource 已经在手,可以直接 platform_get_resource() 拿寄存器地址然后 ioremap。
二、操作设备树的常用内核 API
2.1 头文件在哪
- 处理 dtb / 顶层:
include/linux/of.h、include/linux/of_fdt.h(of_get_root_node()、of_get_next_child())
- 处理 device_node:
include/linux/of.h(of_find_node_by_name、of_property_read_u32、of_get_next_child)
- 处理 platform_device:
include/linux/of_platform.h、include/linux/platform_device.h
2.2 platform_device 相关函数
struct platform_device *pdev;
struct resource *res;
/* 按 device_node 反查 platform_device */
pdev = of_find_device_by_node(np);
/* 取资源:IORESOURCE_MEM / IORESOURCE_IRQ 等 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ioremap(res->start, resource_size(res));
常见读写属性的函数:
of_property_read_u32(np, "reg", &val); // 读 u32
of_property_read_string(np, "status", &s); // 读字符串
of_get_next_child(np, &child); // 遍历子节点
of_find_node_by_name(NULL, "xxx"); // 按名字找节点
2.3 有些节点不会生成 platform_device,怎么访问
比如 i2c 下的子节点生成的是 i2c_client、spi 下生成的是 spi_device。这时驱动里用对应总线的 API 拿节点,而不是 platform 那套:
struct i2c_client *client;
client = of_resolve_aliases...
client->dev.of_node // 从 client 拿回 device_node
i2c_get_client_data(client);
一句话:先找到对象(device_node),再按它属于哪条总线选择访问方式。这是驱动调试里最容易卡住的点——你在 DTS 里明明写了节点,驱动 probe 却不进,八成是节点类型走错了总线。
三、调试与排错
- 看内核实际解析出什么:启动参数加
openfdt 相关调试,或在驱动里 pr_info("node name: %s\n", np->name) 打印。
- probe 没被调用:先用
of_machine_is_compatible() 或 cat /sys/firmware/devicetree/base/ 确认节点在 sysfs 里存在,再查 driver 的 of_match_table 字符串有没有拼错(大小写、模块名前缀都要一致)。
- 同一份 DTS 编译报错:先用
dtc -I dts -O dtb -o x.dtb x.dts 离线编译一遍,语法错误在编译期就暴露,别到板子上再看内核日志 。
- 资源地址不对:检查
reg 的地址单位(某些平台是字节、某些按 #address-cells 分格),以及有没有和 iomem 重叠。
小结
整条链路四步:dtb 解析成 device_node 树 → 按规则(有 compatible、非特殊节点)生成 platform_device → platform 总线上按 of_match_table / id_table 顺序配对 → probe 里用 of_* 系列 API 取属性取资源。记住「不是所有节点都会变成 platform_device」和「总线决定访问方式」这两条,能解决驱动里绝大多数「节点写了却不生效」的问题。
qidao123.com企服评测-应用市场 编辑提示 |