platform_driver device_driver dts自动加载platform_device及休眠唤醒的关系

本文深入探讨了Linux内核中的platform总线机制,详细解释了platform_bus_init、platform_device_register和__platform_driver_register等关键函数的作用。文章还分析了设备与驱动之间的匹配过程,以及休眠唤醒机制在platform总线上的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

platform总线是我们驱动编程中经常用的一种,之间的大体关系如下:
在这里插入图片描述
1.platform_bus_init 
由start_kernel在加载各自init之前调用。主要就是注册一个bus,最终会添加到bus_kset链表中去,总线本身不会涉及到休眠唤醒,其休眠唤醒函数都是一个中间过程。为各自具体的设备驱动准备的。
2.platform_device_register
如上图描述所示,主要内部逻辑有下:
1):首先将dev添加到devices_kset链表中去
2):其次将dev添加到总线的devices链表上去
3):将设备添加到电源管理的dpm_list链表上去,为休眠唤醒作准备。通过这一块就是功耗的核心部分
3.__platform_driver_register
这个部分主要有:
1):将自己添加到总线的driver list链表上去
2):调用attach函数就自己与device绑定,并调用driver的probe函数

platform_driver device_driver及PM结构体关系图
在这里插入图片描述
dev_pm_pos还有比较多的回调函数,这里只罗列了几个。从上面可以看到platform_driver device_driver都有probe和suspend函数,pm_ops提供了我们不是太常用的运行时suspend函数。这几者之间的关系是什么,什么时候调用的,下面就从代码的角度来仔细分析下
下面来看一个大体的流程图:
在这里插入图片描述
下面来具体分析每个函数的代码

1.platform_bus_init

int __init platform_bus_init(void)
{
	int error;

	//清空一个链表,这根platform本身关系并不大,这里不用太关注
	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	of_platform_register_reconfig_notifier();
	return error;
}

platform_bus_init上面说了这个是由start_kernel拉起来的。主要是由两个函数调用组成其功能,下面就针对这两个函数具体分析

2.device_register

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}

先来看device_initialize函数:

//需要注意的是所有由platform接口添加的device,都会调用这个函数。后面添加设备时一样会调用。
void device_initialize(struct device *dev)
{
	//将kset指向全局变量devices_kset,devices_kset是一个链表。device本身某个成员kset会被添加到devices_kset的尾部
	dev->kobj.kset = devices_kset;

	//下面的一些必要的初始化,就不仔细再跟踪了,与本篇关注的没有多少关系。等某个时候用到了再去分析
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
#ifdef CONFIG_GENERIC_MSI_IRQ
	INIT_LIST_HEAD(&dev->msi_list);
#endif
}

3.device_add

这个函数是一个核心函数,这里重点来分析下

