Power Management

原创 2013年12月03日 21:53:14
Power Management
迫切的想知道power management 的流程。
对整个流程稍微整理了下
参考这个链接http://blog.csdn.net/suntao222/article/details/8162938
还有这个链接;http://blog.csdn.net/tommy_wxie/article/details/7208633


1、autosleep  流程
上层操作底层的/sys/power/autosleep 这个接口进行操作底层。在main .c 里面会创建一个autosleep 接口,对应main.c 里面的store 函数。
所对应的source code 在:/kernel/kernel/main.c 里面

static ssize_t autosleep_store(struct kobject *kobj,
                   struct kobj_attribute *attr,
                   const char *buf, size_t n)
{
    suspend_state_t state = decode_state(buf, n);
    int error;


    if (state == PM_SUSPEND_ON
        && strcmp(buf, "off") && strcmp(buf, "off\n"))
        return -EINVAL;

    error = pm_autosleep_set_state(state);
    old_state=state;
    return error ? error : n;
}



当底层接受到上层传递到的值进行一些列的操作,有很多的state 状态:
#define PM_SUSPEND_ON        ((__force suspend_state_t) 0)
#define PM_SUSPEND_STANDBY    ((__force suspend_state_t) 1)
#define PM_SUSPEND_MEM        ((__force suspend_state_t) 3)
#define PM_SUSPEND_MAX        ((__force suspend_state_t) 4)
当底层接受到上层的state 之后,主要是调用pm_autosleep_set_state   这个函数进行power management 的管理。
它的实现如下:
int pm_autosleep_set_state(suspend_state_t state)
{

#ifndef CONFIG_HIBERNATION
    if (state >= PM_SUSPEND_MAX)
        return -EINVAL;
#endif

    __pm_stay_awake(autosleep_ws);

    mutex_lock(&autosleep_lock);

    autosleep_state = state;

    __pm_relax(autosleep_ws);

    if (state > PM_SUSPEND_ON) {
        pm_wakep_autosleep_enabled(true);

        //Add a timer to trigger wakelock debug
        pr_info("[PM]unattended_timer: mod_timer (auto_sleep)\n");
        mod_timer(&unattended_timer, jiffies + msecs_to_jiffies(PM_UNATTENDED_TIMEOUT));

        queue_up_suspend_work();
    } else {
        pm_wakep_autosleep_enabled(false);
        //Add a timer to trigger wakelock debug
        pr_info("[PM]unattended_timer: del_timer (late_resume)\n");
        del_timer(&unattended_timer);
    }

    mutex_unlock(&autosleep_lock);
    return 0;
}


调用的流程:
1、  pm_wakep_autosleep_enabled   设置所注册的wake source 的auto sleep 全部设置成1,ws->autosleep_enabled = set;
2、调用queue_up_suspend_work();  让设备进入suspend 状态。
static DECLARE_WORK(suspend_work, try_to_suspend);
调用try_to_suspend  函数。

pm_wakep_autosleep_enabled  这个函数还是很重要的,我们在下面的wake_lock 章节会详细讲解。


在底层里面的用autosleep_state  这个全部变量去保存目前的suspend 的状态。

    if (autosleep_state >= PM_SUSPEND_MAX) 
        hibernate();  //进入冬眠模式
    else
        pm_suspend(autosleep_state); 进入suspend 的模式
我们主要看下pm_suspend 函数的实现:

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(state);函数进入所要进入的状态。

主要调用下面两个函数:
    error = suspend_prepare
        分配console ,发送PM_SUSPEND_PREPARE这个notify 事件。最后还有的是调用suspend_freeze_processes  冻结所有的进程。
    error = suspend_devices_and_enter(state);
函数实现如下:

