세 공급 자원은 한 묶음의 상태가 아니다
하나의 IP driver가 clock, reset, regulator를 모두 사용할 수 있지만, 세 handle의 획득·활성화·반납 계약은 별개다. DT/ACPI 속성은 provider를 찾는 단서이고 실제 provider 준비 여부·deferred probe는 각 API 결과로 판단한다. 전압 공급·clock·reset의 하드웨어 순서는 선택한 IP의 데이터시트와 driver 구현에 따른다.
clk_getconsumer handle 획득. 아직 enabled로 가정하지 않음clk_prepare_enableprepare + enable 요청; 반환값 확인clk_disable_unprepare → clk_putreset_control_get_exclusive / reset_control_get_shareddeassert/assert; shared는 균형 필요reset_control_putregulator_getsupply handle 획득. rail 상태와 별개set_voltage; regulator_enable 반환 확인regulator_disable → regulator_put세 줄 사이에는 프레임워크가 정한 전역 순서 화살표가 없다. 특정 driver의 probe()가 선택한 순서만 역순 정리의 기준이 된다.
clock balance · reset get variants · regulator enable balance
소스 코드 위치
기존 발췌는 drivers/clk/clk.c의 이름·HW helper와 drivers/reset/core.c의 exclusive acquire 부분이다. 실제 consumer clk_get() 구현은 drivers/clk/clkdev.c에 있고, reset get 변형은 include/linux/reset.h에 선언된다. regulator consumer 계약도 별도 파일에서 읽는다.
| 관심사 | v6.6 고정 원문 | 이 글에서 읽는 부분 |
|---|---|---|
| Clock | drivers/clk/clk.c · drivers/clk/clkdev.c · include/linux/clk.h | 기존 helper 발췌와 별개인 clk_get·prepare/enable 계약 |
| Reset | drivers/reset/core.c · include/linux/reset.h | exclusive acquire 발췌와 get variant·assert/deassert 계약 |
| Regulator | drivers/regulator/core.c · include/linux/regulator/consumer.h | consumer handle, voltage 제약, enable/disable |
대표 코드
Clock 이름·HW helper 구간
clk_get() 구현은 clkdev.c:100–118
return ret;
}
/*** helper functions ***/
const char *__clk_get_name(const struct clk *clk)
{
return !clk ? NULL : clk->core->name;
}
EXPORT_SYMBOL_GPL(__clk_get_name);
const char *clk_hw_get_name(const struct clk_hw *hw)
{
return hw->core->name;
}
EXPORT_SYMBOL_GPL(clk_hw_get_name);
struct clk_hw *__clk_get_hw(struct clk *clk)
{
return !clk ? NULL : clk->core->hw;
}
EXPORT_SYMBOL_GPL(__clk_get_hw);
unsigned int clk_hw_get_num_parents(const struct clk_hw *hw)
{
return hw->core->num_parents;
}
EXPORT_SYMBOL_GPL(clk_hw_get_num_parents);
struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw)
{
return hw->core->parent ? hw->core->parent->hw : NULL;
}
EXPORT_SYMBOL_GPL(clk_hw_get_parent);
static struct clk_core *__clk_lookup_subtree(const char *name,
struct clk_core *core)
{
struct clk_core *child;
struct clk_core *ret;
읽기 기준: 이 조각은 보유한 clock/HW의 이름·포인터를 읽는 helper 범위다. clk_get()도 provider 등록도 수행하지 않는다. 반환값은 해당 helper의 값이며 clock enable 상태를 뜻하지 않는다.
reset_control_acquire() 설명과 함수 앞부분
*
* This is used to explicitly acquire a reset control for exclusive use. Note
* that exclusive resets are requested as acquired by default. In order for a
* second consumer to be able to control the reset, the first consumer has to
* release it first. Typically the easiest way to achieve this is to call the
* reset_control_get_exclusive_released() to obtain an instance of the reset
* control. Such reset controls are not acquired by default.
*
* Consumers implementing shared access to an exclusive reset need to follow
* a specific protocol in order to work together. Before consumers can change
* a reset they must acquire exclusive access using reset_control_acquire().
* After they are done operating the reset, they must release exclusive access
* with a call to reset_control_release(). Consumers are not granted exclusive
* access to the reset as long as another consumer hasn't released a reset.
*
* See also: reset_control_release()
*/
int reset_control_acquire(struct reset_control *rstc)
{
struct reset_control *rc;
if (!rstc)
return 0;
if (WARN_ON(IS_ERR(rstc)))
return -EINVAL;
if (reset_control_is_array(rstc))
return reset_control_array_acquire(rstc_to_array(rstc));
mutex_lock(&reset_list_mutex);
if (rstc->acquired) {
mutex_unlock(&reset_list_mutex);
return 0;
}
list_for_each_entry(rc, &rstc->rcdev->reset_control_head, list) {
if (rstc != rc && rstc->id == rc->id) {
if (rc->acquired) {
읽기 기준: 이 조각은 이미 얻은 reset control의 exclusive 접근권을 acquire하는 설명과 함수 앞부분이다. handle을 새로 얻는 reset get API가 아니다. NULL·error pointer·array·acquired 조건을 구분하고 나머지 분기는 원문 뒤를 읽는다.
발췌의 실제 함수와 consumer API를 나눠 읽기
위 대표 코드 첫 조각은 clk_get()이 아니라 clock 이름·HW 정보 helper들의 연속 구간이다. 두 번째는 reset_control_get() 구현이 아니라 reset_control_acquire()의 설명과 함수 앞부분이다. v6.6 발췌의 끝은 __clk_lookup_subtree 함수 시작이다.
발췌 A · clock helper
__clk_get_name, clk_hw_get_name 등은 이미 가진 clock/HW의 정보를 읽는다. provider 등록이나 consumer get을 실행하지 않는다.
실제 clk_get(dev,id)는 clkdev.c에서 OF lookup 결과를 시도하고 필요 시 clkdev lookup을 사용한다.
발췌 B · reset acquire
reset_control_acquire(rstc)는 released exclusive handle에 접근권을 얻는 함수다. NULL이면 0, error pointer는 -EINVAL, 경쟁 acquired handle이면 -EBUSY 경로가 있다.
handle 생성은 reset_control_get_exclusive / reset_control_get_shared 등 별도 API에서 한다.
clk_get / reset get variant / regulator_get| API 층 | 반환/상태 | 짝이 되는 작업 | 주의 |
|---|---|---|---|
clk_get | struct clk * 또는 ERR_PTR; source는 clkdev.c | clk_put | 획득만으로 enabled 아님 |
clk_prepare_enable | 성공하면 사용 count 증가 | clk_disable_unprepare | get/put과 다른 균형 |
| reset get / acquire | exclusive 또는 shared handle; released exclusive는 별도 acquire | reset_control_release(해당 variant), reset_control_put | shared assert/deassert는 균형 조건 |
regulator_get / enable | handle과 rail enable 요청은 별개 | regulator_disable, regulator_put | 전압 제약·다른 consumer·bootloader 상태와 구분 |
clk_get 구현 · clock helper 발췌 · reset acquire 발췌 · regulator_get 구현
handle 수명과 활성 상태는 서로 다르다
devm_* get은 보통 detach 때 handle을 반납하지만, 일반 get으로 켠 자원을 반드시 꺼 준다는 뜻은 아니다. 결합 helper를 사용했는지 실제 driver 코드를 봐야 한다.
clk_get → clk_put는 참조 수명. clk_prepare_enable → clk_disable_unprepare는 사용 상태. 이름/HW helper는 어느 장부도 증가시키지 않는다.regulator_enable → regulator_disable는 사용 count. set_voltage는 별도 제약 요청이며 단순 enable의 대체가 아니다.하드웨어의 실제 전기적 상태는 bootloader 초기 상태, 다른 consumer, provider의 구현에 좌우될 수 있으므로 소프트웨어 count와 동일시하지 않는다.
| 객체 | 획득 | 활성/제어 | 해제 시 확인 |
|---|---|---|---|
| clock consumer handle | clk_get 또는 devm_clk_get | clk_prepare_enable 성공 여부 | 활성 균형 후 put 또는 devres |
| reset control handle | exclusive/shared/optional variant | acquire 필요 variant, assert/deassert 정책 | access release·line 상태·put을 분리 |
| regulator consumer handle | regulator_get 또는 devm | 전압 제약과 enable 성공 여부 | 성공한 enable을 disable한 뒤 put |
probe 오류가 난 위치에 따라 정리가 달라진다
다음 그림은 특정 하드웨어에 강제하는 순서가 아니라, driver가 세 자원을 모두 요청했을 때 정리 장부를 작성하는 방법이다. 실제 요청 순서는 해당 IP의 데이터시트와 probe 구현으로 채운다.
-EPROBE_DEFER 여부 확인; 이미 성공한 동작을 driver 순서에 맞춰 정리성공 전
handle만 얻은 자원은 활성 해제가 필요하지 않다. 첫 enable 전에 실패했다면 해당 enable의 역함수를 무조건 호출하지 않는다.
일부 성공 후
성공한 clock/regulator 사용 count, reset line 조작을 대응시킨다. devm handle 반납과 하드웨어 상태 복구는 다른 책임이다.
이 세 framework를 하나의 원자적 트랜잭션으로 취급하지 않는다. 별도 bulk·managed helper의 보장 범위도 호출한 API 구현마다 확인한다.
| 실패 시점 | 이미 남을 수 있는 상태 | driver가 확인할 정리 | 원인 분리 |
|---|---|---|---|
| clock get | 앞서 획득한 다른 handle | 그 handle의 put/devres | lookup error와 -EPROBE_DEFER |
| reset get/acquire | clock handle 또는 이미 활성화한 clock | 활성화했다면 disable/unprepare 후 handle 정리 | exclusive 충돌, optional 없음, provider 지연 |
| regulator enable | handle·앞선 성공 동작 | regulator enable 실패를 성공으로 세지 않고 앞선 활성 동작 정리 | 전압 제약/공급자 결과 |
| IP 초기화 후속 단계 | 세 자원의 일부 또는 전부 활성 | 그 driver에서 성공시킨 상태의 역순 정리 | 자원 획득과 IP 오류를 구분 |
세 자원에서 지켜야 할 균형
clk_get 성공과 clk_prepare_enable 성공을 별도로 기록한다. enable을 못 했는데 disable하거나, 활성 상태를 남긴 채 put하지 않는다.
exclusive·shared·optional 중 실제 variant를 기록한다. released-exclusive는 acquire/release 접근권이 필요하고 shared deassert/assert에는 균형 조건이 있다.
regulator_get과 regulator_enable은 다른 단계다. 필요하다면 전압 제약 요청 결과도 따로 기록하고 성공한 enable을 disable한다.
세 framework가 공통 순서나 전체 rollback을 제공한다고 가정하지 않는다. MMIO 접근 조건과 teardown 순서는 선택한 IP의 driver·datasheet에서 확인한다.
읽기 전용으로 provider와 consumer 상태를 확인
아래 debugfs 파일은 해당 framework와 debugfs 설정이 있을 때만 보인다. 값은 순간 관측치이며 대상 driver의 성공 경로를 대체하지 않는다. 전역 tracer/filter를 바꾸지 않는다.
| 가설 | 읽기 전용 관찰 | 해석 한계 |
|---|---|---|
| clock provider/consumer | 있으면 cat /sys/kernel/debug/clk/clk_summary | prepare/enable count와 실제 IP 동작을 구분 |
| regulator rail | 있으면 cat /sys/kernel/debug/regulator/regulator_summary | 다른 consumer·bootloader 초기 상태 고려 |
| provider 지연 | 있으면 cat /sys/kernel/debug/devices_deferred | deferred 목록이 빈 것만으로 probe 성공 아님 |
| reset line | 선택한 reset provider/driver 로그와 reset_control_status 지원 여부 | status callback 미구현이면 -ENOTSUPP 가능 |
trace가 꼭 필요할 때는 테스트 보드에서 사용 가능한 event를 먼저 조회하고 특정 driver·함수 범위로 제한한다. 공용 시스템의 current_tracer나 set_ftrace_filter를 예제로 덮어쓰지 않는다.
선택한 IP driver에서 확인할 질문
- 각 get의 실제 variant와 반환값은 무엇이며
-EPROBE_DEFER는 어디서 전달되는가? clk_prepare_enable·regulator_enable·reset deassert 가운데 어느 동작까지 성공했는가?- driver가 선택한 전원·clock·reset 순서는 데이터시트의 MMIO 접근 전제와 일치하는가?
- 오류 label, remove, suspend 경로가 성공한 동작을 각각 균형 있게 되돌리는가?
- devm get을 썼다면 handle release 외의 활성 상태 정리를 누가 맡는가?