工作性能点(OPP)库¶
2009-2010 Nishanth Menon <nm@ti.com>, Texas Instruments Incorporated
1. 简介¶
1.1 什么是工作性能点(OPP)?¶
当今复杂的SoC由多个协同工作的子模块组成。在执行各种用例的运行系统中,并非SoC中的所有模块都需要始终以其最高性能频率运行。为了方便这一点,SoC中的子模块被分组到域中,允许一些域以较低的电压和频率运行,而其他域以较高的电压/频率对运行。
设备将支持的每个域的离散元组集合,包括频率和电压对,称为工作性能点或OPP。
例如
让我们考虑一个MPU设备,它支持以下内容:{300MHz,最小电压为1V},{800MHz,最小电压为1.2V},{1GHz,最小电压为1.3V}
我们可以将这些表示为以下{Hz,uV}元组的三个OPP
{300000000, 1000000}
{800000000, 1200000}
{1000000000, 1300000}
1.2 工作性能点库¶
OPP库提供了一组辅助函数来组织和查询OPP信息。该库位于drivers/opp/目录中,头文件位于include/linux/pm_opp.h中。可以通过从电源管理menuconfig菜单中启用CONFIG_PM_OPP来启用OPP库。某些SoC(例如德州仪器的OMAP框架)允许可选地以某个OPP启动而无需cpufreq。
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。此列表预计是一个最佳的小数字,通常每个设备大约5个。此初始列表包含框架希望默认情况下在系统中安全启用的OPP集合。
关于OPP可用性的说明¶
随着系统继续运行,SoC框架可能会选择基于各种外部因素使某些OPP在每个设备上可用或不可用。用例:散热管理或其他特殊情况,SoC框架可能会选择禁用更高的频率OPP,以安全地继续运行,直到可以重新启用该OPP为止(如果可能)。
OPP库在其实现中促进了这一概念。以下操作函数仅在可用的opps上运行:dev_pm_opp_find_freq_{ceil, floor},dev_pm_opp_get_voltage,dev_pm_opp_get_freq,dev_pm_opp_get_opp_count。
dev_pm_opp_find_freq_exact旨在用于查找opp指针,然后可以使用该指针进行dev_pm_opp_enable/disable函数以根据需要使opp可用。
警告:OPP库的用户应在使用get_opp_count刷新其可用性计数,如果在设备上调用了dev_pm_opp_enable/disable函数,则触发这些的确切机制或其他依赖子系统(如cpufreq)的通知机制留给SoC特定的框架酌情决定使用OPP库。在这些操作的情况下,还需要注意刷新cpufreq表。
2. 初始OPP列表注册¶
SoC实现迭代调用dev_pm_opp_add函数以添加每个设备的OPP。预计SoC框架将以最佳方式注册OPP条目 - 典型数字范围小于5。通过注册OPP生成的列表由OPP库在整个设备操作过程中维护。SoC框架可以随后使用dev_pm_opp_enable/disable函数动态控制OPP的可用性。
- dev_pm_opp_add
为由设备指针表示的特定域添加新的OPP。OPP是使用频率和电压定义的。添加后,假定OPP可用,并且可以使用dev_pm_opp_enable/disable函数控制其可用性。OPP库在dev_pm_opp结构中内部存储和管理此信息。SoC框架可以使用此函数来定义一个最佳列表,以满足SoC使用环境的需求。
- 警告
请勿在中断上下文中使用此函数。
示例
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 */ }
3. OPP搜索函数¶
诸如cpufreq之类的高级框架以频率运行。为了将频率映射回相应的OPP,OPP库提供了便捷的函数来搜索OPP库在内部管理的OPP列表。如果找到匹配项,这些搜索函数将返回表示opp的匹配指针,否则返回错误。这些错误应由标准错误检查(例如IS_ERR()
)处理,并且调用方应采取适当的操作。
这些函数的调用者在使用OPP后应调用dev_pm_opp_put()。否则,OPP的内存将永远不会被释放,并导致内存泄漏。
- dev_pm_opp_find_freq_exact
基于确切的频率和可用性搜索OPP。此函数对于启用默认情况下不可用的OPP特别有用。示例:在SoC框架检测到可以使更高频率可用的情况下,它可以使用此函数在调用dev_pm_opp_enable以实际使其可用之前查找OPP。
opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false); dev_pm_opp_put(opp); /* dont operate on the pointer.. just do a sanity check.. */ if (IS_ERR(opp)) { pr_err("frequency not disabled!\n"); /* trigger appropriate actions.. */ } else { dev_pm_opp_enable(dev,1000000000); }
- 注意
这是唯一可以在不可用的OPP上运行的搜索函数。
- dev_pm_opp_find_freq_floor
搜索至多为提供的频率的可用OPP。此函数在搜索较小的匹配项或以降低频率的顺序处理OPP信息时很有用。示例:查找设备的最高opp
freq = ULONG_MAX; opp = dev_pm_opp_find_freq_floor(dev, &freq); dev_pm_opp_put(opp);
- dev_pm_opp_find_freq_ceil
搜索至少为提供的频率的可用OPP。此函数在搜索更高的匹配项或以增加频率的顺序处理OPP信息时很有用。示例1:查找设备的最低opp
freq = 0; opp = dev_pm_opp_find_freq_ceil(dev, &freq); dev_pm_opp_put(opp);
示例2:SoC 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 */ }
4. OPP可用性控制函数¶
使用OPP库注册的默认OPP列表可能无法满足所有可能的情况。OPP库提供了一组函数来修改OPP列表中OPP的可用性。这允许SoC框架对哪些OPP在操作上可用进行细粒度的动态控制。这些函数旨在临时删除在诸如散热考虑因素(例如,在温度降至某个温度之前不要使用OPPx)之类的条件下的OPP。
- 警告
请勿在中断上下文中使用这些函数。
- dev_pm_opp_enable
使OPP可用于操作。示例:假设只有当SoC温度低于某个阈值时才能使用1GHz OPP。SoC框架实现可能会选择执行以下操作
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。SoC框架实现可能会选择执行以下操作
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; }
5. OPP数据检索函数¶
由于OPP库抽象了OPP信息,因此有必要提供一组从dev_pm_opp结构中提取信息的函数。使用搜索函数检索OPP指针后,SoC框架可以使用以下函数来检索OPP层内部表示的信息。
- dev_pm_opp_get_voltage
检索opp指针表示的电压。示例:在cpufreq转换到不同频率时,SoC框架需要使用稳压器框架将OPP表示的电压设置为提供电压的电源管理芯片
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 = kcalloc(num_available, sizeof(u32), 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 */ }
6. 数据结构¶
通常,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的标识符。
- 警告
struct dev_pm_opp指针不应由用户解析或修改。实例的默认值由dev_pm_opp_add填充,但是OPP的可用性可以通过dev_pm_opp_enable/disable函数修改。
- struct device
这用于将域标识到OPP层。设备的性质及其实现留给OPP库的用户(例如SoC框架)。
总体而言,在一个简单的视图中,数据结构操作表示如下
Initialization / modification:
+-----+ /- dev_pm_opp_enable
dev_pm_opp_add --> | opp | <-------
| +-----+ \- dev_pm_opp_disable
\-------> domain_info(device)
Search functions:
/-- dev_pm_opp_find_freq_ceil ---\ +-----+
domain_info<---- dev_pm_opp_find_freq_exact -----> | opp |
\-- dev_pm_opp_find_freq_floor ---/ +-----+
Retrieval functions:
+-----+ /- dev_pm_opp_get_voltage
| opp | <---
+-----+ \- dev_pm_opp_get_freq
domain_info <- dev_pm_opp_get_opp_count