← Documents Documentation/cpu-freq/cpu-drivers.rst GitHub 원문 ↗

Linux 6.18.37 · CPU Frequency

How to Implement a new CPUFreq Processor Driver

새 CPUFreq processor driver의 등록, cpufreq_driver callback, policy 초기화 필드, verify와 frequency 전환 callback 선택, intermediate frequency, frequency table 및 iterator helper를 설명합니다.

Source pathDocumentation/cpu-freq/cpu-drivers.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

cpu-drivers.rst:1-285

Driver는 hardware 적합성을 확인한 뒤 `cpufreq_register_driver()`로 `struct cpufreq_driver`를 등록하고, policy마다 `.init()`과 `.exit()`을 한 번씩 수행합니다.

Fixed frequency table을 쓰는 hardware는 `target_index` 또는 `fast_switch` 계열 callback을, hardware가 limit 안에서 자율적으로 조절하는 경우에는 `setpolicy`를 사용합니다.

Policy 초기화에서는 지원 frequency, transition latency, 현재 frequency, default policy, DVFS를 공유하는 CPU mask를 정확히 설정해야 합니다. Error 시에는 `policy->restore_freq`로 복원해야 합니다.

Frequency table은 `CPUFREQ_TABLE_END`로 끝나며 `CPUFREQ_ENTRY_INVALID` entry를 건너뜁니다. Core verification과 iterator helper를 활용하면 driver code를 단순화할 수 있습니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ===============================================
4 How to Implement a new CPUFreq Processor Driver
5 ===============================================
6
7 Authors:
8
9
10 - Dominik Brodowski <linux@brodo.de>
11 - Rafael J. Wysocki <rafael.j.wysocki@intel.com>
12 - Viresh Kumar <viresh.kumar@linaro.org>
13
14 .. Contents
15
16 1. What To Do?
17 1.1 Initialization
18 1.2 Per-CPU Initialization
19 1.3 verify
20 1.4 target/target_index or setpolicy?
21 1.5 target/target_index
22 1.6 setpolicy
23 1.7 get_intermediate and target_intermediate
24 2. Frequency Table Helpers
25
26
27
28 1. What To Do?
29 ==============
30
31 So, you just got a brand-new CPU / chipset with datasheets and want to
32 add cpufreq support for this CPU / chipset? Great. Here are some hints
33 on what is necessary:
34
35
36 1.1 Initialization
37 ------------------
38
39 First of all, in an __initcall level 7 (module_init()) or later
40 function check whether this kernel runs on the right CPU and the right
41 chipset. If so, register a struct cpufreq_driver with the CPUfreq core
42 using cpufreq_register_driver()
43
44 What shall this struct cpufreq_driver contain?
45
46 .name - The name of this driver.
47
48 .init - A pointer to the per-policy initialization function.
49
50 .verify - A pointer to a "verification" function.
51
52 .setpolicy _or_ .fast_switch _or_ .target _or_ .target_index - See
53 below on the differences.
54
55 And optionally
56
57 .flags - Hints for the cpufreq core.
58
59 .driver_data - cpufreq driver specific data.
60
61 .get_intermediate and target_intermediate - Used to switch to stable
62 frequency while changing CPU frequency.
63
64 .get - Returns current frequency of the CPU.
65
66 .bios_limit - Returns HW/BIOS max frequency limitations for the CPU.
67
68 .exit - A pointer to a per-policy cleanup function called during
69 CPU_POST_DEAD phase of cpu hotplug process.
70
71 .suspend - A pointer to a per-policy suspend function which is called
72 with interrupts disabled and _after_ the governor is stopped for the
73 policy.
74
75 .resume - A pointer to a per-policy resume function which is called
76 with interrupts disabled and _before_ the governor is started again.
77
78 .ready - A pointer to a per-policy ready function which is called after
79 the policy is fully initialized.
80
81 .attr - A pointer to a NULL-terminated list of "struct freq_attr" which
82 allow to export values to sysfs.
83
84 .boost_enabled - If set, boost frequencies are enabled.
85
86 .set_boost - A pointer to a per-policy function to enable/disable boost
87 frequencies.
88
89
90 1.2 Per-CPU Initialization
91 --------------------------
92
93 Whenever a new CPU is registered with the device model, or after the
94 cpufreq driver registers itself, the per-policy initialization function
95 cpufreq_driver.init is called if no cpufreq policy existed for the CPU.
96 Note that the .init() and .exit() routines are called only once for the
97 policy and not for each CPU managed by the policy. It takes a ``struct
98 cpufreq_policy *policy`` as argument. What to do now?
99
100 If necessary, activate the CPUfreq support on your CPU.
101
102 Then, the driver must fill in the following values:
103
104 +-----------------------------------+--------------------------------------+
105 |policy->cpuinfo.min_freq _and_ | |
106 |policy->cpuinfo.max_freq | the minimum and maximum frequency |
107 | | (in kHz) which is supported by |
108 | | this CPU |
109 +-----------------------------------+--------------------------------------+
110 |policy->cpuinfo.transition_latency | the time it takes on this CPU to |
111 | | switch between two frequencies in |
112 | | nanoseconds |
113 +-----------------------------------+--------------------------------------+
114 |policy->cur | The current operating frequency of |
115 | | this CPU (if appropriate) |
116 +-----------------------------------+--------------------------------------+
117 |policy->min, | |
118 |policy->max, | |
119 |policy->policy and, if necessary, | |
120 |policy->governor | must contain the "default policy" for|
121 | | this CPU. A few moments later, |
122 | | cpufreq_driver.verify and either |
123 | | cpufreq_driver.setpolicy or |
124 | | cpufreq_driver.target/target_index is|
125 | | called with these values. |
126 +-----------------------------------+--------------------------------------+
127 |policy->cpus | Update this with the masks of the |
128 | | (online + offline) CPUs that do DVFS |
129 | | along with this CPU (i.e. that share|
130 | | clock/voltage rails with it). |
131 +-----------------------------------+--------------------------------------+
132
133 For setting some of these values (cpuinfo.min[max]_freq, policy->min[max]), the
134 frequency table helpers might be helpful. See the section 2 for more information
135 on them.
136
137
138 1.3 verify
139 ----------
140
141 When the user decides a new policy (consisting of
142 "policy,governor,min,max") shall be set, this policy must be validated
143 so that incompatible values can be corrected. For verifying these
144 values cpufreq_verify_within_limits(``struct cpufreq_policy *policy``,
145 ``unsigned int min_freq``, ``unsigned int max_freq``) function might be helpful.
146 See section 2 for details on frequency table helpers.
147
148 You need to make sure that at least one valid frequency (or operating
149 range) is within policy->min and policy->max. If necessary, increase
150 policy->max first, and only if this is no solution, decrease policy->min.
151
152
153 1.4 target or target_index or setpolicy or fast_switch?
154 -------------------------------------------------------
155
156 Most cpufreq drivers or even most cpu frequency scaling algorithms
157 only allow the CPU frequency to be set to predefined fixed values. For
158 these, you use the ->target(), ->target_index() or ->fast_switch()
159 callbacks.
160
161 Some cpufreq capable processors switch the frequency between certain
162 limits on their own. These shall use the ->setpolicy() callback.
163
164
165 1.5. target/target_index
166 ------------------------
167
168 The target_index call has two arguments: ``struct cpufreq_policy *policy``,
169 and ``unsigned int`` index (into the exposed frequency table).
170
171 The CPUfreq driver must set the new frequency when called here. The
172 actual frequency must be determined by freq_table[index].frequency.
173
174 It should always restore to earlier frequency (i.e. policy->restore_freq) in
175 case of errors, even if we switched to intermediate frequency earlier.
176
177 Deprecated
178 ----------
179 The target call has three arguments: ``struct cpufreq_policy *policy``,
180 unsigned int target_frequency, unsigned int relation.
181
182 The CPUfreq driver must set the new frequency when called here. The
183 actual frequency must be determined using the following rules:
184
185 - keep close to "target_freq"
186 - policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!)
187 - if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal
188 target_freq. ("L for lowest, but no lower than")
189 - if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal
190 target_freq. ("H for highest, but no higher than")
191
192 Here again the frequency table helper might assist you - see section 2
193 for details.
194
195 1.6. fast_switch
196 ----------------
197
198 This function is used for frequency switching from scheduler's context.
199 Not all drivers are expected to implement it, as sleeping from within
200 this callback isn't allowed. This callback must be highly optimized to
201 do switching as fast as possible.
202
203 This function has two arguments: ``struct cpufreq_policy *policy`` and
204 ``unsigned int target_frequency``.
205
206
207 1.7 setpolicy
208 -------------
209
210 The setpolicy call only takes a ``struct cpufreq_policy *policy`` as
211 argument. You need to set the lower limit of the in-processor or
212 in-chipset dynamic frequency switching to policy->min, the upper limit
213 to policy->max, and -if supported- select a performance-oriented
214 setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
215 powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
216 the reference implementation in drivers/cpufreq/longrun.c
217
218 1.8 get_intermediate and target_intermediate
219 --------------------------------------------
220
221 Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
222
223 get_intermediate should return a stable intermediate frequency platform wants to
224 switch to, and target_intermediate() should set CPU to that frequency, before
225 jumping to the frequency corresponding to 'index'. Core will take care of
226 sending notifications and driver doesn't have to handle them in
227 target_intermediate() or target_index().
228
229 Drivers can return '0' from get_intermediate() in case they don't wish to switch
230 to intermediate frequency for some target frequency. In that case core will
231 directly call ->target_index().
232
233 NOTE: ->target_index() should restore to policy->restore_freq in case of
234 failures as core would send notifications for that.
235
236
237 2. Frequency Table Helpers
238 ==========================
239
240 As most cpufreq processors only allow for being set to a few specific
241 frequencies, a "frequency table" with some functions might assist in
242 some work of the processor driver. Such a "frequency table" consists of
243 an array of struct cpufreq_frequency_table entries, with driver specific
244 values in "driver_data", the corresponding frequency in "frequency" and
245 flags set. At the end of the table, you need to add a
246 cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END.
247 And if you want to skip one entry in the table, set the frequency to
248 CPUFREQ_ENTRY_INVALID. The entries don't need to be in sorted in any
249 particular order, but if they are cpufreq core will do DVFS a bit
250 quickly for them as search for best match is faster.
251
252 The cpufreq table is verified automatically by the core if the policy contains a
253 valid pointer in its policy->freq_table field.
254
255 cpufreq_frequency_table_verify() assures that at least one valid
256 frequency is within policy->min and policy->max, and all other criteria
257 are met. This is helpful for the ->verify call.
258
259 cpufreq_frequency_table_target() is the corresponding frequency table
260 helper for the ->target stage. Just pass the values to this function,
261 and this function returns the of the frequency table entry which
262 contains the frequency the CPU shall be set to.
263
264 The following macros can be used as iterators over cpufreq_frequency_table:
265
266 cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
267 table.
268
269 cpufreq_for_each_valid_entry(pos, table) - iterates over all entries,
270 excluding CPUFREQ_ENTRY_INVALID frequencies.
271 Use arguments "pos" - a ``cpufreq_frequency_table *`` as a loop cursor and
272 "table" - the ``cpufreq_frequency_table *`` you want to iterate over.
273
274 For example::
275
276 struct cpufreq_frequency_table *pos, *driver_freq_table;
277
278 cpufreq_for_each_entry(pos, driver_freq_table) {
279 /* Do something with pos */
280 pos->frequency = ...
281 }
282
283 If you need to work with the position of pos within driver_freq_table,
284 do not subtract the pointers, as it is quite costly. Instead, use the
285 macros cpufreq_for_each_entry_idx() and cpufreq_for_each_valid_entry_idx().
286

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