int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;

	//如果device没有初始化,则退出
	dev = get_device(dev);
	if (!dev)
		goto done;

	//如果成员变量 device_private *p为空,就为其分配内存,并初始化。如果分配内存失败则退出
	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;
	//kobject_add这个函数就是将dev->kobj 添加到 devices_kset链表的尾部,具体的代码如下:
	/*
	static void kobj_kset_join(struct kobject *kobj)
	{
		if (!kobj->kset)
			return;
	
		kset_get(kobj->kset);
		spin_lock(&kobj->kset->list_lock);
		//kobj->entry是在kobject_init时候初始化的,而kset也是在初始化的时候赋值为全局变量,通过entry可以获取kobj对象本身。所以这里就是将kobj添加到全局链表变量的尾部
		list_add_tail(&kobj->entry, &kobj->kset->list);
		spin_unlock(&kobj->kset->list_lock);
	}
	*/

	/* notify platform of device entry */
	//platform_notify这是一个回调函数,正常情况下并没有设置为NULL,如果我们有这个需要,只需要调用#include <linux/device.h>然后给它赋值即可如下:
	//platform_notify = acpi_platform_notify;
	if (platform_notify)
		platform_notify(dev);

	//为设备创建系统属性文件,具体的创建过程没太看明白。好像生成一个node然后添加到了平衡二叉树上,至于二叉树被访问和文件的创建地方没进一步研究了
	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;

	//在两个object之前创建符号链接
	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	//为设备创建属性文件,属性文件有三处:.class成员的属性组
	2.device_type成员的属性组
	3.device自己的属性组
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	//这个函数是针对有挂在bus总线下的device,platform本身就是bus不满足条件,对planform_device没什么用。但是对其它挂在platform总线上的设备就有用了,其最主要的目地,就是将device添加到Bus总线的成员klist_devices链表的尾部,后面driver在进行匹配时,拿device就是从这个链表拿的。添加的关键代码如下:
	//klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	//创建电源相关的属性组
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	//将设备添加到dpm_list链表上,当系统休眠的时候会从链表上一一获取device然后调用driver的suspend函数,后面会说到
	device_pm_add(dev);

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto DevAttrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto SysEntryError;

		devtmpfs_create_node(dev);
	}

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	//告诉注册的bus_notifier,添加了新设备了
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	//向上层发送uevent事件
	kobject_uevent(&dev->kobj, KOBJ_ADD);
	//如果bus开启了drivers_autoprobe这个标志,那么device会自动去找driver链表进行匹配,如果能匹配成功则相互绑定。并调用probe函数,所以device和driver并不一定是先注册device然后再注册driver,开启这个drivers_autoprobe标准就OK了,我们在注册platform总线的时候,这个标志是开启的。当然你也可以通过属性名为drivers_autoprobe,输入0来关闭这个标志
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;

4.device_pm_add

void device_pm_add(struct device *dev)
{
	pr_debug("PM: Adding info for %s:%s\n",
		 dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
	device_pm_check_callbacks(dev);
	mutex_lock(&dpm_list_mtx);
	if (dev->parent && dev->parent->power.is_prepared)
		dev_warn(dev, "parent %s should not be sleeping\n",
			dev_name(dev->parent));
	list_add_tail(&dev->power.entry, &dpm_list);
	mutex_unlock(&dpm_list_mtx);
}

void device_pm_check_callbacks(struct device *dev)
{
	spin_lock_irq(&dev->power.lock);
	dev->power.no_pm_callbacks =
		(!dev->bus || pm_ops_is_empty(dev->bus->pm)) &&
		(!dev->class || pm_ops_is_empty(dev->class->pm)) &&
		(!dev->type || pm_ops_is_empty(dev->type->pm)) &&
		(!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
		(!dev->driver || pm_ops_is_empty(dev->driver->pm));
	spin_unlock_irq(&dev->power.lock);
}

这个函数上面已经介绍了,这里把代码贴出来。更容易理解,基本上所有的device都会加入到dpm_list链表上。但加上去了不代表一定能操作参与休眠过程,需要满足上面第二个函数no_pm_callbacks不为真。这里至少可以看出platform_device这个设备肯定不会参与休眠了

5.platform_device_register

int platform_device_register(struct platform_device *pdev)
{
	//这个初始化函数上面已经说过了
	device_initialize(&pdev->dev);
	//这是一个__weak标志的空函数,如果谁有兴趣,可以定义一个同名函数。调用的时候就会调用你创建的同名(同参数)函数
	arch_setup_pdev_archdata(pdev);
	//下面分析这个函数
	return platform_device_add(pdev);
}

6.platform_device_add

int platform_device_add(struct platform_device *pdev)
{
	int i, ret;

	if (!pdev)
		return -EINVAL;

	if (!pdev->dev.parent)
		pdev->dev.parent = &platform_bus;

	//这里bus指向了platform,为后面的休眠及设备的添加提供了依据
	pdev->dev.bus = &platform_bus_type;

	switch (pdev->id) {
	default:
		dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
		break;
	case PLATFORM_DEVID_NONE:
		dev_set_name(&pdev->dev, "%s", pdev->name);
		break;
	case PLATFORM_DEVID_AUTO:
		/*
		 * Automatically allocated device ID. We mark it as such so
		 * that we remember it must be freed, and we append a suffix
		 * to avoid namespace collision with explicit IDs.
		 */
		ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
		if (ret < 0)
			goto err_out;
		pdev->id = ret;
		pdev->id_auto = true;
		dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
		break;
	}

	for (i = 0; i < pdev->num_resources; i++) {
		struct resource *p, *r = &pdev->resource[i];

		if (r->name == NULL)
			r->name = dev_name(&pdev->dev);

		p = r->parent;
		if (!p) {
			if (resource_type(r) == IORESOURCE_MEM)
				p = &iomem_resource;
			else if (resource_type(r) == IORESOURCE_IO)
				p = &ioport_resource;
		}

		//将资源添加到资源树上
		if (p && insert_resource(p, r)) {
			dev_err(&pdev->dev, "failed to claim resource %d\n", i);
			ret = -EBUSY;
			goto failed;
		}
	}

	pr_debug("Registering platform device '%s'. Parent at %s\n",
		 dev_name(&pdev->dev), dev_name(pdev->dev.parent));

	//核心的一步,上面已经介绍了这个函数
	ret = device_add(&pdev->dev);
	if (ret == 0)
		return ret;

 failed:
	if (pdev->id_auto) {
		ida_simple_remove(&platform_devid_ida, pdev->id);
		pdev->id = PLATFORM_DEVID_AUTO;
	}

	while (--i >= 0) {
		struct resource *r = &pdev->resource[i];
		if (r->parent)
			release_resource(r);
	}

 err_out:
	return ret;
}

这个函数主要作用有如下几点:
1.将bus指向platform
2.添加资源好父类的资源树上
3.调用device_add函数添加设备

接下来看看驱动注册

7.__platform_driver_register

int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	drv->driver.probe = platform_drv_probe;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

代码很简洁,初一看来。这个函数就是操作device_driver的,跟platform_driver 没撒关系,就是用到了成员device_driver而已。实际上跟踪代码可以得知道,platform_driver 的几个回调函数实际上都是在platform_drv_xxx里面调用的,这里提供了一个封装下面来看看driver_register

8.driver_register

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	//从drivers_kset链表中查找所有驱动,看是否有同名驱动如果有。代表访驱动已经存在退出。这里是从drivers_kset寻找,不是从klist_drivers。实际上我认为从这两处地方都能获得所有的drvier
	other = driver_find(drv->name, drv->bus);
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}
	
	//下面具体来分析这个函数
	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}

