1.编写设备树
cd arch/riscv/boot/dts/
再cd到厂商,例如下述内容。
2.编译设备树(dts->dtb)通过dtc命令来转换
3.解析设备树
例如上述内容,都是对设备树的解析。
这里重点说一下内核对设备树的处理吧,因为这个内容是设备树的重点了。
从源代码文件
dts
文件开始,设备树的处理过程为:
dts
在
PC
机上被编译为
dtb
文件;
u-boot
把
dtb
文件传给内核;
内核解析
dtb
文件,把每一个节点都转换为
device_node
结构体;
对于某些
device_node
结构体,会被转换为
platform_device
结构体。
dtb
中每一个节点都被转换为
device_node
结构体


根节点被保存在全局变量
of_root
中,从
of_root
开始可以访问到任意节点。
哪些设备树节点会被转换为
platform_device
根节点下含有
compatile
属性的
子节点
含有特定
compatile
属性的节点的
子节点
如果一个节点的
compatile
属性,它的值是这
4
者之一:
"simple
bus","simple-mfd","isa","arm,amba-bus",
那 么 它 的
子结点
(
需 含
compatile
属性
)
也可以转换为
platform_device
。
总线 I2C
、
SPI
节点下的子节点:
不转换
为
platform_device。
某个总线下到子节点,应该交给对应的总线驱动程序来处理,
它们不应该被转换为 platform_device。
比如以下的节点中:
/mytest
会被转换为
platform_device,
因为它兼容
"simple-bus";
它的子节点/mytest/mytest@0
也会被转换为
platform_device
/i2c
节点一般表示
i2c
控制器
,
它会被转换为
platform_device,
在内核中有对应的 platform_driver;
/i2c/at24c02
节点不会被转换为
platform_device,
它被如何处理完全由父节点的 platform_driver
决定
,
一般是被创建为一个
i2c_client
。
类似的也有
/spi
节点
,
它一般也是用来表示
SPI
控制器
,
它会被转换为platform_device, 在内核中有对应的
platform_driver; /spi/flash@0 节点不会被转换为
platform_device,
它被如何处理完全由父节点的 platform_driver
决定
,
一般是被创建为一个
spi_device
。
怎么转换为
platform_device
内核处理设备树的函数调用过程,这里不去分析;我们只需要得到如下结论:
platform_device
中含有
resource
数组
,
它来自
device_node
的 reg, interrupts 属性
;
platform_device.dev.of_node
指向
device_node,
可以通过它获得其他属性;
platform_device
如何与
platform_driver
配对

最先比较:是否强制选择某个
driver
然后比较:设备树信息
接下来比较:
platform_device_id
platform_device.name
和
platform_driver.driver.name
platform_driver.id_table
可能为空,
这 时 可 以 根 据
platform_driver.driver.name
来 寻 找 同 名 的
platform_device
。
内核里操作设备树的常用函数
内核源码中
include/linux/
目录下有很多
of
开头的头文件,
of
表示“
open
firmware
”即开放固件。
设备树的处理过程是:
dtb -> device_node -> platform_device
处理
DTB
of_fdt.h // dtb 文件的相关操作函数, 我们一般用不到,
// 因为 dtb 文件在内核中已经被转换为 device_node 树(它更易于使用)
处理
device_node
of.h // 提供设备树的一般处理函数,
// 比如 of_property_read_u32(读取某个属性的 u32 值),
// of_get_child_count(获取某个 device_node 的子节点数)
of_address.h // 地址相关的函数,
// 比如 of_get_address(获得 reg 属性中的 addr, size 值)
// of_match_device (从 matches 数组中取出与当前设备最匹配的一项)
of_dma.h // 设备树中 DMA 相关属性的函数
of_gpio.h // GPIO 相关的函数
of_graph.h // GPU 相关驱动中用到的函数, 从设备树中获得 GPU 信息
of_iommu.h // 很少用到
of_irq.h // 中断相关的函数
of_mdio.h // MDIO (Ethernet PHY) API
of_net.h // OF helpers for network devices.
of_pci.h // PCI 相关函数
of_pdt.h // 很少用到
of_reserved_mem.h // reserved_mem 的相关函数
处理
platform_device
of_platform.h // 把 device_node 转换为 platform_device 时用到的函数,
// 比如 of_device_alloc(根据 device_node 分配设置 platform_device),
// of_find_device_by_node (根据 device_node 查找到 platform_device),
// of_platform_bus_probe (处理 device_node 及它的子节点)
of_device.h // 设备相关的函数, 比如 of_match_device
platform_device
相关的函数
of_platform.h
中声明了很多函数,但是作为驱动开发者,我们只使用其中的 1
、
2
个。其他的都是给内核自己使用的,内核使用它们来处理设备树,转换得到 platform_device
。
of_find_device_by_node
函数原型为:
extern struct platform_device *of_find_device_by_node(struct device_node *np);
设备树中的每一个节点,在内核里都有一个
device_node
;你可以使用device_node 去找到对应的
platform_device
。
platform_get_resource
这 个 函 数 跟 设 备 树 没 什 么 关 系 , 但 是 设 备 树 中 的 节 点 被 转 换 为platform_device 后,设备树中的
reg
属性、
interrupts 属性也会被转换为 “resource”。 这时,你可以使用这个函数取出这些资源。
/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type // 取哪类资源?IORESOURCE_MEM、IORESOURCE_REG
* // IORESOURCE_IRQ 等
* @num: resource index // 这类资源中的哪一个?
*/
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num);
对于设备树节点中的
reg
属性,它对应
IORESOURCE_MEM
类型的资源;
对于设备树节点中的
interrupts
属性,它对应
IORESOURCE_IRQ
类型的资
源。
有些节点不会生成
platform_device
,怎么访问它们
内核会把
dtb
文件解析出一系列的
device_node
结构体,我们可以直接访
问这些
device_node
。
内核源码
incldue/linux/of.h
中声明了
device_node
和属性
property 的操作函数
of_find_node_by_path of_find_node_by_name 等等都可以。