새 CPUFreq processor driver 구현 방법

1-27
.. SPDX-License-Identifier: GPL-2.0

새 CPUFreq processor driver 구현 방법

저자:

  • Dominik Brodowski <linux@brodo.de>
  • Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Viresh Kumar <viresh.kumar@linaro.org>

목차:

  • 1. 무엇을 해야 하는가?
  • 1.1 초기화
  • 1.2 CPU별 초기화
  • 1.3 verify
  • 1.4 target/target_index 또는 setpolicy?
  • 1.5 target/target_index
  • 1.6 setpolicy
  • 1.7 get_intermediate와 target_intermediate
  • 2. Frequency table helper

1. 무엇을 해야 하는가?

28-35

1. 무엇을 해야 하는가?

새 CPU 또는 chipset과 datasheet를 확보하고 이 CPU/chipset에 cpufreq 지원을 추가하려 한다면, 다음 사항을 구현해야 합니다.

1.1 초기화

36-89

1.1 초기화

먼저 `__initcall` level 7인 `module_init()` 또는 그 이후에 실행되는 함수에서 kernel이 올바른 CPU와 chipset에서 실행 중인지 검사합니다. 조건이 맞으면 `cpufreq_register_driver()`를 사용하여 `struct cpufreq_driver`를 CPUfreq core에 등록합니다.

