kernel中device休眠唤醒顺序分析

804 阅读2分钟

写在前面

进行Linux驱动开发的同学们,应该会经历过调试休眠唤醒的方案。休眠唤醒机制的目的是为了让设备在不需要执行动作时,进入一种低功耗状态,可以在设备进入suspend(休眠)的时候,对其进行下电,在进入resume(唤醒)的时候,再对其进行上电操作。本文重点对于休眠唤醒过程中的device的执行顺序进行分析。

场景假设

假设有如下一场景: 自己添加的设备my_dev在唤醒的时候需要通过other_dev中的获取的数据,那么,系统必须确保唤醒时other_dev早于自己的设备my_dev

设备的休眠唤醒顺序

其实决定device休眠唤醒顺序的数据结构为dpm_list。向该链表中添加数据通过device_pm_add函数。 suspend的顺序为dpm_list链表的从尾到头的设备。 resume的顺序为dpm_list链表的从头到尾的设备。

类型1:通过device-tree添加设备

普遍来说,如果一个设备树节点包含compatible属性,那么其在开机过程中会自动转化为一个device。 假设我们在device-tree的"/"节点下,添加一个自己的结点xp_test_node1

rk_wlan: wireless-wlan {
        compatible = "wlan-platdata";
        rockchip,grf = <&grf>;
        wifi_chip_type = "ap6354";
        sdio_vref = <1800>;
        WIFI,host_wake_irq = <&gpio0 3 GPIO_ACTIVE_HIGH>; /* GPIO0_a3 */
        status = "okay";
};

rk_bluetooth: wireless-bluetooth {
        compatible = "bluetooth-platdata";
        clocks = <&rk808 1>;
        clock-names = "ext_clock";
        //wifi-bt-power-toggle;
        uart_rts_gpios = <&gpio2 19 GPIO_ACTIVE_LOW>; /* GPIO2_C3 */
        pinctrl-names = "default", "rts_gpio";
        pinctrl-0 = <&uart0_rts>;
        pinctrl-1 = <&uart0_gpios>;
        //BT,power_gpio  = <&gpio3 19 GPIO_ACTIVE_HIGH>; /* GPIOx_xx */
        BT,reset_gpio    = <&gpio0 9 GPIO_ACTIVE_HIGH>; /* GPIO0_B1 */
        BT,wake_gpio     = <&gpio2 26 GPIO_ACTIVE_HIGH>; /* GPIO2_D2 */
        BT,wake_host_irq = <&gpio0 4 GPIO_ACTIVE_HIGH>; /* GPIO0_A4 */
        status = "okay";
};

test-power {
        status = "okay";
};

/* my_dts_node */
xp_test_node1: xp_test_node1 {
        compatible = "timelessxp,test_dts_node";
        status = "ok";
};

我们在kernel/drivers/base/power/main.c中的device_pm_add函数中通过打调用栈的方式来定位这个过程。

void device_pm_add(struct device *dev)
{
        /* Skip PM setup/initialization. */
        if (device_pm_not_required(dev))
                return;

        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);
        pr_err("%s: %s->", __func__, dev_name(dev));
        if (strcmp(dev_name(dev), "xp_test_node1") == 0) {
                pr_err("%s: %s->", __func__, dev_name(dev));
                dump_stack();
        }
        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);
        dev->power.in_dpm_list = true;
        mutex_unlock(&dpm_list_mtx);
}

打印出的调用栈如下:

...
[    0.160441] device_pm_add: edp-panel->
[    0.160868] device_pm_add: adc-keys->
[    0.161293] device_pm_add: gpio-leds->
[    0.161713] device_pm_add: sdio-pwrseq->
[    0.162142] device_pm_add: wireless-wlan->
[    0.162600] device_pm_add: wireless-bluetooth->
[    0.163059] device_pm_add: xp_test_node1->
[    0.163569] device_pm_add: xp_test_node1->
[    0.163967] CPU: 5 PID: 1 Comm: swapper/0 Not tainted 4.19.111 #9
[    0.164939] Hardware name: FriendlyElec NanoPC-T4 (DT)
[    0.165431] Call trace:
[    0.165679] dump_backtrace+0x0/0x178
[    0.166032] show_stack+0x14/0x20
[    0.166360] dump_stack+0x94/0xb4
[    0.166683] device_pm_add+0x11c/0x128
[    0.167046] device_add+0x338/0x6d8
[    0.167387] of_device_add+0x5c/0x70
[    0.167731] of_platform_device_create_pdata+0xac/0x108
[    0.168232] of_platform_bus_create+0x13c/0x358
[    0.168667] of_platform_populate+0x74/0xd0
[    0.169073] of_platform_default_populate_init+0xac/0xc8
[    0.169584] do_one_initcall+0x48/0x240
[    0.169956] kernel_init_freeable+0x210/0x37c
[    0.170376] kernel_init+0x10/0x108
[    0.170713] ret_from_fork+0x10/0x18
...

由此可见,通过device-tree中添加的device,添加到dpm_list的顺序为:

of_platform_default_populate_init 
    -> of_platform_populate 
        -> of_platform_bus_create 
            -> of_platform_device_create_pdata 
                -> of_device_add 
                    -> device_add
                            -> device_pm_add

另外从日志中我们可以看到

[    0.162142] device_pm_add: wireless-wlan->
[    0.162600] device_pm_add: wireless-bluetooth->
[    0.163059] device_pm_add: xp_test_node1->

添加的顺序为我们在设备树中添加节点的顺序。