CPUFreq驱动

2023-11-07 09:10
文章标签 驱动 cpufreq

本文主要是介绍CPUFreq驱动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CPUFreq子系统位于 drivers/cpufreq目录下,负责进行运行过程中CPU频率和电压的动态调整,即DvFS( Dynamic Voltage Frequency Scaling,动态电压频率调整)。运行时进行CPU电压和频率调整的原因是:CMOS电路中的功耗与电压的平方成正比、与频率成正比(P∝fV2)因此降低电压和频率可降低功耗。
CPUFreq的核心层位于drivers/cpufreq/cpufreq,c下,它为各个SoC的CPUFreq驱动的实现提供了一套统一的接口,并实现了一套notifier机制,可以在 CPUFreq的策略和频率改变的时候向其他模块发出通知。另外,在CPU运行频率发生变化的时候,内核的 loops perify常数也会发生相应变化。

SOC的CPUFreq驱动实现

每个SoC的具体CPUFreq驱动实例只需要实现电压、频率表,以及从硬件层面完成这些变化。
CPUFreq核心层提供了如下API以供SoC注册自身的CPUFreq驱动:
int cpufreq_register_driver(struct cpufreq_driver *driver_data)
其参数为一个cpufreq_driver结构体指针,实际上,cpufreq_driver封装了一个具体的SoC的CPUFreq驱动的主体,该结构体形如代码如下所示。

struct cpufreq_driver {char            name[CPUFREQ_NAME_LEN];u8          flags;/* needed by all drivers */int (*init)     (struct cpufreq_policy *policy);int (*verify)   (struct cpufreq_policy *policy);/* define one out of two */int (*setpolicy)    (struct cpufreq_policy *policy);int (*target)   (struct cpufreq_policy *policy, /* Deprecated */unsigned int target_freq,unsigned int relation);int (*target_index) (struct cpufreq_policy *policy,unsigned int index);/* should be defined, if possible */unsigned int    (*get)  (unsigned int cpu);/* optional */int (*bios_limit)   (int cpu, unsigned int *limit);int (*exit)     (struct cpufreq_policy *policy);int (*suspend)  (struct cpufreq_policy *policy);int (*resume)   (struct cpufreq_policy *policy);struct freq_attr    **attr;
};

其中的 owner成员一般被设置为 THIS MODULE;name成员是CPUFreq驱动的名字,如drivers/cpufreq/s5pv210-cpufreq.c设置name为s5pv210, drivers/cpufreq/omap-cpufreq.c设置name为omap;falgs是一些暗示性的标志,譬如,若设置了 CPUFREQ_CONST_LOOPS,则是告诉内核loops_per_jiffy不会因为CPU频率的变化而变化。
init()成员是一个per-CPU初始化函数指针,每当一个新的CPU被注册进系统的时候,该函数就被调用,该函数接受一个cpufreq_policy的指针参数,在init()成员函数中,可进行如下设置:

policy->cpuinfo.min_freq
policy->cpuinfo.max_freq

上述代码描述的是该CPU支持的最小频率和最大频率(单位是kHz)。

policy->cur

上述代码描述的是CPU的当前频率;

policy->policy
policy->governor
policy->min
policy->max

上述代码定义该CPU的缺省策略,以及在缺省策略情况下,该策略支持的最小、最大CPU频率。
verify成员函数用于对用户的 CPUFreq策略设置进行有效性验证和数据修正。每当用户设定一个新策略时,该函数根据老的策略和新的策略,检验新策略设置的有效性并对无效设置进行必要的修正。在该成员函数的具体实现中,常用到如下辅助函数:

static inline void
cpufreq_verify_within_cpu_limits(struct cpufreq_policy *policy)
{cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,policy->cpuinfo.max_freq);
}

setpolicyo成员函数接受一个policy参数(包含policy->policypolicy->minpolicy->max等成员),实现了这个成员函数的CPU一般具备在一个范围(limit,从policy->minpolicy->max)里自动调整频率的能力。目前只有少数驱动(如intel_pstate.c和longrun.c)包含这样的成员函数,而绝大多数CPU都不会实现此函数,一般只实现target()成员函数,target()的参数直接就是一个指定的频率。
target()成员函数用于将频率调整到一个指定的值,接受3个参数: policy、 target_freq和relation, target freq是目标频率,实际驱动总是要设定真实的CPU频率到最接近于 target_feq,并且设定的频率必须位于 policy->min到 policy->max之间。在设定频率接近 target_feq的情况下, relation若为 CPUFREQ REL I,则暗示设置的频率应该大于或等于 target_freq; relation若为 CPUFREQ_REL_H,则暗示设置的频率应该小于或等于 target_freq。
表19.1描述了 setpolicy()和 target()所针对的CPU以及调用方式上的区别。

