
之前的文章设置clock的时候多次提到了(Operating Performance Point)OPP,例如DEVFreq、CPUFreq等,在现代SoC上存在有Power Domain,也可以以Power Domain为单位进行OPP的电压频率定义。
# 1. 什么是OPP,怎么用?
>在SoC内,某些domain可以运行在较低的频率和电压下,而其他domain可以运行在较高的频率和电压下,某个domain所支持的<频率,电压>对的集合被称为Operating Performance Point,缩写OPP。
在DTS中配置后自动有OPP框架驱动加载使用,例如CPU的OPP,从设备树文件arch/arm/boot/dts/imx6ull.dtsi中找到
```
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
clock-latency = <61036>; /* two CLK32 periods */
operating-points = <
/* kHz uV */
900000 1275000
792000 1225000
528000 1175000
396000 1025000
198000 950000
>;
fsl,soc-operating-points = <
/* KHz uV */
900000 1175000
792000 1175000
528000 1175000
396000 1175000
198000 1175000
>;
```
# 2. 系统初始化加载OPP信息
```
DT_MACHINE_START
--》imx6ul_init_late
--》imx6ul_opp_init
--》_of_add_opp_table_v1(dev);
--》_opp_add_v1
--》_opp_add
```
_of_add_opp_table_v1中会根据DTS中信息找到对应的信息:

_opp_add_v1中会把DTS中信息提取出来,存入struct dev_pm_opp *new_opp;

这里struct dev_pm_opp如下:
```
struct dev_pm_opp {
struct list_head node;
bool available;
unsigned long rate;
unsigned long u_volt;
struct device_opp *dev_opp;
struct rcu_head head;
};
```
**node:** 用于链表管理此设备下的opp。
**available:** 用于判断此opp使能可以使用。
**rate:** 频率,单位Hz
**u_volt:** 电压。
**dev_opp:** struct device_opp类型指针,指向此opp所属的设备。
# 3. 触发使用
例如输入命令:
````
echo 700000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
````
__cpufreq_driver_target->__target_index->cpufreq_driver->target_index
```
static int set_target(struct cpufreq_policy *policy, unsigned int index)
{
struct private_data *priv = policy->driver_data;
return dev_pm_opp_set_rate(priv->cpu_dev,
policy->freq_table[index].frequency * 1000);
}
```
dev_pm_opp_set_rate()函数在drivers/base/power/opp/core.c中定义
```
opp_table = _find_opp_table(dev);
clk = opp_table->clk;
freq = clk_round_rate(clk, target_freq);
if ((long)freq <= 0)="" freq="target_freq;" old_freq="clk_get_rate(clk);" ret="_generic_set_opp_clk_only(dev," clk,="" old_freq,="" freq);="" ```="" clk_set_rate进行频率设置。="" #="" 4.="" api介绍="" -="" dev_pm_opp_add="" :(="" warning:="" do="" not="" use="" this="" function="" in="" interrupt="" context.)="" 向指定的设备添加一个频率="" 电压(opp="" table)组合,频率和电压的单位分别是hz和uv。="" dev_pm_opp_remove:="" remove="" an="" opp="" from="" table.="" dev_pm_opp_get:="" increment="" the="" reference="" count="" of="" opp.="" dev_pm_opp_enable:="" 用于使能指定的opp,调用dev_pm_opp_add添加进去的opp,默认是enable的。="" dev_pm_opp_disable:="" 虽然设备支持某些opp,但driver有可能觉得比较危险,不想使用,则可以调用dev_pm_opp_disable接口,禁止该opp。="" dev_pm_opp_get_voltage:="" 获得电压。="" dev_pm_opp_get_freq:="" 获得频率。="" dev_pm_opp_set_regulators:="" 进行voltage="" scaling="" dev_pm_opp_put_regulators:="" free="" resources="" acquired="" by="" core="" dev_pm_opp_set_rate:="" routine="" configures="" device="" for="" with="" lowest="" frequency="" greater="" than="" or="" equal="" to="" target="" frequency.="" dev_pm_opp_get_opp_count:="" 获取opp="" table="" opps="" numbers="" dev_pm_opp_of_add_table="" :="" 解析并初始化一个设备的opp="" table。="" opp的查询接口包括:="" dev_pm_opp_find_freq_floor,查询小于或者等于指定freq的opp,在返回opp的同时,从freq指针中返回实际的freq值;="" dev_pm_opp_find_freq_ceil,查询大于或者等于指定freq的opp,在返回opp的同时,从freq指针中返回实际的freq值;="" dev_pm_opp_find_freq_exact,精确查找指定freq的opp,同时通过available变量,可以控制是否查找处于disable状态的opp。上面两个查找接口,是不查找处于disable状态的opp的。="">后记:
>
>Linux驱动的套路其实就是DTS里面有个compatible,然后内核启动的时候走各种平台设备初始化就会去寻找加载,然后变成链表结构体。在使用的时候:用户通过设备节点或者中断产生或者内核进程触发就可以运行。
“啥都懂一点,啥都不精通,
干啥都能干,干啥啥不是,
专业入门劝退,堪称程序员杂家”。
欢迎各位自己有博客公众号的留言:申请转载,多谢!
后续会继续更新,纯干货分析,欢迎分享给朋友,欢迎点赞、收藏、在看、划线和评论交流以!
=>61036>0>频率,电压>