如何實現一個新的 CPUFreq 處理器驅動程式

作者

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 *policyunsigned 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 *policyunsigned 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()。