前言
在以前我们都是显示的添加平台设备信息,在引入设备树机制后,设备的相关信息都用设备树的方式来表示。内核或者驱动通过of系列函数来获取到这些信息。然后在内核中用链表的形式组织起来。
相关的结构体
属性:
struct property {
char *name;
int length;
void *value; //重点
struct property *next;
unsigned long _flags;
unsigned int unique_id;
struct bin_attribute attr;
};
设备节点:
struct device_node {
const char *name;
const char *type;
phandle phandle;
const char *full_name;
struct fwnode_handle fwnode;
struct property *properties;
struct property *deadprops; /* removed properties */
struct device_node *parent;
struct device_node *child;
struct device_node *sibling;
struct kobject kobj;
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
};
平台设备:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
函数调用过程
开发板创建platform设备过程
设备树板级启动:
DT_MACHINE_START(IMX6UL, "Freescale i.MX6 UltraLite (Device Tree)")
.map_io = imx6ul_map_io,
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.init_late = imx6ul_init_late,
.dt_compat = imx6ul_dt_compat,
MACHINE_END
板级设备的初始化,其中会调用和平台设备相关的这个函数:of_platform_default_populate()。
static void __init imx6ul_init_machine(void)
{
struct device *parent;
parent = imx_soc_device_init();
if (parent == NULL)
pr_warn("failed to initialize soc device\n");
of_platform_default_populate(NULL, NULL, parent);
imx6ul_enet_init();
imx_anatop_init();
imx6ul_pm_init();
}
在下面的包裹函数中会调用创建平台设备的开始函数。
int of_platform_default_populate(struct device_node *root,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
return of_platform_populate(root, of_default_bus_match_table, lookup,
parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);
在下面的函数中会遍历root节点下的所有子节点,分别调用of_platform_bus_create()来创建平台设备。
drivers/of/platform.c:
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
{
...
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc) {
of_node_put(child);
break;
}
}
...
}
核心函数,这个函数的主要作用是为节点和它的子节点创建设备。
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
...
/* Make sure it has a compatible property *
/* 检查属性,上面的注释很明确的说明了需要有“compatible”属性。*/
if (strict && (!of_get_property(bus, "compatible", NULL))) {
pr_debug("%s() - skipping %pOF, no compatible prop\n",
__func__, bus);
return 0;
}
/* 2.填充相关信息 */
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
platform_data = auxdata->platform_data;
}
/* 3.创建平台设备 */
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
/* 4.递归调用本函数为子节点创建平台设备 */
for_each_child_of_node(bus, child) {
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
...
}
...
}
函数调用过程简览:
of_platform_populate(){
-->of_platform_bus_create()
-->of_platform_device_create_pdata()
-->of_device_alloc()
-->of_msi_configure()
-->of_device_add()
-->device_add()
-->of_platform_bus_create()
}
对比设备树和设备信息
首先查看设备树中关于lcd接口的信息:
再查看sysfs文件系统下面的platform的信息:
然后再查看lcdif的的设备树节点信息:
最后查看下clock-names,发现和我们的设备树信息是一致的。
总结
- 在设备树中描述的平台设备信息,会在内核代码中自动展开,生成对应的平台设备信息,添加到设备链表中。
- 自动转换成平台设备的条件:
该节点必须含有compatible属性
根节点的子节点(节点必须含有compatible属性),也即根节点下含有compatible属性的子节点。
含有特殊compatible属性的节点的子节点(子节点必须含有compatible属性): 这些特殊的compatilbe属性为: “simple-bus”,“simple-mfd”,“isa”,“arm,amba-bus” - 不转化的条件:
1.根节点下子节点,没有上边的那4种属性,那么该子节点的子节点都不转化。
2.总线I2c,SPI节点下的子节点,由对应的总线驱动程序处理,不转化。
3.根节点是例外的,生成platfrom_device时,即使有compatible属性也不会处理。
注意:i2c, spi等总线节点会转换为platform_device,但是,spi、i2c下的子节点无论compatilbe是否为: “simple-bus”,“simple- mfd”,“isa”,"arm,amba-bus "都应该交给对应的总线驱动程序来处理而不会被转换为platform_device。
测试代码
/* mydrv.c */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
//产生一个for循环用于检查所有的子节点
#define device_for_each_child_node(dev, child) \
for (child = device_get_next_child_node(dev, NULL); child; \
child = device_get_next_child_node(dev, child))
int myprobe(struct platform_device *pdev)
{
struct fwnode_handle *fwhandle;
const char *str;
u32 val;
//获取设备子节点的个数
printk("child node count : %d\n", device_get_child_node_count(&pdev->dev));
//获取设备属性autorepeat的值
printk("%d\n", device_property_read_bool(&pdev->dev, "autorepeat"));
//遍历设备的每个子节点
device_for_each_child_node(&pdev->dev, fwhandle) {
//获取设备子节点的label属性值
fwnode_property_read_string(fwhandle, "label", &str);
printk("label = %s\n", str);
//获取设备子节点的code属性值
fwnode_property_read_u32(fwhandle, "code", &val);
printk("code = %x\n", val);
};
return 0;
}
int myremove(struct platform_device *pdev)
{
printk("in myremove ...\n");
return 0;
}
struct of_device_id ids[] = {
{.compatible = "mynodes"},
{},
};
struct platform_driver mydrv = {
.probe = myprobe,
.remove = myremove,
.driver = {
.owner = THIS_MODULE,
.name = "mydrv" ,
.of_match_table = ids,
},
};
module_platform_driver(mydrv);
MODULE_LICENSE("GPL");
参考
1.参考博文