.. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_TW.rst :Original: :doc:`../../../cpu-freq/cpu-drivers` :Translator: Yanteng Si <siyanteng@loongson.cn> Hu Haowen <src.res.211@gmail.com> .. _tw_cpu-drivers.rst: ======================================= 如何實ç¾ä¸€å€‹æ–°çš„CPUFreq處ç†å™¨é©…動程åºï¼Ÿ ======================================= 作者: - Dominik Brodowski <linux@brodo.de> - Rafael J. Wysocki <rafael.j.wysocki@intel.com> - Viresh Kumar <viresh.kumar@linaro.org> .. Contents 1. 怎麼åšï¼Ÿ 1.1 åˆå§‹åŒ– 1.2 Per-CPU åˆå§‹åŒ– 1.3 é©—è‰ 1.4 target/target_index 或 setpolicy? 1.5 target/target_index 1.6 setpolicy 1.7 get_intermediate 與 target_intermediate 2. é »çŽ‡è¡¨åŠ©æ‰‹ 1. 怎麼åšï¼Ÿ =========== 如æ¤ï¼Œä½ 剛剛得到了一個全新的CPU/晶片組åŠå…¶æ•¸æ“šæ‰‹å†Šï¼Œä¸¦å¸Œæœ›çˆ²é€™å€‹CPU/æ™¶ç‰‡çµ„æ·»åŠ cpufreq 支æŒï¼Ÿå¾ˆå¥½ï¼Œé€™è£¡æœ‰ä¸€äº›è‡³é—œé‡è¦çš„æ示: 1.1 åˆå§‹åŒ– ---------- 首先,在__initcall_level_7 (module_init())或更é 後的函數ä¸æª¢æŸ¥é€™å€‹å…§æ ¸æ˜¯å¦ é‹è¡Œåœ¨æ£ç¢ºçš„CPUå’Œæ£ç¢ºçš„晶片組上。如果是,則使用cpufreq_register_driver()å‘ CPUfreqæ ¸å¿ƒå±¤è¨»å†Šä¸€å€‹cpufreq_driverçµæ§‹é«”。 çµæ§‹é«”cpufreq_driver應該包å«ä»€éº¼æˆå“¡? .name - é©…å‹•çš„åå—。 .init - 一個指å‘per-policyåˆå§‹åŒ–函數的指é‡ã€‚ .verify - 一個指å‘"verification"函數的指é‡ã€‚ .setpolicy 或 .fast_switch 或 .target 或 .target_index - 差異見 下文。 並且å¯é¸æ“‡ .flags - cpufreqæ ¸çš„æ示。 .driver_data - cpufreq驅動程åºçš„特定數據。 .get_intermediate å’Œ target_intermediate - 用於在改變CPUé »çŽ‡æ™‚åˆ‡æ›åˆ°ç©©å®š çš„é »çŽ‡ã€‚ .get - 返回CPU的當å‰é »çŽ‡ã€‚ .bios_limit - 返回HW/BIOSå°CPUçš„æœ€å¤§é »çŽ‡é™åˆ¶å€¼ã€‚ .exit - 一個指å‘per-policy清ç†å‡½æ•¸çš„指é‡ï¼Œè©²å‡½æ•¸åœ¨cpu熱æ’æ‹”éŽç¨‹çš„CPU_POST_DEAD 階段被調用。 .suspend - 一個指å‘per-policyæš«åœå‡½æ•¸çš„指é‡ï¼Œè©²å‡½æ•¸åœ¨é—œä¸æ–·ä¸”在該ç–略的調節器åœæ¢ 後被調用。 .resume - 一個指å‘per-policyæ¢å¾©å‡½æ•¸çš„指é‡ï¼Œè©²å‡½æ•¸åœ¨é—œä¸æ–·ä¸”在調節器å†ä¸€æ¬¡é–‹å§‹å‰è¢« 調用。 .ready - 一個指å‘per-policy準備函數的指é‡ï¼Œè©²å‡½æ•¸åœ¨ç–略完全åˆå§‹åŒ–之後被調用。 .attr - 一個指å‘NULLçµå°¾çš„"struct freq_attr"列表的指é‡ï¼Œè©²å‡½æ•¸å…許導出值到 sysfs。 .boost_enabled - 如果è¨ç½®ï¼Œå‰‡å•“用æå‡(boost)é »çŽ‡ã€‚ .set_boost - 一個指å‘per-policy函數的指é‡ï¼Œè©²å‡½æ•¸ç”¨ä¾†é–‹å•“/關閉æå‡(boost)é »çŽ‡åŠŸèƒ½ã€‚ 1.2 Per-CPU åˆå§‹åŒ– ------------------ æ¯ç•¶ä¸€å€‹æ–°çš„CPU被註冊到è¨å‚™æ¨¡åž‹ä¸ï¼Œæˆ–者在cpufreq驅動註冊自己之後,如果æ¤CPUçš„cpufreqç– ç•¥ä¸å˜åœ¨ï¼Œå‰‡æœƒèª¿ç”¨per-policyçš„åˆå§‹åŒ–函數cpufreq_driver.init。請注æ„,.init()å’Œ.exit()ç¨‹åº åªå°ç–略調用一次,而ä¸æ˜¯å°ç–略管ç†çš„æ¯å€‹CPU調用一次。它需è¦ä¸€å€‹ ``struct cpufreq_policy *policy`` 作爲åƒæ•¸ã€‚ç¾åœ¨è©²æ€Žéº¼åšå‘¢ï¼Ÿ 如果有必è¦ï¼Œè«‹åœ¨ä½ çš„CPU上激活CPUfreq功能支æŒã€‚ 然後,驅動程åºå¿…é ˆå¡«å¯«ä»¥ä¸‹æ•¸å€¼: +-----------------------------------+--------------------------------------+ |policy->cpuinfo.min_freq å’Œ | | |policy->cpuinfo.max_freq | 該CPU支æŒçš„æœ€ä½Žå’Œæœ€é«˜é »çŽ‡ï¼ˆkHz) | | | | | | | +-----------------------------------+--------------------------------------+ |policy->cpuinfo.transition_latency | | | | CPUåœ¨å…©å€‹é »çŽ‡ä¹‹é–“åˆ‡æ›æ‰€éœ€çš„時間,以 | | | ç´ç§’爲單ä½ï¼ˆå¦‚é©ç”¨ï¼Œå¦å‰‡æŒ‡å®š | | | CPUFREQ_ETERNAL) | +-----------------------------------+--------------------------------------+ |policy->cur | 該CPU當å‰çš„å·¥ä½œé »çŽ‡(如é©ç”¨) | | | | +-----------------------------------+--------------------------------------+ |policy->min, | | |policy->max, | | |policy->policy and, if necessary, | | |policy->governor | å¿…é ˆåŒ…å«è©²cpuçš„ 「默èªç–ç•¥ã€ã€‚ç¨å¾Œ | | | 會用這些值調用 | | | cpufreq_driver.verify and either | | | cpufreq_driver.setpolicy or | | | cpufreq_driver.target/target_index | | | | +-----------------------------------+--------------------------------------+ |policy->cpus | 用與這個CPU一起åšDVFSçš„(在線+離線) | | | CPU(å³èˆ‡å®ƒå…±äº«æ™‚é˜/電壓軌)的掩碼更新 | | | 這個 | | | | +-----------------------------------+--------------------------------------+ å°æ–¼è¨ç½®å…¶ä¸çš„一些值(cpuinfo.min[max]_freq, policy->min[max])ï¼Œé »çŽ‡è¡¨åŠ©æ‰‹å¯èƒ½æœƒæœ‰å¹« 助。關於它們的更多信æ¯ï¼Œè«‹åƒè¦‹ç¬¬2節。 1.3 é©—è‰ -------- 當用戶決定è¨ç½®ä¸€å€‹æ–°çš„ç–ç•¥(ç”± 「policy,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),å³ä½¿æˆ‘們之å‰åˆ‡æ›åˆ°ä¸é–“é »çŽ‡ã€‚ 已棄用 ---------- 目標調用有三個åƒæ•¸ã€‚``struct cpufreq_policy * policy``, unsigned int target_frequency, unsigned int relation. CPUfreqé©…å‹•åœ¨èª¿ç”¨é€™è£¡æ™‚å¿…é ˆè¨ç½®æ–°çš„é »çŽ‡ã€‚å¯¦éš›çš„é »çŽ‡å¿…é ˆä½¿ç”¨ä»¥ä¸‹è¦å‰‡ä¾†ç¢ºå®šã€‚ - ç·Šè·Ÿ "ç›®æ¨™é »çŽ‡"。 - 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()應該將CPUè¨ç½®çˆ² è©²é »çŽ‡ï¼Œç„¶å¾Œå†è·³è½‰åˆ°'index'å°æ‡‰çš„é »çŽ‡ã€‚æ ¸å¿ƒæœƒè² è²¬ç™¼é€é€šçŸ¥ï¼Œé©…å‹•ä¸å¿…在target_intermediate()或 target_index()ä¸è™•ç†ã€‚ 在驅動程åºä¸æƒ³å› 爲æŸå€‹ç›®æ¨™é »çŽ‡åˆ‡æ›åˆ°ä¸é–“é »çŽ‡çš„æƒ…æ³ä¸‹ï¼Œå®ƒå€‘å¯ä»¥å¾žget_intermediate()ä¸è¿”回'0'ã€‚åœ¨é€™ç¨®æƒ…æ³ ä¸‹ï¼Œæ ¸å¿ƒå°‡ç›´æŽ¥èª¿ç”¨->target_index()。 注æ„:->target_index()應該在失敗的情æ³ä¸‹æ¢å¾©åˆ°policy->restore_freqï¼Œå› çˆ²core會爲æ¤ç™¼é€é€šçŸ¥ã€‚ 2. é »çŽ‡è¡¨åŠ©æ‰‹ ============= 由於大多數cpufreq處ç†å™¨åªå…許被è¨ç½®çˆ²å¹¾å€‹ç‰¹å®šçš„é »çŽ‡ï¼Œå› æ¤ï¼Œä¸€å€‹å¸¶æœ‰ä¸€äº›å‡½æ•¸çš„ ã€Œé »çŽ‡è¡¨ã€å¯èƒ½æœƒè¼”助處ç†å™¨é©…å‹• 程åºçš„一些工作。這樣的 "é »çŽ‡è¡¨" 由一個cpufreq_frequency_tableæ¢ç›®æ§‹æˆçš„數組組æˆï¼Œ"driver_data" ä¸åŒ… å«äº†é©…動程åºçš„具體數值,"frequency" ä¸åŒ…å«äº†ç›¸æ‡‰çš„é »çŽ‡ï¼Œä¸¦è¨ç½®äº†æ¨™èªŒã€‚在表的最後,需è¦æ·»åŠ 一個 cpufreq_frequency_tableæ¢ç›®ï¼Œé »çŽ‡è¨ç½®çˆ²CPUFREQ_TABLE_END。而如果想跳éŽè¡¨ä¸çš„一個æ¢ç›®ï¼Œå‰‡å°‡é »çŽ‡è¨ç½®çˆ² 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() 。