Device Tree Overlay 적용 안됨, 어디서부터 보나

Device Tree Overlayconfig.txt에 적어 두었는데도 장치가 안 뜨면, 드라이버부터 의심하기보다 컴파일 심볼 → 로더 경로 → 런타임 트리 순으로 좁히는 편이 문서상 원인과 맞습니다. 이 글은 보드별 실화·가격 없이, Linux 커널 Devicetree Dynamic Resolver Notes와 Raspberry Pi overlays README·configuration dtoverlay에 맞춰 점검 순서만 정리합니다.

dtc -@를 빼면 무슨 일이?

한 줄 답: -@ 없이 만든 .dtbo라벨·phandle을 나중에 붙일 __symbols__ / __fixups__ 메타가 빠지기 쉽고, /plugin/ 오버레이의 target = <&label> 해석이 깨집니다. 커널 리졸버는 “올바른 dtc 옵션 + /plugin/”으로 만든 트리를 전제로 __fixups__·__local_fixups__를 씁니다.

오버레이 소스는 보통 이렇게 시작합니다.

/dts-v1/;
/plugin/;

/ {
    fragment@0 {
        target = <&i2c1>;
        __overlay__ {
            /* status, compatible, reg … */
        };
    };
};

&i2c1 같은 베이스 트리 라벨은 컴파일 시점에 숫자 phandle로 확정되지 않습니다. dtc -@가 심볼·픽스업 노드를 넣어야, 병합 쪽에서 베이스 DTB의 __symbols__와 맞춰 target 자리를 패치합니다. U-Boot/커널 쪽 오버레이 문서도 베이스와 오버레이 모두 -@로 컴파일하라고 안내합니다.

권장 컴파일 예:

dtc -@ -I dts -O dtb -o myoverlay.dtbo myoverlay.dts

점검:

  1. fdtdump myoverlay.dtbo 또는 dtc -I dtb -O dts myoverlay.dtbo__fixups__ / __symbols__ / __local_fixups__ 유무를 본다.
  2. /plugin/;가 소스에 있는지 확인한다. 없으면 플러그인 오버레이로 취급되지 않을 수 있다.
  3. #include가 필요하면 Raspberry Pi kdtc처럼 cpp를 거치는 래퍼를 쓴다. 헤더만 빠진 채 dtc만 돌리면 문법 오류로 아예 빌드가 실패한다.

-@를 빼면 “파일은 있는데 적용 후 노드가 안 생김 / 적용 실패”로 보이는 경우가 많습니다. 먼저 바이너리에 픽스업이 있는지 확인하는 것이 1순위입니다.

config.txt 경로 확인은?

한 줄 답: 펌웨어는 dtoverlay=이름확장자 없는 이름으로 읽고, 기본적으로 **…/overlays/이름.dtbo**를 찾습니다. 줄 문법·마운트된 boot 파티션 경로·파일 이름이 어긋나면 로더 단계에서 이미 실패합니다.

Raspberry Pi overlays README 기준:

  • dtoverlay=i2c-rtc,ds1307/boot/overlays/i2c-rtc.dtbo 로드(문서 표기).
  • 파라미터는 쉼표로 이어 붙인다. dtparam=으로 베이스 인터페이스를 켤 수 있다.
  • dtoverlay=(값 없음)은 파라미터 목록 종료 등으로 쓰이며, 맨 앞에 두면 HAT 오버레이 로드를 막을 수 있다(공식 config 문서).

실무 체크리스트:

확인할 것
dtoverlay=foo vs foo.dtbo설정에는 이름만. .dtbo를 붙이면 파일을 못 찾는 경우가 많다
파일이 있는 디렉터리구 OS는 /boot/overlays/, Bookworm 계열은 흔히 /boot/firmware/overlays/ (boot 파티션이 /boot/firmware에 마운트)
config.txt 위치같은 boot 파티션의 config.txt(예: /boot/firmware/config.txt)를 편집했는지
줄 길이·쉼표오버레이 파라미터는 쉼표 구분. 공백으로 나누면 파싱이 깨질 수 있다(문서: 줄 길이 제한 언급)
device_tree= 빈 값DT 자체를 끄는 설정. 오버레이만의 문제가 아님
# Bookworm 예시 — 실제 마운트를 먼저 확인
ls -l /boot/firmware/overlays/myoverlay.dtbo
grep -nE '^(dtoverlay|dtparam|device_tree)' /boot/firmware/config.txt

