当前位置: 首页 > article >正文

linux opp 模块

目录

  • 一、Linux OPP 介绍
  • 二、关键数据结构
    • dev_pm_opp
    • opp_table
  • 三、OPP 的 dts 结构
    • 3.1 operating-points
    • 3.2 operating-points-v2
      • opp table dts 属性
      • opp dts 属性
  • 四、关键接口
    • 4.1 创建/删除 opp_table
    • 4.2 获取 opp_table
    • 4.3 获取 opp 相关接口

一、Linux OPP 介绍

在Linux中,OPP(Operating Performance Points)结构是一种用于描述设备性能状态的机制,主要用于电源管理和性能调节,特别是在处理器、GPU和其他硬件设备中。OPP结构允许系统根据负载需求动态调整设备的性能和功耗,以实现更高的能效。

OPP的主要组成部分:

1. 频率:设备在不同性能状态下的工作频率。
2. 电压:相应的工作电压,通常与频率成正比。
3. 功耗:在特定频率和电压下设备的功耗。

OPP的使用场景:

1. 动态频率调节:在设备负载增加时提升频率,以提高性能;在负载降低时降低频率,以节省电能。
2. 热管理:根据设备的温度和功耗情况调整性能状态,以避免过热。
3. 负载平衡:在多核处理器中,可以根据任务需求将负载动态分配给不同核心,并调整其OPP以优化性能和能耗。

实现方式:

1. OPP通常通过设备树(Device Tree)描述,包含每个性能点的频率、电压和功耗信息。
2. Linux内核中的cpufreq、cpupower、dvfs、thermal 等子系统可以管理这些OPP,以实现对设备的频率和电压的动态调整。

通过OPP机制,Linux能够在提供所需性能的同时,有效管理能耗和热量,从而延长设备的使用寿命和电池续航时间。

二、关键数据结构

dev_pm_opp

dev_pm_opp 在 OPP 中表示单个性能点。

struct dev_pm_opp {
    struct list_head node;
      struct kref kref;    // 用于引用计数,确保相关变量在使用期间不被释放,防止内存泄漏。

    bool available;    // enable/disable
    bool dynamic;      // 标记动态性能点
    bool turbo;        // 标记节点为高性能模式
    bool suspend;      // suspend时是否有效
    unsigned int pstate;  // 表示该节点的性能状态
    unsigned long rate;   // 该opp点的频率
    unsigned int level;   // 该opp项的级别

    struct dev_pm_opp_supply *supplies;   // power电压信息

    unsigned long clock_latency_ns;    // 切换到该opp项的时间延时

    struct dev_pm_opp **required_opps;    //  指针数组,指向该opp项依赖的其它opp项
    struct opp_table *opp_table;  // 包含该opp项的opp表

    struct device_node *np;

#ifdef CONFIG_DEBUG_FS
    struct dentry *dentry;
#endif
};

opp_table

opp_table 负责管理一个设备的 OPP 表,一个 opp 表由多个dev_pm_opp表示的 opp 性能节点组成。

struct opp_table {
    struct list_head node;

    struct blocking_notifier_head head; //注册管理OPP状态变化时的通知回调
    struct list_head dev_list;  // 包含该opp table的设备列表
    struct list_head opp_list;  // 包含所有dev_pm_opp的列表
    struct kref kref;
    struct kref list_kref;  // 引用计数
    struct mutex lock;

    struct device_node *np;
    unsigned long clock_latency_ns_max;  // 表示设备在切换opp时最大的延迟

    /* For backward compatibility with v1 bindings */
    unsigned int voltage_tolerance_v1;  // 为向后兼容v1保留的电压容忍度(波动范围)

    bool parsed_static_opps;    // 指示opp表节点是否已解析
    enum opp_table_access shared_opp;    // 是否共享
    struct dev_pm_opp *suspend_opp;    // 指向suspend情况下使用的opp

    struct mutex genpd_virt_dev_lock;    // 保护虚拟设备的访问
    struct device **genpd_virt_devs;     // 与opp表关联的虚拟设备数组
    struct opp_table **required_opp_tables;  // 依赖的其它opp表
    unsigned int required_opp_count;

    unsigned int *supported_hw;   // 支持的硬件配置(dts中配置支持的硬件表示符)
    unsigned int supported_hw_count;
    const char *prop_name;
    struct clk *clk;    // 与opp相关的时钟
    struct regulator **regulators;  // 与opp表相关的电压调节器
    int regulator_count;
    bool genpd_performance_state;
    bool is_genpd;

    int (*set_opp)(struct dev_pm_set_opp_data *data);  // 指向调整opp的函数指针
    struct dev_pm_set_opp_data *set_opp_data;  // 设置opp所需要的数据

#ifdef CONFIG_DEBUG_FS
    struct dentry *dentry;
    char dentry_name[NAME_MAX];
#endif
};

三、OPP 的 dts 结构

参考Documentation\devicetree\bindings\opp\opp.txt。

在设备树中,OPP(Operating Performance Points)的结构主要用于描述设备的性能状态,包括频率、电压等信息。OPP的DTS结构通常包含多个OPP节点,每个节点对应一个性能点,通常在设备的设备树节点下定义。