int suspend_devices_and_enter(suspend_state_t state)
{
    int error;
    bool wakeup = false;

    if (!suspend_ops)
        return -ENOSYS;

    trace_machine_suspend(state);
    if (suspend_ops->begin) {//判断suspend_ops 里面有没有begin 函数,关于suspend_ops 的赋值,我们会单独有一个标题讲解
        error = suspend_ops->begin(state);
        if (error)
            goto Close;
    }

    //Add a timer to trigger wakelock debug
    pr_info("[PM]unattended_timer: del_timer\n");
    del_timer ( &unattended_timer );

    suspend_console();//console 进行冬眠
    suspend_test_start();
    error = dpm_suspend_start(PMSG_SUSPEND);
    if (error) {
        printk(KERN_ERR "PM: Some devices failed to suspend\n");
        goto Recover_platform;
    }
    suspend_test_finish("suspend devices");
    if (suspend_test(TEST_DEVICES))
        goto Recover_platform;

    do {
        error = suspend_enter(state, &wakeup);
    } while (!error && !wakeup
        && suspend_ops->suspend_again && suspend_ops->suspend_again());

 Resume_devices:
    suspend_test_start();
    dpm_resume_end(PMSG_RESUME);
    suspend_test_finish("resume devices");
    resume_console();

    //Add a timer to trigger wakelock debug
    pr_info("[PM]unattended_timer: mod_timer\n");
    mod_timer(&unattended_timer, jiffies + msecs_to_jiffies(PM_UNATTENDED_TIMEOUT));

 Close:
    if (suspend_ops->end)
        suspend_ops->end();
    trace_machine_suspend(PWR_EVENT_EXIT);
    return error;

 Recover_platform:
    if (suspend_ops->recover)
        suspend_ops->recover();
    goto Resume_devices;
}



suspend_console();  //冻结终端
suspend_test_start//打出suspend 开始的时间

其中dpm_suspend_start  试下如下:
int dpm_suspend_start(pm_message_t state)
{
    int error;

    error = dpm_prepare(state);  为设备准备链表
    if (error) {
        suspend_stats.failed_prepare++;
        dpm_save_failed_step(SUSPEND_PREPARE);
    } else
        error = dpm_suspend(state);将准备的设备进入suspend
    return error;
}
EXPORT_SYMBOL_GPL(dpm_suspend_start);
在dpm_suspendl里面,我们会将while (!list_empty(&dpm_list)) {
        struct device *dev = to_device(dpm_list.next);  dpm_list 里面所有的设备都遍历出来,
最终哦将遍历出来的设备添加到另外一个链表里面去:list_move_tail(&dev->power.entry, &dpm_prepared_list.至于dpm_list 是如何来的,我们在另外一个章节进行讲解。

接下来dpm_suspend(state);  进行suspend 设备。
dpm_suspend 的函数实现如下:

int dpm_suspend(pm_message_t state)
{
    ktime_t starttime = ktime_get();
    int error = 0;

    might_sleep();

    mutex_lock(&dpm_list_mtx);
    pm_transition = state;
    async_error = 0;
    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;
        }
        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);
    return error;
}
然后调用device_suspend 函数
-----》__device_suspend  ------》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);
            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);
            goto End;
        }
    }

调用pm_op函数调用设备、class、 bus 等的suspend函数,同样的resume 函数也是一样的流程。
 
dpm_suspend_start  函数执行完了之后。我们再次回到suspend_devices_and_enter 函数中来。



    do {
        error = suspend_enter(state, &wakeup);
    } while (!error && !wakeup
        && suspend_ops->suspend_again && suspend_ops->suspend_again());

这里是一个死循环。只有wakeup  为1 的时候,我们才会调出这个循环。
我们设备的suspend的时候有很多的操作,每一个操作都放在不同的链表里面,然后我们根据不同suspend的类型,进行不同的操作。
举例如下:
    error = dpm_suspend_start(PMSG_SUSPEND);

    error = dpm_prepare(state);  会调用到to_device(dpm_list.next);  这个链表 ,这个链表是我们在创建device 的时候,我们所有创建设备的power 控制的结点。遍历过dpm_list 的链表后,我们将设备放到dpm_prepared_list 链表里面,再进行dpm_prepared_list 这个链表的操作。下面的几种状态也是同样的效果,只是不同的操作罢了。

2、设备的注册
我们在注册一个设备的时候,通常会调用device_register  进行设备的注册。下面设备的流程,我们只care power management 部分,别的细节暂时不care 。

整个设备注册的流程:

    return device_add(dev);  ----》
    device_pm_add(dev);-------》

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));
    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);
    dev_pm_qos_constraints_init(dev);
    mutex_unlock(&dpm_list_mtx);
}