필수 `struct cpufreq_driver` member:

  • .name: driver 이름
  • .init: policy별 초기화 함수를 가리키는 pointer
  • .verify: verification 함수를 가리키는 pointer
  • .setpolicy 또는 .fast_switch 또는 .target 또는 .target_index: 차이점은 아래 절 참조

선택적 member:

  • .flags: cpufreq core에 제공하는 hint
  • .driver_data: cpufreq driver 전용 data
  • .get_intermediate와 .target_intermediate: CPU frequency를 변경하는 동안 안정적인 frequency로 전환할 때 사용
  • .get: CPU의 현재 frequency 반환
  • .bios_limit: CPU에 대한 HW/BIOS maximum frequency 제한 반환
  • .exit: CPU hotplug 과정의 CPU_POST_DEAD phase에서 호출되는 policy별 cleanup 함수 pointer
  • .suspend: interrupt가 비활성화되고 policy governor가 중지된 뒤 호출되는 policy별 suspend 함수 pointer
  • .resume: interrupt가 비활성화되고 governor가 다시 시작되기 전에 호출되는 policy별 resume 함수 pointer
  • .ready: policy 초기화가 완전히 끝난 뒤 호출되는 policy별 ready 함수 pointer
  • .attr: sysfs로 값을 export할 수 있게 하는 NULL-terminated `struct freq_attr` 목록 pointer
  • .boost_enabled: 설정되어 있으면 boost frequency 활성화
  • .set_boost: boost frequency를 활성화하거나 비활성화하는 policy별 함수 pointer

