电子说
之前的文章设置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 opp 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 the resources acquired by the OPP core
dev_pm_opp_set_rate:
This routine configures the device for the OPP with the lowest frequency greater than or equal to the 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,然后内核启动的时候走各种平台设备初始化就会去寻找加载,然后变成链表结构体。在使用的时候:用户通过设备节点或者中断产生或者内核进程触发就可以运行。
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !