Android7 Linux3.18设备休眠过程

在编写设备驱动的时候,我们会填写device_driver的pm方法, 这些方法是必须要有的,系统无法正常休眠,设置导致死机。驱动实现例如:

static const struct dev_pm_ops raydium_ts_pm_ops = {
    .suspend    = raydium_ts_suspend,
    .resume        = raydium_ts_resume,
};

static struct i2c_driver raydium_ts_driver = {
    .probe = raydium_ts_probe,
    .remove = raydium_ts_remove,
    .id_table = raydium_ts_id,
    .driver = {
        .name = RAYDIUM_NAME,
        .owner = THIS_MODULE,
        .of_match_table = raydium_match_table,
        .pm    = &raydium_ts_pm_ops,
    },
};

我们在device_driver->dev_pm_ops里填写了设备suspend和resume方法,这两个是设备被挂起到RAM是的PM.

应用层操作操作系统进入休眠时,应用会写入操作/sys/power/state节点,往节点写入字符串"mem"可是系统挂起到RAM.

设备挂起到RAM流程如下:

state_store ->  /kernel/kernel/power/main.c
  pm_suspend(state); -> /kernel/kernel/power/suspend.c
      enter_state(state);
        suspend_devices_and_enter(state); -> /kernel/kernel/power/suspend.c
      

详细看下suspend_devices_and_enter方法的实现:

int suspend_devices_and_enter(suspend_state_t state)
{
    error = platform_suspend_begin(state);
    
    error = dpm_suspend_start(PMSG_SUSPEND);
    if (error) {
		pr_err("PM: Some devices failed to suspend, or early wake event detected\n");
		log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected");
		goto Recover_platform;
	}
}

我们关注dpm_suspend_start方法。如果改方法执行失败,此处将打印:

<3>[  350.999928] PM: Some devices failed to suspend, or early wake event detected
<6>[  351.081086] PM: resume of devices complete after 81.136 msecs

提示suspend失败。我们进入dpm_suspend_start方法,就可以看到suspend失败的原因了。

/kernel/drivers/base/power/main.c
int dpm_suspend_start(pm_message_t state)
{
    int error;
    // 执行设备 ->prepare,然后将设备键入到dpm_prepared_list链表中
    error = dpm_prepare(state);
    
    error = dpm_suspend(state);
    return error;
}

dpm_prepare函数将从dpm_list链表中取出各个设备,这些设备是在设备注册是键入的。取出后调用设备的parpare回调方法并回调执行。最后将设备加入到dpm_prepared_list链表中。

int dpm_suspend(pm_message_t state)
{
    cpufreq_suspend();
    
    while (!list_empty(&dpm_prepared_list)) {
        struct device *dev = to_device(dpm_prepared_list.prev);
        
        error = device_suspend(dev); // 执行休眠
        
        if (!list_empty(&dev->power.entry))
            list_move(&dev->power.entry, &dpm_suspended_list);
        put_device(dev); // 设备使用引用计数
    }
}

dpm_suspend调用device_suspend继续休眠操作。休眠后,将对设备引用计数做自减。

static int __device_suspend(struct device *dev, pm_message_t state, bool async)
{

    pm_callback_t callback = NULL;
    struct timer_list timer;
    
    init_timer_on_stack(&timer);
	timer.expires = jiffies + HZ * 12;
	timer.function = dpm_drv_timeout;
	timer.data = (unsigned long)&data;
	add_timer(&timer);
	
	if (dev->pm_domain) {
		info = "power domain ";
		callback = pm_op(&dev->pm_domain->ops, state);
		goto Run;
	}

	if (dev->type && dev->type->pm) {
		info = "type ";
		callback = pm_op(dev->type->pm, state);
		goto Run;
	}

	if (dev->class) {
		if (dev->class->pm) {
			info = "class ";
			callback = pm_op(dev->class->pm, state);
			goto Run;
		} else if (dev->class->suspend) {
			pm_dev_dbg(dev, state, "legacy class ");
			error = legacy_suspend(dev, state, dev->class->suspend,
						"legacy class ");
			goto End;
		}
	}

	if (dev->bus) {
		if (dev->bus->pm) {
			info = "bus ";
			callback = pm_op(dev->bus->pm, state);
		} else if (dev->bus->suspend) {
			pm_dev_dbg(dev, state, "legacy bus ");
			error = legacy_suspend(dev, state, dev->bus->suspend,
						"legacy bus ");
			goto End;
		}
	}

 Run:
	if (!callback && dev->driver && dev->driver->pm) {
		info = "driver ";
		callback = pm_op(dev->driver->pm, state);
	}

	error = dpm_run_callback(callback, dev, state, info);
	...
}

此处按照pm_domain->class->bus->driver的优先级,取得pm.suspend操作。对于SPI,I2C设备,pm操作在bus, class都是没有实现的,最终调用到device_driver中的suspend中。也就是我们最开始填写的dev_pm_ops中的resume和suspend函数。设备挂起到RAM操作就结束了。

除了挂起到RAM,dev_pm_ops中还有runtime resume和runtime suspend,也就是运行时PM。运行PM和挂起到RAM时的PM不太一样,运行时PM是针对单个设备,系统在非睡眠状态的情况下,某个设备在空闲时进入运行时挂起状态,而在不空闲时执行运行时回复,使得设备进入正常工作状态。如此,这个设备在运行时会更省电。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值