设备树(三):device_node转换为platform_device

kernel版本:4.4.143

一、总结

a. 哪些device_node可以转换为platform_device?
根节点下含有compatile属性的子节点
如果一个结点的compatile属性含有这些特殊的值("simple-bus","simple-mfd","isa","arm,amba-bus")之一,
并且自己成功注册成了platform_device,, 那么它的子结点(需含compatile属性)也可以转换为platform_device。
i2c, spi等总线节点下的子节点, 应该交给对应的总线驱动程序来处理, 它们不应该被转换为platform_device。

b. 怎么转换?
platform_device中含有resource数组, 它来自device_node的reg, interrupts属性;
platform_device.dev.of_node指向device_node, 可以通过它获得其他属性。

总结:
a. 内核函数customize_machine, 遍历device_node树, 生成platform_device
b. 并非所有的device_node都会转换为platform_device
   只有以下的device_node会转换:
b.1 该节点必须含有compatible属性
b.2 根节点的子节点(节点必须含有compatible属性)
b.3 含有特殊compatible属性的节点的子节点(子节点必须含有compatible属性):
    这些特殊的compatilbe属性为: "simple-bus","simple-mfd","isa","arm,amba-bus"

参考韦东山“设备树详细分析.txt”

二、源码分析

//kernel启动会通过下面的函数调用按顺序执行.init.data代码段中的函数
--> start_kernel     // init/main.c
----> rest_init();
------> pid = kernel_thread(kernel_init, NULL, CLONE_FS);
--------> kernel_init
----------> kernel_init_freeable();
------------> do_basic_setup()
--------------> do_initcalls();
----------------> do_initcall_level(int level)
		for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
				do_one_initcall(*fn);
在\kernel\arch\arm\kernel\Setup.c中定义了customize_machine,customize_machine被arch_initcall修饰
//arch_initcall(customize_machine);展开为
//static initcall_t __initcall_customize_machine3 
//               __used __attribute__((__section__(".initcall3.init"))) = customize_machine; 
//该段代码最终会被链接到.init.data代码段的.initcall3.init中
//内核链接文件在./arch/arm/kernel/vmlinux.lds

static int __init customize_machine(void)     //  arch\arm\kernel\Setup.c
{
	/*
	 * customizes platform devices, or adds new ones
	 * On DT based machines, we fall back to populating the
	 * machine from the device tree, if no callback is provided,
	 * otherwise we would always need an init_machine callback.
	 */
	of_iommu_init();
	if (machine_desc->init_machine) //如果存在machine_desc->init_machine则调用
		machine_desc->init_machine();
#ifdef CONFIG_OF
	else
		of_platform_populate(NULL, of_default_bus_match_table,
					NULL, NULL);
#endif
	return 0;
}
arch_initcall(customize_machine);
//下面以arch/arm/mach-rockchip/rockchip.c中的machine_des为例
//machine_desc->init_machine实际指向函数为rockchip_dt_init
static void __init rockchip_dt_init(void)
{
	rockchip_suspend_init();
	   /*
		*		const struct of_device_id of_default_bus_match_table[] = {
		*			{ .compatible = "simple-bus", },
		*			{ .compatible = "simple-mfd", },
		*		#ifdef CONFIG_ARM_AMBA
		*			{ .compatible = "arm,amba-bus", },
		*		#endif /* CONFIG_ARM_AMBA */
		*			{} /* Empty terminated list */
		*		};
		*/
	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}

//kernel\drivers\of\Platform.c
1、of_platform_populate

int of_platform_populate(struct device_node *root,
			const struct of_device_id *matches,
			const struct of_dev_auxdata *lookup,
			struct device *parent)
{
	struct device_node *child;
	int rc = 0;

	root = root ? of_node_get(root) : of_find_node_by_path("/");
	if (!root)
		return -EINVAL;

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true); //遍历根节点所有子节点
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(root, OF_POPULATED_BUS);

	of_node_put(root);
	return rc;
}