上面的注册会将设备添加到dpm_list里面。  list_add_tail(&dev->power.entry, &dpm_list);
3、suspend_ops  的操作

在整个power management 里面经常会调用到suspend_ops  这个操作集合里面提供的interface 进行
suspend_ops  是存放在kerel/kernel/suspend.c 里面的全部变量。
suspend_set_ops  这个函数会为suspend_set_ops  这个变量进行赋值。
void suspend_set_ops(const struct platform_suspend_ops *ops)
{
    lock_system_sleep();
    suspend_ops = ops;
    unlock_system_sleep();
}

suspend_set_ops 是在/kernel/arch/arm/mach-msm/pm-8x60.c 里面。

static int __init msm_pm_init(void)
{
    enum msm_pm_time_stats_id enable_stats[] = {
        MSM_PM_STAT_IDLE_WFI,
        MSM_PM_STAT_RETENTION,
        MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
        MSM_PM_STAT_IDLE_POWER_COLLAPSE,
        MSM_PM_STAT_SUSPEND,
    };
    msm_pm_mode_sysfs_add();
    msm_pm_add_stats(enable_stats, ARRAY_SIZE(enable_stats));
    suspend_set_ops(&msm_pm_ops);


msm_pm_ops 的实现如下:
static const struct platform_suspend_ops msm_pm_ops = {
    .enter = msm_pm_enter,
    .valid = suspend_valid_only_mem,
    .prepare_late = msm_suspend_prepare,
    .wake = msm_suspend_wake,
};


看到没有,这个里面实现ops 里面的enter、valid 、prepare_late 、wake 函数。
这些函数在suspend 的时候都会调用到,如:
static int suspend_prepare(void)
{
    int error;

    if (!suspend_ops || !suspend_ops->enter)
        return -EPERM;


这里就在调用enter 函数。

4.设备resume 的流程

当我们按下power key 之后,我们的设备会进入suspend ,整个流程如上面讲解suspend 的流程一样。
source code :


    do {
        error = suspend_enter(state, &wakeup);
    } while (!error && !wakeup
        && suspend_ops->suspend_again && suspend_ops->suspend_again());

suspend_enter 函数的实现:

static int suspend_enter(suspend_state_t state, bool *wakeup)
{
    int error;

    if (suspend_ops->prepare) {
        error = suspend_ops->prepare();
        if (error)
            goto Platform_finish;
    }

    error = dpm_suspend_end(PMSG_SUSPEND);
    if (error) {
        printk(KERN_ERR "PM: Some devices failed to power down\n");
        goto Platform_finish;
    }

    if (suspend_ops->prepare_late) {
        error = suspend_ops->prepare_late();
        if (error)
            goto Platform_wake;
    }

    if (suspend_test(TEST_PLATFORM))
        goto Platform_wake;

    error = disable_nonboot_cpus();
    if (error || suspend_test(TEST_CPUS))
        goto Enable_cpus;

    arch_suspend_disable_irqs();
    BUG_ON(!irqs_disabled());

    error = syscore_suspend();
    if (!error) {
        *wakeup = pm_wakeup_pending();
        if (!(suspend_test(TEST_CORE) || *wakeup)) {
            error = suspend_ops->enter(state);
            events_check_enabled = false;
        }
        syscore_resume();
    }

    arch_suspend_enable_irqs();
    BUG_ON(irqs_disabled());

 Enable_cpus:
    enable_nonboot_cpus();

 Platform_wake:
    if (suspend_ops->wake)
        suspend_ops->wake();

    dpm_resume_start(PMSG_RESUME);

 Platform_finish:
    if (suspend_ops->finish)
        suspend_ops->finish();

    return error;
}




当我们进入suspend 的时候,我们最后会调用到suspend_ops->enter 函数里面,当执行到这个函数的时候,我们进入了要求的suspend  状态,只有当唤醒的时候才会返回。关于suspend_ops->enter 函数的实现,我们在讲解suspend_ops的时候里面有赋值(msm_pm_enter)。

其实我目前还没有明白为何进入msm_pm_enter 函数为何就停止在哪里?
设置CPU 的寄存器,让整个CPU 进入睡眠,进入睡眠之后,执行的code 就停止执行了, 就停止在那里了,当我们按下power button 的时候,CPU 又起来来,code 又继续执行了,所以就走下面的resume 流程。
power button 是硬件设计好了的wake source 源,硬件设计好了的。



当返回的时候,我们就会调用resume设备的 函数。

 Resume_devices:
    suspend_test_start();
    dpm_resume_end(PMSG_RESUME);
    suspend_test_finish("resume devices");
    resume_console();

其实resume 的过程就和suspend 的流程差不多,也是从设备的链表里面遍历设备,最后调用设备的resume 函数。
因为下面执行的是goto 语句,所以依次就会执行:


        syscore_resume();
    }

    arch_suspend_enable_irqs();  //打开中断
    BUG_ON(irqs_disabled());

 Enable_cpus:
    enable_nonboot_cpus();//启动非启动CPU


 Platform_wake:
    if (suspend_ops->wake)
        suspend_ops->wake();

    dpm_resume_start(PMSG_RESUME);//Execute "noirq" and "early" device callbacks.

 Platform_finish:
    if (suspend_ops->finish)
      就是整个resume 设备的流程。

5.wake_lock机制
基本原理如下:当启动一个应用程序的时候,它都可以申请一个wake_lock唤醒锁,每当申请成功之后都会在内核中注册一下(通知系统内核,现在已经有锁被申请),当应用程序在某种情况下释放wake_lock的时候,会注销之前所申请的wake_lock特别要注意的是:只要是系统中有一个wake_lock的时候,系统此时都不能进行睡眠
下面将讲解上层获得wake_lock  的整个过程。
frameworks/base/services/java/com/android/server/power/PowerManagerService.java
        public void acquire() {
            synchronized (this) {
                mReferenceCount += 1;
                if (mReferenceCount == 1) {
                    if (DEBUG_SPEW) {
                        Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
                    }
                    nativeAcquireSuspendBlocker(mName);
                }
            }
        }


会调用nativeAcquireSuspendBlocker  这个 函数的实现是放在jni 里面的:
frameworks/base/services/jni/com_android_server_power_PowerManagerService.cpp

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

最后调用acquire_wake_lock 进行申请wake_lock
enum {
    PARTIAL_WAKE_LOCK = 1,  // the cpu stays on, but the screen is off
    FULL_WAKE_LOCK = 2      // the screen is also on
};
通过注释也可以看出这两种wake_lock 的区别。
我们继续trace code :
acquire_wake_lock的实现:
hardware/libhardware_legacy/power/power.c

int
acquire_wake_lock(int lock, const char* id)
{
    initialize_fds();//获得我们设备结点的设备描述符号,并将我们获得的设备描述符号放到g_fds  这个数组里面。或者    "/sys/power/wake_lock","/sys/power/wake_unlock",这两个设备结点的设备描述符号。

//    ALOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];//获得wake_lock 的设备节点的设备描述符
    }
    else {
        return EINVAL;
    }

    return write(fd, id, strlen(id));//我们获得对应的设备描述符后,我们直接操作描述符。

}
write(fd, id, strlen(id)  会调用到wake_lock 里面的store 函数。在下面的小章节会讲解到wakelock.c 这个文件。
5.1.wake_lock_store
当我们使用到wake_lock 设备节点的时候,最终调用到wake_lock_store  函数。
static ssize_t wake_lock_store(struct kobject *kobj,
                   struct kobj_attribute *attr,
                   const char *buf, size_t n)
{
    int error = pm_wake_lock(buf);
    return error ? error : n;
}
pm_wake_lock函数的实现如下:

int pm_wake_lock(const char *buf)
{
    const char *str = buf;
    struct wakelock *wl;
    u64 timeout_ns = 0;
    size_t len;
    int ret = 0;

    while (*str && !isspace(*str))
        str++;

    len = str - buf;
    if (!len)
        return -EINVAL;

    if (*str && *str != '\n') {
        /* Find out if there's a valid timeout string appended. */
        ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
        if (ret)
            return -EINVAL;
    }

    mutex_lock(&wakelocks_lock);

    wl = wakelock_lookup_add(buf, len, true);
    if (IS_ERR(wl)) {
        ret = PTR_ERR(wl);
        goto out;
    }
    if (timeout_ns) {
        u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;

        do_div(timeout_ms, NSEC_PER_MSEC);
        __pm_wakeup_event(&wl->ws, timeout_ms);
    } else {
        __pm_stay_awake(&wl->ws);
    }

    wakelocks_lru_most_recent(wl);

 out:
    mutex_unlock(&wakelocks_lock);
    return ret;
}


上面的函数主要讲解wakelock_lookup_add
会调用到void wakeup_source_add(struct wakeup_source *ws)函数将我们申请的wake_lock 添加到
list_add_rcu(&ws->entry, &wakeup_sources); wakeup_sources  链表中去,我们在suspend 的时候会去遍历wakeup_sources 这个链表里面的wake source 的状态。
并根据  timeout_ns  的时间进行调用不同的函数,最终都是将对应的wake_source 设置为true.    ws->active = true;

 if (timeout_ns) {
        u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;

        do_div(timeout_ms, NSEC_PER_MSEC);
        __pm_wakeup_event(&wl->ws, timeout_ms);
    } else {
        __pm_stay_awake(&wl->ws);
    }

现在我们再回到上面讲解的pm_wakep_autosleep_enabled 这个函数里面:
void pm_wakep_autosleep_enabled(bool set)
{
    struct wakeup_source *ws;
    ktime_t now = ktime_get();

    rcu_read_lock();
    list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
        spin_lock_irq(&ws->lock);
        if (ws->autosleep_enabled != set) {
            ws->autosleep_enabled = set;
            if (ws->active) {
                if (set)
                    ws->start_prevent_time = now;
                else
                    update_prevent_sleep_time(ws, now);
            }
        }
        spin_unlock_irq(&ws->lock);
    }
    rcu_read_unlock();
}
#endif /* CONFIG_PM_AUTOSLEEP *

