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

相关文章

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo

嵌入式Linux之使用设备树驱动GPIO的实现方式

《嵌入式Linux之使用设备树驱动GPIO的实现方式》:本文主要介绍嵌入式Linux之使用设备树驱动GPIO的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、设备树配置1.1 添加 pinctrl 节点1.2 添加 LED 设备节点二、编写驱动程序2.1

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

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安装驱动 右击,