3.1 operating-points

OPP dts 的 v1 版本结构,仅支持电压-频率对,节点名为 operating-points,节点包含了频率和电压的组合。

cpu@0 {
    compatible = "arm,cortex-a9";
    reg = <0>;
    next-level-cache = <&L2>;
    operating-points = <
    /* kHz    uV */
    792000  1100000
    396000  950000
    198000  850000
    >;
};

3.2 operating-points-v2

OPP dts 的 v2 版本结构,支持更复杂的电压-频率组合。

opp table dts 属性

opp table的dts中可能包含以下属性节点:

  • compatible:必须是 “operating-points-v2”;
  • opp-shared:可选属性,当 opp table 的 dts 包含该属性时,表示该 opp table 会影响所有使用该 opp table 的设备,任意的 opp 切换会作用到所有的设备上;
  • opp 节点;

opp dts 属性

每个opp节点的dts,可能包含以下属性:

  • opp-microvolt:电压,uv。size 为 1 或者 3 的数组,size 为 1 时表示,size 为 3 时表示;
  • opp-microvolt-:特殊命名的opp-microvolt,可以为一个 opp 提供多个不同属性的电压值,每个电压值可能对应了不同的运行模式,例如 opp-microvolt-slow、opp-microvolt-fast;
  • opp-microamp:该 opp 下,设备最大的电流消耗,只有定义 opp-microvolt 前提下,opp-microamp 才是有意义的;
  • opp-microamp-:特殊命名的 opp-microamp;
  • opp-level:当前节点的性能等级,方便低功耗和高性能状态时调整设备的频率和电压;
  • clock-latency-ns:从任何 opp 切换到该 opp 所需要的耗时;
  • turbo-mode:指示是否支持短时间内超频;
  • opp-suspend:opp 的 dts 包含该属性时,表示当设备 suspend 时,切到该 opp。如果多个 opp 都包含该属性,在 suspend 时会切到最高频率的 opp;
  • opp-supported-hw:可选属性,用于指示哪些硬件版本可以支持该 opp,具体的硬件版本号是用户自己定义的;
  • required-opps:指示当前 opp 运行时所依赖的其它 opp;

举例

	cpu_opp_table: opp_table0 {
		compatible = "operating-points-v2";
		opp-shared;

		opp-1000000000 {
			opp-hz = /bits/ 64 <1000000000>;
			opp-microvolt = <975000 970000 985000>;
			opp-microamp = <70000>;
			opp-microvolt-slow = <915000 900000 925000>;
			opp-microvolt-fast = <975000 970000 985000>;
			opp-microamp-slow =  <70000>;
			opp-microamp-fast =  <71000>;
			clock-latency-ns = <300000>;
            opp-supported-hw = <0xF 0xFFFFFFFF 0xFFFFFFFF>;
			opp-suspend;
		};
		opp-1100000000 {
			opp-hz = /bits/ 64 <1100000000>;
			opp-microvolt = <1000000 980000 1010000>;
			opp-microamp = <80000>;
			clock-latency-ns = <310000>;
            opp-supported-hw = <0x20 0xff0000ff 0x0000f4f0>;
			opp-microvolt-slow = <915000 900000 925000>, /* Supply vcc0 */
					      <925000 910000 935000>; /* Supply vcc1 */
			opp-microvolt-fast = <975000 970000 985000>, /* Supply vcc0 */
					     <965000 960000 975000>; /* Supply vcc1 */
			opp-microamp =  <70000>; /* Will be used for both slow/fast */
		};
		opp-1200000000 {
			opp-hz = /bits/ 64 <1200000000>;
			opp-microvolt = <1025000>;
			clock-latency-ns = <290000>;
			turbo-mode;
		};
	};
    cpus {
		#address-cells = <1>;
		#size-cells = <0>;

		cpu@0 {
			compatible = "arm,cortex-a9";
			reg = <0>;
			next-level-cache = <&L2>;
			clocks = <&clk_controller 0>;
			clock-names = "cpu";
			cpu-supply = <&cpu_supply0>;
			operating-points-v2 = <&cpu0_opp_table>;
		};

		cpu@1 {
			compatible = "arm,cortex-a9";
			reg = <1>;
			next-level-cache = <&L2>;
			clocks = <&clk_controller 0>;
			clock-names = "cpu";
			cpu-supply = <&cpu_supply0>;
			operating-points-v2 = <&cpu0_opp_table>;
		};
	};

四、关键接口

4.1 创建/删除 opp_table

int dev_pm_opp_of_add_table(struct device *dev);
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index);
void dev_pm_opp_of_remove_table(struct device *dev);

以上是部分删除和创建 opp_table 的接口。
dev_pm_opp_of_add_table 接口通过从设备树中解析 opp 信息,并注册 opp_table、添加到设备中。