setpolicy()target()
CPU有在一定范围内独立调整频率的能力CPU只能指定频率
CPU freq policy 调用到setpolicy(),由CPU独立在一个范围内调整频率由CPU Freq核心层根据系统负载和策略综合决定目标频率

根据芯片内部PLL和分频器的关系, ARM SOC一般不具备独立调整频率的能力,往往SoC的 CPUFreq驱动会提供一个频率表,频率在该表的范围内进行变更,因此一般实现target()成员函数。
CPUFreq核心层提供了一组与频率表相关的辅助API。

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,struct cpufreq_frequency_table *table)
{unsigned int min_freq = ~0;unsigned int max_freq = 0;unsigned int i;for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {unsigned int freq = table[i].frequency;if (freq == CPUFREQ_ENTRY_INVALID) {pr_debug("table entry %u is invalid, skipping\n", i);continue;}pr_debug("table entry %u: %u kHz, %u driver_data\n",i, freq, table[i].driver_data);if (freq < min_freq)min_freq = freq;if (freq > max_freq)max_freq = freq;}policy->min = policy->cpuinfo.min_freq = min_freq;policy->max = policy->cpuinfo.max_freq = max_freq;if (policy->min == ~0)return -EINVAL;elsereturn 0;
}

它是 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的策略

SoCCPUFreq驱动只是设定了CPU的频率参数,以及提供了设置频率的途径,但是它并不会管CPU自身究竟应该运行在哪种频率上。究竟频率依据的是哪种标准,进行何种变化而这些完全由 CPUFreq的策略( policy)决定,这些策略如表19.2所示。

CPUFreq的策略策略实现的方式
cpufreq_ondemand平时以低速的方式运行,当系统负载提高需自动提高频率
cpufreq_performancecpu以最高频率运行,即scaling_max_freq
cpufreq_consevative字面含义是传统的、保守的,跟ondemand相似,区别在于动态频率在变更的时候采用渐进的方式
cpufreq_powersavecpu以最低频率运行,即scaling_min_freq
cpufreq_userspace让根用户通过sys节点scaling_setspeed设置频率

在 Android系统中,则增加了1个交互策略,该策略适合于对延迟敏感的U1交互任务,当有UI交互任务的时候,该策略会更加激进并及时地调整CPU频率。
总而言之,系统的状态以及CPUFreq的策略共同决定了CPU频率跳变的目标, CPUFreq核心层并将目标频率传递给底层具体SoC的 CPUFreq驱动,该驱动修改硬件,完成频率的变换,如图19.2所示。
811006-20180723165559101-843483131.png

用户空间一般可通过 /sys/devices/system/cpu/cpux/cpufreq节点来设置 CPUFreq。譬如,我们要设置 CPUFreq到700Mhz,采用 userspace策略,则运行如下命令:

echo userspace >/sys/devices/system/cpu/cpu0/cpufreq/scaling governor
echo 700000>/sys/devices/system/cpu/cpu/cpufreq/scaling set speed

CPUFreq的性能测试和调优

使用cpufreq-bench工具可以帮助工程师分析采用CPUFreq后对系统性能的影响;

CPUFreq通知

CPUFreq子系统会发出通知的情况有两种:CPUFreq的策略变化或者CPU运行频率变化。
在策略变化的过程中,会发送3次通知:

  • CPUFREQ ADJUST:所有注册的 notifier可以根据硬件或者温度的情况去修改范围(即 policy->min和 policy->max);
  • CPUFREQ INCOMPATIBLE:除非前面的策略设定可能会导致硬件出错,否则被注册的notifier不能改变范围等设定;
  • CPUFREQ NOTIFY:所有注册的notifier都会被告知新的策略已经被设置。在频率变化的过程中,会发送2次通知;
  • CPUFREQ PRECHANGE:准备进行频率变更;
  • CPUFREQ POSTCHANGE:已经完成频率变更。

notifier中的第3个参数是一个 cpufreq_freqs的结构体,包含cpu(CPU号)、old(过去的频率)和new(现在的频率)这3个成员。发送 CPUFREQ_PRECHANGE和 CPUFREQ_POSTCHANGE的代码如下:

int srcu_notifier_call_chain(struct srcu_notifier_head *nh,unsigned long val, void *v)

如果某模块关心 CPUFREQ_PRECHANGE或 CPUFREQ_POSTCHANGE事件,可简单地使用 Linux notifier机制监控。譬如, drivers/video/sallo0fbc在CPU频率变化过程中需对自身硬件进行相关设置,因此它注册了 notifier并在 CPUFREQ _PRECHANGE和CPUFREQ_POSTCHANGE情况下分别进行不同的处理,如代码清单19.3所示。

#ifdef CONFIG_CPU_FREQfbi->freq_transition.notifier_call = sa1100fb_freq_transition;fbi->freq_policy.notifier_call = sa1100fb_freq_policy;cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);cpufreq_register_notifier(&fbi->freq_policy, CPUFREQ_POLICY_NOTIFIER);
#endif/** CPU clock speed change handler.  We need to adjust the LCD timing* parameters when the CPU clock is adjusted by the power management* subsystem.*/
static int
sa1100fb_freq_transition(struct notifier_block *nb, unsigned long val,void *data)
{struct sa1100fb_info *fbi = TO_INF(nb, freq_transition);struct cpufreq_freqs *f = data;u_int pcd;switch (val) {case CPUFREQ_PRECHANGE:set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);break;case CPUFREQ_POSTCHANGE:pcd = get_pcd(fbi->fb.var.pixclock, f->new);fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);break;}return 0;
}

此外,如果在系统挂起/恢复的过程中CPU频率会发生变化,则 CPUFreq子系统也会发出CPUFREQ_SUSPENDCHANGE和 CPUFREQ _RESUMECHANGE这两个通知。

值得一提的是,除了CPU以外,一些非CPU设备也支持多个操作频率和电压,存在多个OPP。Linux3.2之后的内核也支持针对这种非CPU设备的DVFS,该套子系统为Devfreq。与CPUFreq存在一个drivers/cpufreq目录相似,在内核中也存在一个drivers/devore的目录。

转载于:https://www.cnblogs.com/linhaostudy/p/9355595.html

这篇关于CPUFreq驱动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/362611

相关文章

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

windows10 卸载网络驱动以及重新安装

右键桌面此电脑的图标,点击管理,设备管理器—网络适配器,找到下图中的驱动(不同的系统或者显卡会导致网卡驱动名称与下图不一样,多为Realtek开头),右键选择卸载设备,然后重启电脑,系统会自动重新安装驱动 新电脑首次安装驱动: 根据主板厂家,比如华硕,进入华硕官网,点击服务支持,点击下载中心,选择型号,点击右侧驱动程序和工具软件,选择windows版本,下载相应的驱动,下载完之后在对应文件中找

笔记整理—内核!启动!—kernel部分(1)驱动与内核的关系

首先,恭喜完成了uboot部分的内容整理,其次补充一点,uboot第一部分和第二部分的工作不是一定的,在不同的版本中,可能这个初始化早一点,那个的又放在了第二部分,版本不同,造成的工作顺序不同,但终归是要完成基本内容初始化并传参给kernel的。         那么至于驱动与内核的关系,用一张图来说明最适合不过:         驱动位于OS层的中下层与硬件相接。驱动是内

读源码笔记--文件过滤驱动FileSpy第1篇 -- DriverEntry

今天只读FileSpy的DriverEntry,位于源文件:filespy.c。 // // 全局变量. // ULONG gFileSpyDebugLevel = DEFAULT_FILESPY_DEBUG_LEVEL; #if WINVER >= 0x0501 ULONG gFileSpyAttachMode = FILESPY_ATTACH_ALL_VOLUMES; #else ULON

Circuit Design 三极管驱动蜂鸣器电路 及 蜂鸣器两端电压正确但是不响的解决方案

利用三极管进行电流放大的蜂鸣器驱动电路图: (百度图片找的) 我用有源蜂鸣器实现的这个电路,但是蜂鸣器不响。 details: 1. VCC =5V 蜂鸣器两端的直接电压约为4.5V, 但是蜂鸣器不响。 2. 将蜂鸣器直接接在4.5V的电源两端,蜂鸣器响。(说明蜂鸣器是好的) 3. 测了三极管各个管脚的电压, 和理论上的是一致的。 情况很奇怪,换了好几个三极管结果都是一样的,