9.bus_add_driver

int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	bus = bus_get(drv->bus);
	//如果不是挂下bus下面,这个函数就不会起作用。比如platform bus device
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	//将这个device私有的数据的kset指向bus的drivers_ket
	priv->kobj.kset = bus->p->drivers_kset;
	//这里添加kobject事实上把自己添加到了drivers_kset上面,所以上面可以从上面查找是否驱动已经存在
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;
	
	//将自己添加到链表klist_drivers上,所以可以通过遍历klist_drivers和driver_ktype获得所有添加的driver
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	//如果开启了drivers_autoprobe属性(事实上,初始化的时候已经开启)
	if (drv->bus->p->drivers_autoprobe) {
		if (driver_allows_async_probing(drv)) {
			pr_debug("bus: '%s': probing driver %s asynchronously\n",
				drv->bus->name, drv->name);
			//这个函数实现事实上与driver_attach是一模一样的,差异就是这里是异步的,而下面else的调用是同步的。后面会具体分析driver_attach函数实现
			async_schedule(driver_attach_async, drv);
		} else {
			error = driver_attach(drv);
			if (error)
				goto out_unregister;
		}
	}
	//文件系统添加驱动模块,会创建对应的文件节点
	module_add_driver(drv->owner, drv);

	error = driver_create_file(drv, &driver_attr_uevent);
	if (error) {
		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
			__func__, drv->name);
	}
	error = driver_add_groups(drv, bus->drv_groups);
	if (error) {
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
			__func__, drv->name);
	}

	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
		if (error) {
			/* Ditto */
			printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
				__func__, drv->name);
		}
	}

	return 0;

10.driver_attach

