.. SPDX-License-Identifier: GPL-2.0 .. include:: ../disclaimer-zh_CN.rst :Original: Documentation/cpu-freq/cpu-drivers.rst :翻译: å¸å»¶è…¾ Yanteng Si <siyanteng@loongson.cn> :æ ¡è¯‘: å”艺舟 Tang Yizhou <tangyeechou@gmail.com> ======================================= 如何实现一个新的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å’Œ | 该CPU支æŒçš„最低和最高频率(kHz) | |policy->cpuinfo.max_freq | | | | | +-----------------------------------+--------------------------------------+ |policy->cpuinfo.transition_latency | CPU在两个频率之间切æ¢æ‰€éœ€çš„时间,以 | | | 纳秒为å•ä½ï¼ˆå¦‚ä¸é€‚用,设定为 | | | CPUFREQ_ETERNAL) | | | | +-----------------------------------+--------------------------------------+ |policy->cur | 该CPU当å‰çš„工作频率(如适用) | | | | +-----------------------------------+--------------------------------------+ |policy->min, | 必须包å«è¯¥CPUçš„"默认ç–ç•¥"。ç¨åŽ | |policy->max, | 会用这些值调用 | |policy->policy and, if necessary, | cpufreq_driver.verify和下é¢å‡½æ•° | |policy->governor | 之一:cpufreq_driver.setpolicy或 | | | cpufreq_driver.target/target_index | | | | +-----------------------------------+--------------------------------------+ |policy->cpus | 该policy通过DVFS框架影å“的全部CPU | | | (å³ä¸Žæœ¬CPU共享"时钟/电压"对)æž„æˆ | | | 掩ç (åŒæ—¶åŒ…å«åœ¨çº¿å’Œç¦»çº¿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()回调。 有些具有硬件调频能力的处ç†å™¨å¯ä»¥è‡ªè¡Œä¾æ®æŸäº›é™åˆ¶æ¥åˆ‡æ¢CPU频率。它们应使用->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驱动在调用这里时必须设置新的频率。实际的频率必须使用以下规则æ¥ç¡®å®šã€‚ - å°½é‡è´´è¿‘"ç›®æ ‡é¢‘çŽ‡"。 - 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'对应的频率。cpufreqæ ¸å¿ƒä¼šè´Ÿè´£å‘é€é€šçŸ¥ï¼Œé©±åŠ¨ä¸å¿…在 target_intermediate()或target_index()ä¸å¤„ç†å®ƒä»¬ã€‚ 在驱动程åºä¸æƒ³ä¸ºæŸä¸ªç›®æ ‡é¢‘率切æ¢åˆ°ä¸é—´é¢‘率的情况下,它们å¯ä»¥è®©get_intermediate()返回'0'。 在这ç§æƒ…况下,cpufreqæ ¸å¿ƒå°†ç›´æŽ¥è°ƒç”¨->target_index()。 注æ„:->target_index()应该在å‘生失败的情况下将频率æ¢å¤åˆ°policy->restore_freq, å› ä¸ºcpufreqæ ¸å¿ƒä¼šä¸ºæ¤å‘é€é€šçŸ¥ã€‚ 2. 频率表辅助函数 ================= 由于大多数支æŒcpufreq的处ç†å™¨åªå…è®¸è¢«è®¾ç½®ä¸ºå‡ ä¸ªç‰¹å®šçš„é¢‘çŽ‡ï¼Œå› æ¤ï¼Œ"频率表"和一些相关函数å¯èƒ½ä¼šè¾…助处ç†å™¨é©±åŠ¨ 程åºçš„ä¸€äº›å·¥ä½œã€‚è¿™æ ·çš„"频率表"是一个由struct 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() 。