精华内容
下载资源
问答
  • 电源管理

    2016-05-14 22:17:51
    电源管理是一个重要的考虑对移动VR开发。 现在移动设备是令人惊讶强大的,你能把它放入你的口袋--你能合理的期望找到4个2.6GHzCPU核和一个600MHz GPU。充分利用,它们能交付更好的性能比一个Xbox360或者PS3在一些...

    电源管理是一个重要的考虑对移动VR开发。

    现在移动设备是令人惊讶强大的,你能把它放入你的口袋--你能合理的期望找到4个2.6GHzCPU核和一个600MHz GPU。充分利用,它们能交付更好的性能比一个Xbox360或者PS3在一些案例中。

    在设备监视器上一个管理者处理一个内部的温度和尝试正确的动作当温度上升到超过某一级别来预防故障或者滚烫的表面温度。正确的动作包含降低时钟速率。


    如果你辛苦的运行到限制,温度会继续攀升尽管时钟速率是低的,CPU时钟可以下降到300MHz。设备可能恐慌在极端的条件下。VR性能会灾难性的下降。


    默认的VR应用时钟速率是1.8GHz,双核,GPU 389MHz。如果你始终使用这些,你最终会运行进热量管理,尽管你最初没有问题。一个典型的表现是可怜的应用性能在10分钟好的演示之后。如果你过滤了日志输出为“thermal”你会看到多种多样的传感器读取的通知和采取的动作。


    一个在移动和PC/控制台开发之间的不同鉴定是没有浪费最佳化。不考虑电源,如果你有帧及时准备好,这是没关系的你是使用90%的有效时间或10%。在移动上,每一个操作消耗电池和加热设备。当然,最佳化必须努力(它也能达到一个昂贵的东西),但是它是重要的去注意权衡。


    固定时钟级别API

    固定时钟级别API允许应用设置一个固定的CPU频率和一个固定的GPU频率。


    在现代设备上,CPU和GPU时钟速率是完全固定的由应用设置的值直到设备温度达到限制,在这时CPU和GPU时钟会改变电源来保存频率。这个改变能被检测到。一些应用可以继续操作在一个退化的样式中,可能改变到30FPS或者单通道渲染。其他的应用可以显示一个警告屏来告诉演示不能继续了。


    由固定时钟等级API设置的固定CPU等级和固定的GPU等级是抽象的等级,不是MHz/GHz,所以一些现在的努力使他们兼容未来的设备。对现在的硬件,0,1,2,3为CPU和GPU等级。0是最慢的和最有效率的;3是最快的和最容易发热的。典型的在0和3等级之间的不同是两个因素(速度和温度)。


    不是所有的时钟组合是有效的对所有设备。例如。最高的GPU等级可以不是有效的为使用两个最高的CPU等级。如果一个无效的矩阵组合被提供,系统不会收到请求和时钟设置会进入动态模式。VrApi会断言和解决一个警告在这个案例中。

    注意:组合(2,3)和(3,3)是允许初始化设备的发布版。然而,我们强烈阻止使用它们,就和它们喜欢快速导致过热。我们也不推荐使用CPU等级4和5和GPU 等级4,它们由S6,S6Edge和Note5的一些模型支持,就像它们喜欢导致可怜的性能在设备上(它们不支持这些等级)。


    电源管理和性能


    这儿没有魔法设置在SDK中来修复电源消耗--这是鉴定过的。

    你的应用的运行的时间(进入热限制之前)长度依赖两个因素:你的应用做多少工作,时钟频率。改变时钟频率一直下降也只能减少大约25%在电源消耗上为同样的任务数量,所以很多电源节省是减少你应用的工作。


    如果你的应用能运行在(0,0)设置,它应用不会有发热问题。两核工作在1GHz和一个240MHz GPU,所以它是必定可行的使复杂应用在那个等级上,但是Unity应用可能是困难的来最优化这个设置。


    这是有效的工具来减少需求的GPU性能:

    1.不使用色彩失常修正在时间异常上。

    2.不使用4xMSAA。

    3.减少眼目标的分辨率。

    4.使用16位色彩和深度缓冲区可能有帮助。

    5.它可能不是一个好的方法来减少到2xMSAA--你应该用减小眼目标的分辨率来代替。


    这些是承担质量权衡(它们需要被平衡违反步骤)你可以在你的应用中采用:

    1.减少透支(尤其是混合粒子)和复杂着色。

    2.一直确保纹理是压缩的和变频解码的。


    大体上,CPU加载导致更多的热量问题不GPU加载。减小CPU性能需求不是直接了当的。Unity应用应该一直使用多线程渲染选项,因为双核运行在1GHz工作比单核运行在2GHz更有效。


    如果你发现你只是没有接近,你可能需要设置MinimumVsyncs为2并运行你的游戏在30FPS,用TimeWarp产生另外的帧。一些事情解决好就像这样,但是一些接口风格和场景结构强调局限性。更多的怎样设置MinimumVsyncs的信息,请看TimeWarp技术注意。


    总之,我们一般的设备:

    如果你制作一个应用,它可能会使用一个长的时间周期,就像一个电影播放器,挑选非常低的等级。理想上使用(0,0),但是它可能使用更多的图形如果CPUs是一直大部分停顿,可以上升到(0,2)。


    如果你是可以对应用被限制在10分钟演示的块,你能选择高的时钟等级。如果它工作的很好在(2,2),你可能需要做一些高级的工作。


    在时钟频率固定下,在日志输出中观察FPS和GPU时间。GPU时间报告不包含时间花费在解决渲染回退到主内存重片上内存,所以它是被低估的。如果GPU时间保持在12ms或之下,你能减少你的GPU时钟等级。如果GPU时间是低的,但是帧率不是60FPS,你的CPU被限制的。


    一直构建最佳化的应用版本为发布。甚至如果一个调试构建执行的很好,它会消耗更多的电力和加热设备比一个发布版本。


    最优化知道它运行的很好。

    更多的关于怎么改善你的Unity应用的性能的信息,请看“最佳实践:Mobile”在Unity集成指南中,移动SDK文档


    电源状态通知和减少策略


    移动SDK提供电源等级状态监测和处理。


    电源等级状态涉及到是否设备是正在操作在正常时钟频率下或者设备已经上升超过了一个热量阈值等正在发生。在电量节省模式下,CPU和GPU频率会被切换到电源节省等级。电源节省等级是等价的设置固定CPU和GPU时钟等级为(0,0)。如果温度继续上升,时钟频率会被设置为小数值(它们不是VR应用能支持的)。


    一旦我们检测到热量正超过阀门正在发生,应用可以选择继续操作在一个退化的模式或者立即退出到Oculus菜单并发出一个头部追踪错误消息。


    在第一种情况下,当应用第一次从正常操作转换电源节省模式,下面的会发生:

    1.通用菜单会出现来显示一个可解雇的警告消息指示设备需要冷却。

    2.一旦消息被解雇,应用汇恢复到30Hz时间异常模式并关闭色彩失真修正。

    3.如果设备时钟频率超过节气阀到小等级在继续使用之后,一个不能消失的错误消息会被显示,用户必须离开设备。

    在这个模式下,应用可以选择采取附加的应用指定测量来减少性能需求。在native应用,你可以使用下面的应用接口调用来检测是否是电源节省模式:GetPowerSaveActive().在Unity中,你可以使用下面的插件调用:OVR_IsPowerSaveActive().在Moonling/OVRModeParms.cs有更多的细节。


    在第二种情况下,当应用从正常操作转换到电源节省模式,Universal Menu会被显示并展示一个不能消失的错误消息和用户必须离开设备。这个模式倾向应用不可以好好的执行在减少等级甚至打开30Hz TimeWarp。


    你可以使用下面的调用来关闭或打开电源节省模式策略:在Native,设置settings.ModeParms.AllowPowerSave在VrAppInterface::Configure()为真来让电源节省模式处理,或值为假来立即显示头部追踪错误消息。

    在Unity中,你可以使能或不使能电源节省模式处理渠道OVR_VrModeParms_SetAllowPowerSave().查看Moonlight/OVRModeParms.cs获取更多的细节。

    展开全文
  • 模拟电源管理与数字电源管理、电子技术,开发板制作交流
  • 人们需要高效率、低功耗、符合能效规范的电子设备,需要更高性能、更小形状因数的无线系统,这为电源和电源管理设计提出巨大的排战。设计人员要为各种DSP、MCU、FPGA、ASIC、音频/视频和显示电路提供多电压、更大...
  • 电源管理芯片

    2018-11-30 13:10:25
    本资料为电源管理芯片DAP013的datasheet,对于设计维修电源有帮助
  • 人们需要高效率、低功耗、符合能效规范的电子设备,需要更高性能、更小形状因数的无线系统,这为电源和电源管理设计提出巨大的排战。设计人员要为各种DSP、MCU、FPGA、ASIC、音频/视频和显示电路提供多电压、更大...
  • 人们需要高效率、低功耗、符合能效规范的电子设备,需要更高性能、更小形状因数的无线系统,这为电源和电源管理设计提出巨大的排战。设计人员要为各种DSP、MCU、FPGA、ASIC、音频/视频和显示电路提供多电压、更大...
  • WinCE实现电源管理

    2021-01-19 17:52:16
    对于移动设备来说,电源管理是比较重要的。为了让设备有更长的待机和工作时间,实现一个完善的电源管理功能也是必须的。下文将讲述如何实现电源管理。  [背景和早期版本]  我们对电源管理的目的是出于节能,使系统...
  • OCCTPT电源管理软件

    2018-12-22 09:28:40
    电源管理软件,方便知道电脑的磨损情况电源管理软件,方便知道电脑的磨损情况电源管理软件,方便知道电脑的磨损情况
  • 当今的大多数系统除了主要的CPU、逻辑电路FPGA、DDR等数字芯片外,就只剩下电源管理芯片了,因此电源管理芯片的可控性和集成度就显得极为重要了,数字电源管理正是顺应了市场的这种需求。  数字电源管理的几种主要...
  • 易语言电源管理模块源码,电源管理模块,屏保开启,屏保禁用,待机开启,待机禁用,电源管理启用,电源管理禁用,启动屏保,启动屏保线程,注册电源管理,弹出关机窗口,窗口处理程序,关机,重启,取消,退出,提升进程权限,输入激活...
  • 嵌入式系统电源管理

    千次阅读 2019-08-04 16:29:01
    1、电源管理 1.1 概述 目的: 在系统可维持正常所期望工作状态的情况下,尽可能降低功耗。Linux电源管理涉及到系统待机,频率电压变换,系统空闲处理,运行时期电源管理等多个方面。 措施: 《Linux设备驱动...

    1、电源管理

    1.1 概述

    目的:

          在系统可维持正常所期望工作状态的情况下,尽可能降低功耗。Linux电源管理涉及到系统待机,频率电压变换,系统空闲处理,运行时期电源管理等多个方面。

    措施:

    《Linux设备驱动开发详解》一书中对电源管理的总结:

    1.2 单片机

    对于51单片机的电源管理,提供了低速,空闲和掉电三种模式来省电。

    • 通过分频使系统降频工作
    • 空闲模式:

    • 掉电模式

    1.3 linux电源管理模型

    1.3.1 休眠模型

    (1)电源状态描述

    - On(on)                                                  S0 -  Working
    - Standby (standby)                                S1 -  CPU and RAM are powered but not executed
    - to RAM(mem)                                         S3 -  RAM is powered and the running content is saved to RAM【Suspend】
    - Suspend to Disk,Hibernation(disk)    S4 -  All content is saved to Disk and power down 【Hibernate】

    (2)cat /sys/power/state

    查看支持的状态:

    "suspend" 在嵌入式设备一般是指进入到suspend-to-RAM状态

    1.3.2 Runtime模型

    在系统运行阶段进入低功耗状态

    1.4 Android电源管理

    2、Linux电源管理

    linux系统电源管理涉及多个子系统,如休眠唤醒,调频调压,充电等等。

    2.1 Suspend to RAM

    2.1.1 触发方式

    查看支持的休眠方式:

    cat /sys/power/state

    休眠一般是由应用程序触发 -> 内核休眠处理 -> 硬件。最简单的触发方式:

    echo mem > /sys/power/state
    

    2.1.2 Suspend 流程

    系统平台到驱动级挂起流程

     

    https://blog.csdn.net/lixiaojie1012/article/details/23707681

    https://blog.csdn.net/wenjin359/article/details/83056560

    https://blog.csdn.net/u011006622/article/details/47417823

    dev_pm_ops(include/linux/pm.h)

    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_late)(struct device *dev);
    	int (*resume_early)(struct device *dev);
    	int (*freeze_late)(struct device *dev);
    	int (*thaw_early)(struct device *dev);
    	int (*poweroff_late)(struct device *dev);
    	int (*restore_early)(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);
    };

    2.1.3 如何在驱动中支持Suspend to RAM

    (1)提供回调函数

    定义PM相关回调函数结构体dev_pm_ops,并实现PM相关回调函数。

    static struct dev_pm_ops lcd_pm = {
    	.suspend = lcd_suspend,
    	.resume  = lcd_resume,	
    };
    
    struct platform_driver lcd_drv = {
    	.probe		= lcd_probe,
    	.remove		= lcd_remove,
    	.driver		= {
    		.name	= "mylcd",
    		.pm     = &lcd_pm,
    	}
    };

    (2)如果有需要则注册notifie事件,在休眠唤醒的不同阶段执行准备工作。

    static int lcd_suspend_notifier(struct notifier_block *nb,
                    unsigned long event,
                    void *dummy)
    {
    
        switch (event) {
        case PM_SUSPEND_PREPARE:
            printk("lcd suspend notifiler test: PM_SUSPEND_PREPARE\n");
            return NOTIFY_OK;
        case PM_POST_SUSPEND:
            printk("lcd suspend notifiler test: PM_POST_SUSPEND\n");
            return NOTIFY_OK;
    
        default:
            return NOTIFY_DONE;
        }
    }
    
    static struct notifier_block lcd_pm_notif_block = {
        .notifier_call = lcd_suspend_notifier,
    };
    register_pm_notifier(&lcd_pm_notif_block);

    (3)至少设置1个唤醒源,这样就可以完成系统的休眠,并唤醒。   

     /* 指定这些中断可以用于唤醒系统 */
        irq_set_irq_wake(irq, 1);

    hrtimer:The count is = 26
    hrtimer:The count is = 27
    echo mem > /sys/power/state
    PM: Syncing filesystems ... done.
    Freezing user space processes ... (elapsed 0.001 seconds) done.
    Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
    Suspending console(s) (use no_console_suspend to debug)
    PM: suspend of devices complete after 6.982 msecs
    PM: suspend devices took 0.010 seconds
    PM: late suspend of devices complete after 2.199 msecs
    PM: noirq suspend of devices complete after 2.082 msecs
    Disabling non-boot CPUs ...
    PM: noirq resume of devices complete after 1.463 msecs
    PM: early resume of devices complete after 1.646 msecs
    gpmi-nand 1806000.gpmi-nand: enable the asynchronous EDO mode 5
    PM: resume of devices complete after 73.038 msecs
    PM: resume devices took 0.080 seconds
    Restarting tasks ... done.
    root@mys6ul14x14:/mnt# hrtimer:The count is = 28
    hrtimer:The count is = 29
    hrtimer:The count is = 30
    hrtimer:The count is = 31
    hrtimer:The count is = 32

    2.2 Runtime Suspend

    运行时期的电源管理机制,有些时候,并不希望直接让整个系统Suspend to RAM,那么对于一些独立的设备希望做到电源管理,内核提供Runtim PM,应用程序根据需要,动态的管理某个设备的休眠唤醒。做到对子模块在运行时期的电源管理。将Suspend to RAM与Runtime PM结合会更有效的实现节能降耗。

    2.2.1 如何使驱动支持Runtime Suspend

    1. 驱动初始化中使能rpm
    2. 提供rpm相关回调函数
    3. 根据需要在合适的时候调用相关API对设备进行休眠或者唤醒操作
    4. 销毁函数中禁止掉相关资源

    简之,一个驱动想要实现rpm,在dev_pm_ops 增加回调函数,在驱动合适的时候调用rpm相关API,实现对设备的在线时期电源管理。

    2.2.2 Runtime API

    • 使能和关闭

    void pm_runtime_enable(struct device *dev)
    
    static inline void pm_runtime_disable(struct device *dev)

    如下:pm_runtime_enable主要是对变量disable_depth进行-1操作。反之pm_runtime_disable对disable_depth进行+1操作。disable_depth在初始化的时候初始值为1。

    void pm_runtime_enable(struct device *dev)
    {
    	unsigned long flags;
    
    	spin_lock_irqsave(&dev->power.lock, flags);
    
    	if (dev->power.disable_depth > 0)
    		dev->power.disable_depth--;
    	else
    		dev_warn(dev, "Unbalanced %s!\n", __func__);
    
    	spin_unlock_irqrestore(&dev->power.lock, flags);
    }
    • 休眠与唤醒
    static inline int pm_runtime_get_sync(struct device *dev)
    
    static inline int pm_runtime_put_sync(struct device *dev)

    pm_runtime_get_sync与pm_runtime_put_sync供给驱动在合适的时候调用,从而导致驱动提供的休眠唤醒回调函数被调用。其大致调用流程如下:

    pm_runtime_get_sync流程

    (1)增加引用计数

    (2)判断是否使能,是否处于休眠等,不满足条件直接返回

    (3)检测是否设置timer_autosuspends,决定是否延迟进行操作

    (4)如果设备处于RPM_RESUMING或者RPM_SUSPENDING,则等待

    (5)如果有父设备的话,进行父设备的相关唤醒操作

    (6) 调用子系统及驱动提供的相关回调函数,执行最终目的操作 

    子系统级别回调函数         

                callback = dev->pm_domain->ops.runtime_resume; 或
                callback = dev->type->pm->runtime_resume;      或
                callback = dev->class->pm->runtime_resume;     或
                callback = dev->bus->pm->runtime_resume;       或

    设备级别回调函数
                callback = dev->driver->pm->runtime_resume;

    2.2.3 LCD Runtime Suspend例子

    1、在probe的时候使能,在remove的时候禁止

    static int lcd_probe(struct platform_device *pdev)
    {
    	pm_runtime_set_active(&pdev->dev); //默认状态是运行
    	pm_runtime_enable(&pdev->dev);
    	return 0;
    }
    static int lcd_remove(struct platform_device *pdev)
    {
    	pm_runtime_disable(&pdev->dev);
    	return 0;
    }

    2、提供相关回调函数

    struct platform_driver lcd_drv = {
    	.probe		= lcd_probe,
    	.remove		= lcd_remove,
    	.driver		= {
    		.name	= "mylcd",
    		.pm     = &lcd_pm,
    	}
    };
    
    static struct dev_pm_ops lcd_pm = {
    	.suspend = lcd_suspend,
    	.resume  = lcd_resume,	
    	.runtime_suspend = lcd_suspend,
    	.runtime_resume  = lcd_resume,	
    };

    3、在open的时候唤醒,在关闭时候休眠设备

    static int mylcd_open(struct fb_info *info, int user)
    {
        pm_runtime_get_sync(&lcd_dev.dev);
        return 0;
    }
    static int mylcd_release(struct fb_info *info, int user)
    {
        pm_runtime_put_sync(&lcd_dev.dev);
        return 0;
    }

    2.2.4 sys接口

    上面通过驱动中提供接口的,APP可以在合适的时候触发RPM,系统sys接口也提供了相关接口

    echo on >  /sys/devices/.../power/control  

    control_store -> pm_runtime_forbid(dev); :
                    atomic_inc(&dev->power.usage_count);
                    rpm_resume(dev, 0);

    echo auto >  /sys/devices/.../power/control

    control_store -> pm_runtime_allow(dev); 
                    atomic_dec_and_test(&dev->power.usage_count)
                    rpm_idle(dev, RPM_AUTO);

    2.3 USB休眠唤醒机制分析

    2.3.1 USB PM框架

    bus-》devices

    2.3.2 USB回调函数

    static const struct dev_pm_ops usb_device_pm_ops = {
    	.prepare =	usb_dev_prepare,
    	.complete =	usb_dev_complete,
    	.suspend =	usb_dev_suspend,
    	.resume =	usb_dev_resume,
    	.freeze =	usb_dev_freeze,
    	.thaw =		usb_dev_thaw,
    	.poweroff =	usb_dev_poweroff,
    	.restore =	usb_dev_restore,
    #ifdef CONFIG_PM_RUNTIME
    	.runtime_suspend =	usb_runtime_suspend,
    	.runtime_resume =	usb_runtime_resume,
    	.runtime_idle =		usb_runtime_idle,
    #endif
    };
    
    struct device_type usb_device_type = {
    	.name =		"usb_device",
    	.release =	usb_release_dev,
    	.uevent =	usb_dev_uevent,
    	.devnode = 	usb_devnode,
    #ifdef CONFIG_PM
    	.pm =		&usb_device_pm_ops,
    #endif
    };
    
    usb_alloc_dev
    ->dev->dev.bus = &usb_bus_type;
    

     

    typedef struct pm_message {
        int event;
    } pm_message_t;


     

    #define PM_EVENT_INVALID	(-1)
    #define PM_EVENT_ON		0x0000
    #define PM_EVENT_FREEZE 	0x0001
    #define PM_EVENT_SUSPEND	0x0002
    #define PM_EVENT_HIBERNATE	0x0004
    #define PM_EVENT_QUIESCE	0x0008
    #define PM_EVENT_RESUME		0x0010
    #define PM_EVENT_THAW		0x0020
    #define PM_EVENT_RESTORE	0x0040
    #define PM_EVENT_RECOVER	0x0080
    #define PM_EVENT_USER		0x0100
    #define PM_EVENT_REMOTE		0x0200
    #define PM_EVENT_AUTO		0x0400

     

     

    3、Andorid电源管理

     

    展开全文
  •  在这些环境下,需要电源管理功能与构建单元结合在一起,并构成一个完整工作、健全的电源系统。电源管理包括:电源系统监控,定序和跟踪,监视和失效保护。事实上,每个功能都与DC-DC转换的大电流电路隔断。 ...
  • 本文介绍了这些方法的概念,以及我们如何运用它们实现节能的目的,同时还讨论了帮助处理器芯片获益的一些外部电源管理器件和电源 IC。  有源电源管理  片上电源管理技术主要适用于两类应用:管理有源系统功耗和...
  • 系统设计师要更充分的发掘电源管理器件的潜能,透彻的了解系统电源定序的需求。  电源定序的必要性  在研究具有电源管理工能的芯片之前,我们必须重视关系到器件工作和器件长期可靠性的问题。  如果电源定序...
  • 由于电路板组件集成了越来越多的子系统,他们的电源分配和管理系统的复杂性不断上升。由于这些系统变得越来越复杂,传统的固定功能的以硬件为中心的电源管理方案很快变得相当笨拙。
  •  电源管理半导体从所包含的器件来说,明确强调电源管理集成电路(电源管理IC,简称电源管理芯片)的位置和作用。电源管理半导体包括两部分,即电源管理集成电路和电源管理分立式半导体器件。  电源管理集成电路...
  •  电源管理半导体从所包含的器件来说,明确强调电源管理集成电路(电源管理IC,简称电源管理芯片)的位置和作用。电源管理半导体包括两部分,即电源管理集成电路和电源管理分立式半导体器件。  电源管理集成电路...
  • 本文介绍了数字电源的基本特点、数字电源相比于模拟电源的优势和数字电源管理的主要内容,也介绍了数字电源管理技术的应用。  新一代集成电路需要3.3V,1.8V甚至更低的电源电压,单个器件需要多路电压供电,而且...
  • 为了应对电源管理技术面临的挑战,芯片厂商推出了效率越来越高的电源管理芯片以及对能耗管理功能更加强大、精细的微处理器。以此为基础,如何设计高效、智能的系统软件对嵌入式设备进行能源管理,已成为研究热点。...
  • Linux电源管理

    千次阅读 2019-05-06 09:17:56
    CSDN仅用于增加百度收录权重,排版未...探究Linux电源管理模型,并为Tiny4412的LCD驱动添加电源管理。 这是2018的第一篇博客,选了一个几乎没有接触过的角度作为开篇,希望2018年学习更多,记录更多。 1.电源管理...

    CSDN仅用于增加百度收录权重,排版未优化,日常不维护。请访问:www.hceng.cn 查看、评论。
    本博文对应地址: https://hceng.cn/2018/01/18/Linux电源管理/#more

    探究Linux电源管理模型,并为Tiny4412的LCD驱动添加电源管理。

    这是2018的第一篇博客,选了一个几乎没有接触过的角度作为开篇,希望2018年学习更多,记录更多。

    1.电源管理的两种模型

    以往接触的Linux驱动,没遇到使用电池供电的情况,因此几乎没关注电源的管理。
    然而实际中,不少使用电池供电的硬件平台,例如手机、POS机等,就需要对电源进行管理,比如在不使用设备的时候,休眠屏幕省电。

    Linux电源管理模型有两种:系统睡眠模型suspendRuntime电源管理模型

    1.1系统睡眠模型Suspend

    On (on)                  S0 - Working
    Standby (standby)            S1 - CPU and RAM are powered but not executed
    Suspend to RAM (mem)          S3 - RAM is powered and the running content is saved to RAM
    Suspend to Disk, Hibernation (disk)    S4 - All content is saved to Disk and power down

    S3 aka STR(suspend to ram),挂起到内存,简称待机。计算机将目前的运行状态等数据存放在内存,关闭硬盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。对DDR的耗电情况进行优化是S3性能的关键,大多数手持设备都是用S3待机。

    S4 aka STD(suspend to disk),挂起到硬盘,简称休眠。把运行状态等数据存放在硬盘上某个文件或者某个特定的区域,关闭硬盘、外设等设备,进入关机状态。此时计算机完全关闭,不耗电。恢复时计算机从休眠文件/分区中读出数据,回到休眠前的状态,恢复速度较慢

    系统休眠模型给我的感觉是以整机角度进行省电。
    S3类似电脑的睡眠,在教长时间不使用电脑后,电脑黑屏,再次敲击键盘迅速显示桌面,原来的工作内容仍不变。
    S4类似电脑的休眠,在长时间不使用电脑后,电脑黑屏,再次敲击键盘无反应,按下电源键,开机,原来的工作内容仍不变。

    对于嵌入式设备,更多的是使用S3,将数据暂时放在内存里,以实现快速恢复,就像手机的电源键按下黑屏,再次按下迅速亮屏。

    在Linux中,通过cat /sys/power/state可以得知当前设备支持的节能模式,一般情况有如下选项:

    • freeze:不涉及具体的Hardware或Driver,只是冻结所有的进程,包括用户空间进程及内核线程,能节省的能量较少,使用场景不多;
    • standby:前面的S1状态,CPU处于浅睡眠模式,主要针对CPU功耗;
    • mem:前面的S3状态,Suspend to RAM;
    • disk:前面的S4状态,Suspend to Disk;

    需要设置以上模式,只需echo mem > /sys/power/state即可。

    1.2 Runtime电源管理模型

    Runtime电源管理模型给我的感觉是以模块角度进行省电。
    某种程度上是“高内聚和低耦合”的体现。
    每个设备(包括CPU)都处理好自身的电源管理工作,尽量以最低的能耗完成交代的任务,尽量在不需要工作的时候进入低功耗状态,尽量不和其它模块有过多耦合。每个设备都是最节省的话,整个系统一定是最节省的。

    2. 系统睡眠模型suspend

    2.1 Suspend流程分析

    suspend的流程还是挺复杂的,向/sys/power/state写入命令后再到唤醒,将进行以下流程:

    • 对源码进行分析,其休眠过程如下:

      驱动程序里休眠相关的电源管理函数的调用过程:prepare—>suspend—>suspend_late—>suspend_noirq

    • 对源码进行分析,其唤醒过程如下:

      驱动程序里唤醒相关的电源管理函数的调用过程:resume_noirq—>resume_early—>resume->complete

    对于驱动程序,我们主要关心Device PM(针对每一个驱动)和少量Platform dependent PM(针对CPU芯片相关)的内容。

    2.2 使用Suspend功能

    首先将suspend功能加入内核:

    Power management options  --->
        [*] Suspend to RAM and standby
    

    这里默认是勾选上了的,就不管了。

    进入Tiny4412内核,尝试休眠echo mem > /sys/power/state,系统提示**No wake-up sources!**。

    可见,要进入休眠,必须要有唤醒源,没有唤醒源,休眠也没有意义。

    2.2.1 设置唤醒源

    唤醒源最常见的就是按键中断,就如同手机进入锁屏状态下,按下电源键唤醒一样,因此先写一个按键驱动。

    • 原理图:

      底板上有四个按键,分别连在GPX3_2、GPX3_3、GPX3_4、GPX3_5,引脚状态常高,按键按下变低电平。

    • 设备树

        button_interrupt: button_interrupt {
            compatible         = "tiny4412,button_interrupt";
            tiny4412,gpx3_2 = <&gpx3 2 GPIO_ACTIVE_HIGH>;
            tiny4412,gpx3_3 = <&gpx3 3 GPIO_ACTIVE_HIGH>;
            tiny4412,gpx3_4 = <&gpx3 4 GPIO_ACTIVE_HIGH>;
            tiny4412,gpx3_5 = <&gpx3 5 GPIO_ACTIVE_HIGH>;
        };      
    
            //设置为中断唤醒源
            irq_set_irq_wake(irq, 1);
    
    #ifdef CONFIG_PM_SLEEP
    static int usb4604_i2c_suspend(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct usb4604 *hub = i2c_get_clientdata(client);
    
    	usb4604_switch_mode(hub, USB4604_MODE_STANDBY);
    
    	return 0;
    }
    
    static int usb4604_i2c_resume(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct usb4604 *hub = i2c_get_clientdata(client);
    
    	usb4604_switch_mode(hub, hub->mode);
    
    	return 0;
    }
    #endif
    
    static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
    		usb4604_i2c_resume);       
    

    2.2.2 休眠唤醒(未成功)

    加入中断源后,休眠过程不再提示No wake-up sources!,看样子休眠应该成功了。

    此时,按下按键,板子并未唤醒,琢磨了一阵,初步怀疑有以下原因:

    • 唤醒的时候,应该需要uboot配合,uboot读取某个寄存器来判断是正常启动还是唤醒;
    • Exynos4412的PMU特性没摸透,可能需要其它额外的操作;
    • Exynos4412启动时的BL1和BL2,可能也有影响;

    这里先卡住,继续后面。

    2.3 使驱动支持Suspend

    2.3.1 通知Notifier

    前面的suspend流程分析里面,
    冻结APP之前,使用pm_notifier_call_chain(PM_SUSPEND_PREPARE)来通知驱动程序;
    重启APP之后,使用pm_notifier_call_chain(PM_POST_SUSPEND)来通知驱动程序;

    因此,如果驱动程序有事情在上述时机要处理,可以使用notifier机制。
    使用步骤:

    a.定义notifier_block结构体

    static struct notifier_block lcd_pm_notif_block = {
    	.notifier_call = lcd_suspend_notifier,
    };
    

    b.notifier操作函数

    static int lcd_suspend_notifier(struct notifier_block *nb, unsigned long event, void *dummy)
    {
    	switch (event) {
    	case PM_SUSPEND_PREPARE:
    		printk("lcd suspend notifiler test: PM_SUSPEND_PREPARE\n");
    		return NOTIFY_OK;
    	case PM_POST_SUSPEND:
    		printk("lcd suspend notifiler test: PM_POST_SUSPEND\n");
    		return NOTIFY_OK;
    
    	default:
    		return NOTIFY_DONE;
    	}
    }
    

    c.注册notifier
    在驱动init()或probe()里注册:

    	ret = register_pm_notifier(&lcd_pm_notif_block);
        if(ret) {
            printk("failed to register pm notifier.\n");
            return  -EINVAL;
        }
    

    在前面LCD驱动上修改lcd_drv.c,测试如下:

    2.3.2 Suspend和Resume

    前面的notifier只是通知,在冻结APP之前重启APP之后通知,
    而电源管理应该刚好相反,是在冻结APP之后重启APP之前对驱动的电源进行控制,
    这就需要suspendresume来实现。

    • a.在platform_driver里的driver里添加pm结构体:
    static struct platform_driver lcd_driver =
    {
        .driver        = {
            .name           = "lcd_s702",
            .pm             = &lcd_pm,
            .of_match_table = of_match_ptr(lcd_dt_ids),
        },
        .probe         = lcd_probe,
        .remove        = lcd_remove,
    };
    
    • b.设置pm成员函数:
    static struct dev_pm_ops lcd_pm = {
    	.suspend = s702_lcd_suspend,
    	.resume  = s702_lcd_resume,	
    };
    
    • c.编写成员函数:
      {% codeblock lang:c %}
      static int s702_lcd_suspend(struct device *dev)
      {
      //lcd休眠操作
      //Direct Off: ENVID and ENVID_F are set to “0” simultaneously.
      unsigned int temp;

      printk(“enter %s\n”, func);

      temp = readl(lcd_regs_base + VIDCON0);
      temp &= ~(0x01 << 1 | 0x01 << 0);
      writel(temp, lcd_regs_base + VIDCON0);

      return 0;
      }

    static int s702_lcd_resume(struct device *dev)
    {
    //lcd唤醒操作
    //Display On: ENVID and ENVID_F are set to “1”.
    unsigned int temp;

    printk("enter %s\n", __func__);
    
    temp = readl(lcd_regs_base + VIDCON0);
    writel(temp | (0x01 << 1) | (0x01 << 0), lcd_regs_base + VIDCON0);
    
    return 0;
    

    }
    {% endcodeblock %}

    这里只是简单的关闭/打开显示,理论上的操作应该是:
    休眠时先备份所有LCD相关寄存器,恢复时再恢复所有寄存器,以及其它可能操作,比如重新开启时钟等。

    同理,因为LCD显示和backlight是分开的,因此需要在backlight里也进行类似操作。

    3.Runtime电源管理模型

    前面的suspend系统睡眠模型是将整个系统进行休眠,但如果需要在系统运行时,单独对某个模块进行休眠,就需要Runtime电源管理模型,这两个模型互相协作,才能最大的发挥电源管理的效果。

    Runtime电源管理模型的原理比较简单,就是计数,
    当该设备驱动被使用时就加1,放弃使用时就减1,
    计数大于1时,就打开该设备的电源,等于0时就关闭电源。

    Runtime PM相关的函数:
    a. 使能/禁止 Runtime PM:pm_runtime_enable / pm_runtime_disable (修改disable_depth变量)
    b. 增加计数/减少计数:pm_runtime_get_sync / pm_runtime_put_sync (修改usage_count变量)
    c. 回调函数 暂停/恢复/空闲:runtime_suspend / runtime_resume / runtime_idle

    3.1 Runtime流程分析

    • 调用pm_runtime_get_sync增加使用次数以及恢复的流程如下:

    • 调用pm_runtime_put_sync减少使用次数以及暂停的流程如下:

    前面的两个流程,只看到了runtime_resumeruntime_idle的调用,没有看到runtime_suspend
    实际上,如果设备不提供runtime_idle, 则最终会调用runtime_suspend

    3.2 使用Runtime功能

    首先将Runtime功能加入内核,但本内核4.13.9里没找到相关选项,应该默认已经加入到内核里面了。

    • 调用方式一
      驱动程序提供接口, APP来调用。
      在驱动函数的open()close()里,增加和减少引用计数。
      APP调用驱动的时候就能相应的恢复、暂停设备。

    • 调用方式二
      直接操作应用层文件:
      恢复:

    echo on >  /sys/devices/.../power/control
    

    流程:control_store(drivers\base\power\sysfs.c) -> pm_runtime_forbid -> atomic_inc -> rpm_resume

    暂停:

    echo auto >  /sys/devices/.../power/control
    

    流程:control_store(drivers\base\power\sysfs.c) -> pm_runtime_allow -> atomic_dec_and_test -> rpm_idle

    3.3 使驱动支持Runtime

    • a.在platform_driver里的driver里添加pm结构体:(和前面的一样,这里就无需操作)
    static struct platform_driver lcd_driver =
    {
        .driver        = {
            .name           = "lcd_s702",
            .pm             = &lcd_pm,
            .of_match_table = of_match_ptr(lcd_dt_ids),
        },
        .probe         = lcd_probe,
        .remove        = lcd_remove,
    };
    
    • b.设置pm成员函数:
    static struct dev_pm_ops lcd_pm =
    {
        .suspend = s702_lcd_suspend,
        .resume  = s702_lcd_resume,
        .runtime_suspend = s702_lcd_suspend,
        .runtime_resume  = s702_lcd_resume,
    };
    

    添加runtime_suspendruntime_resume,runtime和suspend的暂停配置是一样的,直接使用前面的。

    • c.编写成员函数:(和前面的一样,这里就无需操作)
      {% codeblock lang:c %}
      static int s702_lcd_suspend(struct device *dev)
      {
      //lcd休眠操作
      //Direct Off: ENVID and ENVID_F are set to “0” simultaneously.
      unsigned int temp;

      printk(“enter %s\n”, func);

      temp = readl(lcd_regs_base + VIDCON0);
      temp &= ~(0x01 << 1 | 0x01 << 0);
      writel(temp, lcd_regs_base + VIDCON0);

      return 0;
      }

    static int s702_lcd_resume(struct device *dev)
    {
    //lcd唤醒操作
    //Display On: ENVID and ENVID_F are set to “1”.
    unsigned int temp;

    printk("enter %s\n", __func__);
    
    temp = readl(lcd_regs_base + VIDCON0);
    writel(temp | (0x01 << 1) | (0x01 << 0), lcd_regs_base + VIDCON0);
    
    return 0;
    

    }
    {% endcodeblock %}

    • d.使能Runtime:
      对于Runtime PM,默认状态下设备的状态是Suspended,
      如果硬件上它是运行状态,需要调用pm_runtime_set_active()来修改它的状态,
      然后调用pm_runtime_enable()来使能Runtime PM。

    probe()函数的后面添加:

        pm_runtime_set_active(&pdev->dev);
        pm_runtime_enable(&pdev->dev);
    

    反之,还要在remove()里禁止:

    pm_runtime_disable(&pdev->dev);
    
    • e.修改计数:
      一般在open()release()里面增加和减少引用计数:
      {% codeblock lang:c %}
      static int s702_lcd_open(struct fb_info *info, int user)
      {
      struct device *dev = info->dev;
      int ret;

      printk(“enter %s\n”, func);

      ret = pm_runtime_get_sync(dev);
      if (ret < 0 && ret != -EACCES)
      {
      pm_runtime_put_sync(dev);
      return ret;
      }

      return 0;
      }
      static int s702_lcd_release(struct fb_info *info, int user)
      {
      struct device *dev = info->dev;

      printk(“enter %s\n”, func);

      pm_runtime_put_sync(dev);

      return 0;
      }

    static struct fb_ops tiny4412_lcdfb_ops =
    {
    .owner = THIS_MODULE,
    .fb_setcolreg = cfb_setcolreg, //设置调色板,实现伪颜色表
    .fb_fillrect = cfb_fillrect, //填充矩形
    .fb_copyarea = cfb_copyarea, //数据复制
    .fb_imageblit = cfb_imageblit, //图形填充

    .fb_open            = s702_lcd_open,
    .fb_release         = s702_lcd_release
    

    };
    {% endcodeblock %}

    • f.优化——加入延时机制:
      现在的程序基本完成,测试的时候,先加载backlight驱动insmod backlight_drv.ko,运行背光应用程序设置亮度./app 200
      然后加载LCD驱动insmod lcd_drv.ko,运行图片显示应用程序jpg_rgb显示图像./jpg_rgb cq.jpg,结果并没有显示图像,
      手动的echo on > /sys/devices/platform/11c00000.lcd_s702/power/control才正常显示图像。

    上述流程中,运行图片显示应用程序时,先open()了一次,引用计数加1,程序调用完又马上close,引用计数减1,导致看不到显示,
    重新操作/sys/devices/platform/11c00000.lcd_s702/power/control就立即显示了图像。

    对于正常的使用情景是,运行应用程序,立即图片显示,然后维持显示一段时间,如果有操作继续显示,没有的话再自己熄灭。
    因此,想要实现上面的功能,还需要加入自动休眠。

    在之前的probe()中加入pm_runtime_use_autosuspend():
    {% codeblock lang:c %}
    //Runtime
    pm_runtime_use_autosuspend(&pdev->dev);//add autosleep
    pm_runtime_set_autosuspend_delay(&pdev->dev, 5000);

    pm_runtime_set_active(&pdev->dev);
    pm_runtime_enable(&pdev->dev);
    

    {% endcodeblock %}

    同时,release()也要修改:
    {% codeblock lang:c %}
    static int s702_lcd_open(struct fb_info *info, int user)
    {
    struct device *dev = info->dev;
    int ret;

    printk("enter %s\n", __func__);
    
    ret = pm_runtime_get_sync(dev);
    if (ret < 0 && ret != -EACCES)
    {
        pm_runtime_put_sync(dev);
    
        return ret;
    }
    
    return 0;
    

    }
    static int s702_lcd_release(struct fb_info *info, int user)
    {
    struct device *dev = info->dev;

    printk("enter %s\n", __func__);
    
    //pm_runtime_put_sync(dev);
    pm_runtime_mark_last_busy(dev);
    pm_runtime_put_sync_autosuspend(dev);
    
    return 0;
    

    }
    {% endcodeblock %}

    此时,加载驱动后,运行应用程序,屏幕显示,5s后,屏幕自动熄灭,再次运行程序或者修改control来重新显示。

    通过函数pm_runtime_set_autosuspend_delay()或修改echo xx > /sys/devices/.../power/autosuspend_delay_ms来修改自动休眠时间。

    完整代码见Github

    4.regulator系统

    前面的两个电源管理模型偏“软”,regulator系统偏“硬”,
    在复杂的单板中,有专门的电源管理芯片控制各个模块电源,regulator系统就是为这个电源芯片编写驱动,实现电源管理。

    4.1 regulator框架

    ①Regulator(稳定器):指可以自动维持恒定电压(voltage)或电流(current)的装置,一般指电源芯片。在嵌入式设备中,基本上每一种电压,都是经过regulator输出的;
    ②③Consumer(使用者):使用电源的装置,Regulator是给Consumer供电的;
    ④Machine(单板):使用软件语言(struct regulator_init_data),静态的描述regulator在板级的物理现状,包含:
      a.级联关系:Regulator A的输出是Regulator B的输入,Regulator A就是Supply regulator,B是Consumer regulator
      b.约束限制:Regulator Constraints,比如电压/电流最大值/最小值、允许的操作等;

    从设备驱动的角度看,regulator系统比较简单,
    Machine提供Supply与Consumer的对应关系、单板相关的约束条件(device);
    Regulator提供电源芯片的控制函数,如使能/去能、设置电压/电流等(driver);
    Consumer调用Regulator相关函数控制电源的开关、调节(use);
    即一个描述关系,一个提供相关函数,一个调用相关函数。

    4.2 regulator流程

    4.3 regulator驱动

    regulator系统仍然是采用***总线设备驱动模型***。
    device采用c文件或设备树的形式,提供硬件相关信息;
    driver加载后,一但和device名字匹配,就调用probe()函数注册register,并绑定操作函数;

    后面将使用两种实现regulator驱动。
    这两种方式的核心都是一样的,
    device先提供Supply与Consumer的对应关系、单板相关的约束条件;
    driver提供电源芯片的控制函数,如使能/去能、设置电压/电流等。

    4.3.1 C文件方式

    #if 1

    static struct regulator_consumer_supply tiny4412_regulator_supplies[] =
    {
    REGULATOR_SUPPLY(“VCC_LCD”, “11c00000.lcd_s702”),//consumer的电源引脚名称;consumer的名字
    };

    #else

    static struct regulator_consumer_supply tiny4412_regulator_supplies[] =
    {
    REGULATOR_SUPPLY(“VCC_LCD”, “lcd_s702”),
    };

    #endif

    static struct regulator_init_data tiny4412_regulator_init_data =
    {
    .constraints = {
    //.name = “tiny4412_regulator”,
    .min_uV = 1000000,
    .max_uV = 1000000,
    .valid_modes_mask = REGULATOR_MODE_NORMAL,
    .valid_ops_mask = REGULATOR_CHANGE_STATUS,
    .boot_on = 0,
    .always_on = 0,
    },
    .num_consumer_supplies = 1,
    .consumer_supplies = tiny4412_regulator_supplies,
    };

    static void tiny4412_regulator_release(struct device *dev)
    {
    }

    static struct platform_device tiny4412_regulator_dev =
    {
    .name = “tiny4412_regulator”,
    .id = -1,
    .dev = {
    .release = tiny4412_regulator_release,
    .platform_data = &tiny4412_regulator_init_data,
    },
    };

    static int tiny4412_regulator_machine_init(void)
    {
    printk(“enter %s\n”, func);

    platform_device_register(&tiny4412_regulator_dev);
    return 0;
    

    }

    static void tiny4412_regulator_machine_exit(void)
    {
    printk(“enter %s\n”, func);

    platform_device_unregister(&tiny4412_regulator_dev);
    

    }

    module_init(tiny4412_regulator_machine_init);
    module_exit(tiny4412_regulator_machine_exit);

    MODULE_LICENSE(“GPL”);
    MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
    MODULE_DESCRIPTION(“Tiny4412 machine driver.”);
    MODULE_ALIAS(“Exynos4412_machine”);
    MODULE_VERSION(“V1.0”);
    {% endcodeblock %}

    static int regulator_states = 0;
    static int tiny4412_regulator_enable(struct regulator_dev *rdev)
    {
    printk(“enter %s\n”, func);

    printk("------LCD Power Open.------\n");
    regulator_states = 1;
    
    return 0;
    

    }

    static int tiny4412_regulator_disable(struct regulator_dev *rdev)
    {
    printk(“enter %s\n”, func);

    printk("------LCD Power Close.------\n");
    regulator_states = 0;
    
    return 0;
    

    }

    static int tiny4412_regulator_is_enabled(struct regulator_dev *rdev)
    {
    printk(“enter %s\n”, func);

    printk("------LCD Power Test.------\n");
    if (regulator_states)
        return 1;
    else
        return 0;
    

    }

    static struct regulator_ops tiny4412_regulator_ops =
    {
    .enable = tiny4412_regulator_enable,
    .disable = tiny4412_regulator_disable,
    .is_enabled = tiny4412_regulator_is_enabled,
    };

    static struct regulator_desc tiny4412_regulator_desc =
    {
    .name = “tiny4412_regulator”,
    .ops = &tiny4412_regulator_ops,
    .type = REGULATOR_VOLTAGE,//电压源
    .id = 0,
    .owner = THIS_MODULE,
    .n_voltages = 1,//能提供的电压数量
    };

    static struct regulator_dev *tiny4412_regulator_dev;
    static int tiny4412_regulator_probe(struct platform_device *pdev)
    {
    struct regulator_config config = { };
    config.dev = &pdev->dev;
    config.init_data = dev_get_platdata(&pdev->dev);

    printk("enter %s\n", __func__);
    
    tiny4412_regulator_dev = devm_regulator_register(&pdev->dev, &tiny4412_regulator_desc, &config);
    if (IS_ERR(tiny4412_regulator_dev))
    {
        printk("devm_regulator_register error!\n");
        return PTR_ERR(tiny4412_regulator_dev);
    }
    
    return 0;
    

    }

    static int tiny4412_regulator_remove(struct platform_device *pdev)
    {
    printk(“enter %s\n”, func);

    devm_regulator_unregister(&pdev->dev, tiny4412_regulator_dev);
    
    return 0;
    

    }

    struct platform_driver tiny4412_regulator_drv =
    {
    .probe = tiny4412_regulator_probe,
    .remove = tiny4412_regulator_remove,
    .driver = {
    .name = “tiny4412_regulator”,
    }
    };

    static int tiny4412_regulator_init(void)
    {
    printk(“enter %s\n”, func);

    platform_driver_register(&tiny4412_regulator_drv);
    return 0;
    

    }

    static void tiny4412_regulator_exit(void)
    {
    printk(“enter %s\n”, func);

    platform_driver_unregister(&tiny4412_regulator_drv);
    

    }

    module_init(tiny4412_regulator_init);
    module_exit(tiny4412_regulator_exit);

    MODULE_LICENSE(“GPL”);
    MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
    MODULE_DESCRIPTION(“Tiny4412 regulator driver.”);
    MODULE_ALIAS(“Exynos4412_regulator”);
    MODULE_VERSION(“V1.0”);
    {% endcodeblock %}

    • lcd_drv
      加载前面的machine.koregulator.ko,名字匹配后调用probe()注册regulator
      在LCD驱动中,若想使用regulator系统,需要先在LCD驱动的probe()根据名字获取对应regulator
        //regulator
        tiny4412_regulator = regulator_get(&pdev->dev, "VCC_LCD");
        if (IS_ERR(tiny4412_regulator))
        {
            printk("regulator_get error!\n");
            return -EIO;
        }
    

    再在suspend()resume()封面便调用regulator_disable()regulator_enable()

    此时,如果使用 系统睡眠模型Runtime电源模型 进行休眠操作,就会调用到regulator系统的操作函数,实现电源管理芯片的关闭。

    使用完后,释放regulator

    static int lcd_remove(struct platform_device *pdev)
    {
        //Direct Off: ENVID and ENVID_F are set to "0" simultaneously.
        unsigned int temp;
        
        temp = readl(lcd_regs_base + VIDCON0);
        temp &= ~(0x01 << 1 | 0x01 << 0);
        writel(temp, lcd_regs_base + VIDCON0);
    
        regulator_put(tiny4412_regulator);
    
        pm_runtime_disable(&pdev->dev);
    
        unregister_framebuffer(tiny4412_lcd);
        dma_free_writecombine(NULL, tiny4412_lcd->fix.smem_len, tiny4412_lcd->screen_base, tiny4412_lcd->fix.smem_start);
        framebuffer_release(tiny4412_lcd);
    
        return 0;
    }
    

    4.3.2 设备树方式

    与前面的操作几乎一样,只不过是在dts实现device

    • dts
      regulators节点下添加新的regulator,设置约束条件
    	regulators {
        		compatible = "simple-bus";
        		#address-cells = <1>;
        		#size-cells = <0>;
    
            mmc_reg: regulator@0{
    			compatible = "regulator-fixed";
    			reg = <0>;
    			regulator-name = "VMEM_VDD_2.8V";
    			regulator-min-microvolt = <2800000>;
    			regulator-max-microvolt = <2800000>;
    		}; 
    
            lcd_reg: regulator@1{
    			compatible = "tiny4412,lcd_regulator";
    			regulator-name = "VCC_LCD";
    			regulator-min-microvolt = <1200000>;
    			regulator-max-microvolt = <1200000>;
    		};
    	};
    

    在lcd节点下,添加级联关系

        lcd_s702@11C00000 {
            compatible = "tiny4412, lcd_s702";
            reg = <0x11C00000  0x20c0 0x10010210 0x08 0x10023c80 0x04 0x1003c000 0x1000>;
            pinctrl-names = "default";
            pinctrl-0 = <&lcd_s702>;
            clocks = <&clock CLK_FIMD0 &clock CLK_ACLK160>;
            clock-names = "fimd0","aclk160";
            vlcd-supply = <&lcd_reg>;
            status = "okay";
        }; 
    

    其中vlcd-supply与前面的regulator联系了起来。

    static int regulator_states = 0;
    static int tiny4412_regulator_enable(struct regulator_dev *rdev)
    {
    printk(“enter %s\n”, func);

    printk("------LCD Power Open.------\n");
    regulator_states = 1;
    
    return 0;
    

    }

    static int tiny4412_regulator_disable(struct regulator_dev *rdev)
    {
    printk(“enter %s\n”, func);

    printk("------LCD Power Close.------\n");
    regulator_states = 0;
    
    return 0;
    

    }

    static int tiny4412_regulator_is_enabled(struct regulator_dev *rdev)
    {
    printk(“enter %s\n”, func);

    printk("------LCD Power Test.------\n");
    if (regulator_states)
        return 1;
    else
        return 0;
    

    }

    static struct regulator_ops tiny4412_regulator_ops =
    {
    .enable = tiny4412_regulator_enable,
    .disable = tiny4412_regulator_disable,
    .is_enabled = tiny4412_regulator_is_enabled,
    };

    static struct regulator_desc tiny4412_regulator_desc =
    {
    .name = “tiny4412_regulator_dev”,
    .ops = &tiny4412_regulator_ops,
    .type = REGULATOR_VOLTAGE,//电压源
    .id = 0,
    .owner = THIS_MODULE,
    .n_voltages = 1,//能提供的电压数量
    };

    static struct regulator_dev *tiny4412_regulator_dev;
    static int tiny4412_regulator_probe(struct platform_device *pdev)
    {
    struct regulator_config config = { };
    config.dev = &pdev->dev;
    config.init_data = dev_get_platdata(&pdev->dev);

    printk("enter %s\n", __func__);
    
    tiny4412_regulator_dev = devm_regulator_register(&pdev->dev, &tiny4412_regulator_desc, &config);
    if (IS_ERR(tiny4412_regulator_dev))
    {
        printk("devm_regulator_register error!\n");
        return PTR_ERR(tiny4412_regulator_dev);
    }
    
    return 0;
    

    }

    static int tiny4412_regulator_remove(struct platform_device *pdev)
    {
    printk(“enter %s\n”, func);

    devm_regulator_unregister(&pdev->dev, tiny4412_regulator_dev);
    
    return 0;
    

    }

    static const struct of_device_id regulators_of_match[] =
    {
    { .compatible = “tiny4412,lcd_regulator” },
    { },
    };
    MODULE_DEVICE_TABLE(of, regulators_of_match);

    struct platform_driver tiny4412_regulator_drv =
    {
    .probe = tiny4412_regulator_probe,
    .remove = tiny4412_regulator_remove,
    .driver = {
    .name = “tiny4412_regulator_drv”,
    .of_match_table = of_match_ptr(regulators_of_match),
    }
    };

    static int tiny4412_regulator_init(void)
    {
    printk(“enter %s\n”, func);

    platform_driver_register(&tiny4412_regulator_drv);
    return 0;
    

    }

    static void tiny4412_regulator_exit(void)
    {
    printk(“enter %s\n”, func);

    platform_driver_unregister(&tiny4412_regulator_drv);
    

    }

    module_init(tiny4412_regulator_init);
    module_exit(tiny4412_regulator_exit);

    MODULE_LICENSE(“GPL”);
    MODULE_AUTHOR(“hceng huangcheng.job@foxmail.com”);
    MODULE_DESCRIPTION(“Tiny4412 regulator driver.”);
    MODULE_ALIAS(“Exynos4412_regulator”);
    MODULE_VERSION(“V1.0”);
    {% endcodeblock %}

    • lcd_drv
      和前面的使用完全一致。

    参考资料:
    韦东山第三期项目视频_电源管理
    蜗窝科技

    展开全文
  • Zilker Labs推出据称是业界首款整合了全数字电源管理功能的3A电源转换单芯片解决方案。该公司表示,ZL2105将Zilker Labs的创新Digital-DC技术扩展到了低功耗应用领域中,并且在6×6毫米的小型封装中将同步降压转换器...
  • LINUX电源管理

    2020-11-30 15:20:31
    Linux的系统suspend和resume Linux内核 runtime_PM 框架 Linux内核电源管理综述

    Linux的系统suspend和resume

    Linux内核 runtime_PM 框架

    Linux内核电源管理综述

    翻译:Linux的电源管理架构

    Linux电源管理-Runtime PM

    Linux电源管理(1)_整体架构

    Linux动态频率调节系统CPUFreq之一:概述

    Linux动态频率调节系统CPUFreq之二:核心(core)架构与API

    Linux动态频率调节系统CPUFreq之三:governor

    Linux的系统suspend和resume

    android 电池(三):android电池系统

    healthd 升级

    高通功耗问题分析

    Linux在消费电子领域的应用相当普遍,而对于消费电子产品,省电是一个重要的议题。

    1、Linux电源管理的总体架构

    2、CPUFreq、CPUIdle、CPU热插拔以及底层的基础设施Regulator(稳压电源管理)、OPP(某个domain所支持的<频率,电压>对的集合)以及电源管理的调试工具PowerTop

    3、系统挂起到RAM的过程以及设备驱动是如何对挂起到RAM支持的

    4、设备驱动的运行时挂起

    备注:

    PM QoS(Quality of Service,电源管理相关的服务质量)

    DVFS(Dynamic Voltage Frequency Scaling 动态电压频率扩缩,功耗会降的更低)

    OPP(某个domain所支持的<频率,电压>对的集合),Operating Performance Point简称OPP

    Regulator(稳压电源管理)

    挂起->休眠

    恢复->唤醒

    Linux电源管理全局架构

    Linux电源管理非常复杂,牵扯到系统级的待机、频率电压变换、系统空闲时的处理以及每个设备驱动对于系统待机的支持和每个设备的运行时电源管理,可以说和系统中的每个设备驱动都息息相关。

    对于消费电子产品来说,电源管理相当重要。因此,这部分工作往往在开发周期中占据相当大的比重,图1呈现了Linux内核电源管理的整体架构。大体可以归纳为如下几类:

    1. CPU在运行时根据系统负载进行动态电压和频率变换的CPUFreq

    2. CPU在系统空闲时根据空闲的情况进行低功耗模式的CPUIdle

    3. 多核系统下CPU的热插拔支持

    4. 系统和设备对于延迟的特别需求而提出申请的PM QoS,它会作用于CPUIdle的具体策略

    5. 设备驱动针对系统Suspend to RAM/Disk的一系列入口函数

    6. SoC进入suspend状态、SDRAM自刷新的入口

    7. 设备的runtime(运行时)动态电源管理,根据使用情况动态开关设备

    8. 底层的时钟、稳压器、频率/电压表(OPP模块完成)支撑,各驱动子系统都可能用到

    在这里插入图片描述

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,364
精华内容 5,745
关键字:

电源管理