一、MTK电源管理组成
这里讲的电源管理驱动主要是SPM驱动,CPU idle驱动,CPU Hotplug驱动,CPU Frequency驱动等,代码主要集中在以下目录。
1:/home/ubuntu/Mediatek/code/kernel-3.18/drivers/misc/mediatek/base/power/
2:/home/ubuntu/Mediatek/code/kernel-3.18/drivers/base/power
3:/home/ubuntu/Mediatek/code/kernel-3.18/kernel/power/
4:/home/ubuntu/Mediatek/code/kernel-3.18/drivers/cpufreq/
5:/home/ubuntu/Mediatek/code/kernel-3.18/drivers/cpuidle/
二、Linux kernel Generic Power
1:Linux power初始化,首先使用pm_init初始化power manager工作队列,设置休眠镜像大小,创建attribute属性等。
/home/ubuntu/Mediatek/code/kernel-3.18/kernel/power/main.c
static struct attribute * g[] = {
&state_attr.attr,
#ifdef CONFIG_PM_TRACE
&pm_trace_attr.attr,
&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
&pm_async_attr.attr,
&wakeup_count_attr.attr,
#ifdef CONFIG_PM_AUTOSLEEP
&autosleep_attr.attr,
#endif
#ifdef CONFIG_PM_WAKELOCKS
&wake_lock_attr.attr,
&wake_unlock_attr.attr,
#endif
#ifdef CONFIG_PM_DEBUG
&pm_test_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP_DEBUG
&pm_print_times_attr.attr,
#endif
#endif
#ifdef CONFIG_FREEZER
&pm_freeze_timeout_attr.attr,
#endif
//CodeSwitching start
#if 1// HIGUA
&custom_changelogo_attr.attr,
#endif
//CodeSwitching end
NULL,
};
static struct attribute_group attr_group = {
.attrs = g,
};
struct workqueue_struct *pm_wq;
EXPORT_SYMBOL_GPL(pm_wq);
static int __init pm_start_workqueue(void)
{
pm_wq = alloc_workqueue("pm", WQ_FREEZABLE, 0);
return pm_wq ? 0 : -ENOMEM;
}
static int __init pm_init(void)
{
int error = pm_start_workqueue();
if (error)
return error;
hibernate_image_size_init();
hibernate_reserved_size_init();
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
error = sysfs_create_group(power_kobj, &attr_group);
if (error)
return error;
pm_print_times_init();
return pm_autosleep_init();
}
core_initcall(pm_init);
2:Android用户层通过按键按钮触发 ‘echo mem > /sys/power/state’进行休眠相关的一些列流程
用户层这个命令是通过上述命令进行触发,处理代码为
/home/ubuntu/Mediatek/code/kernel-3.18/kernel/power/main.c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state;
int error;
#ifdef CONFIG_MTK_HIBERNATION
char *p;
int len;
#endif
error = pm_autosleep_lock();
if (error)
return error;
if (pm_autosleep_state() > PM_SUSPEND_ON) {
error = -EBUSY;
goto out;
}
state = decode_state(buf, n);
#ifdef CONFIG_MTK_HIBERNATION
p = memchr(buf, '\n', n);
len = p ? p - buf : n;
if (len == 8 && !strncmp(buf, "hibabort", len)) {
hib_log("abort hibernation...\n");
error = mtk_hibernate_abort();
goto out;
}
#endif
pr_warn("[%s]: state = (%d)\n", __func__, state);
if (state < PM_SUSPEND_MAX) {
error = pm_suspend(state);
pr_warn("[%s]: pm_suspend() return (%d)\n", __func__, error);
} else if (state == PM_SUSPEND_MAX) {
#ifdef CONFIG_MTK_HIBERNATION
hib_log("trigger hibernation...\n");
if (!pre_hibernate()) {
error = 0;
error = mtk_hibernate();
}
#else /* !CONFIG_MTK_HIBERNATION */
error = hibernate();
#endif /* CONFIG_MTK_HIBERNATION */
} else {
error = -EINVAL;
}
out:
pm_autosleep_unlock();
return error ? error : n;
}
power_attr(state);
3:进入pm_suspend流程,进入enter_state处理,然后进行相关的suspend_syssync_enqueue,suspend_prepare,suspend_devices_and_enter。
代码如下
/**
* pm_suspend - Externally visible function for suspending the system.
* @state: System sleep state to enter.
*
* Check if the value of @state represents one of the supported states,
* execute enter_state() and update system suspend statistics.
*/
int pm_suspend(suspend_state_t state)
{
int error;
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
pm_suspend_marker("entry");
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else {
suspend_stats.success++;
}
pm_suspend_marker("exit");
return error;
}
EXPORT_SYMBOL(pm_suspend);
/**
* enter_state - Do common work needed to enter system sleep state.
* @state: System sleep state to enter.
*
* Make sure that no one else is trying to put the system into a sleep state.
* Fail if that's not the case. Otherwise, prepare for system suspend, make the
* system enter the given sleep state and clean up after wakeup.
*/
static int enter_state(suspend_state_t state)
{
int error;
trace_suspend_resume(TPS("suspend_enter"), state, true);
if (state == PM_SUSPEND_FREEZE) {
#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warning("PM: Unsupported test mode for freeze state,"
"please choose none/freezer/devices/platform.\n");
return -EAGAIN;
}
#endif
} else if (!valid_state(state)) {
return -EINVAL;
}
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
if (state == PM_SUSPEND_FREEZE)
freeze_begin();
trace_suspend_resume(TPS("sync_filesystems"), 0, true);
printk(KERN_INFO "PM: Syncing filesystems ... ");
#if MTK_SOLUTION
error = suspend_syssync_enqueue();
if (error) {
pr_err("sys_sync timeout.\n");
goto Unlock;
}
#else
sys_sync();
#endif
printk("done.\n");
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
error = suspend_prepare(state);
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
trace_suspend_resume(TPS("suspend_enter"), state, false);
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state);
pm_restore_gfp_mask();
Finish:
pr_debug("PM: Finishing wakeup.\n");
suspend_finish();
Unlock:
mutex_unlock(&pm_mutex);
return error;
}
三、suspend和resume流程
流程图如下所示:
在suspend中最终进入到syscore_suspend中最后调用到ops->suspend,反之在suspend流程在代码中同样suspend后执行逆向的过程
syscore_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
enable_nonboot_cpus();
Platform_wake:
platform_resume_noirq(state);
dpm_resume_noirq(PMSG_RESUME);
Platform_early_resume:
platform_resume_early(state);
Devices_early_resume:
dpm_resume_early(PMSG_RESUME);
Platform_finish:
platform_resume_finish(state);