大家好,又见面了,我是你们的朋友全栈君。
当拿到driver,不能用起来的时候需要去检查device了。虽说device和bus通常都是系统中带的,但也不要想当然的认为这个系统是帮你建好的。
通常 bus device driver三者中,bus基本不用干预,device 干预的少,driver 干预的多。
从设备树中生成device
从设备树中识别 device的入口为 arch_initcall_sync(of_platform_default_populate_init);
static int __init of_platform_default_populate_init(void)
{
// /firmware 节点
node = of_find_node_by_path("/firmware");
if (node) {
of_platform_populate(node, NULL, NULL, NULL);
of_node_put(node);
}
// 传参为NULL,默认获取 / 节点
of_platform_default_populate(NULL, NULL, NULL);
return 0;
}
往下调用
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);
}
注意参数 of_default_bus_match_table
const struct of_device_id of_default_bus_match_table[] = {
{
.compatible = "simple-bus", },
{
.compatible = "simple-mfd", },
{
.compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
{
.compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
{
} /* Empty terminated list */
};
到函数of_platform_populate后,出现两个条件情况
第一个 root = node = of_find_node_by_path(“/firmware”);
第二个 root = of_find_node_by_path(“/”) + of_default_bus_match_table[]
然后执行 of_platform_bus_create(child, matches, lookup, parent, true);
第一种情况:child 从 “/firmware” 节点开始找,没有match限制
第二种情况:child 从 “/” 节点开始找,有match限制
这两种情况中,通常第二点作为创建platform device的主要规则
进入该函数,该函数就是创建 platform device 的
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)
{
struct platform_device *dev;
int rc = 0;
/* 节点 必须要有 compatible 词条 */
if (strict && (!of_get_property(bus, "compatible", NULL)))
return 0;
/* 节点含有 of_skipped_node_table中定义的 compatible 词条,不做 device */
if (unlikely(of_match_node(of_skipped_node_table, bus)))
return 0;
// 属性为 arm,primecell ,创建完就退出。针对 arm 的
if (of_device_is_compatible(bus, "arm,primecell")) {
of_amba_device_create(bus, bus_id, platform_data, parent);
return 0;
}
// --------------------********重点重点重点在下面,规则的体现********--------------
// 需要有 of_default_bus_match_table 中定义的 compatible 词条
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
// 走到此处需要停下来 ,静静思考:若当前节点含有compatible词条,但词条不在
// of_default_bus_match_table 中,则当前节点下面的所有子节点,不做为platform device
if (!dev || !of_match_node(matches, bus))
return 0;
//词条在of_default_bus_match_table 中,则当前节点的下一个子节点,做为platform device
for_each_child_of_node(bus, child) {
// 若节点还有子节点,递归创建device,条件为上述,直到不满足条件退出
rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
}
}
return rc;
}
of_platform_device_create_pdata 就是 具体创建 设备树中符合条件的device,将节点相关信息解析并保存到struct platform_device中。比如 irq,resource (内存信息,地址等)等。
一般情况 从 ” / ” 节点,找child,作为bus node ,child 节点是否有 compatible ,有则往下走;将child创建为device,然后判断child是否含有of_default_bus_match_table中的compatible,有,则child作为platform device就成了。然后继续以child 作为 parent,继续循环下去,找child,作为bus node ,child 节点是否有 compatible ,有则往下走;将child创建为device,然后判断child是否含有of_default_bus_match_table中的compatible,有,则child作为platform device就成了 …
所以,格式如下
1 最常规的,/ 下,A成了platform device
/ {
A {
compatible = "xxx"
}
}
2 A成了platform device,b不为platform device,一般我们都是这么写的
A {
compatible = " of_default_bus_match_table. compatible"
B {
compatible = " xxx"
}
}
3 无限接力的.基本不存在
A {
compatible = " of_default_bus_match_table. compatible"
B {
compatible = " of_default_bus_match_table. compatible "
C {
...compatible 同上
};
};
};
平台总线的设备对应的bus为platform_bus_type,不属于这个bus范畴就不是platform device。
dev->dev.bus = &platform_bus_type;
platform bus的设备对应目录 /sys/bus/platform/devices
题外话
注意到属性arm,primecell,这个是arm中的AMBA总线。含有amba属性的节点会被注册成amba设备,不会成为platform设备。比如pl011串口,还有其他pl0xx乱七八糟其他arm特有的设备。
dev->dev.bus = &amba_bustype;
amba bus的设备对应目录 /sys/bus/amba/devices/
有人看到/sys/devices/platform这个目录,是社么?
虽然带有platform字眼,但是这个目录表示的是所有外部设备,soc外接的所有设备都汇总在这个目录下。不是平台总线。毕竟目录中没有总线bus这个词!
至于这目录怎么创建出来的,不懂。
从ACPI 生成device
acpi初始化函数
static struct acpi_scan_handler apd_handler = {
.ids = acpi_apd_device_ids,
.attach = acpi_apd_create_device,
};
void __init acpi_apd_init(void)
{
acpi_scan_add_handler(&apd_handler);
}
其中的ids,存放了 acpi中定义的属性,如下格式。这些都是厂商私有数据,不像设备树的of_default_bus_match_table 为通用属性。
static const struct acpi_device_id acpi_apd_device_ids[] = {
/* Generic apd devices */
#ifdef CONFIG_ARM64
{
"NXP0001", APD_ADDR(nxp_i2c_desc) },
#endif
// 自定义的描述符,需要添加到此处,否则系统起来,不会添加。和设备树比起来,多了一步。
{
}
};
当然也有禁用的 acpi 描述符 存放在forbidden_id_list
第一个字符串id 类比设备树里的 compatible 。注意在acpi table中的描述文件中也要添加该字符串
第二个是描述信息
static const struct apd_device_desc nxp_i2c_desc = {
//如果这个 desc 没有定义 fixed_clk_rate, setup 函数直接 return 0
.setup = acpi_apd_setup,
.fixed_clk_rate = 350000000,
};
创建入口为 acpi_apd_create_device ——- acpi_create_platform_device —– platform_device_register_full
device 保存路径
最终会调用device_add,以kobject的形式呈现在sysfs下
/sys/devices/platform
I2C设备
i2c总线节点,一般是在 “ / ” 下,所以默认生成 platform device
i2c0: i2c@10002000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "arm,versatile-i2c";
reg = <0x10002000 0x1000>;
rtc@68 {
compatible = "dallas,ds1338";
reg = <0x68>;
};
};
如上 i2c节点在“ / ” 下,i2c@10002000会被注册成 platform device。
i2c节点下有 compatible ,但不是of_default_bus_match_table中的词条,所以该节点的子节点rtc不会被注册成 platform device,但是会注册成 i2c device。
到 i2c 代码初始化后,i2c@10002000 会被注册成 i2c adapter,以 i2c-0/1/2/…形式展示,然后会将其下的i节点注册成i2c device。
详见函数 of_i2c_register_devices
和 i2c 区别的是:
platform device 对应driver使用 module_platform_driver,
i2c device 对应的driver使用 module_i2c_driver。
两者匹配上也比较相似。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163790.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...