工作效能點(OPP)庫

  1. 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