int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start,
		     void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus || !bus->p)
		return -EINVAL;
	
	//前面说过所有清册的device,都会添加到klist_devices上,所以遍历klist_devices就能获取所有的device。这里的代码意思就是说,遍历每个dev,然后调用回调函数fn,如果返回值不为0代表找到了,退出循环。接下来看看回调函数__driver_attach的实现
	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

11.__driver_attach

static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;
	if (!driver_match_device(drv, dev))
		return 0;

	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver)
		driver_probe_device(drv, dev);
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}

首先大体的看一眼,发现无论什么情况返回值都是0,所以第10步那个while循环不会中途退出了,所有会存在一个driver绑定了多个device 的情况。也就是一个probe会被执行多次,这种情况在hisi平台上我第一次碰到。这个函数核心就是driver_match_device,看函数定义

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

这就是调用bus的match函数来匹配,bus match函数实现这里也贴下

static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
		return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	/* Then try to match against the id table */
	if (pdrv->id_table)
		return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */
	return (strcmp(pdev->name, drv->name) == 0);
}

总体来说匹配就是字符串比较,具体的可以看看
https://blog.youkuaiyun.com/bruk_spp/article/details/130501199?spm=1001.2014.3001.5501
当匹配成功后,如何此device设备没有绑定过driver,那么就会调用driver_probe_device函数来和driver相互绑定,并调用dirver的probe函数,下面来看看代码实现

12.driver_probe_device

int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	if (dev->parent)
		pm_runtime_get_sync(dev->parent);
	
	//字面意思是设置内存屏障
	pm_runtime_barrier(dev);
	//真正实现目地的地方,下面来具体分析下
	ret = really_probe(dev, drv);
	//pm运行时休眠,告诉pm进行idle状态。后面会说到runtime
	pm_request_idle(dev);

	if (dev->parent)
		pm_runtime_put(dev->parent);

	return ret;
}

13.really_probe

static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = -EPROBE_DEFER;
	int local_trigger_count = atomic_read(&deferred_trigger_count);

	//如果打开了defer_all_probes,那么会将设备挂到deferred_probe_pending_list链表的尾部,在某个合适的时候一一加载,避免竞争出问题
	if (defer_all_probes) {
		/*
		 * Value of defer_all_probes can be set only by
		 * device_defer_all_probes_enable() which, in turn, will call
		 * wait_for_device_probe() right after that to avoid any races.
		 */
		dev_dbg(dev, "Driver %s force probe deferral\n", drv->name);
		driver_deferred_probe_add(dev);
		return ret;
	}

	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));

	//将device与driver相互绑定
	dev->driver = drv;

	/* If using pinctrl, bind pins now before probing */
	ret = pinctrl_bind_pins(dev);
	if (ret)
		goto pinctrl_bind_failed;

	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	if (dev->pm_domain && dev->pm_domain->activate) {
		ret = dev->pm_domain->activate(dev);
		if (ret)
			goto probe_failed;
	}

	/*
	 * Ensure devices are listed in devices_kset in correct order
	 * It's important to move Dev to the end of devices_kset before
	 * calling .probe, because it could be recursive and parent Dev
	 * should always go first
	 */
	devices_kset_move_last(dev);

	//这里将执行probe函数,在platform_bus_type这个定义里面,并没有定义probe函数。所以dev->bus->probe是为空的,走的是else分支,而drv->probe这个在驱动注册的时候已经指向了platform的platform_drv_probe函数,这里再贴下初始化代码
	/*
	int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	**drv->driver.probe = platform_drv_probe**;
	drv->driver.remove = platform_drv_remove;
	drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}
	*/
	既然这里实际上调用的是platform_drv_probe函数,那么下面就具体来看看这个函数的实现,因为到目前为止还没有调用到我们自己定义的platform_driver结构体中的probe回调函数
	if (dev->bus->probe) {
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	pinctrl_init_done(dev);

	if (dev->pm_domain && dev->pm_domain->sync)
		dev->pm_domain->sync(dev);

	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

14.platform_drv_probe

static int platform_drv_probe(struct device *_dev)
{
	struct platform_driver *drv = to_platform_driver(_dev->driver);
	struct platform_device *dev = to_platform_device(_dev);
	int ret;

	ret = of_clk_set_defaults(_dev->of_node, false);
	if (ret < 0)
		return ret;

	ret = dev_pm_domain_attach(_dev, true);
	//这里才是调用到我们自己定义的probe函数
	if (ret != -EPROBE_DEFER && drv->probe) {
		ret = drv->probe(dev);
		if (ret)
			dev_pm_domain_detach(_dev, true);
	}

	if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
		dev_warn(_dev, "probe deferral not supported\n");
		ret = -ENXIO;
	}

	return ret;
}

