如何實現一個新的 CPUFreq 處理器驅動程式¶
作者
Dominik Brodowski <linux@brodo.de>
Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Viresh Kumar <viresh.kumar@linaro.org>
1. 應該做什麼?¶
你剛剛獲得了一個全新的 CPU / 晶片組,並且有資料手冊,想要為這個 CPU / 晶片組新增 cpufreq 支援嗎?太棒了。這裡有一些關於需要做什麼的提示
1.1 初始化¶
首先,在 __initcall 級別 7 (module_init()) 或更高版本的函式中,檢查此核心是否在正確的 CPU 和正確的晶片組上執行。如果是,則使用 cpufreq_register_driver() 向 CPUfreq 核心註冊一個 struct cpufreq_driver
這個 struct cpufreq_driver 應該包含什麼?
.name - 此驅動程式的名稱。
.init - 指向每個策略初始化函式的指標。
.verify - 指向“驗證”函式的指標。
.setpolicy _or_ .fast_switch _or_ .target _or_ .target_index - 請參閱下面的差異。
以及可選的
.flags - CPUfreq 核心的提示。
.driver_data - cpufreq 驅動程式特定資料。
.get_intermediate 和 target_intermediate - 用於在更改 CPU 頻率時切換到穩定頻率。
.get - 返回 CPU 的當前頻率。
.bios_limit - 返回 CPU 的 HW/BIOS 最大頻率限制。
.exit - 指向每個策略清理函式的指標,該函式在 CPU 熱插拔過程的 CPU_POST_DEAD 階段呼叫。
.suspend - 指向每個策略掛起函式的指標,該函式在中斷停用且_在_ governor 停止用於策略之後呼叫。
.resume - 指向每個策略恢復函式的指標,該函式在中斷停用且_在_ governor 再次啟動之前呼叫。
.ready - 指向每個策略就緒函式的指標,該函式在策略完全初始化後呼叫。
.attr - 指向 NULL 終止的 “struct freq_attr” 列表的指標,該列表允許將值匯出到 sysfs。
.boost_enabled - 如果設定,則啟用加速頻率。
.set_boost - 指向每個策略函式的指標,用於啟用/停用加速頻率。
1.2 每個 CPU 初始化¶
每當向裝置模型註冊新的 CPU 時,或者在 cpufreq 驅動程式註冊自身之後,如果沒有 CPUfreq 策略存在於 CPU 上,則會呼叫每個策略初始化函式 cpufreq_driver.init。請注意,.init() 和 .exit() 例程僅對策略呼叫一次,而不是對策略管理的每個 CPU 呼叫。它接受一個 struct cpufreq_policy *policy 作為引數。現在該怎麼辦?
如有必要,啟用 CPU 上對 CPUfreq 的支援。
然後,驅動程式必須填寫以下值
policy->cpuinfo.min_freq _and_ policy->cpuinfo.max_freq |
此 CPU 支援的最小和最大頻率(以 kHz 為單位) |
policy->cpuinfo.transition_latency |
在此 CPU 上在兩個頻率之間切換所需的時間,以納秒為單位(如果適用,否則指定 CPUFREQ_ETERNAL) |
policy->cur |
此 CPU 的當前工作頻率(如果適用) |
policy->min、policy->max、policy->policy 以及(如果必要)policy->governor |
必須包含此 CPU 的“預設策略”。稍後,將使用這些值呼叫 cpufreq_driver.verify 和 cpufreq_driver.setpolicy 或 cpufreq_driver.target/target_index。 |
policy->cpus |
使用與此 CPU 一起進行 DVFS 的(線上 + 離線)CPU 的掩碼更新此項(即,與它共享時鐘/電壓軌)。 |
對於設定其中一些值(cpuinfo.min[max]_freq、policy->min[max])而言,頻率表助手可能會有所幫助。有關它們的更多資訊,請參見第 2 節。
1.3 驗證¶
當用戶決定設定新策略(包括“策略,governor,min,max”)時,必須驗證此策略,以便可以糾正不相容的值。對於驗證這些值,cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min_freq, unsigned int max_freq) 函式可能會有所幫助。有關頻率表助手的詳細資訊,請參見第 2 節。
您需要確保至少一個有效頻率(或工作範圍)在 policy->min 和 policy->max 之內。如有必要,首先增加 policy->max,並且僅在沒有解決方案時,才減少 policy->min。
1.4 target 或 target_index 或 setpolicy 或 fast_switch?¶
大多數 cpufreq 驅動程式,甚至大多數 CPU 頻率調節演算法,都只允許將 CPU 頻率設定為預定義的固定值。對於這些,您可以使用 ->target()、->target_index() 或 ->fast_switch() 回撥。
一些支援 cpufreq 的處理器可以自行在一定限制之間切換頻率。這些應使用 ->setpolicy() 回撥。
1.5. target/target_index¶
target_index 呼叫有兩個引數:struct cpufreq_policy *policy 和 unsigned int 索引(指向公開的頻率表)。
CPUfreq 驅動程式必須在此處呼叫時設定新頻率。實際頻率必須由 freq_table[index].frequency 確定。
在發生錯誤時,即使我們之前切換到中間頻率,它也應始終恢復到早期頻率(即 policy->restore_freq)。
已棄用¶
target 呼叫有三個引數:struct cpufreq_policy *policy、unsigned int target_frequency、unsigned int relation。
CPUfreq 驅動程式必須在此處呼叫時設定新頻率。實際頻率必須使用以下規則確定
保持接近 “target_freq”
policy->min <= new_freq <= policy->max (這必須有效!!!)
如果 relation==CPUFREQ_REL_L,請嘗試選擇大於或等於 target_freq 的 new_freq。(“L 代表最低,但不低於”)
如果 relation==CPUFREQ_REL_H,請嘗試選擇小於或等於 target_freq 的 new_freq。(“H 代表最高,但不高於”)
同樣,頻率表助手可以為您提供幫助 - 有關詳細資訊,請參見第 2 節。
1.6. fast_switch¶
此函式用於從排程程式的上下文中進行頻率切換。並非所有驅動程式都需要實現它,因為不允許從此回撥中休眠。此回撥必須經過高度最佳化,才能儘可能快地進行切換。
此函式有兩個引數:struct cpufreq_policy *policy 和 unsigned int target_frequency。
1.7 setpolicy¶
setpolicy 呼叫僅接受一個 struct cpufreq_policy *policy 作為引數。您需要將處理器內或晶片組內動態頻率切換的下限設定為 policy->min,上限設定為 policy->max,並且 - 如果支援 - 當 policy->policy 為 CPUFREQ_POLICY_PERFORMANCE 時,選擇以效能為導向的設定,而當 CPUFREQ_POLICY_POWERSAVE 時,選擇以省電為導向的設定。另請檢視 drivers/cpufreq/longrun.c 中的參考實現
1.8 get_intermediate 和 target_intermediate¶
僅適用於具有 target_index() 且 CPUFREQ_ASYNC_NOTIFICATION 未設定的驅動程式。
get_intermediate 應該返回平臺想要切換到的穩定中間頻率,而 target_intermediate() 應該在跳轉到與 ‘index’ 對應的頻率之前,將 CPU 設定為該頻率。核心將負責傳送通知,並且驅動程式不必在 target_intermediate() 或 target_index() 中處理它們。
如果驅動程式不想為某些目標頻率切換到中間頻率,則可以從 get_intermediate() 返回 ‘0’。在這種情況下,核心將直接呼叫 ->target_index()。
注意:在發生故障時,->target_index() 應該恢復到 policy->restore_freq,因為核心會發送該通知。
2. 頻率表助手¶
由於大多數 cpufreq 處理器只允許設定為一些特定頻率,因此帶有某些函式的“頻率表”可能有助於處理器驅動程式的一些工作。這樣的“頻率表”由 struct cpufreq_frequency_table 條目的陣列組成,其中 “driver_data” 中包含驅動程式特定的值,“frequency” 中包含對應的頻率,並且設定了標誌。在表的末尾,您需要新增一個頻率設定為 CPUFREQ_TABLE_END 的 cpufreq_frequency_table 條目。如果您想跳過表中的一個條目,請將頻率設定為 CPUFREQ_ENTRY_INVALID。條目不需要按任何特定順序排序,但如果它們已排序,則 cpufreq 核心會更快地為它們執行 DVFS,因為搜尋最佳匹配的速度更快。
如果策略的 policy->freq_table 欄位中包含有效的指標,則 cpufreq 表由核心自動驗證。
cpufreq_frequency_table_verify() 確保至少一個有效頻率在 policy->min 和 policy->max 之內,並且滿足所有其他條件。這對於 ->verify 呼叫很有幫助。
cpufreq_frequency_table_target() 是 ->target 階段的對應頻率表助手。只需將值傳遞給此函式,此函式將返回包含 CPU 應設定到的頻率的頻率表條目。
以下宏可用作 cpufreq_frequency_table 上的迭代器
cpufreq_for_each_entry(pos, table) - 迭代頻率表的所有條目。
cpufreq_for_each_valid_entry(pos, table) - 迭代所有條目,不包括 CPUFREQ_ENTRY_INVALID 頻率。使用引數 “pos” - 一個 cpufreq_frequency_table * 作為迴圈游標,而 “table” - 你想要迭代的 cpufreq_frequency_table *。
例如
struct cpufreq_frequency_table *pos, *driver_freq_table;
cpufreq_for_each_entry(pos, driver_freq_table) {
/* Do something with pos */
pos->frequency = ...
}
如果您需要使用 driver_freq_table 中 pos 的位置,請不要減去指標,因為它非常昂貴。相反,請使用宏 cpufreq_for_each_entry_idx() 和 cpufreq_for_each_valid_entry_idx()。