1. 前言
本文是分析cpufreq framework之前的一篇前置文章,用於介紹Linux電源管理中的Operating Performance Point (OPP)接口。
OPP是一個單純的軟件library,用於歸納、管理各個硬件模塊的、可工作的{頻率}/ {電壓}組合。它不涉及任何硬件,也沒有複雜的邏輯,再加上Kernel document(Documentation/power/opp.txt )描述的非常清晰,因此本文只是簡單的從功能和API兩個方便介紹OPP,不再分析其source code及內部實現邏輯。
2. 功能說明
2.1 什麼是OPP
“Documentation/power/opp.txt ”中解釋OPP的原話(我翻譯了一下)是:
當前複雜的SoCs都包括多個協同工作的子模塊。根據具體的應用場景,很多子模塊(蝸蝸注:典型的例子是CPU)並不需要一直工作在最高的頻率上。因此SoC中的子模塊劃分爲不同的domains,允許一些domains在較低的電壓/頻率下工作,而另一些在較高的電壓/頻率下工作。
domain中的設備支持的所有頻率和電壓的組合,稱作Operating Performance Points,簡稱爲OPPs。
注1:爲什麼一定是頻率和電壓的組合?因爲頻率高低決定器件的工作性能,降低性能的目的是節省功耗。但頻率對功耗的影響是有限的,而電壓對功耗的影響卻相當可觀。那頻率和電壓有什麼關係呢?通常情況下,當頻率降低之後,器件的工作電壓也是可以降低的(回憶一下數字電路)。因此不同的“頻率/電壓”組合,就組成了器件在性能和功耗之間的蹺蹺板,我們需要做的,就是根據實際場景,選擇一個合適的組合。
2.2 使用場景
對具備多個OPP的設備而言,相關的使用場景包括:
1)需要一個三維數組,保存所有的OPPs。
2)可以方便的更改OPP條目。
3)當需要改變設備的OPP時,可以方便的查詢設備支持哪些OPP。
4)可以通過一定的條件查詢OPP信息,例如以頻率值查詢、以電壓值查詢、以頻率或者電壓範圍查詢等等。
5)其它需求。
其實蠻簡單的,但考慮到這些需求對所有設備(應該也不是很多)都是相同,kernel就抽象出來一個library--就是OPP library,實現上述功能。具體可參考後續的描述。
2.3 實現思路
試下OPP library的主要思路,就是以設備爲單位,管理OPP信息。如下(摘錄自Documentation/power/opp.txt ):
*
* Internal data structure organization with the OPP layer library is as
* follows:
* dev_opp_list (root)
* |- device 1 (represents voltage domain 1)
* | |- opp 1 (availability, freq, voltage)
* | |- opp 2 ..
* ... ...
* | `- opp n ..
* |- device 2 (represents the next voltage domain)
* ...
* `- device m (represents mth voltage domain)
* device 1, 2.. are represented by dev_opp structure while each opp
* is represented by the opp structure.
*/
按理說,可以在設備指針(struct device)中,添加一個字段,用於保存該設備的OPP列表。但OPP人微言輕,無法打入設備模型的內部,只好自己處理了。就是上面的結構:
在OPP內部(drivers/base/power/opp.c)維護一個list,用於保存每個設備的OPP信息(由struct device_opp抽象),每個設備下面,維護了所有的OPP列表(由struct dev_pm_opp結構抽象)。所有的OPP操作,都會從root list開始遍歷,直到找到合適的地方爲止。
struct device_opp和struct dev_pm_opp只在OPP library的內部使用,不會把細節呈現給調用者,因爲本文不會過多涉及OPP library的內部實現(比較簡單),所以後面就不再描述這些結構了。
3. 接口說明
OPP library的source code位於drivers/base/power/opp.c中,header位於include/linux/pm_opp.h中,提供的接口包括(本文基於linux-3.18-rc4,其它版本的文件名或者接口名可能會有些不同):
1: int dev_pm_opp_add(struct device *dev, unsigned long freq,
2: unsigned long u_volt);
向指定的設備添加一個頻率/電壓組合,OPP library會在內部處理好所有事情。這裏頻率和電壓的單位分別是Hz和uV,後面都是這個單位。
1: int dev_pm_opp_enable(struct device *dev, unsigned long freq);
2:
3: int dev_pm_opp_disable(struct device *dev, unsigned long freq);
雖然設備支持某些OPP,但driver有可能覺得比較危險,不想使用,則可以調用dev_pm_opp_disable接口,禁止該OPP。這是後面很多查詢結構(除了dev_pm_opp_find_freq_exact)都無法查到該OPP。
相反,dev_pm_opp_enable用於使能指定的OPP。
注:調用dev_pm_opp_add添加進去的OPP,默認是enable的。
1: int dev_pm_opp_get_opp_count(struct device *dev);
2:
3: struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
4: unsigned long freq,
5: bool available);
6:
7: struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev,
8: unsigned long *freq);
9:
10: struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev,
11: unsigned long *freq);
OPP的查詢接口,返回相應的dev_pm_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的。
1: unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp);
2:
3: unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp);
從指定的OPP句柄中,獲得頻率或者電壓值。
1: #if defined(CONFIG_PM_OPP) && defined(CONFIG_OF)
2: int of_init_opp_table(struct device *dev);
3: #else
4: static inline int of_init_opp_table(struct device *dev)
5: {
6: return -EINVAL;
7: }
8: #endif
從DTS中,解析並初始化一個設備的opp table。DTS的格式如下(參考arch/arm/boot/dts/omap34xx.dtsi):
cpus {
cpu@0 {
/* OMAP343x/OMAP35xx variants OPP1-5 */
operating-points = <
/* kHz uV */
125000 975000
250000 1075000
500000 1200000
550000 1270000
600000 1350000
>;
clock-latency = <300000>; /* From legacy driver */
};
};以“operating-points”爲名稱,指定頻率和電壓組合,單位分別爲kHz和uV。具體解析過程不再描述。