LoginSignup
0
1

More than 3 years have passed since last update.

How to Implement a new CPUFreq Processor Driver

Posted at

もともと、Linux Kernelのソースコードの一部なので、GPLv2扱いになる(はずの認識)。

https://www.kernel.org/doc/html/latest/index.html

Licensing documentation

The following describes the license of the Linux kernel source code (GPLv2), how to properly mark the license of individual files in the source tree, as well as links to the full license text.

https://www.kernel.org/doc/html/latest/process/license-rules.html#kernel-licensing

https://www.kernel.org/doc/html/latest/cpu-freq/cpu-drivers.html
を読んでいく。


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

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:

さて、あなたが真新しいCPU / chipsetとデータシートを手に入れ、このCPU chipsetのcpufreq supportをしたいと考えますね? よろしい、ここにはそれに必要なヒントがいくつかあります。

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()

最初に、 __initcall level 7 ( module_init() ) 以降の関数で、このカーネルが適切なCPUと適切なchipsetで実行されているかをチェックします。もしそうならば、cpufreq_register_driver()を用いて、the CPUfreq coreに対して、cpufreq_driver構造体を登録します。

What shall this struct cpufreq_driver contain?

このcpufreq_driver 構造体には何が含まれるのか?

.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.

  • .name : このドライバの名称
  • .init : それぞれのpolicyが初期化関数へのポインタ
  • .verify : "verification" 関数へのポインタ
  • .setpolicy 、.fast_switch 、 .target もしくは、.taget_index : これらの違いは後述する。

And optionally

更にオプションとして以下を含む

.flags - Hints for the cpufreq core.
.driver_data - cpufreq driver specific data.
.resolve_freq - Returns the most appropriate frequency for a target frequency. Doesn’t change the frequency though.
.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.
.stop_cpu - A pointer to a per-policy stop function called during CPU_DOWN_PREPARE 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.

  • .flags - cpufreq core へのヒント
  • .driver_data - cpufreq driver 固有のデータ
  • .resolve_freq - ターゲット周波数に最適な周波数を返します。ただし、周波数は変更しません。
  • .get_intermediate and target_intermediate - CPU周波数を変更しながら、安定した周波数に変更するのに利用されます。
  • .get - CPUの現在の周波数を返します。
  • .bios_limit - HW/BIOSでの制限となるCPUの最大周波数
  • .exit - cpu hotplug processでCPU_POST_DEAD phase中に呼び出される、ポリシー毎のcleanup 関数へのポインター。
  • .stop_cpu - hotplug processでCPU_DOWN_PREPARE phase中に呼び出される、ポリシー毎のstop 関数へのポインター。
  • .suspend - 割り込みが無効化されて、更に、policyでgonerorがstopに設定された後に、ポリシー毎のsuspend関数へのポインター。
  • .resume - 割り込みが無効化されて、更に、policyでgonerorがstartedに再設定された前に呼ばれる、ポリシー毎のresume関数へのポインター。
  • .ready - policyが完全に初期化を完了させた後に呼ばれる、ポリシー毎のready関数へのポインター。
  • .attr - sysfsが値を出力するために、struct freq_attrのNULL terminated listへのポインタ。
  • .boost_enabled - セットされた場合、boost frequencyが有効となる。
  • .set_boost - boost freqiemcuが有効/無効となった場合の、ポリシー毎の関数へのポインタ。

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?

新しいCPUがデバイスモデルに登録されるとき、または、cpufreqドライバーが自分自身を登録した後、CPUにcpufreqポリシーが存在しない場合、ポリシーごとの初期化関数cpufreq_driver.init()が呼び出されます。
.init()および.exit()ルーチンは、ポリシーによって一度だけ呼び出され、ポリシーによって管理されている各CPUに対しては呼び出されないことに注意してください。 引数にはcpufreq_policy * policy構造体を取ります。 そうしたら何をするべきか?

If necessary, activate the CPUfreq support on your CPU.

もし可能であれば、あなたのCPUをサポートしているCPUfreqがアクティベートされます。

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.min_freq と、 policy->cpuinfo.max_freq
 このCPUでサポートしている、最小と最大の周波数(kHz単位)

policy->cpuinfo.transition_latency
the time it takes on this CPU to switch between two frequencies in nanoseconds (if appropriate, else specify CPUFREQ_ETERNAL)

このCPUで周波数切り替えに要するナノ秒単位での時間(我当する場合、CPUFREQ_ETERNALを指定します)

policy->cur
The current operating frequency of this CPU (if appropriate)

このCPUを動かしている現在の動作周波数(該当する場合)

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->min, policy->max, policy->policy and, if necessary, policy->governor
このCPUの"標準的なポリシー"を含める必要があります。しばらく経過した後、これらの値を用いて、cpufreq_driver.verify()と、cpufreq_driver.setpolicy()あるいはcpufreq_driver.target()/target_index()が呼び出されます。

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).

このCPUでDVFSを実行する場合の、(online + offline) CPUのマスクの更新。(つまり、CPUとクロック/電圧レールを共有します)。

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.

cpuinfo.(min/max)_freqや、polict->(min/max)の値を設定するのに、frequency tabkeが役立つ場合があります。それらの詳細については、セクション2を参照してください。

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.

