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;
//为设备创建属性文件,属性文件有三处:
1.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是否注册有很大关系了
本篇到这里就结束了,如果有错误和理解不正确的地方敬请指出。