1.2 CPU별 초기화

90-137

1.2 CPU별 초기화

새 CPU가 device model에 등록되거나 cpufreq driver가 자신을 등록한 뒤, 해당 CPU에 기존 cpufreq policy가 없으면 policy별 초기화 함수 `cpufreq_driver.init`이 호출됩니다. `.init()`과 `.exit()` routine은 policy가 관리하는 각 CPU가 아니라 policy마다 한 번만 호출됩니다. Argument는 `struct cpufreq_policy *policy`입니다.

필요하다면 CPU에서 CPUfreq 지원을 활성화합니다.

그 다음 driver는 다음 값을 채워야 합니다.

cpufreq_policy 초기화 필드
필드설정할 값
policy->cpuinfo.min_freq / policy->cpuinfo.max_freq이 CPU가 지원하는 minimum 및 maximum frequency, 단위 kHz
policy->cpuinfo.transition_latency이 CPU가 두 frequency 사이를 전환하는 데 걸리는 시간, 단위 nanosecond
policy->cur해당하는 경우 CPU의 현재 operating frequency
policy->min / policy->max / policy->policy / policy->governorCPU의 default policy. 이후 cpufreq_driver.verify와 cpufreq_driver.setpolicy 또는 cpufreq_driver.target/target_index가 이 값으로 호출됨
policy->cpus이 CPU와 함께 DVFS를 수행하며 clock 또는 voltage rail을 공유하는 online 및 offline CPU mask
Frequency 단위min_freq와 max_freq는 kHz입니다.
Latency 단위transition_latency는 nanosecond입니다.

