요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
===============================================
How to Implement a new CPUFreq Processor Driver
===============================================
Authors:
- Dominik Brodowski <linux@brodo.de>
- Rafael J. Wysocki <rafael.j.wysocki@intel.com>
- Viresh Kumar <viresh.kumar@linaro.org>
.. Contents
1. What To Do?
1.1 Initialization
1.2 Per-CPU Initialization
1.3 verify
1.4 target/target_index or setpolicy?
1.5 target/target_index
1.6 setpolicy
1.7 get_intermediate and target_intermediate
2. Frequency Table Helpers
1. What To Do?
==============
So, you just got a brand-new CPU / chipset with datasheets and want to
add cpufreq support for this CPU / chipset? Great. Here are some hints
on what is necessary:
1.1 Initialization
------------------
First of all, in an __initcall level 7 (module_init()) or later
function check whether this kernel runs on the right CPU and the right
chipset. If so, register a struct cpufreq_driver with the CPUfreq core
using cpufreq_register_driver()
What shall this struct cpufreq_driver contain?
.name - The name of this driver.
.init - A pointer to the per-policy initialization function.
.verify - A pointer to a "verification" function.
.setpolicy _or_ .fast_switch _or_ .target _or_ .target_index - See
below on the differences.
And optionally
.flags - Hints for the cpufreq core.
.driver_data - cpufreq driver specific data.
.get_intermediate and target_intermediate - Used to switch to stable
frequency while changing CPU frequency.
.get - Returns current frequency of the CPU.
.bios_limit - Returns HW/BIOS max frequency limitations for the CPU.
.exit - A pointer to a per-policy cleanup function called during
CPU_POST_DEAD phase of cpu hotplug process.
.suspend - A pointer to a per-policy suspend function which is called
with interrupts disabled and _after_ the governor is stopped for the
policy.
.resume - A pointer to a per-policy resume function which is called
with interrupts disabled and _before_ the governor is started again.
.ready - A pointer to a per-policy ready function which is called after
the policy is fully initialized.
.attr - A pointer to a NULL-terminated list of "struct freq_attr" which
allow to export values to sysfs.
.boost_enabled - If set, boost frequencies are enabled.
.set_boost - A pointer to a per-policy function to enable/disable boost
frequencies.
1.2 Per-CPU Initialization
--------------------------
Whenever a new CPU is registered with the device model, or after the
cpufreq driver registers itself, the per-policy initialization function
cpufreq_driver.init is called if no cpufreq policy existed for the CPU.
Note that the .init() and .exit() routines are called only once for the
policy and not for each CPU managed by the policy. It takes a ``struct
cpufreq_policy *policy`` as argument. What to do now?
If necessary, activate the CPUfreq support on your CPU.
Then, the driver must fill in the following values:
+-----------------------------------+--------------------------------------+
|policy->cpuinfo.min_freq _and_ | |
|policy->cpuinfo.max_freq | the minimum and maximum frequency |
| | (in kHz) which is supported by |
| | this CPU |
+-----------------------------------+--------------------------------------+
|policy->cpuinfo.transition_latency | the time it takes on this CPU to |
| | switch between two frequencies in |
| | nanoseconds |
+-----------------------------------+--------------------------------------+
|policy->cur | The current operating frequency of |
| | this CPU (if appropriate) |
+-----------------------------------+--------------------------------------+
|policy->min, | |
|policy->max, | |
|policy->policy and, if necessary, | |
|policy->governor | must contain the "default policy" for|
| | this CPU. A few moments later, |
| | cpufreq_driver.verify and either |
| | cpufreq_driver.setpolicy or |
| | cpufreq_driver.target/target_index is|
| | called with these values. |
+-----------------------------------+--------------------------------------+
|policy->cpus | Update this with the masks of the |
| | (online + offline) CPUs that do DVFS |
| | along with this CPU (i.e. that share|
| | clock/voltage rails with it). |
+-----------------------------------+--------------------------------------+
For setting some of these values (cpuinfo.min[max]_freq, policy->min[max]), the
frequency table helpers might be helpful. See the section 2 for more information
on them.
1.3 verify
----------
When the user decides a new policy (consisting of
"policy,governor,min,max") shall be set, this policy must be validated
so that incompatible values can be corrected. For verifying these
values cpufreq_verify_within_limits(``struct cpufreq_policy *policy``,
``unsigned int min_freq``, ``unsigned int max_freq``) function might be helpful.
See section 2 for details on frequency table helpers.
You need to make sure that at least one valid frequency (or operating
range) is within policy->min and policy->max. If necessary, increase
policy->max first, and only if this is no solution, decrease policy->min.
1.4 target or target_index or setpolicy or fast_switch?
-------------------------------------------------------
Most cpufreq drivers or even most cpu frequency scaling algorithms
only allow the CPU frequency to be set to predefined fixed values. For
these, you use the ->target(), ->target_index() or ->fast_switch()
callbacks.
Some cpufreq capable processors switch the frequency between certain
limits on their own. These shall use the ->setpolicy() callback.
1.5. target/target_index
------------------------
The target_index call has two arguments: ``struct cpufreq_policy *policy``,
and ``unsigned int`` index (into the exposed frequency table).
The CPUfreq driver must set the new frequency when called here. The
actual frequency must be determined by freq_table[index].frequency.
It should always restore to earlier frequency (i.e. policy->restore_freq) in
case of errors, even if we switched to intermediate frequency earlier.
Deprecated
----------
The target call has three arguments: ``struct cpufreq_policy *policy``,
unsigned int target_frequency, unsigned int relation.
The CPUfreq driver must set the new frequency when called here. The
actual frequency must be determined using the following rules:
- keep close to "target_freq"
- policy->min <= new_freq <= policy->max (THIS MUST BE VALID!!!)
- if relation==CPUFREQ_REL_L, try to select a new_freq higher than or equal
target_freq. ("L for lowest, but no lower than")
- if relation==CPUFREQ_REL_H, try to select a new_freq lower than or equal
target_freq. ("H for highest, but no higher than")
Here again the frequency table helper might assist you - see section 2
for details.
1.6. fast_switch
----------------
This function is used for frequency switching from scheduler's context.
Not all drivers are expected to implement it, as sleeping from within
this callback isn't allowed. This callback must be highly optimized to
do switching as fast as possible.
This function has two arguments: ``struct cpufreq_policy *policy`` and
``unsigned int target_frequency``.
1.7 setpolicy
-------------
The setpolicy call only takes a ``struct cpufreq_policy *policy`` as
argument. You need to set the lower limit of the in-processor or
in-chipset dynamic frequency switching to policy->min, the upper limit
to policy->max, and -if supported- select a performance-oriented
setting when policy->policy is CPUFREQ_POLICY_PERFORMANCE, and a
powersaving-oriented setting when CPUFREQ_POLICY_POWERSAVE. Also check
the reference implementation in drivers/cpufreq/longrun.c
1.8 get_intermediate and target_intermediate
--------------------------------------------
Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.
get_intermediate should return a stable intermediate frequency platform wants to
switch to, and target_intermediate() should set CPU to that frequency, before
jumping to the frequency corresponding to 'index'. Core will take care of
sending notifications and driver doesn't have to handle them in
target_intermediate() or target_index().
Drivers can return '0' from get_intermediate() in case they don't wish to switch
to intermediate frequency for some target frequency. In that case core will
directly call ->target_index().
NOTE: ->target_index() should restore to policy->restore_freq in case of
failures as core would send notifications for that.
2. Frequency Table Helpers
==========================
As most cpufreq processors only allow for being set to a few specific
frequencies, a "frequency table" with some functions might assist in
some work of the processor driver. Such a "frequency table" consists of
an array of struct cpufreq_frequency_table entries, with driver specific
values in "driver_data", the corresponding frequency in "frequency" and
flags set. At the end of the table, you need to add a
cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END.
And if you want to skip one entry in the table, set the frequency to
CPUFREQ_ENTRY_INVALID. The entries don't need to be in sorted in any
particular order, but if they are cpufreq core will do DVFS a bit
quickly for them as search for best match is faster.
The cpufreq table is verified automatically by the core if the policy contains a
valid pointer in its policy->freq_table field.
cpufreq_frequency_table_verify() assures that at least one valid
frequency is within policy->min and policy->max, and all other criteria
are met. This is helpful for the ->verify call.
cpufreq_frequency_table_target() is the corresponding frequency table
helper for the ->target stage. Just pass the values to this function,
and this function returns the of the frequency table entry which
contains the frequency the CPU shall be set to.
The following macros can be used as iterators over cpufreq_frequency_table:
cpufreq_for_each_entry(pos, table) - iterates over all entries of frequency
table.
cpufreq_for_each_valid_entry(pos, table) - iterates over all entries,
excluding CPUFREQ_ENTRY_INVALID frequencies.
Use arguments "pos" - a ``cpufreq_frequency_table *`` as a loop cursor and
"table" - the ``cpufreq_frequency_table *`` you want to iterate over.
For example::
struct cpufreq_frequency_table *pos, *driver_freq_table;
cpufreq_for_each_entry(pos, driver_freq_table) {
/* Do something with pos */
pos->frequency = ...
}
If you need to work with the position of pos within driver_freq_table,
do not subtract the pointers, as it is quite costly. Instead, use the
macros cpufreq_for_each_entry_idx() and cpufreq_for_each_valid_entry_idx().
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-351. 무엇을 해야 하는가?
새 CPU 또는 chipset과 datasheet를 확보하고 이 CPU/chipset에 cpufreq 지원을 추가하려 한다면, 다음 사항을 구현해야 합니다.
1.1 초기화
36-891.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-1371.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는 다음 값을 채워야 합니다.
Driver의 policy별 init callback이 설정해야 하는 값과 단위를 원문의 ASCII table과 동일한 구조로 정리했습니다.
`cpuinfo.min[max]_freq`와 `policy->min[max]` 같은 일부 값을 설정할 때 frequency table helper가 유용할 수 있습니다. 자세한 내용은 2절을 참조하십시오.
1.3 verify
138-1521.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-1641.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-1941.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-2061.6 fast_switch
이 함수는 scheduler context에서 frequency를 전환할 때 사용합니다. Callback 안에서는 sleep할 수 없으므로 모든 driver가 구현할 필요는 없습니다. 가능한 한 빠르게 전환하도록 고도로 최적화해야 합니다.
Argument는 `struct cpufreq_policy *policy`와 `unsigned int target_frequency` 두 개입니다.
1.7 setpolicy
207-2171.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-2361.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-2632. 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를 사용합니다.
요약과 해설
cpu-drivers.rst:1-285Driver는 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를 단순화할 수 있습니다.