我们把设备树.dts 变成plantform_device的资源 过程一步步了解清楚
设备树只是起一个信息传递的作用,对这些信息配置的处理,也比较简单,即从设备树的DTB文件中,把这些设备信息提取出来赋给内核中的某个变量即可。
1./chosen节点中bootargs属性就是内核启动的命令行参数,它里面可以指定根文件系统在哪里,第一个运行的应用程序是哪一个,指定内核的打印信息从哪个设备里打印出来。
2./memory中的reg属性指定了不同板子内存的大小和起始地址。
3.根节点的#address-cells和#size-cells属性指定属性参数的位数,比如指定前面memory中的reg属性的地址是32位还是64位,大小是用一个32位表示,还是两个32位表示。
一个编译成uImage的内核镜像文件,可以支持多个单板,这里假设支持smdk2410、smdk2440、jz2440(其中smdk2410、smdk2440是厂家的公板,国内的厂家参考公板设计出了自己的板子,比如jz2440)。
这些板子的配置稍有不同,需要做一些单独的初始化,在内核里面,对于这些单板,都构造了一个machinedesc结构体,里面有.init和.nr。 对于JZ2440,它源自smdk2440,内核没有它的单独文件,它使用smdk2440的相关文件,代码。 在上一节视频里面我们说过,以前uboot使用ATAGS给内核传参数时,它会传入一个机器ID,内核会使用这个机器ID找到最合适的machinedesc。即机器ID与machine_desc里面的.nr比较,相等就表示找到了对应的machinedesc。 当我们的uboot不使用ATAGS传参数,而使用DTB文件时,那么这时内核是如何选择对应的machinedesc呢? 在设备树文件的根节点里,有如下两行:
c model = "SMDK24440"; compatible = "samsung,smdk2440","samsung,smdk24140","samsung,smdk24xx";
这里的compatible属性声明想要什么machinedesc,属性值可以是一系列字符串,依次与machinedesc匹配。 内核最好支持samsung,smdk2440,如果不支持,再尝试是否支持samsung,smdk24140,再不支持,最后尝试samsung,smdk24xx
a. 设备树根节点的compatible属性列出了一系列的字符串, 表示它兼容的单板名,从"最兼容"到次之;
b. 内核中有多个machinedesc, 其中有dtcompat成员, 它指向一个字符串数组, 里面表示该machine_desc支持哪些单板;
c. 使用compatile属性的值, 跟’’‘每一个machinedesc.dtcompat’’'比较, 成绩为"吻合的compatile属性值的位置", 成绩越低越匹配, 对应的machine_desc即被选中
在dts文件里,每个大括号{ }代表一个节点,比如根节点里有个大括号,对应一个devicenode结构体;memory也有一个大括号,也对应一个devicenode结构体。 节点里面有各种属性,也可能里面还有子节点,所以它们还有一些父子关系。 根节点下的memory、chosen、led等节点是并列关系,兄弟关系。 对于父子关系、兄弟关系,在device_node结构体里面肯定有成员来描述这些关系。
打开include/linux/Of.h可以看到devicenode结构体的定义如下:
struct devicenode { const char *name; // 来自节点中的name属性, 如果没有该属性, 则设为"NULL"
const char *type; // 来自节点中的device_type属性, 如果没有该属性, 则设为"NULL"
phandle phandle;
const char *fullname; // 节点的名字, node-name[@unit-address] struct fwnodehandle fwnode;
struct property *properties; // 节点的属性
struct property *deadprops; /* removed properties */
struct device_node *parent; // 节点的父亲
struct device_node *child; // 节点的孩子(子节点)
struct device_node *sibling; // 节点的兄弟(同级节点)
#if defined(CONFIG_OF_KOBJ)
struct kobject kobj;
#endif
unsigned long _flags;
void *data;
#if defined(CONFIG_SPARC)
const char *path_component_name;
unsigned int unique_id;
struct of_irq_controller *irq_trans;
#endif
};
a.根节点下含有compatile属性的子节点
b.除了根节点的子节点外,子节点的子节点里compatile属性里有特殊值(“simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus”)之一,那么子节点的子节点可以转换为plantform_device
c.i2c,spi已经转化为plantform_device,所以他们的子节点应该被,总线drv的probe来调用
eg1:
/dts-v1/;
/memreserve/ 0x33f00000 0x100000;
/ {
model = "SMDK24440";
compatible = "samsung,smdk2440";
#address-cells = <1>;
#size-cells = <1>;
memory { /* /memory */
device_type = "memory";
reg = <0x30000000 0x4000000 0 4096>;
};
/*
cpus {
cpu {
compatible = "arm,arm926ej-s";
};
};
*/
chosen {
bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
};
led {
compatible = "jz2440_led";
pin = <S3C2410_GPF(5)>;
};
};
上面只有led节点有compatbile属性,他被转化为drv
a. platform_device中含有resource数组, 它来自device_node的reg, interrupts属性;
b. platform_device.dev.of_node指向device_node, 可以通过它获得其他属性(我们自定义比如pin)
eg:
/ {
mytest {
compatile = "mytest", "simple-bus";
mytest@0 {
compatile = "mytest_0";
};
};
i2c {
compatile = "samsung,i2c";
at24c02 {
compatile = "at24c02";
};
};
spi {
compatile = "samsung,spi";
flash@0 {
compatible = "winbond,w25q32dw";
spi-max-frequency = <25000000>;
reg = <0>;
};
};
};
/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。
当我们把设备树文件转换为dev时,就能进行drv的匹配
比较的优先顺序:
a. 比较 platform_dev.driver_override 和 platform_driver.drv->name
b. 比较 platform_dev.dev.of_node的compatible属性 和 platform_driver.drv->of_match_table
c. 比较 platform_dev.name 和 platform_driver.id_table
d. 比较 platform_dev.name 和 platform_driver.drv->name
有一个成功, 即匹配成功
对这个文件使用命令
hexdump -C /sys/firmware/fdt
以目录结构程现的dtb文件, 根节点对应base目录, 每一个节点对应一个目录, 每一个属性对应一个文件
同样用命令hexdump -C ./led
进行查看
系统中所有的platform_device, 有来自设备树的, 也有来有.c文件中注册的
对于来自设备树的platform_device,
可以进入 /sys/devices/platform/<设备名>/of_node 查看它的设备树属性