zl程序教程

您现在的位置是:首页 >  硬件

当前栏目

内核杂谈——关于platform device 创建

内核 创建 关于 device Platform 杂谈
2023-06-13 09:12:41 时间

大家好,又见面了,我是你们的朋友全栈君。

当拿到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。 两者匹配上也比较相似。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/163790.html原文链接:https://javaforall.cn