/


上面在进入suspend 的时候会判断ws->active ,如果为true 就不会进入suspend .


安卓linux uevent内核上报机制实例

uevent可以实现内核通知上层的一种机制,最常见的电池状态的变化就是kernel uevent通知的,每次百分比或者其他的变化通过power_supply_changed通知上层update; ...
  • eqwewr
  • eqwewr
  • 2015年01月20日 21:21
  • 2139

Android Power Management HOWTO

Introduction Wake Locks Types of Wake Locks Exploring a Wake Lock Example PowerManager class...
  • kiIIer
  • kiIIer
  • 2011年09月30日 17:38
  • 308

Android Power Management

Android Power Management Overview The above picture shows the overall architecture design of And...
  • myspor
  • myspor
  • 2011年03月30日 17:18
  • 1107

Android Power management

Android Power Management            总体上来说Android的电源管理还是比较简单的, 主要就是通过锁和定时器来切换系统的状态,使系统的功耗降至最低,整个系...

Android Power Management

http://blog.csdn.net/hzdysymbol/article/details/4004791 总体上来说Android的电源管理还是比较简单的, 主要就是通过锁和定时器来切换系统的...

Power Management of Online Data-Intensive Services

Power Management of Online Data-Intensive Services I met Google’s Wolf-Dietrich Weber at the ...

Linux 2.6内核配置说明(6----Power management options电源管理选项)

Power Management support 电源管理有APM和ACPI两种标准且不能同时使用.即使关闭该选项,X86上运行的Linux也会在空闲时发出HLT指令将CPU进入睡眠状态 ...

中关村-DIY电脑之BIOS的电源管理(Power Management)

为了节省能源,电脑普遍都有省电设计。在一段时间没有使用时,电脑可依据设置的条件自动进入不同阶段的省电模式,以节省耗电量。 使用BIOS的电源管理(Power Management)设置可配置电脑...

**arm 时钟(CLOCK)和电源管理(POWER MANAGEMENT )

2013-07-15 19:55:10 分类: 嵌入式 arm体系结构:arm920t  一,概述: 时钟(clock)和电源管理(power management)的关系:     电...

arm 时钟(CLOCK)和电源管理(POWER MANAGEMENT )

原文地址:http://blog.chinaunix.net/uid-28458801-id-3804521.html
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Power Management
举报原因:
原因补充:

(最多只允许输入30个字)