OPP-1-文档翻译-opp.rst
一、opp.rst
注: 本文翻译自 msm-5.4/Documentation/power/opp.rst
===========================================
运行性能点 (OPP) 库
==========================================
(C) 2009-2010 Nishanth Menon <nm@ti.com>,德州仪器公司
目录
1. 引言
2. OPP 列表初始注册
3. OPP 搜索功能
4. OPP 可用性控制功能
5. OPP 数据检索功能
6. 数据结构
1. 引言
================
1.1 什么是运行性能点 (OPP)?
-------------------------------------------------
如今复杂的片上系统 (SoC) 由多个协同工作的子模块组成。在执行各种应用场景的运行系统中,SoC 中的所有模块并非始终都需要以最高性能频率运行。为了实现这一点,SoC 中的子模块被分组到不同的域中,允许某些域以较低的电压和频率运行,而其他域则以更高的电压/频率组合运行。
设备在每个域中支持的频率和电压组合的离散元组集合称为运行性能点 (OPP)。
例如:
假设我们考虑一个支持以下频率的微处理器 (MPU):
{300MHz,最低电压 1V},{800MHz,最低电压 1.2V},{1GHz,最低电压 1.3V}
我们可以将这些频率表示为以下三个输出参数 (OPP):{Hz, μV} 元组。
- {300000000, 1000000}
- {800000000, 1200000}
- {1000000000, 1300000}
1.2 运行性能点库
----------------------------------------
OPP 库提供了一组辅助函数,用于组织和查询 OPP 信息。该库位于 drivers/opp/ 目录下,头文件位于 include/linux/pm_opp.h。可以通过在电源管理菜单的配置菜单中启用 CONFIG_PM_OPP 来启用 OPP 库。OPP 库依赖于 CONFIG_PM,因为某些 SoC(例如德州仪器的 OMAP 框架)允许
在不需要 cpufreq 的情况下,选择性地以特定 OPP 启动。
OPP 库的典型用法如下:
(用户)-> 注册一组默认 OPP -> (库)
SoC 框架 -> 根据需要修改某些 OPP -> OPP 层
-> 查询以搜索/检索信息 ->
OPP 层期望每个域都由一个唯一的设备指针表示。SoC 框架向 OPP 层注册每个设备的一组初始 OPP。此列表预计数量较少,通常每个设备约为 5 个。此初始列表包含一组框架预期在系统中默认安全启用的 OPP(可选功能)。
关于 OPP 可用性的说明:
^^^^^^^^^^^^^^^^^^^^^^^^^
随着系统运行,SoC 框架可能会根据各种外部因素,选择在每个设备上启用或禁用某些 OPP。例如:在散热管理或其他特殊情况下,SoC 框架可能会选择禁用高频 OPP,以确保系统安全运行,直到该 OPP 可以重新启用为止。
OPP 库在其实现中支持此概念。以下操作函数仅对可用的 OPP 进行操作: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。
警告:如果对某个设备调用了 `dev_pm_opp_enable/disable` 函数,则 OPP 库的用户应使用 `get_opp_count` 函数刷新其可用性计数。触发这些函数的具体机制,以及向其他依赖子系统(例如 cpufreq)发送通知的机制,由使用 OPP 库的 SoC 特定框架自行决定。在执行这些操作时,也需要谨慎地刷新 cpufreq 表。
2. 初始 OPP 列表注册
================================
SoC 实现会迭代调用 dev_pm_opp_add 函数,为每个设备添加 OPP。SoC 框架会以最优方式注册 OPP 条目——通常数量小于 5。注册 OPP 生成的列表由 OPP 库在设备运行期间维护。SoC 框架随后可以使用 dev_pm_opp_enable / disable 函数动态控制 OPP 的可用性。
(1) dev_pm_opp_add
为设备指针表示的特定域添加新的 OPP。OPP 由频率和电压定义。添加后,OPP 即被视为可用,其可用性可以通过 dev_pm_opp_enable / disable 函数进行控制。OPP 库在内部将此信息存储在 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 的匹配指针,否则返回错误。这些错误应由标准错误检查(例如 IS_ERR())以及调用者采取的相应措施来处理。
这些函数的调用者在使用 OPP 后应调用 dev_pm_opp_put() 函数。否则,OPP 占用的内存将永远不会被释放,从而导致内存泄漏。
(1) 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 的搜索函数。
(2) 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);
(2) 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 集可操作进行精细的动态控制。这些函数旨在*临时*移除某些 OPP,例如在温度降低的情况下(例如,在温度下降之前不要使用 OPPx)。
警告:
请勿在中断上下文中使用这些函数。
(1) 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; }
(2) 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 信息,因此需要一组函数来从 OPP 结构中提取信息。一旦使用搜索函数检索到 OPP 指针,SoC 框架就可以使用以下函数来检索 OPP 层中表示的信息。
(1) dev_pm_opp_get_voltage
检索 OPP 指针表示的电压。
示例:当 CPU 频率转换到不同频率时,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 */ }
(2) 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.. */ }
(3) dev_pm_opp_get_opp_count
获取设备可用的操作数
示例:假设 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 */ }
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 框架)的用户自行决定。
总而言之,简而言之,数据结构操作如下所示:
初始化/修改:
+-----+ /- 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
posted on 2026-03-19 18:28 Hello-World3 阅读(2) 评论(0) 收藏 举报
浙公网安备 33010602011771号