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)
更多推荐
设备树和Platform架构--5--platform_device创建
发布评论