2、of_platform_bus_create

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)
{
	const struct of_dev_auxdata *auxdata;
	struct device_node *child;
	struct platform_device *dev;
	const char *bus_id = NULL;
	void *platform_data = NULL;
	int rc = 0;

	/* Make sure it has a compatible property */
	if (strict && (!of_get_property(bus, "compatible", NULL))) { //如果节点没有compatible 属性直接返回
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	auxdata = of_dev_lookup(lookup, bus); //auxdata = NULL
	if (auxdata) {
		bus_id = auxdata->name; 
		platform_data = auxdata->platform_data;
	}

	if (of_device_is_compatible(bus, "arm,primecell")) {
		/*
		 * Don't return an error here to keep compatibility with older
		 * device tree files.
		 */
		of_amba_device_create(bus, bus_id, platform_data, parent);
		return 0;
	}

	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); //为根结点下有compatible属性的节点注册为platform_device
																		       //bus_id = NULL, platform_data = NULL, parent = NULL
	if (!dev || !of_match_node(matches, bus))
		return 0;

	for_each_child_of_node(bus, child) {
		pr_debug("   create child: %s\n", child->full_name);
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); //递归调用,为compatible属性中含有matches->compatible字段的节点注册为platform_device
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(bus, OF_POPULATED_BUS);
	return rc;
}

2.1、of_platform_device_create_pdata

static struct platform_device *of_platform_device_create_pdata(
					struct device_node *np,
					const char *bus_id,
					void *platform_data,
					struct device *parent)
{
	struct platform_device *dev;

	if (!of_device_is_available(np) ||        //检查节点status是否为okay或ok
	    of_node_test_and_set_flag(np, OF_POPULATED)) //置位device_node中的_flags的第三位为bit 1
		return NULL;

	dev = of_device_alloc(np, bus_id, parent); //申请platform_device结构体,并初始化platform_device中的dev成员的部分内容,解析dev成员中的resource成员
	if (!dev)
		goto err_clear_flag;

	dev->dev.bus = &platform_bus_type;
	dev->dev.platform_data = platform_data;
	of_dma_configure(&dev->dev, dev->dev.of_node);
	of_msi_configure(&dev->dev, dev->dev.of_node);

	if (of_device_add(dev) != 0) {
		of_dma_deconfigure(&dev->dev);
		platform_device_put(dev);
		goto err_clear_flag;
	}

	return dev;

err_clear_flag:
	of_node_clear_flag(np, OF_POPULATED);
	return NULL;
}

2.1.1、of_device_alloc

struct platform_device *of_device_alloc(struct device_node *np,
				  const char *bus_id,
				  struct device *parent)
{
	struct platform_device *dev;
	int rc, i, num_reg = 0, num_irq;
	struct resource *res, temp_res;

	dev = platform_device_alloc("", -1); 
	if (!dev)
		return NULL;

	/* count the io and irq resources */
	while (of_address_to_resource(np, num_reg, &temp_res) == 0) //计算IO资源数量
		num_reg++;
	num_irq = of_irq_count(np); //计算中断资源数量

	/* Populate the resource table */
	if (num_irq || num_reg) {
		res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL); //申请IO和中断资源所有内存空间
		if (!res) {
			platform_device_put(dev);
			return NULL;
		}

		dev->num_resources = num_reg + num_irq;
		dev->resource = res;
		for (i = 0; i < num_reg; i++, res++) {
			rc = of_address_to_resource(np, i, res); //真正把device_node下的reg资源转换为struct resource
			WARN_ON(rc);
		}
		if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
			pr_debug("not all legacy IRQ resources mapped for %s\n",
				 np->name);
	}

	dev->dev.of_node = of_node_get(np);
	dev->dev.fwnode = &np->fwnode;
	dev->dev.parent = parent ? : &platform_bus;//如果存在是根结点子节点dev->dev.parent=platform_bus,否则等于parent

	if (bus_id)
		dev_set_name(&dev->dev, "%s", bus_id);
	else
		of_device_make_bus_id(&dev->dev);//设置struct device 中kobj的kobj.name

	return dev;
}

2.1.1.1、of_address_to_resource

int of_address_to_resource(struct device_node *dev, int index,
			   struct resource *r)
{
	const __be32	*addrp;
	u64		size;
	unsigned int	flags;
	const char	*name = NULL;

	addrp = of_get_address(dev, index, &size, &flags); //addrp指向device_node的reg属性值的index组address,size等于index组的size,flag将代表resource的类型
	if (addrp == NULL)
		return -EINVAL;

	/* Get optional "reg-names" property to add a name to a resource */
	of_property_read_string_index(dev, "reg-names",	index, &name);//name指向reg-names属性的第index组

	return __of_address_to_resource(dev, addrp, size, flags, name, r);
}

2.1.1.1.1、__of_address_to_resource

static int __of_address_to_resource(struct device_node *dev,
		const __be32 *addrp, u64 size, unsigned int flags,
		const char *name, struct resource *r)
{
	u64 taddr;

	if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
		return -EINVAL;
	taddr = of_translate_address(dev, addrp);
	if (taddr == OF_BAD_ADDR)
		return -EINVAL;
	memset(r, 0, sizeof(struct resource));
	if (flags & IORESOURCE_IO) {
		unsigned long port;
		port = pci_address_to_pio(taddr);
		if (port == (unsigned long)-1)
			return -EINVAL;
		r->start = port;
		r->end = port + size - 1;
	} else {
		r->start = taddr;
		r->end = taddr + size - 1;
	}
	r->flags = flags;
	r->name = name ? name : dev->full_name;

	return 0;
}

三、3个重要的结构体

下面总结一下struct platform_device、struct device 、struct resource 三个结构体最终赋值结果

struct platform_device {
	const char	*name;  //等于dev.kobj.name, 可通过dev_name(const struct device *dev)获得
	int		id;   //默认为-1,
	bool		id_auto;
	struct device	dev; //等于下面struct device
	u32		num_resources; //等于resource的总数量,IO资源+中断资源
	struct resource	*resource; //指向下面的struct 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 device {
	struct device		*parent; //根结点的子节点指向struct device platform_bus = {.init_name	= "platform",};
	                            //其他节点指向父节点struct device

	struct device_private	*p; //p->device等于自己

	struct kobject kobj;  //kobj.name = reg属性address内容+.+device_node的name,例如i2c控制器    ff650000.i2c
	const char		*init_name; /* initial name of the device */ //等于NULL
	const struct device_type *type;

	struct mutex		mutex;	/* mutex to synchronize calls to
					 * its driver.
					 */

	struct bus_type	*bus;		/* type of bus device is on */ //struct bus_type platform_bus_type = {
															   //               .name		= "platform",
															   //				.dev_groups	= platform_dev_groups,
															   //				.match		= platform_match,
															   //				.uevent		= platform_uevent,
															   //				.pm		= &platform_dev_pm_ops,
															   //			};
	struct device_driver *driver;	/* which driver has allocated this device */ platform_device与platform_driver匹配成功后指向platform_driver的device_driver
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */ //platform_data=NULL
	void		*driver_data;	/* Driver data, set and get with
					   dev_set/get_drvdata */
	struct dev_pm_info	power;
	struct dev_pm_domain	*pm_domain;

#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
	struct irq_domain	*msi_domain;
#endif
#ifdef CONFIG_PINCTRL
	struct dev_pin_info	*pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQ
	struct list_head	msi_list;
#endif

#ifdef CONFIG_NUMA
	int		numa_node;	/* NUMA node this device is close to */
#endif
	u64		*dma_mask;	/* dma mask (if dma'able device) */
	u64		coherent_dma_mask;/* Like dma_mask, but for
					     alloc_coherent mappings as
					     not all hardware supports
					     64 bit addresses for consistent
					     allocations such descriptors. */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

	struct list_head	dma_pools;	/* dma pools (if dma'ble) */

	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#ifdef CONFIG_DMA_CMA
	struct cma *cma_area;		/* contiguous memory area for dma
					   allocations */
#endif
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */ //指向对应的device_node
	struct fwnode_handle	*fwnode; /* firmware device node */ //指向对应的device_node的fwnode_handle

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
	u32			id;	/* device instance */

	spinlock_t		devres_lock;
	struct list_head	devres_head;

	struct klist_node	knode_class;
	struct class		*class;
	const struct attribute_group **groups;	/* optional groups */

	void	(*release)(struct device *dev);     //static void platform_device_release(struct device *dev)
	struct iommu_group	*iommu_group;

	bool			offline_disabled:1;
	bool			offline:1;
};
//如果为IO资源,resource将代表reg属性下的一组值,如果为中断资源,以后专门分析
struct resource {
	resource_size_t start; //device_node的reg属性的address值对应的cpu地址
	resource_size_t end;//device_node的reg属性的address+size值对应的cpu地址的结尾
	const char *name;//如果device_node有reg-names,等于reg-names对应的值,否则为device_node的full_name
	unsigned long flags;//代表resource的类型,\include\linux\Ioport.h中定义了各种IORESOURCE类型
	struct resource *parent, *sibling, *child;
};
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值