• UFS Power Management 介绍


    一 . UFS Power Management Overview

    1. UFS Power Management 管理UFS Power相关资源,

    在收到访问请求的时候唤醒UFS工作,切换为工作模式,在完成请求后让UFS进行睡眠,切换为睡眠模式,能够节省功耗,提高续航。

    主要是分为UFS Runtime Power Management和UFS System Power Management。

    2.  UFS Runtime Power Management:

    UFS模块运行时睡眠唤醒机制,只需要UFS模块空闲,即可进入睡眠状态,不受其他模块的睡眠唤醒状态影响,通俗点讲就是个人自扫门前雪。

    3. UFS System Power Management:

    UFS系统睡眠唤醒机制,和系统的睡眠唤醒状态有关,需要系统所有的模块都睡眠下去,UFS 模块才会睡眠下去,当只有存在系统一个模块是唤醒状态/或者系统存在唤醒源导致睡眠部下去,UFS模块也是不能成功睡眠下去,通俗点讲就是有福同享,有难同当,不放弃任何一个民众。

    二 . UFS Power Management Detail

    1.  UFS Runtime Power Management : 

    (1) UFS Runtime Power Management是在Linux Kernel RPM Framework下进行管理的,在没有访问请求/UFS空闲的时候,将UFS置为睡眠状态,当UFS有访问请求/不是空闲状态时,将UFS置为唤醒状态。

    (2) UFS  Driver RPM Power Management 主要是管理UFS Device Power Mode State和MPHY Link State。

    UFS PM Level State划分为六个等级,默认rpm_lvl 是3,也就是UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE。

    UFS_SLEEP_PWR_MODE:指的是UFS Device PowerMode为Sleep Mode, 需要发送SSU命令切换UFS Power Mode。

    UIC_LINK_ACTIVE_STATE:指的是将UFS MPHY Link设置为Hibernate状态,需要发送DME_HIBERNATE_ENTER命令切换Link Status.

    1. struct ufs_pm_lvl_states ufs_pm_lvl_states[] = {
    2.     {UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE},
    3.     {UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE},
    4.     {UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE},
    5.     {UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE},
    6.     {UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE},
    7.     {UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE},
    8. };

    (3) UFS RPM 相关Sysfs节点:

    1. UFS RPM Sysfs节点目录: /sys/device/platform/soc/1d84.ufshci/power
    2. UFS RPM 相关节点:
    3. 1. control: Report/change current runtime PM setting of the device
    4. *
    5. * Runtime power management of a device can be blocked with the help of
    6. * this attribute. All devices have one of the following two values for
    7. * the power/control file:
    8. * + "auto\n" to allow the device to be power managed at run time;
    9. * + "on\n" to prevent the device from being power managed at run time;
    10. 2. autosuspend_delay_ms - Report/change a device's autosuspend_delay value
    11. *
    12. * Some drivers don't want to carry out a runtime suspend as soon as a
    13. * device becomes idle; they want it always to remain idle for some period
    14. * of time before suspending it. This period is the autosuspend_delay
    15. * value (expressed in milliseconds) and it can be controlled by the user.
    16. * If the value is negative then the device will never be runtime
    17. * suspended.
    18. 3. runtime_active_time: report runtime active status totol time.
    19. 4. runtime_suspended_time: report runtime suspended status totol time.
    20. 5. runtime_status: report runtime status.

    (4) UFS 设备Runtime Power Management状态:

    1. /*
    2. * Device run-time power management status.
    3. *
    4. * These status labels are used internally by the PM core to indicate the
    5. * current status of a device with respect to the PM core operations. They do
    6. * not reflect the actual power state of the device or its status as seen by the
    7. * driver.
    8. *
    9. * RPM_ACTIVE Device is fully operational. Indicates that the device
    10. * bus type's ->runtime_resume() callback has completed
    11. * successfully.
    12. *
    13. * RPM_SUSPENDED Device bus type's ->runtime_suspend() callback has
    14. * completed successfully. The device is regarded as
    15. * suspended.
    16. *
    17. * RPM_RESUMING Device bus type's ->runtime_resume() callback is being
    18. * executed.
    19. *
    20. * RPM_SUSPENDING Device bus type's ->runtime_suspend() callback is being
    21. * executed.
    22. */
    23. enum rpm_status {
    24. RPM_ACTIVE = 0,
    25. RPM_RESUMING,
    26. RPM_SUSPENDED,
    27. RPM_SUSPENDING,
    28. };

    (5) UFS 驱动常用Runtime PM使用接口和使用场景

    在访问UFS 设备之前,为了防止UFS 进入Suspend , 我们需要调用以下两个接口增加设备使用计数,然后唤醒UFS 设备(Resume)为Active状态,这里UFS设备指的是UFS Host Controller, 

    Note:不管是UFS驱动还是USB驱动还是其他驱动,在访问设备之前都应该遵循以上流程,唤醒设备为Active状态,保证设备正常工作,否则如果设备进入了Suspend状态去访问会出现未知的错误。

    pm_runtime_get_sync

    pm_runtime_get

    访问UFS设备之后,就可以让UFS 设备进入Suspend, 我们需要调用以下两个接口减少设备使用技术,然后使得UFS设备进入睡眠状态(Suspended), 这里UFS设备指的是UFS Host Controller.

    pm_runtime_put

    pm_runtime_put_sync

    下面是一个具体的使用场景:

    在对UFS Host Controller做访问之前,调用pm,_runtime_get_sync增加设备引用计数,唤醒设备,然后对设备进行访问操作,访问完成后调用pm_runtimr_put_sync减少设备引用计数,让设备睡眠。 

    1. int ufshcd_clk_scaling_enable(struct ufs_hba *hba, int value)
    2. {
    3. int err;
    4. value = !!value;
    5. mutex_lock(&ufshcd_clkscale_lock);
    6. if (value == hba->clk_scaling.is_allowed)
    7. goto out;
    8. // 获取UFS PM Runtime 资源,增加PM Usage Count, 让UFS Host处于Active Status
    9. pm_runtime_get_sync(hba->dev);
    10. ufshcd_hold(hba, false);
    11. cancel_work_sync(&hba->clk_scaling.suspend_work);
    12. cancel_work_sync(&hba->clk_scaling.resume_work);
    13. hba->clk_scaling.is_allowed = value;
    14. if (value) {
    15. ufshcd_resume_clkscaling(hba);
    16. } else {
    17. ufshcd_suspend_clkscaling(hba);
    18. err = ufshcd_devfreq_scale(hba, true);
    19. if (err)
    20. dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
    21. __func__, err);
    22. }
    23. ufshcd_release(hba, false);
    24. // 释放UFS PM Runtime资源,减少PM Usage Count, 让UFS Host处于Suspend Status
    25. pm_runtime_put_sync(hba->dev);
    26. out:
    27. mutex_unlock(&ufshcd_clkscale_lock);
    28. return 0;
    29. }

    阻塞UFS设备在Linux RPM Power Management 管理运行时进入Suspend. 

    增加设备引用计数和清除设备power.runtime_auto标志

    除非调用pm_runtime_allow,否则UFS设备不会在运行的时候进入Suspend状态

    pm_runtime_forbid

    UFS设备在Linux RPM Power Management 管理运行时允许挂起(进入Suspended)

    减少设备引用计数和设置设备power.runtime_auto标志

    pm_runtime_allow

    下面是一个具体的使用场景: 

    static const char ctrl_auto[] = "auto";
    static const char ctrl_on[] = "on";

    static ssize_t control_show(struct device *dev, struct device_attribute *attr,
                    char *buf)
    {
        return sprintf(buf, "%s\n",
                    dev->power.runtime_auto ? ctrl_auto : ctrl_on);
    }

    static ssize_t control_store(struct device * dev, struct device_attribute *attr,
                     const char * buf, size_t n)
    {
        device_lock(dev);
        if (sysfs_streq(buf, ctrl_auto))
            pm_runtime_allow(dev);
        else if (sysfs_streq(buf, ctrl_on))
            pm_runtime_forbid(dev);
        else
            n = -EINVAL;
        device_unlock(dev);
        return n;
    }

    2. UFS System Power Management:

    (1)UFS System Power Management是受整个内核系统影响的,需要内核系统所有模块都睡眠    了或者内核系统没有唤醒事件,UFS设备才会进入UFS System Suspend。

     UFS手机功耗续航测试就是在整个系统进入System Suspend(包括UFS System Suspend)后进行   测试的,相关开发同事需要对整个系统的System Suspend/Resume 机制非常了解,才能清晰的     定位到某个模块影响了系统进入System Suspend,导致功耗问题。

    (2)  UFS  Driver RPM Power Management 主要是管理UFS Device Power Mode State和            MPHY Link State 还有UFS Device的供电VCC。

       UFS PM Level State划分为六个等级,默认spm_lvl 是3,也就是UFS_SLEEP_PWR_MODE,         UIC_LINK_ACTIVE_STATE。

       UFS_SLEEP_PWR_MODE:指的是UFS Device PowerMode为Sleep Mode, 需要发送SSU命       令切换UFS Power Mode。

       UIC_LINK_ACTIVE_STATE:指的是将UFS MPHY Link设置为Hibernate状态,需要发送               DME_HIBERNATE_ENTER命令切换Link Status.

       VCC: UFS Device进入System Suspend的时候,会将VCC电压关闭节省耗电,减少功耗。

       (3)  UFS System Suspend相关Sysfs节点:

      系统PM节点目录:  /sys/power/*

      wake_lock:  系统唤醒锁,如果有进程持有wake_lock, 整个系统就不会进入System                                    Suspend, 当然UFS 设备也不能进入system suspend.

       wake_unlock:  释放系统唤醒锁,让整个系统可以进入System Suspend, 当然UFS 设备也                             能进入system suspend.

     (4) UFS 设备相关System PM状态:

    系统System PM 相关状态:

    #define PM_SUSPEND_ON        ((__force suspend_state_t) 0)
    #define PM_SUSPEND_TO_IDLE    ((__force suspend_state_t) 1)
    #define PM_SUSPEND_STANDBY    ((__force suspend_state_t) 2)
    #define PM_SUSPEND_MEM        ((__force suspend_state_t) 3)
    #define PM_SUSPEND_MIN        PM_SUSPEND_TO_IDLE
    #define PM_SUSPEND_MAX        ((__force suspend_state_t) 4)

    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;

        error = pm_autosleep_lock();
        if (error)
            return error;

        if (pm_autosleep_state() > PM_SUSPEND_ON) {
            error = -EBUSY;
            goto out;
        }

        state = decode_state(buf, n);
        if (state < PM_SUSPEND_MAX) {
            if (state == PM_SUSPEND_MEM)
                state = mem_sleep_current;

            error = pm_suspend(state);
        } else if (state == PM_SUSPEND_MAX) {
            error = hibernate();
        } else {
            error = -EINVAL;
        }

     out:
        pm_autosleep_unlock();
        return error ? error : n;
    }

    UFS System PM相关状态:

    /* Used to differentiate the power management options */
    enum ufs_pm_op {
        UFS_RUNTIME_PM,
        UFS_SYSTEM_PM,
        UFS_SHUTDOWN_PM,
    };
     

    /**
     * ufshcd_system_suspend - system suspend routine
     * @hba: per adapter instance
     *
     * Check the description of ufshcd_suspend() function for more details.
     *
     * Returns 0 for success and non-zero for failure
     */
    int ufshcd_system_suspend(struct ufs_hba *hba)
    {
        int ret = 0;
        ktime_t start = ktime_get();

        if (!hba || !hba->is_powered)
            return 0;

        if ((ufs_get_pm_lvl_to_dev_pwr_mode(hba->spm_lvl) ==
             hba->curr_dev_pwr_mode) &&
            (ufs_get_pm_lvl_to_link_pwr_state(hba->spm_lvl) ==
             hba->uic_link_state))
            goto out;

        if (pm_runtime_suspended(hba->dev)) {
            /*
             * UFS device and/or UFS link low power states during runtime
             * suspend seems to be different than what is expected during
             * system suspend. Hence runtime resume the devic & link and
             * let the system suspend low power states to take effect.
             * TODO: If resume takes longer time, we might have optimize
             * it in future by not resuming everything if possible.
             */
            ret = ufshcd_runtime_resume(hba);
            if (ret)
                goto out;
        }

        ret = ufshcd_suspend(hba, UFS_SYSTEM_PM);
    out:
        trace_ufshcd_system_suspend(dev_name(hba->dev), ret,
            ktime_to_us(ktime_sub(ktime_get(), start)),
            hba->curr_dev_pwr_mode, hba->uic_link_state);
        if (!ret)
            hba->is_sys_suspended = true;
        return ret;
    }
    EXPORT_SYMBOL(ufshcd_system_suspend);

     

    (6)UFS 驱动常用System PM使用接口和使用场景:

    系统开始进入System Suspend的时候,会循环遍历每个设备都进入System Suspend状态,包      括UFS设备,UFS驱动没有显性调用System PM的接口,主要是受系统Suspend机制管理,当系    统开始进入Suspend流程的时候,UFS设备也是空闲的状态的时候,此时UFS设备就会进入System Suspend的状态,UFS驱动发送SSU命令让UFS Device进入SLEEP Mode, 发送DME_HIBER_ENTER让MPHY Link 进入Hibernate状态,并且UFS Device的VCC供电也会关闭,节省功耗。

    三. 参考资料

    1. Kernel System Power Management  Source Code

    2 . Kernel Runtime Power Management  Source Code

    3.  Kernel UFS Power Management Source Code

  • 相关阅读:
    C语言—位运算符
    Unity解决:导出AndroidStudio工程 出现如下报错的解决方法
    【go学习笔记】Go errors 最佳实践
    【封装UI组件库系列】搭建项目及准备工作
    Python 编程基础 | 第一章-预备知识 | 1.4、包管理工具
    基于Redis网络模型的简易网络库
    docker基本概念及安装
    下载安装包,安装 PySide2 到 windows 系统
    使用 Docker 安装 Kibana 8.4.3
    【Python】:数据可视化之相关系数热力图绘制(二)(seaborn版本)
  • 原文地址:https://blog.csdn.net/guozhidixian/article/details/125364022