요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
========================
The Common Clk Framework
========================
:Author: Mike Turquette <mturquette@ti.com>
This document endeavours to explain the common clk framework details,
and how to port a platform over to this framework. It is not yet a
detailed explanation of the clock api in include/linux/clk.h, but
perhaps someday it will include that information.
Introduction and interface split
================================
The common clk framework is an interface to control the clock nodes
available on various devices today. This may come in the form of clock
gating, rate adjustment, muxing or other operations. This framework is
enabled with the CONFIG_COMMON_CLK option.
The interface itself is divided into two halves, each shielded from the
details of its counterpart. First is the common definition of struct
clk which unifies the framework-level accounting and infrastructure that
has traditionally been duplicated across a variety of platforms. Second
is a common implementation of the clk.h api, defined in
drivers/clk/clk.c. Finally there is struct clk_ops, whose operations
are invoked by the clk api implementation.
The second half of the interface is comprised of the hardware-specific
callbacks registered with struct clk_ops and the corresponding
hardware-specific structures needed to model a particular clock. For
the remainder of this document any reference to a callback in struct
clk_ops, such as .enable or .set_rate, implies the hardware-specific
implementation of that code. Likewise, references to struct clk_foo
serve as a convenient shorthand for the implementation of the
hardware-specific bits for the hypothetical "foo" hardware.
Tying the two halves of this interface together is struct clk_hw, which
is defined in struct clk_foo and pointed to within struct clk_core. This
allows for easy navigation between the two discrete halves of the common
clock interface.
Common data structures and api
==============================
Below is the common struct clk_core definition from
drivers/clk/clk.c, modified for brevity::
struct clk_core {
const char *name;
const struct clk_ops *ops;
struct clk_hw *hw;
struct module *owner;
struct clk_core *parent;
const char **parent_names;
struct clk_core **parents;
u8 num_parents;
u8 new_parent_index;
...
};
The members above make up the core of the clk tree topology. The clk
api itself defines several driver-facing functions which operate on
struct clk. That api is documented in include/linux/clk.h.
Platforms and devices utilizing the common struct clk_core use the struct
clk_ops pointer in struct clk_core to perform the hardware-specific parts of
the operations defined in clk-provider.h::
struct clk_ops {
int (*prepare)(struct clk_hw *hw);
void (*unprepare)(struct clk_hw *hw);
int (*is_prepared)(struct clk_hw *hw);
void (*unprepare_unused)(struct clk_hw *hw);
int (*enable)(struct clk_hw *hw);
void (*disable)(struct clk_hw *hw);
int (*is_enabled)(struct clk_hw *hw);
void (*disable_unused)(struct clk_hw *hw);
unsigned long (*recalc_rate)(struct clk_hw *hw,
unsigned long parent_rate);
long (*round_rate)(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate);
int (*determine_rate)(struct clk_hw *hw,
struct clk_rate_request *req);
int (*set_parent)(struct clk_hw *hw, u8 index);
u8 (*get_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate);
int (*set_rate_and_parent)(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate,
u8 index);
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
unsigned long parent_accuracy);
int (*get_phase)(struct clk_hw *hw);
int (*set_phase)(struct clk_hw *hw, int degrees);
void (*init)(struct clk_hw *hw);
void (*debug_init)(struct clk_hw *hw,
struct dentry *dentry);
};
Hardware clk implementations
============================
The strength of the common struct clk_core comes from its .ops and .hw pointers
which abstract the details of struct clk from the hardware-specific bits, and
vice versa. To illustrate consider the simple gateable clk implementation in
drivers/clk/clk-gate.c::
struct clk_gate {
struct clk_hw hw;
void __iomem *reg;
u8 bit_idx;
...
};
struct clk_gate contains struct clk_hw hw as well as hardware-specific
knowledge about which register and bit controls this clk's gating.
Nothing about clock topology or accounting, such as enable_count or
notifier_count, is needed here. That is all handled by the common
framework code and struct clk_core.
Let's walk through enabling this clk from driver code::
struct clk *clk;
clk = clk_get(NULL, "my_gateable_clk");
clk_prepare(clk);
clk_enable(clk);
The call graph for clk_enable is very simple::
clk_enable(clk);
clk->ops->enable(clk->hw);
[resolves to...]
clk_gate_enable(hw);
[resolves struct clk gate with to_clk_gate(hw)]
clk_gate_set_bit(gate);
And the definition of clk_gate_set_bit::
static void clk_gate_set_bit(struct clk_gate *gate)
{
u32 reg;
reg = __raw_readl(gate->reg);
reg |= BIT(gate->bit_idx);
writel(reg, gate->reg);
}
Note that to_clk_gate is defined as::
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
This pattern of abstraction is used for every clock hardware
representation.
Supporting your own clk hardware
================================
When implementing support for a new type of clock it is only necessary to
include the following header::
#include <linux/clk-provider.h>
To construct a clk hardware structure for your platform you must define
the following::
struct clk_foo {
struct clk_hw hw;
... hardware specific data goes here ...
};
To take advantage of your data you'll need to support valid operations
for your clk::
struct clk_ops clk_foo_ops = {
.enable = &clk_foo_enable,
.disable = &clk_foo_disable,
};
Implement the above functions using container_of::
#define to_clk_foo(_hw) container_of(_hw, struct clk_foo, hw)
int clk_foo_enable(struct clk_hw *hw)
{
struct clk_foo *foo;
foo = to_clk_foo(hw);
... perform magic on foo ...
return 0;
};
Below is a matrix detailing which clk_ops are mandatory based upon the
hardware capabilities of that clock. A cell marked as "y" means
mandatory, a cell marked as "n" implies that either including that
callback is invalid or otherwise unnecessary. Empty cells are either
optional or must be evaluated on a case-by-case basis.
.. table:: clock hardware characteristics
+----------------+------+-------------+---------------+-------------+------+
| | gate | change rate | single parent | multiplexer | root |
+================+======+=============+===============+=============+======+
|.prepare | | | | | |
+----------------+------+-------------+---------------+-------------+------+
|.unprepare | | | | | |
+----------------+------+-------------+---------------+-------------+------+
+----------------+------+-------------+---------------+-------------+------+
|.enable | y | | | | |
+----------------+------+-------------+---------------+-------------+------+
|.disable | y | | | | |
+----------------+------+-------------+---------------+-------------+------+
|.is_enabled | y | | | | |
+----------------+------+-------------+---------------+-------------+------+
+----------------+------+-------------+---------------+-------------+------+
|.recalc_rate | | y | | | |
+----------------+------+-------------+---------------+-------------+------+
|.round_rate | | y [1]_ | | | |
+----------------+------+-------------+---------------+-------------+------+
|.determine_rate | | y [1]_ | | | |
+----------------+------+-------------+---------------+-------------+------+
|.set_rate | | y | | | |
+----------------+------+-------------+---------------+-------------+------+
+----------------+------+-------------+---------------+-------------+------+
|.set_parent | | | n | y | n |
+----------------+------+-------------+---------------+-------------+------+
|.get_parent | | | n | y | n |
+----------------+------+-------------+---------------+-------------+------+
+----------------+------+-------------+---------------+-------------+------+
|.recalc_accuracy| | | | | |
+----------------+------+-------------+---------------+-------------+------+
+----------------+------+-------------+---------------+-------------+------+
|.init | | | | | |
+----------------+------+-------------+---------------+-------------+------+
.. [1] either one of round_rate or determine_rate is required.
Finally, register your clock at run-time with a hardware-specific
registration function. This function simply populates struct clk_foo's
data and then passes the common struct clk parameters to the framework
with a call to::
clk_register(...)
See the basic clock types in ``drivers/clk/clk-*.c`` for examples.
Disabling clock gating of unused clocks
=======================================
Sometimes during development it can be useful to be able to bypass the
default disabling of unused clocks. For example, if drivers aren't enabling
clocks properly but rely on them being on from the bootloader, bypassing
the disabling means that the driver will remain functional while the issues
are sorted out.
You can see which clocks have been disabled by booting your kernel with these
parameters::
tp_printk trace_event=clk:clk_disable
To bypass this disabling, include "clk_ignore_unused" in the bootargs to the
kernel.
Locking
=======
The common clock framework uses two global locks, the prepare lock and the
enable lock.
The enable lock is a spinlock and is held across calls to the .enable,
.disable operations. Those operations are thus not allowed to sleep,
and calls to the clk_enable(), clk_disable() API functions are allowed in
atomic context.
For clk_is_enabled() API, it is also designed to be allowed to be used in
atomic context. However, it doesn't really make any sense to hold the enable
lock in core, unless you want to do something else with the information of
the enable state with that lock held. Otherwise, seeing if a clk is enabled is
a one-shot read of the enabled state, which could just as easily change after
the function returns because the lock is released. Thus the user of this API
needs to handle synchronizing the read of the state with whatever they're
using it for to make sure that the enable state doesn't change during that
time.
The prepare lock is a mutex and is held across calls to all other operations.
All those operations are allowed to sleep, and calls to the corresponding API
functions are not allowed in atomic context.
This effectively divides operations in two groups from a locking perspective.
Drivers don't need to manually protect resources shared between the operations
of one group, regardless of whether those resources are shared by multiple
clocks or not. However, access to resources that are shared between operations
of the two groups needs to be protected by the drivers. An example of such a
resource would be a register that controls both the clock rate and the clock
enable/disable state.
The clock framework is reentrant, in that a driver is allowed to call clock
framework functions from within its implementation of clock operations. This
can for instance cause a .set_rate operation of one clock being called from
within the .set_rate operation of another clock. This case must be considered
in the driver implementations, but the code flow is usually controlled by the
driver in that case.
Note that locking must also be considered when code outside of the common
clock framework needs to access resources used by the clock operations. This
is considered out of scope of this document.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
공통 Clock 프레임워크
1-11저자는 Mike Turquette `<mturquette@ti.com>`입니다. 이 문서는 공통 clock 프레임워크의 세부 사항과 플랫폼을 이 프레임워크로 이식하는 방법을 설명합니다.
아직 `include/linux/clk.h`의 clock API를 자세히 설명하는 문서는 아니지만, 향후 해당 정보가 포함될 수 있습니다.
도입과 인터페이스 분리
12-41공통 clock 프레임워크는 여러 장치의 clock 노드를 제어하는 인터페이스입니다. clock gating, 속도 조정, mux 선택과 그 밖의 동작을 제공하며 `CONFIG_COMMON_CLK` 옵션으로 활성화합니다.
인터페이스는 서로의 세부 구현을 감춘 두 부분으로 나뉩니다. 프레임워크 쪽은 여러 플랫폼에 중복되던 회계와 기반 기능을 통합하는 공통 `struct clk` 정의와 `drivers/clk/clk.c`의 `clk.h` API 구현으로 구성됩니다. 이 API 구현은 `struct clk_ops`의 연산을 호출합니다.
하드웨어 쪽은 `struct clk_ops`에 등록하는 하드웨어별 콜백과 특정 clock을 모델링하는 하드웨어별 구조체로 구성됩니다. 이 문서에서 `.enable`, `.set_rate` 같은 `struct clk_ops` 콜백은 해당 하드웨어 구현을 뜻하며, `struct clk_foo`는 가상의 `foo` 하드웨어 고유 부분을 나타내는 약칭입니다.
`struct clk_hw`가 두 부분을 연결합니다. 이 구조체는 `struct clk_foo` 안에 들어가고 `struct clk_core`가 가리키므로 공통 clock 인터페이스의 두 영역 사이를 쉽게 오갈 수 있습니다.
공통 자료 구조와 API
42-102다음은 간결하게 줄인 `drivers/clk/clk.c`의 공통 `struct clk_core` 정의입니다.
struct clk_core {
const char *name;
const struct clk_ops *ops;
struct clk_hw *hw;
struct module *owner;
struct clk_core *parent;
const char **parent_names;
struct clk_core **parents;
u8 num_parents;
u8 new_parent_index;
...
};
이 멤버들은 clock 트리 토폴로지의 핵심을 이룹니다. clock API는 `struct clk`에 작용하는 여러 드라이버용 함수를 정의하며, 해당 API는 `include/linux/clk.h`에 문서화되어 있습니다.
공통 `struct clk_core`를 사용하는 플랫폼과 장치는 `clk-provider.h`에 정의된 동작의 하드웨어 고유 부분을 수행하기 위해 `struct clk_core`의 `struct clk_ops` 포인터를 사용합니다.
struct clk_ops {
int (*prepare)(struct clk_hw *hw);
void (*unprepare)(struct clk_hw *hw);
int (*is_prepared)(struct clk_hw *hw);
void (*unprepare_unused)(struct clk_hw *hw);
int (*enable)(struct clk_hw *hw);
void (*disable)(struct clk_hw *hw);
int (*is_enabled)(struct clk_hw *hw);
void (*disable_unused)(struct clk_hw *hw);
unsigned long (*recalc_rate)(struct clk_hw *hw,
unsigned long parent_rate);
long (*round_rate)(struct clk_hw *hw,
unsigned long rate,
unsigned long *parent_rate);
int (*determine_rate)(struct clk_hw *hw,
struct clk_rate_request *req);
int (*set_parent)(struct clk_hw *hw, u8 index);
u8 (*get_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate);
int (*set_rate_and_parent)(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate,
u8 index);
unsigned long (*recalc_accuracy)(struct clk_hw *hw,
unsigned long parent_accuracy);
int (*get_phase)(struct clk_hw *hw);
int (*set_phase)(struct clk_hw *hw, int degrees);
void (*init)(struct clk_hw *hw);
void (*debug_init)(struct clk_hw *hw,
struct dentry *dentry);
};
하드웨어 clock 구현
103-158공통 `struct clk_core`의 핵심은 `.ops`와 `.hw` 포인터입니다. 이 포인터는 `struct clk`의 공통 세부 사항과 하드웨어 고유 부분을 서로 추상화합니다. `drivers/clk/clk-gate.c`의 간단한 gate clock 구현은 다음과 같습니다.
struct clk_gate {
struct clk_hw hw;
void __iomem *reg;
u8 bit_idx;
...
};
`struct clk_gate`는 `struct clk_hw hw`와 함께 어떤 레지스터와 비트가 clock gating을 제어하는지에 관한 하드웨어 지식을 담습니다. `enable_count`, `notifier_count` 같은 토폴로지 및 회계 정보는 필요하지 않으며 공통 프레임워크와 `struct clk_core`가 처리합니다.
드라이버 코드가 이 clock을 준비하고 활성화하는 시작점은 다음과 같습니다.
struct clk *clk;
clk = clk_get(NULL, "my_gateable_clk");
clk_prepare(clk);
clk_enable(clk);
`clk_enable()` 호출은 연산 테이블을 거쳐 gate 구현과 레지스터 비트 설정 함수로 이어집니다. 원문 호출 그래프를 exact 블록과 구조화 흐름으로 함께 보존합니다.
clk_enable(clk);
clk->ops->enable(clk->hw);
[resolves to...]
clk_gate_enable(hw);
[resolves struct clk gate with to_clk_gate(hw)]
clk_gate_set_bit(gate);
공통 clock API가 하드웨어별 gate 구현을 찾아 실제 레지스터 비트를 설정하는 과정입니다.
`clk_gate_set_bit()`은 대상 레지스터를 읽고 `bit_idx` 비트를 설정한 뒤 다시 기록합니다.
static void clk_gate_set_bit(struct clk_gate *gate)
{
u32 reg;
reg = __raw_readl(gate->reg);
reg |= BIT(gate->bit_idx);
writel(reg, gate->reg);
}
`to_clk_gate()`는 `container_of()`를 사용해 `struct clk_hw`에서 바깥 `struct clk_gate`를 얻습니다.
#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
이 추상화 패턴은 모든 clock 하드웨어 표현에 사용됩니다.
새 clock 하드웨어 지원
159-203새 clock 유형을 지원하려면 먼저 `include/linux/clk-provider.h` provider 헤더를 포함하면 됩니다.
#include <linux/clk-provider.h>
플랫폼용 clock 하드웨어 구조체는 `struct clk_hw`와 하드웨어 고유 데이터를 함께 담도록 정의합니다.
struct clk_foo {
struct clk_hw hw;
... hardware specific data goes here ...
};
하드웨어 데이터를 사용하려면 해당 clock에 유효한 `struct clk_ops` 연산을 제공합니다.
struct clk_ops clk_foo_ops = {
.enable = &clk_foo_enable,
.disable = &clk_foo_disable,
};
각 연산은 `container_of()` 기반 변환으로 `struct clk_hw`에서 하드웨어 고유 구조체를 얻은 뒤 실제 하드웨어 작업을 수행하도록 구현합니다.
#define to_clk_foo(_hw) container_of(_hw, struct clk_foo, hw)
int clk_foo_enable(struct clk_hw *hw)
{
struct clk_foo *foo;
foo = to_clk_foo(hw);
... perform magic on foo ...
return 0;
};
이어지는 행렬은 clock 하드웨어 기능별로 어떤 `clk_ops`가 필수인지 나타냅니다. `y`는 필수, `n`은 해당 콜백이 잘못되었거나 불필요함을 뜻하고, 빈 칸은 선택 사항이거나 개별 상황에 따라 판단해야 함을 뜻합니다.
clock 기능별 필수 연산과 등록
204-251원문 표의 gate, 속도 변경, 단일 부모, multiplexer, root 요구사항과 각 셀 값을 보존했습니다.
마지막으로 하드웨어 고유 등록 함수가 `struct clk_foo` 데이터를 채우고 공통 `struct clk` 매개변수를 프레임워크에 전달해 런타임에 clock을 등록합니다.
clk_register(...)
구현 예제는 `drivers/clk/clk-*.c`의 기본 clock 유형을 참조하십시오.
사용하지 않는 clock gating 비활성화 우회
252-268개발 중에는 사용하지 않는 clock을 기본적으로 끄는 동작을 우회하는 것이 유용할 수 있습니다. 드라이버가 clock을 올바르게 활성화하지 않고 부트로더가 켜 둔 상태에 의존한다면, 자동 비활성화를 우회해 문제를 고치는 동안 드라이버 기능을 유지할 수 있습니다.
다음 커널 매개변수로 부팅하면 어떤 clock이 비활성화됐는지 확인할 수 있습니다.
tp_printk trace_event=clk:clk_disable
비활성화를 우회하려면 커널 bootargs에 `clk_ignore_unused`를 포함합니다.
prepare lock과 enable lock
269-293공통 clock 프레임워크는 전역 잠금 두 개, 즉 prepare lock과 enable lock을 사용합니다.
enable lock은 spinlock이며 `.enable`, `.disable` 연산 호출 동안 유지됩니다. 따라서 이 연산은 잠들 수 없고 `clk_enable()`, `clk_disable()` API는 atomic context에서 호출할 수 있습니다.
`clk_is_enabled()`도 atomic context에서 사용할 수 있도록 설계됐습니다. 다만 enable lock을 잡은 상태로 결과를 다른 작업과 함께 사용하려는 경우가 아니라면 코어에서 lock을 유지할 실익은 없습니다. 함수 반환 후 잠금이 풀리면 활성 상태가 즉시 바뀔 수 있으므로, 사용자는 해당 상태를 이용하는 작업 동안 값이 변하지 않도록 읽기와 사용을 직접 동기화해야 합니다.
prepare lock은 mutex이며 그 밖의 모든 연산 호출 동안 유지됩니다. 이 연산들은 잠들 수 있고, 대응 API 함수는 atomic context에서 호출할 수 없습니다.
공유 리소스 보호와 재진입성
294-312잠금 관점에서 clock 연산은 두 그룹으로 나뉩니다. 같은 그룹 연산끼리 공유하는 리소스는 여러 clock이 함께 사용하더라도 드라이버가 별도로 보호할 필요가 없습니다.
반면 두 그룹의 연산이 함께 사용하는 리소스는 드라이버가 보호해야 합니다. clock 속도와 enable 또는 disable 상태를 모두 제어하는 레지스터가 그 예입니다.
clock 프레임워크는 재진입 가능합니다. 드라이버는 clock 연산 구현 안에서 프레임워크 함수를 호출할 수 있으며, 한 clock의 `.set_rate` 안에서 다른 clock의 `.set_rate`가 호출될 수도 있습니다. 드라이버 구현은 이 경우를 고려해야 하지만 보통 코드 흐름은 드라이버가 통제합니다.
공통 clock 프레임워크 밖의 코드가 clock 연산이 사용하는 리소스에 접근할 때도 잠금을 고려해야 합니다. 이 문제는 문서 범위를 벗어납니다.
요약과 해설
clk.rst:1-312공통 clock 프레임워크의 인터페이스 분리, 자료 구조, 하드웨어 구현과 등록, 필수 연산 행렬 및 잠금 규칙을 설명합니다. 영어 원문 전체와 한국어 전문 번역을 함께 제공하며, C 구조체와 코드, 함수명, 심볼, 표, 소스 경로와 원문 줄 좌표를 보존합니다.