ユーザーが、ポリシー、governor, 最小、最大から構成される新しいpolicyを設定する必要があると判断したとき、このポリシーを検証し、互換性の無い値を修正できるようにする必要があります。これらの値を検証するには、cpufreq_verify_within_limits(struct cpufreq_policy *policy, unsigned int min_freq, unsigned int max_freq)関数が役立つ場合があります。frequency table helpersの詳細については、セクション2を参照してください。

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つの有効な周波数(あるいは適用範囲)が、policy->min から policy->maxに収まっている必要があります。必要であれば、policy->maxを最初に増やし、それでも解決できない場合には、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.

ほとんどのcpufreq ドライバーやcpu 周波数スケーリングアルゴリズムでは、CPU周波数を事前に定義した固定値に設定する事しかできません。この場合、以下の関数を用います。 ->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.

cpufreq対応プロセッサは、独自に特定の制限間で周波数を切り替えます。これらは、->setpolicy() コールバックを利用します。

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).

target_indexの呼び出しには、2つの引数があります。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.

ここで呼び出されると、CPUfreq ドライバーは新しい周波数を設定する必要があります。実際の周波数は、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.

エラーが発生した場合は、以前の周波数に切り替えた場合でも、常に以前の周波数(policy->restore_freq)を復元する必要があります。

Deprecated(非推奨)

The target call has three arguments: struct cpufreq_policy *policy, unsigned int target_frequency, unsigned int relation.

ターゲットは3つの引数で呼び出します。 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:

ここで呼び出されると、CPUfreqドライバーは新しい周波数を設定する必要があります。実際の周波数は次のルールを使用して決定しなければなりません。

  • 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”)
  • “target_freq”を保持する
  • policy->min <= new_freq <= policy->max (制約条件は必ず守らねばならない!)
  • 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.

度々の繰り返しになるが、frequency table helpersが役立つので、セクション2を参照してください。

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.

この関数には2つの引数があります。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

setpolicy呼び出しでは、cpufreq_policy *policy構造体だけを引数に取ります。プロセス内、もしくは、チップセット内での動的周波数スイッチングの下限をpolicy->minに、上限をpolicy->maxにセットする必要があります。また、もしサポートしているならば、 policy->policyがCPUFREQ_POLICY_PERFORMANCEにセットされ、かつ、powersaving-orientedがCPUFREQ_POLICY_POWERSAVEにセットされた場合の、パフォーマンス思考の設定を選択する必要があります。drivers/cpufreq/longrun.cないに、reference実装があるので確認してください。

1.8 get_intermediate and target_intermediate

Only for drivers with target_index() and CPUFREQ_ASYNC_NOTIFICATION unset.

target_index()を供えたdriverで、CPUFREQ_ASYNC_NOTIFICATIONが設定されていない場合のみ。

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().

get_intermediateは、プラットフォームが切り替えたい安定的な中間周波数を返します。target_intermediate()は、indexに対応する周波数にジャンプする前に、CPUをその周波数に設定する必要があります。Coreはnotificationを送信します。ドライバーは、target_intermediate()または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().

ドライバーは、get_intermediate()に対して、ターゲットの周波数において中間的な周波数に設定する必要が無いケースでは0を返すことができます。このケースでは、coreが直接target_index()を返します。

NOTE: ->target_index() should restore to policy->restore_freq in case of failures as core would send notifications for that.

参考: notificationをcoreが送信するケースにおいて障害が発生した場合には、policy->restore_freq()をtarget_index()が復元する必要があります。

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.

cpufreq processorのほとんどは、いくつかの特定周波数だけがセットできるため、行く通貨の関数を含む"frequency table"は、プロセッサードライバの一部作業を支援することがあります。

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.

"frequency table"は、cpufreq_frequency_table構造体 entriesの配列で構成されており、ドライバー固有情報が"driver_data"に、関連する周波数が"frequency"に、そしてフラグも含まれています。

At the end of the table, you need to add a cpufreq_frequency_table entry with frequency set to CPUFREQ_TABLE_END.

tableの終端には、CPUFREQ_TABLE_ENDをセットしたfrequencyを含むentryを、cpufreq frequency_table entryに追加しておく必要があります。

And if you want to skip one entry in the table, set the frequency to CPUFREQ_ENTRY_INVALID.

また、テーブル内で知キップしたいentryについては、frequencyに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.

エントリーは特定順序に並び替えておく必要はありません。ただし、cpufreq coreがDVFSをする場合には、最良の検索が若干早くなるかもしれません

The cpufreq table is verified automatically by the core if the policy contains a valid pointer in its policy->freq_table field.

policyがpolicy->freq_table fieldに含まれている有効なポインタを含む場合、cpufreq tableは、coreによって自動的に検証されます。

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_verify()は、少なくとも1つは、policy->minからpolicy->maxの中に納まるような有効な周波数があり、すべての基準が満たされていることを保証します。これは、->veryfy callするのに有益です。

cpufreq_frequency_table_target() is the corresponding frequency table helper for the ->target stage.

cpufreq_frequency_table_target()は、 ->taget stageに対してfrequency tableを対応するヘルパーです。

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.

この関数に値をセットすることで、この関数は設定されるべき周波数を含むfrequency table entryを返します。

The following macros can be used as iterators over cpufreq_frequency_table:

次のマクロは、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.

posは、ループカーソルとしてのcpufreq_frequency_table を指定します。そして、"table"には、イテレーションを回したいfrequency_tableを設定します。

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().

driver_freq_table内のposの位置を処理する必要がある場合はコスト観点からポインターの差し引かないでください。その代わりに、マクロ cpufreq_for_each_entry_idx() and cpufreq_for_each_valid_entry_idx()を使ってください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1