커스텀 .dtbo를 만든 뒤 이름과 디렉터리가 README의 규칙과 일치하는지만으로도 “적용 안 됨”의 절반은 갈립니다. 런타임 dtoverlay 유틸로 올리는 경우와 부팅 시 펌웨어 병합은 경로·실패 로그가 다릅니다. 부팅 병합 실패는 시리얼/펌웨어 로그·dmesg의 OF 관련 메시지를 함께 봅니다.

/proc/device-tree로 뭘 보나?

한 줄 답: 커널이 들고 있는 병합 결과 트리입니다. 오버레이가 넣으려던 노드·status·compatible·reg가 여기 없으면, 드라이버 로드 전에 DT 단계가 실패한 것으로 본다.

유용한 확인:

# 예상 노드 경로가 생겼는지
ls /proc/device-tree/soc/*/i2c@*/ 2>/dev/null
# 또는 문서·바인딩에 나온 경로를 직접
find /proc/device-tree -iname '*rtc*' 2>/dev/null | head

# status / compatible (바이너리 속성 → strings)
cat /proc/device-tree/.../status; echo
strings /proc/device-tree/.../compatible

# 전체 덤프(용량 큼)
dtc -I fs -O dts /proc/device-tree | less

해석 가이드:

  1. 노드 없음 → 컴파일/경로/target 라벨 불일치·픽스업 실패 쪽을 다시 본다.
  2. 노드는 있는데 status = "disabled" → 오버레이·dtparam이 enable을 안 했거나 다른 fragment가 덮었다.
  3. 노드·status는 okay인데 장치 없음 → 그때부터 compatible ↔ 드라이버, 모듈 블랙리스트, 전원/클럭을 본다(이 글 범위 밖이지만 분기점으로 명확하다).
  4. 런타임으로만 올린 오버레이는 dtoverlay -l 등으로 목록을 볼 수 있으나, 부팅 시 병합된 것은 목록에 안 나올 수 있다. 최종 판정은 /proc/device-tree다.

Raspberry Pi dtoverlay/dtmerge 유틸 문서는 라이브 트리의 compatible·플랫폼 이름도 언급합니다. 디버그 순서를 바꾸지 않는 한, 심볼 → config 경로 → /proc/device-tree 세 단이면 “오버레이가 안 먹는다”의 대부분을 문서 근거로 가를 수 있습니다.

자주 묻는 질문

Q. /plugin/만 있고 -@가 없어도 되나요?
커널 리졸버 문서는 적절한 dtc 옵션과 /plugin/으로 __fixups__·__local_fixups__가 생긴 입력을 전제로 합니다. -@ 없이 라벨 target을 쓰면 병합이 실패하거나 잘못된 phandle로 남을 수 있습니다.

Q. dtoverlay -l에 안 보이면 실패인가요?
부팅 시 펌웨어가 이미 병합한 오버레이는 런타임 목록에 없을 수 있습니다. /proc/device-tree와 기대한 sysfs 장치를 기준으로 판단합니다.

Q. 가격·보드 추천은?
이 글 범위 밖입니다. 공식 오버레이 목록과 바인딩만 사용합니다.

정리

  1. dtc -@ + /plugin/__fixups__/__symbols__ 확인.
  2. config.txtdtoverlay= 이름·overlays/*.dtbo 실경로(Bookworm면 /boot/firmware) 확인.
  3. /proc/device-tree에서 노드·status·compatible 확인 후, 그다음에야 드라이버를 본다.

근거: Dynamic Resolver Notes, rpi overlays README, config.txt dtoverlay.