当前位置:文档之家› 《Linux设备驱动开发详解:基于最新的Linux 4.0内核》19. Linux电源管理系统架构和驱动

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》19. Linux电源管理系统架构和驱动

以下电子书来源于宋宝华《Linux设备驱动开发详解:基于最新的Linux 4.0内核》第19章《Linux电源管理系统架构和驱动》本章导读Linux在消费电子领域的应用已经铺天盖地,而对于消费电子产品而言,省电是一个重要的议题。

本章将介绍Linux设备树(Device Tree)的起源、结构和因为设备树而引起的驱动和BSP 变更。

19.1节阐述了Linux电源管理的总体架构。

19.2~19.8节分别论述了CPUFreq、CPUIdle、CPU热插拔以及底层的基础设施Regulator、OPP以及电源管理的调试工具PowerTop。

19.9节讲解了系统Suspend to RAM的过程以及设备驱动如何提供对Suspend to RAM的支持。

19.10节讲解了设备驱动的Runtime suspend。

本章是相对《Linux设备驱动开发详解(第2版)》全新的一章内容,也是Linux设备驱动工程师必备的知识体系。

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

对于消费电子产品来说,电源管理相当重要。

因此,这部分工作往往在开发周期中占据相当大的比重,图19.1呈现了Linux内核电源管理的整体架构。

大体可以归纳为如下几类:1.CPU在运行时根据系统负载进行动态电压和频率变换的CPUFreq2.CPU在系统空闲时根据空闲的情况进行低功耗模式的CPUIdle3.多核系统下CPU的热插拔支持4.系统和设备对于延迟的特别需求而提出申请的PM QoS,它会作用于CPUIdle的具体策略5.设备驱动针对系统Suspend to RAM/Disk的一系列入口函数6.SoC进入suspend状态、SDRAM自刷新的入口7.设备的runtime(运行时)动态电源管理,根据使用情况动态开关设备8.底层的时钟、稳压器、频率/电压表(OPP模块完成)支撑,各驱动子系统都可能用到图19.1 Linux电源管理系统架构2.CPUFreq驱动CPUFreq子系统位于drivers/cpufreq目录,负责进行运行过程中CPU频率和电压的动态调整,即DVFS(Dynamic Voltage Frequency Scaling,动态电压频率调整)。

运行时进行CPU 电压和频率调整的原因是:CMOS电路中的功耗与电压的平方成正比、与频率成正比(),因此降低电压和频率可降低功耗。

CPUFreq的核心层位于drivers/cpufreq/cpufreq.c,它为各个SoC的CPUFreq驱动的实现提供了一套统一的接口,并实现了一套notifier的机制,可以在CPUFreq的policy和频率改变的时候向其他模块发出通知。

另外,在CPU运行频率发生变化的时候,内核的loops_per_jiffy常数也会发生相应变化。

1.1 SoC的CPUFreq驱动实现具体的每个SoC的CPUFreq驱动实例只需要实现电压、频率表,以及从硬件层面完成这些变化。

CPUFreq核心层提供了如下API供SoC注册自身的CPUFreq驱动:int cpufreq_register_driver(struct cpufreq_driver *driver_data);其参数为一个cpufreq_driver结构体指针,实际上,cpufreq_driver封装了一个具体的SoC 的CPUFreq驱动的主体,该结构体形如代码清单19.1。