int dev_pm_opp_of_add_table(struct device *dev)
{
    struct opp_table *opp_table;
    int ret;

    opp_table = dev_pm_opp_get_opp_table_indexed(dev, 0); // 查找或解析dts创建opp_table
    if (!opp_table)
    return -ENOMEM;

    /*
     * OPPs have two version of bindings now. Also try the old (v1)
     * bindings for backward compatibility with older dtbs.
     */
    if (opp_table->np)
    ret = _of_add_opp_table_v2(dev, opp_table);  // 解析dts,将opp添加到opp_table
    else
    ret = _of_add_opp_table_v1(dev, opp_table);

    if (ret)
    dev_pm_opp_put_opp_table(opp_table);

    return ret;
}
EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);

注意,在 _of_add_opp_table_v2 解析 dts 添加 opp 时,会将 opp freq 从低到高的顺序排序(_opp_is_duplicate)然后再添加到 opp_list 中。方便在遍历 list 时,能最有效地找到大于或者小于给定频率的 opp

dev_pm_opp_get_opp_table_indexed 从已经创建的 opp_table list 查找并返回 opp_table,查找不到的话,则解析 dts 创建 opp_table 并返回。

struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev,
       int index)
  -> _opp_get_opp_table(dev, index);
      -> opp_table = _find_opp_table_unlocked(dev); // 遍历opp_tables和opp_table->dev_list,查找dev的opp_table。查找到的话返回
      -> opp_table = _managed_opp(dev, index);  // 遍历opp_tables查找opp_table(可能其它设备使用相同的opp_table,已经注册过opp_table), 找到直接返回
      -> opp_table = _allocate_opp_table(dev, index);  // 申请内存、解析dts创建opp_table

dev_pm_opp_of_add_table_indexeddev_pm_opp_of_add_table 接口类似,初始化指定 index 的 opp_table.

4.2 获取 opp_table

struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
struct opp_table *dev_pm_opp_get_opp_table_indexed(struct device *dev, int index);

dev_pm_opp_get_opp_table 接口会调用 _opp_get_opp_table 接口,前面已经讲过,_opp_get_opp_table 接口会从已经创建的 opp_table list 查找并返回 opp_table,查找不到的话,则解析 dts 创建 opp_table 并返回。

struct opp_table *dev_pm_opp_get_opp_table(struct device *dev)
{
    return _opp_get_opp_table(dev, 0);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_table);

dev_pm_opp_get_opp_table_indexed 接口类似。

4.3 获取 opp 相关接口

// 根据freq和available精确的查找opp
struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
          unsigned long freq,
          bool available);
// 根据level,精确度查找opp,level通过dts中的“opp-level"定义
struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev,
           unsigned int level);

// 返回小于或等于freq的最高opp,并将opp的freq赋给freq
struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
          unsigned long *freq);
// 根据给定volt查找opp频率上限(大于等于指定打压的最小频率)
struct dev_pm_opp *dev_pm_opp_find_freq_ceil_by_volt(struct device *dev,
         unsigned long u_volt);
// 根据给定的freq查找opp表中大于或等于freq的最小频率。
struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
         unsigned long *freq);

例如:
在驱动程序中,根据不同条件查找确定合适的 opp,方便进一步调频等动作。

unsigned long target_freq = 1200000; // 1.2 GHz
struct dev_pm_opp *opp = dev_pm_opp_find_freq_ceil(dev, &target_freq);
if (!IS_ERR(opp)) {
    // 找到了合适的 OPP,可以获取其频率等信息
    unsigned long freq = opp->rate;
    // 使用频率...
    dev_pm_opp_put(opp); // 减少引用计数
} else {
    // 处理未找到 OPP 的情况
}

http://www.kler.cn/a/387533.html

相关文章:

  • Android CarrierConfig 参数项和正则匹配逻辑
  • 【ARM Coresight OpenOCD 系列 5 -- arp_examine 使用介绍】
  • git修改当前分支名称并推送到远程仓库
  • Vue 中的定时刷新与自动更新实现
  • 产品经理如何使用项目管理软件推进复杂项目按时上线
  • VSCode中python插件安装后无法调试
  • 深入解析 Transformers 框架(四):Qwen2.5/GPT 分词流程与 BPE 分词算法技术细节详解
  • JavaEE初阶---properties类+反射+注解
  • EasyUI弹出框行编辑,通过下拉框实现内容联动
  • go生成4位随机数字
  • 深入了解决策树:机器学习中的经典算法
  • 如何使用HighBuilder前端开发神器
  • ThingsBoard规则链节点:RPC Call Reply节点详解
  • Python的函数
  • 第一部分 Supervised Machine Learning: Regression and Classification
  • 嵌入式系统与机器学习的结合
  • oracle使用CTE递归分解字符串
  • python - leetcode【数据结构-算法】-入门/通关手册
  • Rust移动开发:Rust在iOS端集成使用介绍
  • 搭子小程序定制开发:全新找搭子之旅
  • 计算机网络之物理层
  • Rust:启动与关闭线程
  • Java 中的 Supplier:让数据生成更灵活
  • 设计模式学习总结(一)
  • 【VScode】Html+Css+JavaScript学习计划表
  • 【论文阅读笔记】Mamba模型代码理解