概述
工作中分配了低功耗相关方面的内容,查了相关资料,linux更是有一套pm相关的子系统。
首先需要说明的是既然是低功耗,那么SoC就要支持相关低功耗。
如果要进入低功耗模式,有一种方式可以在用户空间通知内核空间,echo “stanby" > /sys/power/state至于字符串的内容,需要在/kernel/power/suspend.c去查看或者修改。
linux中/sys/中是内核空间数据结构的体现,功能和/proc是相同的,/sys/下文件夹的创建,
sysfs_create_group(power_kobj, &attr_group); 后面的那个参数就是,是之前注册的回调函数。
#define power_attr(_name)
static struct kobj_attribute _name##_attr = {
.attr = {
.name = __stringify(_name),
.mode = 0644,
},
.show = _name##_show,
.store = _name##_store,
}
最后还是会调用到state_store.
既然进入低功耗模式,控制台端口就需要关闭。调试的时候没有任何打印,当然就不知道现在的状态的,所以现在需要在.config中的CONFIG_CMDLINE后面加上no_console_suspend。这样就会有内核打印了。
各硬件外设在注册设备的时候如果有低功耗的功能的时候,会注册相应的suspend和resume的回调函数,但是仔细看一下,发现有好几个结构体的都有这两种回调函数,所以肯定会有调用的先后顺序,先把相应的结构体列举出来。
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
第三个回调函数是后来细化了低功耗相关的动作。
真正去调用这些回调函数是在该函数中:
/**
* dpm_suspend_start - Prepare devices for PM transition and suspend them.
* @state: PM transition of the system being carried out.
*
* Prepare all non-sysdev devices for system PM transition and execute "suspend"
* callbacks for them.
*/
int dpm_suspend_start(pm_message_t state)
{
int error;
error = dpm_prepare(state);
if (!error)
error = dpm_suspend(state);
return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);
这两个分别去调用回调prepare和suspend回调函数,里面的优先级就有很明确的定义。
if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "power domain ");
error = pm_op(dev, &dev->pwr_domain->ops, state);
goto End;
}
if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
goto End;
}
if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
error = pm_op(dev, dev->class->pm, state);
goto End;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend);
goto End;
}
}
if (dev->bus) {
if (dev->bus->pm) {
pm_dev_dbg(dev, state, "");
error = pm_op(dev, dev->bus->pm, state);
} else if (dev->bus->suspend) {
pm_dev_dbg(dev, state, "legacy ");
error = legacy_suspend(dev, state, dev->bus->suspend);
}
}
可见顺序是从小到大的,device->class->bus。
硬件设备进入休眠状态之后,需要让CPU进入休眠状态。
int suspend_enter(suspend_state_t state)
该函数的核心是
error = syscore_suspend();
if (!error) {
if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
error = suspend_ops->enter(state);
events_check_enabled = false;
}
syscore_resume();
}
而suspend_ops的回调函数,是各SoC厂商在arch/cpuxxx/SoCxxx/pm.c中调用函数
static const struct platform_suspend_ops xxx_pm_ops =
{
.valid = xxx_valid,
.enter = xxx_enter,
};
static int __init xxx_pm_init(void)
{
suspend_set_ops(&xxx_pm_ops);
return 0;
}
而在xxx_enter中主要是对SoC中power manager unit寄存器设置,包括设置中断唤醒源,是进入cpuoff模式还是plloff模式。
在完成该低功耗任务时,遇到的最大问题就是,板子上的wifi模块,每次suspend之后,被唤醒源唤醒之后,各个设备能够resume成功,但是唯独wifi模块6212,因为wifi模块是在sdio总线上的,在resume过程中,sdio出现了timeout的情况,跟了很久,发现是在wifi驱动在suspend的时候,注释了相关代码导致的,而实际上最后只需要在sd/mmc驱动中,增加一个mmc标志位就可以了,该情况充分证明了总线和挂载在总线上的紧密联系。
因为之前也弄了实时操作系统的低功耗,通过这两次,发现了,自己并没有对当时的现场保存下来,而我也不可能将全部的现场保存下来,我发现时,当我设置PMU单元进入低功耗模式之后,sdram的内容会被定格下来,也正是这个原因,所以不需要人工手动去保存现场,不然将又是一场浩大的工程。
最后
以上就是热情海燕为你收集整理的linux下pm子系统的全部内容,希望文章能够帮你解决linux下pm子系统所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复