到这里为止,基本上platform驱动的加载就完成了。下面来说下休眠与platform之前的关系,我原本以为休眠的时候,会从platform driver链表上遍历所有driver然后调用其suspend和resume来实现器件的休眠唤醒,实际上是有点出入的。下面为看看休眠唤醒,这里又分两种:
1.是系统的休眠唤醒
2.运行时的休眠唤醒
先来说是系统级别的休眠唤醒

15.suspend_devices_and_enter

int suspend_devices_and_enter(suspend_state_t state)
{
	int error;
	bool wakeup = false;

	if (!sleep_state_supported(state))
		return -ENOSYS;

	//这是平台级别的suspend调用,与我们的platform总线器件的休眠无关不用关心
	error = platform_suspend_begin(state);
	if (error)
		goto Close;

	suspend_console();
	suspend_test_start();
	//这里就正式进入platform总线上的器件suspedn调用了,要注意的是这晨传入的参数是什么,会根据这个值来指定回调函数是哪个
	error = dpm_suspend_start(PMSG_SUSPEND);
	if (error) {
		pr_err("PM: Some devices failed to suspend, or early wake event detected\n");
		goto Recover_platform;
	}
	suspend_test_finish("suspend devices");

16.dpm_suspend_start

int dpm_suspend_start(pm_message_t state)
{
	int error;

	//dpm_prepare :遍历dpm_list链表上的所有设备,调用其prepare函数(如果有)。然后将该设备从dpm_list上移除,添加到dpm_prepared_list链表上,prepare寻找顺序如下:
/*		if (dev->pm_domain) {
		info = "preparing power domain ";
		callback = dev->pm_domain->ops.prepare;
	} else if (dev->type && dev->type->pm) {
		info = "preparing type ";
		callback = dev->type->pm->prepare;
	} else if (dev->class && dev->class->pm) {
		info = "preparing class ";
		callback = dev->class->pm->prepare;
	} else if (dev->bus && dev->bus->pm) {
		info = "preparing bus ";
		callback = dev->bus->pm->prepare;
	}

	if (!callback && dev->driver && dev->driver->pm) {
		info = "preparing driver ";
		callback = dev->driver->pm->prepare;
	}*/
	error = dpm_prepare(state);
	if (error) {
		suspend_stats.failed_prepare++;
		dpm_save_failed_step(SUSPEND_PREPARE);
	} else
		//下面来具体看看这个函数
		error = dpm_suspend(state);
	return error;
}

17.dpm_suspend

int dpm_suspend(pm_message_t state)
{
	ktime_t starttime = ktime_get();
	int error = 0;

	trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
	might_sleep();

	cpufreq_suspend();

	mutex_lock(&dpm_list_mtx);
	pm_transition = state;
	async_error = 0;
	//遍历dpm_prepared_list,对每个设备进行suspend操作
	while (!list_empty(&dpm_prepared_list)) {
		struct device *dev = to_device(dpm_prepared_list.prev);

		get_device(dev);
		mutex_unlock(&dpm_list_mtx);

		error = device_suspend(dev);

		mutex_lock(&dpm_list_mtx);
		if (error) {
			pm_dev_err(dev, state, "", error);
			dpm_save_failed_dev(dev_name(dev));
			put_device(dev);
			break;
		}
		//操作完成后,将device从当前链表移除,然后添加到dpm_suspended_list链表上
		if (!list_empty(&dev->power.entry))
			list_move(&dev->power.entry, &dpm_suspended_list);
		put_device(dev);
		if (async_error)
			break;
	}
	mutex_unlock(&dpm_list_mtx);
	async_synchronize_full();
	if (!error)
		error = async_error;
	if (error) {
		suspend_stats.failed_suspend++;
		dpm_save_failed_step(SUSPEND_SUSPEND);
	} else
		dpm_show_time(starttime, state, NULL);
	trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
	return error;
}

18.device_suspend

static int device_suspend(struct device *dev)
{
	reinit_completion(&dev->power.completion);

	//这里的函数就是进行具体的操作了,下面流程有分支。主要是同步操作还是异步操作,实际上操作的内容是一样的
	if (is_async(dev)) {
		get_device(dev);
		async_schedule(async_suspend, dev);
		return 0;
	}

	return __device_suspend(dev, pm_transition, false);
}

__device_suspend这个函数就不具体介绍了,里面的代码比较多。大体意思就是根据我们最开始传进来的状态值,选择具体的回调函数,然后作回调操作下面罗列下对应在选择关系

static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
{
	switch (state.event) {
#ifdef CONFIG_SUSPEND
	case PM_EVENT_SUSPEND:
		return ops->suspend;
	case PM_EVENT_RESUME:
		return ops->resume;
#endif /* CONFIG_SUSPEND */
#ifdef CONFIG_HIBERNATE_CALLBACKS
	case PM_EVENT_FREEZE:
	case PM_EVENT_QUIESCE:
		return ops->freeze;
	case PM_EVENT_HIBERNATE:
		return ops->poweroff;
	case PM_EVENT_THAW:
	case PM_EVENT_RECOVER:
		return ops->thaw;
		break;
	case PM_EVENT_RESTORE:
		return ops->restore;
#endif /* CONFIG_HIBERNATE_CALLBACKS */
	}

	return NULL;
}

这里对suspend来说,就是调用了platform总线的suspend函数。现在再来回顾下platform总线的具体定义:

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
static const struct dev_pm_ops platform_dev_pm_ops = {
	.runtime_suspend = pm_generic_runtime_suspend,
	.runtime_resume = pm_generic_runtime_resume,
	USE_PLATFORM_PM_SLEEP_OPS
};
#ifdef CONFIG_PM_SLEEP
#define USE_PLATFORM_PM_SLEEP_OPS \
	.suspend = platform_pm_suspend, \
	.resume = platform_pm_resume, \
	.freeze = platform_pm_freeze, \
	.thaw = platform_pm_thaw, \
	.poweroff = platform_pm_poweroff, \
	.restore = platform_pm_restore,
#else
#define USE_PLATFORM_PM_SLEEP_OPS
#endif

事实上就是调用了platform_pm_suspend这个函数。下面就来分析下这个函数

19.platform_pm_suspend

int platform_pm_suspend(struct device *dev)
{
	struct device_driver *drv = dev->driver;
	int ret = 0;

	if (!drv)
		return 0;

	if (drv->pm) {
		//首先看device_driver里面有没有定义PM,如果有就会尝试去调用PM的suspend函数,要注意的是这里是if else分支。所以如果我们同时定义了pm结构体和device_driver的suspend函数,只会调用尝试调用pm里面的回调函数
		if (drv->pm->suspend)
			ret = drv->pm->suspend(dev);
	} else {
		//这下函数的实现也在下面贴出来了,可以看到这个函数最终调用到了我们定义的platform_driver里面的suspend函数
		ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
	}

	return ret;
}

static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
{
	struct platform_driver *pdrv = to_platform_driver(dev->driver);
	struct platform_device *pdev = to_platform_device(dev);
	int ret = 0;

	if (dev->driver && pdrv->suspend)
		ret = pdrv->suspend(pdev, mesg);

	return ret;
}


到这里系统休眠就结束了,系统唤醒也是一个相同的过程。会从suspend_list链表上对所有设备进行resume操作,这里就不具体介绍了。下面来简单介绍下运行时休眠唤醒

20.device运行时休眠唤醒

系统的休眠唤醒,一般是用户按下power键后才会调用的。这个期间时间会比较长,如果要在系统运行的过程中对某些确定不会使用的device进行低功耗处理,系统级的休眠满足不了。这里就来了一个运行时候的休眠唤醒机制,这里需要注意的是系统级的休眠唤醒由系统来实现,我们只要实现相关的接口就可以了,而运行时的休眠唤醒由我们自己控制系统只是提供接口,现在再来回顾下开始的结构体图
在这里插入图片描述
所以如果我们想进行运行时的休眠唤醒操作,只需要实现device_driver下面的pm成员就好了,下面举个例子:

static struct platform_driver omapdss_hdmihw_driver = {
	.probe		= hdmi5_probe,
	.remove		= hdmi5_remove,
	.driver         = {
		.name   = "omapdss_hdmi5",
		.pm	= &hdmi_pm_ops,
		.of_match_table = hdmi_of_match,
		.suppress_bind_attrs = true,
	},
};

我们在pm结构体内需要实现
runtime_resume runtime_suspend runtime_idle三个回调函数,其中idle可要可不要
我们可以使用SET_RUNTIME_PM_OPS宏定义来快速实现

#ifdef CONFIG_PM
#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) \
	.runtime_suspend = suspend_fn, \
	.runtime_resume = resume_fn, \
	.runtime_idle = idle_fn,
#else
#define SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn)
#endif

定义了函数实现,那么就要在合适的地方调用。我们通过系统提供的API来调用,系统提供了大量的API,定义在:include/linux/Pm_runtime.h里面
我们一般情况下通过pm_runtime_put来使dev 进入suspend,调用过程如下:
pm_runtime_put->runtime_idle->runtime->suspend
通过pm_runtime_get来唤醒device,调用过程如下 :
pm_runtime_get->runtime_resume

当然如果我们只定义了运行时的resume和suspend,而没有定义idle就不能使用pm_runtime_put使device进入休眠了,可以通过pm_request_autosuspend函数直接调用到运行时的suspend函数。

最后说下:Pm_runtime.h提供了大量运行时的调用API,大体上可以分成同步和异步两种。我们上面的操作都是异步操作。在使用的时候可以参考下别的同异步的具体差异资料,还选择合适我们设备的运行时调用方式

在plat_form初始化完成后,会自动加载设备树中定义了
代码位置:arch/arm64/kernel/setup.c
在这里插入图片描述
这里就是加载dts到platform总线上,所有我们调用的init_call不应该早于arch_initcall_sync。现在来看看具体的代码。
1.of_have_populated_dt
我们解析的DTS全都放在of_root 这个device_node结构体里,所以这个函数就是判断我们有没有使用设备树
2.of_iommu_init
这个是主要是对应 IOMMU_OF_DECLARE 这个宏定义的代码,从代码段中读取所有的结构体成员,然后与device_node一一对比,然后调用其回调函数
3.of_platform_populate
这个函数就是具体的加载device_node到platform_device链表上

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);
...

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))) {
		pr_debug("%s() - skipping %s, no compatible prop\n",
			 __func__, bus->full_name);
		return 0;
	}

	auxdata = of_dev_lookup(lookup, bus);
	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);
	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);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(bus, OF_POPULATED_BUS);
	return rc;

上面的代码很清楚,主要意图是:
1.从链表的开始处,查找所有含有compatible的device_node,如果有找到则调用函数of_platform_device_create_pdata,将这个device_node注册到platform_device上。顺序如下:
1).调用of_device_alloc创建一个device
2).将其bus成员指向platform_bus_type,代表其挂载到platform总线上
3).调用of_device_add进行device的添加
2.对这个device_node字节点进遍历
plaftform device的添加上面已经说的比较详细了,这里添加device最终调用的接口与platform_device_register是一样的。

所以在看驱动代码的时候,你会发现很多platform_device根本没看到注册的地方,但是其probe函数就能调用,或者你注册了driver但是probe就是没有执行。这就跟device是否注册有很大关系了

本篇到这里就结束了,如果有错误和理解不正确的地方敬请指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bruk_spp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值