【pm】opp介绍

(Operating Performance Point)OPP

devfreq、cpufreq

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中信息找到对应的信息:

static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
{
	const struct property *prop;
	const __be32 *val;
	int nr, ret = 0;

	mutex_lock(&opp_table->lock);
	if (opp_table->parsed_static_opps) {
		opp_table->parsed_static_opps++;
		mutex_unlock(&opp_table->lock);
		return 0;
	}

	opp_table->parsed_static_opps = 1;
	mutex_unlock(&opp_table->lock);

	prop = of_find_property(dev->of_node, "operating-points", NULL);
	if (!prop) {
		ret = -ENODEV;
		goto remove_static_opp;
	}
	if (!prop->value) {
		ret = -ENODATA;
		goto remove_static_opp;
	}

_opp_add_v1中会把DTS中信息提取出来,存入struct dev_pm_opp *new_opp;

int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
		unsigned long freq, long u_volt, bool dynamic)
{
	/* populate the opp table */
	new_opp->rate = freq;
	tol = u_volt * opp_table->voltage_tolerance_v1 / 100;	
	new_opp->supplies[0].u_volt = u_volt;
	new_opp->supplies[0].u_volt_min = u_volt - tol;
	new_opp->supplies[0].u_volt_max = u_volt + tol;
	new_opp->available = true;
	new_opp->dynamic = dynamic;

	ret = _opp_add(dev, new_opp, opp_table, false);

这里struct dev_pm_opp如下

struct dev_pm_opp {
	struct list_head node; /*用于链表管理此设备下的opp*/
	struct kref kref; /*用于跟踪有多少地方引用了这个电源状态*/

	bool available; /*用于判断此opp使能可以使用*/
	bool dynamic; /*标记该电源状态是否为动态创建 如果是动态创建,则可能是在运行时根据需要添加的*/
	bool turbo; /*标记该电源状态是否为涡轮增压(Turbo Boost)模式。涡轮增压模式通常指的是设备在短时间内以高于标称频率运行的状态。*/
	bool suspend; /*标记该电源状态是否为挂起(Suspend)状态*/
	unsigned int pstate; /*电源状态的标识符。通常用于内部标识不同的电源状态。*/
	unsigned long rate; /*频率 单位Hz*/
	unsigned int level; /*电源状态的级别,通常用于排序或优先级分配。*/

	struct dev_pm_opp_supply *supplies; /*指向与该电源状态相关的电源供应对象的指针*/
	struct dev_pm_opp_icc_bw *bandwidth; /*指向与该电源状态相关的带宽信息对象的指针*/

	unsigned long clock_latency_ns; /*设备在切换到该电源状态时的时钟延迟时间,单位为纳秒(ns)*/

	struct dev_pm_opp **required_opps; /*指向其他所需电源状态  在某些情况下,切换到一个电源状态可能需要先切换到其他电源状态。*/
	struct opp_table *opp_table; /*指向一个包含多个电源状态的表格对象的指针*/

	struct device_node *np; /*指向 Device Tree 中与此电源状态相关的节点*/

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

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,然后内核启动的时候走各种平台设备初始化就会去寻找加载,然后变成链表结构体。

在使用的时候:用户通过设备节点或者中断产生或者内核进程触发就可以运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值