简介Operating Performance Points (OPP)
SoC是高度集成的,不是SoC中的所有模块需要一直以最高的频率运作。来为了便于实现这一点,SoC中的子模块被分组到不同的域中,允许一些域运行在较低的电压和频率,而其他域运行在更高的电压/频率对。
由频率和电压对组成的离散元组的集合称为工作性能点(opp)。
例如:让我们考虑一个支持以下功能的MPU设备:
{300MHz,最小电压1V},
{800MHz,最小电压1.2V},
{最低电压1.3V时1GHz},
我们可以用{Hz, uV}元组来表示这三个opp:
{300000000, 1000000}
{800000000, 1200000}
{1000000000, 1300000}
OPP API接口
OPP库提供了一组辅助函数来组织和查询OPP信息。库位于drivers/opp/
目录中,头文件位于include/linux/pm_opp.h
中。可以通过从电源管理菜单CONFIG菜单启用CONFIG_pm_opp来启用opp库。某些SoC,如Texas Instrument的OMAP框架,允许在不需要cpufreq的情况下选择性地在某个OPP启动。
OPP层期望每个域由一个唯一的设备指针来表示。SoC框架在OPP层为每个设备注册一组初始OPP。这个列表的长度一般比较小,通常每个设备大约5个。这个初始列表包含一组 OPP,框架希望系统默认情况下能够安全地启用这些 OPP。
通常使用opp的方式有以下几步:
a. 用户为设备(比如CPU)配置/注册一些默认的opp信息。
b. Soc会根据具体的运行情况,通过opp层去改变/查询设备的opp信息。
(users) -> registers a set of default OPPs -> (library)
SoC framework -> modifies on required cases certain OPPs -> OPP layer ->queries to search/retrieve information
使用OPP的场景
随着系统继续运行,SoC框架可能会根据各种外部因素选择在每个设备上提供某些 OPP 或不提供 OPP。
示例用法:热管理或其他特殊情况,SoC框架可能选择禁用更高频率的OPP以安全地继续操作,直到该OPP可以在可能的情况下重新启用。
警告
:如果设备调用了dev_pm_opp_enable/deisable
函数,则OPP库的用户应使用get_opp_count
刷新其可用性计数,在进行这些操作的情况下,需要注意刷新cpufreq表。
如何使用OPP
OPP列表初始化
迭代调用dev_ pm_opp_ dd
函数为每个设备添加 OPP列表。每个设备的opp个数最好小于5个。
OPP是用频率和电压定义的。一旦完成添加,OPP被认为是可用的,可以用dev_pm_opp_enable/disable函数来控制其可用性。OPP库内部用dev_pm_opp
结构体存储并管理这些信息。
注意:不要在中断上下文中使用此函数。
例如,多次调用dev_pm_opp_add为mpu_dev添加多个opp。
soc_pm_init()
{
/* Do things */
r = dev_pm_opp_add(mpu_dev, 1000000, 900000);
if (!r) {
pr_err("%s: unable to register mpu opp(%d)\n", r);
goto no_cpufreq;
}
/* Do cpufreq things */
no_cpufreq:
/* Do remaining things */
}
OPP查询获取函数
cpufreq等高层框架对频率进行操作,为了将频率映射到相应的OPP,OPP库提供了便利的函数来搜索OPP库内部管理的OPP链表。这些搜索函数如果找到匹配的OPP,将返回指向该OPP的指针,否则返回错误。这些错误预计由标准的错误检查,如IS_ERR()来处理,并由调用者采取适当的行动。
这些函数的调用者应在使用完OPP后调用dev_pm_opp_put()。否则,OPP的内存将永远不会被释放,并导致内存泄露。
函数 | 作用 |
---|---|
dev_pm_opp_find_freq_ceil, dev_pm_opp_find_freq_floor} | 查找某个频率附近的最高频率,或者最低频率 |
dev_pm_opp_find_freq_exact | 此功能对于启用默认情况下不可用的OPP特别有用 |
cpufreq_driver->target的简化实现:
soc_cpufreq_target(..)
{
/* Do stuff like policy checks etc. */
/* Find the best frequency match for the req */
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
dev_pm_opp_put(opp);
if (!IS_ERR(opp))
soc_switch_to_freq_voltage(freq);
else
/* do something when we can't satisfy the req */
/* do other stuff */
}
OPP可用性控制功能
dev_pm_opp_enable
:使OPP可用。示例:假设只有当SoC温度低于某个阈值时,1GHz OPP才可用
if (cur_temp < temp_low_thresh) {
/* Enable 1GHz if it was disabled */
opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false);
dev_pm_opp_put(opp);
/* just error check */
if (!IS_ERR(opp))
ret = dev_pm_opp_enable(dev, 1000000000);
else
goto try_something_else;
}
dev_pm_opp_disable
: OPP不可用于。示例: 如果温度超过阈值,则禁用1GHz OPP。
if (cur_temp > temp_high_thresh) {
/* Disable 1GHz if it was enabled */
opp = dev_pm_opp_find_freq_exact(dev, 1000000000, true);
dev_pm_opp_put(opp);
/* just error check */
if (!IS_ERR(opp))
ret = dev_pm_opp_disable(dev, 1000000000);
else
goto try_something_else;
}
OPP数据检索功能
dev_pm_opp_get_voltage
:检索opp指针表示的电压。示例:在cpufreq转换到不同频率时,需要使用regulator框架将由OPP表示的电压来配置到pmic:
soc_switch_to_freq_voltage(freq)
{
/* do things */
opp = dev_pm_opp_find_freq_ceil(dev, &freq);
v = dev_pm_opp_get_voltage(opp);
dev_pm_opp_put(opp);
if (v)
regulator_set_voltage(.., v);
/* do other things */
}
dev_pm_opp_get_freq
:检索opp指针表示的频率。例如:假设SoC框架使用了几个辅助函数,我们可以传递opp指针,而不是做额外的参数来处理一些安静的数据参数:
soc_cpufreq_target(..)
{
/* do things.. */
max_freq = ULONG_MAX;
max_opp = dev_pm_opp_find_freq_floor(dev,&max_freq);
requested_opp = dev_pm_opp_find_freq_ceil(dev,&freq);
if (!IS_ERR(max_opp) && !IS_ERR(requested_opp))
r = soc_test_validity(max_opp, requested_opp);
dev_pm_opp_put(max_opp);
dev_pm_opp_put(requested_opp);
/* do other things */
}
soc_test_validity(..)
{
if(dev_pm_opp_get_voltage(max_opp) < dev_pm_opp_get_voltage(requested_opp))
return -EINVAL;
if(dev_pm_opp_get_freq(max_opp) < dev_pm_opp_get_freq(requested_opp))
return -EINVAL;
/* do things.. */
}
dev_pm_opp_get_opp_count
:检索设备的可用opp数量示例:假设SoC中的协同处理器需要知道表中的可用频率,主处理器可以通知如下:
soc_notify_coproc_available_frequencies()
{
/* Do things */
num_available = dev_pm_opp_get_opp_count(dev);
speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL);
/* populate the table in increasing order */
freq = 0;
while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) {
speeds[i] = freq;
freq++;
i++;
dev_pm_opp_put(opp);
}
soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available);
/* Do other things */
}
OPP数据结构
通常,一个SoC包含多个可变电压域。每个域由一个设备指针描述。和OPP之间的关系可以按以下方式描述::
SoC
|- device 1
| |- opp 1 (availability, freq, voltage)
| |- opp 2 ..
... ...
| `- opp n ..
|- device 2
...
`- device m
OPP库维护着一个内部链表,SoC框架使用上文描述的各个函数来填充和访问。然而,描述真实OPP和域的结构体是OPP库自身的内部组成,以允许合适的抽象在不同系统中得到复用。
struct dev_pm_opp
OPP库的内部数据结构,用于表示一个OPP。除了频率、电压、可用性信息外,它还包含OPP库运行所需的内部统计信息。指向这个结构体的指针被提供给用户(比如SoC框架)使用,在与OPP层的交互中作为OPP的标识符。
警告:结构体dev_pm_opp的指针不应该由用户解析或修改。一个实例的默认值由dev_pm_opp_add填充,但OPP的可用性由dev_pm_opp_enable/disable函数修改。
struct device
这用于向OPP层标识一个域。设备的性质和它的实现是由OPP库的用户决定的,如SoC框架。
总体来说,以一个简化的视角看,对数据结构的操作可以描述为下面各图::
初始化和修改相关:
+-----+ /- dev_pm_opp_enable
dev_pm_opp_add --> | opp | <-------
| +-----+ \- dev_pm_opp_disable
\-------> domain_info(device)
查找函数:
/-- dev_pm_opp_find_freq_ceil ---\ +-----+
domain_info<---- dev_pm_opp_find_freq_exact -----> | opp |
\-- dev_pm_opp_find_freq_floor ---/ +-----+
检索函数:
+-----+ /- dev_pm_opp_get_voltage
| opp | <---
+-----+ \- dev_pm_opp_get_freq
domain_info <- dev_pm_opp_get_opp_count
OPP的DTS配置
Ref
https://docs.kernel.org/power/opp.html
标签:SoC,OPP,linux,dev,使用,opp,freq,pm From: https://www.cnblogs.com/lvzh/p/17441280.html