设备树和Platform架构--5--platform_device创建

 

platform_device的建立包含两种方式:

(1)在内核初始化时通过device_node转换为platform_device,这种是最新的实现方式,基于设备树,在内核初始化时将设备树中的节点转化为platform_device;

(2)使用platform_device_register注册platform_device;

1 关键数据结构体

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;
};
/**
 * struct device - The basic device structure
 * @parent:	The device's "parent" device, the device to which it is attached.
 * 		In most cases, a parent device is some sort of bus or host
 * 		controller. If parent is NULL, the device, is a top-level device,
 * 		which is not usually what you want.
 * @p:		Holds the private data of the driver core portions of the device.
 * 		See the comment of the struct device_private for detail.
 * @kobj:	A top-level, abstract class from which other classes are derived.
 * @init_name:	Initial name of the device.
 * @type:	The type of device.
 * 		This identifies the device type and carries type-specific
 * 		information.
 * @mutex:	Mutex to synchronize calls to its driver.
 * @bus:	Type of bus device is on.
 * @driver:	Which driver has allocated this
 * @platform_data: Platform data specific to the device.
 * 		Example: For devices on custom boards, as typical of embedded
 * 		and SOC based hardware, Linux often uses platform_data to point
 * 		to board-specific structures describing devices and how they
 * 		are wired.  That can include what ports are available, chip
 * 		variants, which GPIO pins act in what additional roles, and so
 * 		on.  This shrinks the "Board Support Packages" (BSPs) and
 * 		minimizes board-specific #ifdefs in drivers.
 * @driver_data: Private pointer for driver specific info.
 * @links:	Links to suppliers and consumers of this device.
 * @power:	For device power management.
 *		See Documentation/driver-api/pm/devices.rst for details.
 * @pm_domain:	Provide callbacks that are executed during system suspend,
 * 		hibernation, system resume and during runtime PM transitions
 * 		along with subsystem-level and driver-level callbacks.
 * @pins:	For device pin management.
 *		See Documentation/driver-api/pinctl.rst for details.
 * @msi_list:	Hosts MSI descriptors
 * @msi_domain: The generic MSI domain this device is using.
 * @numa_node:	NUMA node this device is close to.
 * @dma_ops:    DMA mapping operations for this device.
 * @dma_mask:	Dma mask (if dma'ble device).
 * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
 * 		hardware supports 64-bit addresses for consistent allocations
 * 		such descriptors.
 * @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA
 *		limit than the device itself supports.
 * @dma_pfn_offset: offset of DMA memory range relatively of RAM
 * @dma_parms:	A low level driver may set these to teach IOMMU code about
 * 		segment limitations.
 * @dma_pools:	Dma pools (if dma'ble device).
 * @dma_mem:	Internal for coherent mem override.
 * @cma_area:	Contiguous memory area for dma allocations
 * @archdata:	For arch-specific additions.
 * @of_node:	Associated device tree node.
 * @fwnode:	Associated device node supplied by platform firmware.
 * @devt:	For creating the sysfs "dev".
 * @id:		device instance
 * @devres_lock: Spinlock to protect the resource of the device.
 * @devres_head: The resources list of the device.
 * @knode_class: The node used to add the device to the class list.
 * @class:	The class of the device.
 * @groups:	Optional attribute groups.
 * @release:	Callback to free the device after all references have
 * 		gone away. This should be set by the allocator of the
 * 		device (i.e. the bus driver that discovered the device).
 * @iommu_group: IOMMU group the device belongs to.
 * @iommu_fwspec: IOMMU-specific properties supplied by firmware.
 *
 * @offline_disabled: If set, the device is permanently online.
 * @offline:	Set after successful invocation of bus type's .offline().
 * @of_node_reused: Set if the device-tree node is shared with an ancestor
 *              device.
 * @dma_coherent: this particular device is dma coherent, even if the
 *		architecture supports non-coherent devices.
 *
 * At the lowest level, every device in a Linux system is represented by an
 * instance of struct device. The device structure contains the information
 * that the device model core needs to model the system. Most subsystems,
 * however, track additional information about the devices they host. As a
 * result, it is rare for devices to be represented by bare device structures;
 * instead, that structure, like kobject structures, is usually embedded within
 * a higher-level representation of the device.
 */
struct device {
	struct device		*parent;

	struct device_private	*p;

	struct kobject kobj;
	const char		*init_name; /* initial name of the device */
	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 device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set_drvdata/dev_get_drvdata */
	struct dev_links_info	links;
	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
	const struct dma_map_ops *dma_ops;
	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. */
	u64		bus_dma_mask;	/* upstream dma_mask constraint */
	unsigned long	dma_pfn_offset;

	struct device_dma_parameters *dma_parms;

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

#ifdef CONFIG_DMA_DECLARE_COHERENT
	struct dma_coherent_mem	*dma_mem; /* internal for coherent mem
					     override */
#endif
#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 */
	struct fwnode_handle	*fwnode; /* firmware device node */

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

	spinlock_t		devres_lock;
	struct list_head	devres_head;

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

	void	(*release)(struct device *dev);
	struct iommu_group	*iommu_group;
	struct iommu_fwspec	*iommu_fwspec;

	bool			offline_disabled:1;
	bool			offline:1;
	bool			of_node_reused:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
    defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
	bool			dma_coherent:1;
#endif
};

TODO:整个数据结构框架??

 

2 内核启动时自动将device_node转换为platform_device

typedef u32 phandle;
typedef u32 ihandle;

struct property {
	char	*name;
	int	length;
	void	*value;
	struct property *next;
	unsigned long _flags;
	unsigned int unique_id;
	struct bin_attribute attr;
};

#if defined(CONFIG_SPARC)
struct of_irq_controller;
#endif

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
};

 

(1)of_platform_default_populate_init                

arch_initcall_sync(of_platform_default_populate_init);
#define arch_initcall_sync(fn)        __define_initcall(fn, 3s)
#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
#define ___define_initcall(fn, id, __sec) \
    static initcall_t __initcall_##fn##id __used \
        __attribute__((__section__(#__sec ".init"))) = fn;

宏展开:
    static initcall_t __initcall_of_platform_default_populate_init3s __used \
        __attribute__((__section__(".initcall3s.init"))) = of_platform_default_populate_init;       

内核编译时,会将of_platform_default_populate_init的地址存放在段".initcall3s.init"中,可以在arch/arm/kernel/vmlinux.lds中查找到这个段属性。
(2)分析内核运行时调用of_platform_default_populate_init

start_kernel	
	>>>rest_init();/* Do the rest non-__init'ed, we're now alive */
		>>>pid = kernel_thread(kernel_init, NULL, CLONE_FS);
		>>>kernel_init
			>>>kernel_init_freeable();
				>>>do_basic_setup();
					>>>do_initcalls();
						for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
							>>>do_initcall_level(level);
								for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
									>>>do_one_initcall(initcall_from_entry(fn));
									// 就是调用"arch_initcall_sync(fn)"中定义的fn函数

 
(3)分析of_platform_default_populate_init注册platform_device

driver/of/platform.c
of_platform_default_populate_init	
	of_platform_default_populate(NULL, NULL, NULL);/* Populate everything else. */
		of_platform_populate(root, of_default_bus_match_table, lookup,parent);
			for_each_child_of_node(root, child) {
				of_platform_bus_create(child, matches, lookup, parent, true);
					//节点必须包含compatible属性,才能创建platform_device
					if (strict && (!of_get_property(bus, "compatible", NULL))) {
						return 0;
					}
					//如果bus节点的compatile属性不吻合matches成表, 就不处理它的子节点
					dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
					if (!dev || !of_match_node(matches, bus))
						return 0;
					--------------------------------------------------------------------
					//如果bus节点的compatile属性吻合matches成表, 就处理它的子节点
					of_platform_device_create_pdata(bus, bus_id, platform_data, parent); 
					//处理它的子节点, of_platform_bus_create是一个递归调用	
					--------------------------------------------------------------------					
			}

通过以上分析总结:

a. 内核函数of_platform_default_populate_init, 遍历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"

注意:

转换完成后,此时,所有的platform_deivce仅仅是以链表的形式添加到platform bus中。

 

3 使用platform_device_register注册platform_device

内核自从引入dts机制后, platform_device_register已经不推荐使用,而是直接通过of_platform_default_populate_init完成platform_device的注册。

platform_device_register()添加device到内核最终调用的device_add函数。 Platform_device_add和device_add最主要的区别是多了一步insert_resource(p, r),即将platform资源(resource)添加进内核,由内核统一管理。

platform_device_register向platform bus注册设备(platform_device)。在此过程中,在系统寻找注册的设备(根据.name),找到后运行.probe进行初始化。

 

/drivers/base/platform.c
platform_device_register
    >>>device_initialize(&pdev->dev);
    >>>arch_setup_pdev_archdata(pdev);
    >>>platform_device_add(pdev);//这里在platform总线上挂设备
        >>>pdev->dev.bus = &platform_bus_type;	
	>>>for (i = 0; i < pdev->num_resources; i++) {
           insert_resource(p, r)
           }
           >>>device_add(&pdev->dev);
                >>>bus_probe_device(dev);
		    >>>device_initial_probe(dev);
			>>>__device_attach(dev, true);
			    >>ret = bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);
                                >>>__device_attach_driver
                                    >>>driver_match_device(drv, dev);
								>>>drv->bus->match ? drv->bus->match(dev, drv) : 1;//调用match函数匹配platform_driver
                                    >>>driver_probe_device(drv, dev)
                                        >>>ret = really_probe(dev, drv);
                                            >>>dev->driver = drv;   //设备挂接驱动
                                            >>>ret = drv->probe(dev);//调用驱动自己的probe函数
                >>>klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);	// 把 platform_device 放入 platform_bus_type的device链表中

在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值