Driver의 policy별 init callback이 설정해야 하는 값과 단위를 원문의 ASCII table과 동일한 구조로 정리했습니다.

`cpuinfo.min[max]_freq`와 `policy->min[max]` 같은 일부 값을 설정할 때 frequency table helper가 유용할 수 있습니다. 자세한 내용은 2절을 참조하십시오.

1.3 verify

138-152

1.3 verify

사용자가 `policy,governor,min,max`로 구성된 새 policy를 선택하면 호환되지 않는 값을 수정할 수 있도록 검증해야 합니다. 이 값의 검증에는 `cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min_freq, unsigned int max_freq)`가 유용할 수 있습니다. Frequency table helper의 자세한 내용은 2절을 참조하십시오.

최소 하나의 유효한 frequency 또는 operating range가 `policy->min`과 `policy->max` 안에 있도록 보장해야 합니다. 필요하면 먼저 `policy->max`를 늘리고, 그것으로 해결되지 않을 때만 `policy->min`을 낮춥니다.

1.4 target, target_index, setpolicy 또는 fast_switch?

153-164

1.4 target, target_index, setpolicy 또는 fast_switch?

대부분의 cpufreq driver와 CPU frequency scaling algorithm은 미리 정의된 고정 값으로만 CPU frequency를 설정할 수 있습니다. 이런 경우 `->target()`, `->target_index()`, `->fast_switch()` callback을 사용합니다.

일부 cpufreq 지원 processor는 특정 limit 사이에서 스스로 frequency를 전환합니다. 이런 processor는 `->setpolicy()` callback을 사용해야 합니다.

1.5 target과 target_index

165-194

1.5 target/target_index

`target_index` 호출은 `struct cpufreq_policy *policy`와 공개된 frequency table 안의 `unsigned int index` 두 argument를 받습니다.

CPUfreq driver는 이 callback에서 새 frequency를 설정해야 합니다. 실제 frequency는 `freq_table[index].frequency`로 결정해야 합니다.

Error가 발생하면 앞서 intermediate frequency로 전환했더라도 항상 이전 frequency인 `policy->restore_freq`로 복원해야 합니다.

Deprecated:

`target` 호출은 `struct cpufreq_policy *policy`, `unsigned int target_frequency`, `unsigned int relation` 세 argument를 받습니다.

CPUfreq driver는 여기서 새 frequency를 설정해야 하며 실제 frequency는 다음 규칙으로 결정합니다.

  • `target_freq`에 가깝게 유지
  • `policy->min <= new_freq <= policy->max`를 반드시 만족
  • `relation == CPUFREQ_REL_L`이면 `target_freq` 이상인 `new_freq` 선택을 시도. L은 lowest이지만 더 낮지 않음을 뜻함
  • `relation == CPUFREQ_REL_H`이면 `target_freq` 이하인 `new_freq` 선택을 시도. H는 highest이지만 더 높지 않음을 뜻함

여기에서도 frequency table helper를 활용할 수 있습니다. 자세한 내용은 2절을 참조하십시오.

1.6 fast_switch

195-206

1.6 fast_switch

이 함수는 scheduler context에서 frequency를 전환할 때 사용합니다. Callback 안에서는 sleep할 수 없으므로 모든 driver가 구현할 필요는 없습니다. 가능한 한 빠르게 전환하도록 고도로 최적화해야 합니다.