代码清单19.1 cpufreq_driver结构体1 struct cpufreq_driver {2 struct module *owner;3 char name[CPUFREQ_NAME_LEN];4 u8 flags;56 /* needed by all drivers */7 int (*init) (struct cpufreq_policy *policy);8 int (*verify) (struct cpufreq_policy *policy);910 /* define one out of two */11 int (*setpolicy) (struct cpufreq_policy *policy);12 int (*target) (struct cpufreq_policy *policy,13 unsigned inttarget_freq,14 unsigned int relation);1516 /* should be defined, if possible */17 unsigned int (*get) (unsigned intcpu);1819 /* optional */20 unsigned int (*getavg) (struct cpufreq_policy *policy,21 unsigned intcpu);22 int (*bios_limit) (intcpu, unsigned int *limit);2324 int (*exit) (struct cpufreq_policy *policy);25 int (*suspend) (struct cpufreq_policy *policy);26 int (*resume) (struct cpufreq_policy *policy);27 structfreq_attr **attr;28 };其中的owner成员一般被设置为THIS_MODULE;name成员是CPUFreq驱动名字,如drivers/cpufreq/s5pv210-cpufreq.c设置name为"s5pv210",如drivers/cpufreq/omap-cpufreq.c 设置name为"omap";flags是一些暗示性的标志,譬如,若设置了CPUFREQ_CONST_LOOPS,则是告诉内核loops_per_jiffy不会因为CPU频率的变化而变化。

init()成员是一个per-CPU初始化函数指针,每当一个新的CPU被注册进系统的时候,该函数就被调用,该函数接受一个cpufreq_policy的指针参数,在init()成员函数中,可进行如下设置:policy->cpuinfo.min_freqpolicy->cpuinfo.max_freq该CPU支持的最小频率和最大频率(单位是kHz)。

policy->cpuinfo.transition_latencyCPU进行频率切换所需要的延迟(单位是纳秒)policy->curCPU当前频率policy->policypolicy->governorpolicy->minpolicy->max定义该CPU的缺省policy,以及缺省policy情况下该policy支持的最小、最大CPU频率。

verify()成员函数用于对用户的CPUFreq policy设置进行有效性验证和数据修正。

每次用户设定一个新policy时,该函数根据老的policy和新的policy,检验新policy设置的有效性并对无效设置进行必要的修正。

在该成员函数的具体实现中,常用到如下辅助函数:cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned intmin_freq, unsigned intmax_freq);setpolicy()成员函数接受一个policy参数(包含policy->policy, policy->min和policy->max 等成员),实现了这个成员函数的CPU一般具备在一个范围(limit, 从policy->min到policy->max)里面自动调整频率的能力。

目前少数驱动如intel_pstate.c和longrun.c包含这样的成员函数。

而绝大多数CPU都不会实现此函数,一般实现target()成员函数,target()的参数直接就是一个指定的频率。

target()成员函数用于将频率调整到一个指定的值,接受3个参数:policy、target_freq 和relation。

target_freq是目标频率,实际驱动总是要设定真实的CPU频率到最接近于target_freq,并且设定的频率必须位于policy->min到policy->max之间。

在设定频率接近target_freq的情况下,relation若为CPUFREQ_REL_L,暗示设置的频率应该大于或等于target_freq;relation若为CPUFREQ_REL_H,暗示设置的频率应该小于或等于target_freq。

下表描述了setpolicy()和target()所针对的CPU以及调用方式上的区别:由于芯片内部PLL和分频器的关系,ARM SoC一般不具备独立调整频率的能力,往往SoC 的CPUFreq驱动会提供一个频率表,在该表的范围内进行变更,所以一般实现target()成员函数。

CPUFreq核心层提供了一组频率表相关的辅助API。

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,struct cpufreq_frequency_table *table);它是cpufreq_driver的init()成员函数的助手,用于将policy->min和policy->max设置为与cpuinfo.min_freq和cpuinfo.max_freq相同的值。

int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,struct cpufreq_frequency_table *table);它是cpufreq_driver的verify()成员函数的助手,确保至少1个有效的CPU频率位于policy->min到policy->max范围内。

int cpufreq_frequency_table_target(struct cpufreq_policy *policy,struct cpufreq_frequency_table *table,unsigned int target_freq,unsigned int relation,unsigned int *index);它是cpufreq_driver的target()成员函数的助手,返回需要设定的频率在频率表中的索引。

相关主题