Linux 内核驱动platform_data分析


前言

在Linux内核驱动开发中,通常会以设备的probe()函数为入口,而在这入口函数,经常会遇到平台资源的情况,导致阅读者不清楚它的来历,本文目的即为分析平台设备的平台资源的来龙去脉。

一、Linux platform 数据结构

struct platform_device {
	const char	*name;
	int		id;
	bool		id_auto;
	struct device	dev;
	u64		dma_mask;
	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;
};
struct of_dev_auxdata {
	char *compatible;
	resource_size_t phys_addr;
	char *name;
	void *platform_data;
}

二、驱动示例

1. 设备树示例

/ {
	compatible = "chipname";
	device_father : device-father {
		compatible = "father";
		info = "this is father info";

		device_child : device-child {
			compatible = "child";
		}
	};
}

2. 设备树入口

对于每一个平台,通常都会定义一个这样的设备树根节点的入口,他会把符合platform device的节点,添加到platform总线中

#define DT_MACHINE_START(_type, _namestr)			 		\
static const struct machine_desc __mach_desc_##_type 		\
	__used													\
	__attribute__ ((__section__(".arch.info.init"))) = { 	\
		.nr		= MACH_TYPE_##_type,						\
		.name	= _namestr,									\

#define MACHINE_END											\
};

DT_MACHINE_START(EXAMPLE_DT, "Board example, Inc.")
	.init_machine	= board_init,
	.dt_compat		= board_dt_match,
MACHINE_END

static void __init board_init(void)
{
	board_dt_populate(NULL);
}

static const char *board_dt_mach[] __initconst = {
	"chipname",
	NULL
};

3. 父设备驱动入口

这个父设备是直接挂载在根节点之下的,因此会被识别为platform_device,并添加到了总线之中,当父设备驱动也被加载到platform总线之中时,会进行兼容性匹配,匹配成功便会执行设备的probe函数

static int father_probe(struct platform_device *pdev)
{
	char *father_info = NULL;
	struct device_node *child_node = NULL;
	struct platform_device *child_device = NULL;
	of_property_read_string(pdev->dev.of_node, "info", &father_info);

	child_node = of_parse_phandle(pdev->dev.of_node, "device-child", 0);
	child_device = of_platform_device_create(child_node, NULL, &pdev->dev);

	child_device->dev.platform_data = father_info;
}

static const struct of_device_id father_dt_match[] = {
	{.compatible = "father"},
	{}
}

static struct platform_driver father_driver = {
	.probe = father_probe,
	.driver = {
		.of_match_table = father_dt_match;
	}
} 

static int __init father_driver_init(void)
{
	platform_driver_register(father_driver);
}
module_init(father_driver_init);

4. 子设备驱动

这个子设备节点,并不符合platform设备的条件,因此不会被自动添加到platform总线之中。
从父设备的probe函数中我们可以看到,他主动找到了子设备的节点,并用它创建了一个platform设备,然后将其添加到platform总线中。最后把自身的信息作为平台信息添加到子设备platform_data中,此时父设备就作为了子设备的平台使用了。

static int child_probe(struct platform_device *pdev)
{
	struct father_info *pdata;
	pdata = dev_get_platdata(&pdev->dev);

	printk("%s, platform_data : %s", __func__, pdata);
}

static const struct of_device_id child_dt_match[] = {
	{.compatible = "child"},
	{}
}

static struct platform_driver child_driver = {
	.probe = child_probe,
	.driver = {
		.of_match_table = child_dt_match;
	}
} 

static int __init child_driver_init(void)
{
	platform_driver_register(child_driver);
}
module_init(child_driver_init);

子设备驱动在加载时,匹配到父设备中添加的子设备节点,便会进行子设备的probe,这里访问了从父设备那里获取而来的platform_data。到此我们便清楚platform_data的来历了

总结

并非所有的设备树中的device_node都会自动变成device,除了常规的三大总线(platform, i2c, spi)的节点,也可能存在不会自动加载的节点。这些节点我们可以在驱动中自行进行添加,并做灵活的处理。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值