Argument는 `struct cpufreq_policy *policy`와 `unsigned int target_frequency` 두 개입니다.

1.7 setpolicy

207-217

1.7 setpolicy

`setpolicy` 호출은 `struct cpufreq_policy *policy` 하나만 argument로 받습니다. Processor 또는 chipset 내부 dynamic frequency switching의 lower limit을 `policy->min`, upper limit을 `policy->max`로 설정해야 합니다. 지원한다면 `policy->policy`가 `CPUFREQ_POLICY_PERFORMANCE`일 때 performance 지향 설정을, `CPUFREQ_POLICY_POWERSAVE`일 때 power-saving 지향 설정을 선택합니다. Reference 구현은 `drivers/cpufreq/longrun.c`를 참조하십시오.

1.8 get_intermediate와 target_intermediate

218-236

1.8 get_intermediate와 target_intermediate

이 callback은 `target_index()`를 제공하고 `CPUFREQ_ASYNC_NOTIFICATION`이 설정되지 않은 driver에만 적용됩니다.

`get_intermediate()`는 platform이 전환하려는 안정적인 intermediate frequency를 반환해야 합니다. `target_intermediate()`는 `index`에 해당하는 frequency로 이동하기 전에 CPU를 그 intermediate frequency로 설정해야 합니다. Core가 notification 전송을 담당하므로 driver는 `target_intermediate()`나 `target_index()`에서 이를 처리할 필요가 없습니다.

특정 target frequency에 대해 intermediate frequency로 전환하지 않으려면 `get_intermediate()`에서 `0`을 반환할 수 있습니다. 이 경우 core는 `->target_index()`를 직접 호출합니다.

Note: 실패 시 core가 notification을 전송하므로 `->target_index()`는 `policy->restore_freq`로 복원해야 합니다.

2. Frequency table helper

237-263

2. Frequency table helper

대부분의 cpufreq processor는 몇 가지 특정 frequency로만 설정할 수 있으므로, 함수와 함께 제공되는 frequency table이 processor driver의 작업을 도울 수 있습니다. 이 table은 `struct cpufreq_frequency_table` entry 배열로 구성되며 각 entry에는 `driver_data`의 driver 전용 값, `frequency`의 대응 frequency, 설정된 flag가 들어 있습니다. Table 끝에는 frequency가 `CPUFREQ_TABLE_END`인 entry를 추가해야 합니다. Entry를 건너뛰려면 frequency를 `CPUFREQ_ENTRY_INVALID`로 설정합니다. Entry를 특정 순서로 정렬할 필요는 없지만 정렬되어 있으면 최적 match 검색이 빨라져 cpufreq core가 DVFS를 조금 더 빠르게 수행합니다.

Policy의 `policy->freq_table` field에 유효한 pointer가 있으면 core가 cpufreq table을 자동으로 검증합니다.

`cpufreq_frequency_table_verify()`는 `policy->min`과 `policy->max` 안에 최소 하나의 유효한 frequency가 있고 다른 모든 기준도 충족되는지 보장합니다. `->verify` 호출에 유용합니다.

`cpufreq_frequency_table_target()`은 `->target` 단계에 대응하는 frequency table helper입니다. 값을 전달하면 CPU가 설정되어야 할 frequency를 포함한 frequency table entry의 index를 반환합니다.

Frequency table iterator

264-285

다음 macro를 `cpufreq_frequency_table` iterator로 사용할 수 있습니다.

`cpufreq_for_each_entry(pos, table)`은 frequency table의 모든 entry를 순회합니다.

`cpufreq_for_each_valid_entry(pos, table)`은 `CPUFREQ_ENTRY_INVALID` frequency를 제외한 모든 entry를 순회합니다. `pos`는 loop cursor인 `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`의 위치가 필요할 때 pointer subtraction은 비용이 크므로 사용하지 마십시오. 대신 `cpufreq_for_each_entry_idx()`와 `cpufreq_for_each_valid_entry_idx()` macro를 사용합니다.