Device Tree Nedir?

August 7, 2026 · ziyaretçi

Bir gömülü karta Linux kurmaya çalıştıysan er ya da geç .dts uzantılı bir dosyayla karşılaşırsın. Genelde de en kötü anda: kart açılmıyor, ekrandan ses yok, birileri sana "device tree'ne bak" diyor. Bu yazı, o dosyanın ne olduğunu, neden var olduğunu ve içinde ne aradığını anlatıyor.

Sorun Neydi

Bir masaüstü bilgisayarda çekirdek donanımı büyük ölçüde kendisi bulabilir. PCI ve USB, resmî olarak tanımlanmış ve numaralandırılabilen veri yollarıdır (1). Cihazlar kendilerini bir üretici ve ürün kimliğiyle tanıtır; sürücü de desteklediği kimlikleri bir tabloda listeler ve çekirdek eşleşen her cihaz için o sürücünün probe() fonksiyonunu çağırır (2). USB tarafında aynı iş benzer bir kimlik tablosuyla yürür (3). Kimsenin kimseye önceden bir şey anlatması gerekmez.

Gömülü kartlarda böyle bir lüks yok. Çekirdeğin belgeleri bu ayrımı açıkça koyar: SoC'ye tümleşik çevre birimleri "PCI ya da USB gibi büyük ve resmî olarak tanımlanmış" veri yollarının aksine, asgari altyapıya sahip yollara bağlanır ve bu cihazların ortak özelliği doğrudan CPU veri yolundan adreslenmeleridir (1). Bir I²C sensörü ya da SPI'a bağlı bir ekran sürücüsü kendini tanıtmaz; orada, o adreste, o kesme hattına bağlı öylece durur. Çekirdeğin bunu bilmesinin tek yolu, birinin söylemesidir.

Uzun süre bu "birisi" kodun kendisiydi. Her kart için çekirdek ağacının içinde, hangi cihazın nereye bağlı olduğunu C ile anlatan bir board dosyası bulunurdu — çekirdek hâlâ bu yolu tarif eder: karta özgü kurulum kodu cihazları tek tek kaydeder (1). ARM tarafında bu dosyalar o kadar çoğaldı ki 2011'de Linus Torvalds durumu bir e-postada sertçe eleştirdi ve ARM kodunun bu gidişle sürdürülemez olduğunu söyledi. Çıkış yolu olarak benimsenen çözüm device tree oldu.

Fikir

Device tree, donanımın nasıl bağlandığını anlatan bir veri dosyasıdır. Kod değil — çalışmıyor, derlenip çekirdeğin içine girmiyor. Sadece bir tarif: şu adreste şu denetleyici var, şu kesme hattını kullanıyor, şu saat kaynağından besleniyor, altında da şu cihazlar bağlı.

Biçimin kendisi resmî bir spesifikasyona dayanıyor (4). Adına "ağaç" denmesinin sebebi, yapısının gerçekten ağaç olmasıdır. Kök düğümden başlar, altına veri yolları, onların altına da o veri yollarına bağlı cihazlar dallanır — donanımın fiziksel bağlantı şemasını birebir yansıtır:

Uydurma bir örnek yerine gerçeğine bakalım. Aşağıdaki düğüm, TI'ın J722S/AM67A ailesi için U-Boot ağacındaki k3-am62p-main.dtsi dosyasından — yani bir AM67A kartı açtığında fiilen okunan tarifin ta kendisi:

main_i2c0: i2c@20000000 {
    compatible = "ti,am64-i2c", "ti,omap4-i2c";
    reg = <0x00 0x20000000 0x00 0x100>;
    interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>;
    #address-cells = <1>;
    #size-cells = <0>;
    power-domains = <&k3_pds 102 TI_SCI_PD_EXCLUSIVE>;
    clocks = <&k3_clks 102 2>;
    clock-names = "fck";
    status = "disabled";
};

Buradaki her satırın bir karşılığı var. reg, denetleyicinin bellek haritasındaki adresini verir — 0x20000000, uzunluğu 0x100. Adreslerin ikişer sayıyla yazılmasının sebebi 64-bit adres alanı; ilk sayı üst 32 biti taşıyor. interrupts, hangi kesme hattının kullanıldığını söyler; GIC_SPI ve IRQ_TYPE_LEVEL_HIGH gibi isimler #include edilen başlık dosyalarından geliyor, yani device tree kaynağı sade bir veri dosyası olsa da C ön işlemcisinden geçiyor.

compatible'ın iki değer taşıması dikkat çekicidir: "ti,am64-i2c" ve "ti,omap4-i2c". Liste en özelden en genele doğru okunur. Çekirdek önce am64'e özel bir sürücü arar, bulamazsa daha eski ve genel omap4 sürücüsüne düşer. Böylece yeni bir yonga, kendisi için ayrı bir sürücü yazılmadan da çalışabilir.

clocks ve clock-names ise cihazın hangi saat kaynağından beslendiğini söyler: &k3_clks'in 102 numaralı cihazının 2 numaralı çıkışı, sürücünün "fck" adıyla bulacağı saat. power-domains de aynı mantıkla güç alanını bağlar. Bunlar frekans ayarı değil, kablolamanın tarifi — hangi saatin nereye gittiği.

Son satır önemli: status = "disabled". Yonga üreticisi çevre birimi tarif eder ama kapalı bırakır, çünkü o I²C hattının senin kartında kullanılıp kullanılmadığını bilemez. Açmak kartın kendi .dts dosyasının işidir. J722S EVM'in kart dosyasında bu şöyle görünüyor:

&main_i2c0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&main_i2c0_pins_default>;
    clock-frequency = <400000>;

    exp1: gpio@23 {
        compatible = "ti,tca6424";
        reg = <0x23>;
        gpio-controller;
        #gpio-cells = <2>;
    };
};

Başındaki &main_i2c0, yukarıdaki düğüme yapılan bir referans — yeni bir düğüm oluşturmuyor, var olanın üzerine yazıyor. status açılıyor, pinler atanıyor, hız 400 kHz'e çekiliyor ve altına gerçek bir cihaz ekleniyor: I²C üzerinde 0x23 adresinde oturan bir TCA6424 GPIO genişletici. SoC tarafındaki #address-cells = <1> yüzünden bu adres tek sayıyla yazılıyor, denetleyicinin kendi reg'i gibi çift değil.

İşte device tree'nin çalışma biçimi bu: üretici SoC'yi bir kez tarif eder, sen kendi kartında neyin bağlı olduğunu söylersin.

Asıl önemli olan compatible. Çekirdek, sürücüyle donanımı eşleştirmek için buraya bakar (5). Bir sürücü "ben ti,tmp102 ile uyumluyum" der, device tree'de aynı dizeyi taşıyan bir düğüm bulununca çekirdek ikisini birleştirir. Bu yüzden yazım hataları sessizce başarısızlığa yol açar: dize tutmazsa hata da vermez, cihaz sadece hiç ortaya çıkmaz.

Dikkat edilecek bir nokta: device tree donanımı tarif eder, sürücünün yapılandırmasını değil. Çekirdeğin binding yazma rehberi bu sınırı özellikle vurgular (6). Ayrım pratikte hep bulanıklaşır ve çekirdek listelerinde en çok tartışma çıkaran konu budur — "bu bilgi gerçekten donanımın bir özelliği mi, yoksa senin tercihin mi?"

Dosyalar

Üç uzantı görürsün ve karışması çok kolay (11).

.dts, senin yazdığın kaynak dosyadır — insanın okuyabildiği hali. .dtsi ise başka dosyaların içine alabildiği bir parçadır; "i" include'dan geliyor. Bir yonga üreticisi tipik olarak SoC'nin tamamını bir .dtsi dosyasında tarif eder; sen kendi kartın için kısa bir .dts yazar, onu include eder ve yalnızca kendi kartına özgü olanı eklersin.

.dtb ise derlenmiş halidir — device tree blob. Çekirdeğin gerçekten okuduğu şey budur. Derleyicisi dtc (7):

dtc -I dts -O dtb -o my-board.dtb my-board.dts

Ters yöne de çalışır ki bu hata ararken çok işe yarar. Elinde çalışan bir .dtb varsa açıp içine bakabilirsin:

dtc -I dtb -O dts -o cozulmus.dts my-board.dtb

Çalışan bir sistemde device tree'yi doğrudan dosya sisteminden de okuyabilirsin. Çekirdek kendi kullandığı ağacı burada açar:

ls /proc/device-tree/

Bu, "benim düzenlediğim dosya gerçekten yüklenen dosya mı?" sorusunun en hızlı cevabıdır. Şaşırtıcı sıklıkta cevap "hayır" çıkıyor.

Kartın Açılışında Ne Oluyor

Sıralama şöyle: bootloader — çoğunlukla U-Boot (8) — .dtb dosyasını belleğe yükler, çekirdeği de yükler, sonra çekirdeği başlatırken blob'un bellek adresini ona bir kayıtta geçirir. ARM'de bu kayıt ve uyulması gereken koşullar çekirdeğin açılış belgesinde tanımlıdır (9). Çekirdek daha kendi sürücülerini başlatmadan önce bu ağacı okur, içindeki cihazları çıkarır ve sürücüleri compatible dizelerine göre eşleştirir.

Bunun pratik sonucu şu: çekirdek ile device tree ayrı ayrı güncellenebilir. Yeni bir sensör eklediğinde çekirdeği yeniden derlemen gerekmez, çoğu zaman .dtb'yi güncellemek yeterlidir. Ama madalyonun diğer yüzü de var — ikisi birbirinden bağımsız olduğu için uyumsuz kalabilirler. Yeni çekirdekle eski .dtb'yi açtığında kart sessizce eksik çalışır ve bunu anlaman uzun sürer.

Bir de overlay kavramı var. Overlay'ler, mevcut bir ağacın üzerine çalışma anında eklenen parçalardır (10). Raspberry Pi'de bir HAT taktığında olan budur; ana ağaç aynı kalır, üzerine küçük bir parça bindirilir. Ana kart hep aynı, üstüne takılan modül değişiyorsa kullanışlıdır.

Bir Şey Çalışmadığında

Device tree hataları kendine has bir sinir bozukluğuna sahip, çünkü genelde hata mesajı vermezler. Cihaz sadece yok. Şu sırayla bakmak zaman kazandırıyor.

Önce cihazın gerçekten oluşup oluşmadığına bak. /proc/device-tree altında düğümün var mı? Yoksa sorun senin düzenlediğin dosyada değil — muhtemelen yanlış .dtb yükleniyor ya da düğüm bir üst seviyede status = "disabled" ile kapatılmıştır. Bu ikincisi çok sık oluyor: üreticinin .dtsi dosyalarında çevre birimler genelde kapalı gelir, kartın .dts'inde tek tek status = "okay" ile açman beklenir.

Düğüm var ama sürücü tutmamışsa, gözün compatible dizesinde olsun. Sürücünün kaynak kodunda of_device_id tablosunu bulup dizeleri harfi harfine karşılaştır. Bir virgül, bir tire farkı yeter.

Bir de şu var: dtc uyarıları çoğu projede kapalı geliyor ve gerçekten işe yarayan şeyler söylüyorlar. Adres ile reg uyuşmazlıkları ya da eksik bir #address-cells, tam da açılışta sessiz arızaya dönüşen türdendir. Derlerken uyarıları açık tutmaya değer.

Kapanış

Device tree'yi anlamayı zorlaştıran şey, aslında ne olduğunun baştan net söylenmemesidir. Bir yapılandırma dosyası gibi görünür ama değil; bir sürücü gibi görünür ama o da değil. En doğrusu onu donanım ile yazılım arasındaki sözleşme olarak düşünmektir: kartın nasıl kablolandığını, çekirdeğin anladığı dilde yazıya dökmek.

Bu çerçeveye oturduğunda geri kalanı yerine oturuyor. compatible neden bu kadar kritik, neden .dtb çekirdekten ayrı duruyor, neden bir şey çalışmadığında ilk bakılacak yer /proc/device-tree — hepsi aynı fikrin sonucudur.

İlgili Yazılar

Kaynakça

  1. Platform Devices and Drivers — SoC çevre birimlerinin PCI/USB'nin aksine neden numaralandırılamadığı; cihazların karta özgü kodla kaydedilmesi.
  2. How To Write Linux PCI Driverspci_device_id kimlik tablosu ve eşleşen cihazlar için probe() çağrısı.
  3. USB Device Drivers — USB tarafındaki kimlik tabanlı sürücü eşleştirmesi.
  4. Devicetree Specification — device tree formatının resmî tanımı.
  5. Linux and the Devicetree — çekirdeğin ağacı nasıl okuduğu ve sürücüleri nasıl eşleştirdiği.
  6. Writing Devicetree Bindings — donanım tarifi ile sürücü yapılandırması arasındaki sınır.
  7. dtc — Device Tree Compiler — derleyicinin kendi deposu ve kullanımı.
  8. U-Boot: Devicetree Control — U-Boot tarafında device tree'nin ele alınışı.
  9. Booting ARM Linux — bootloader'ın blob'u çekirdeğe hangi kayıtla geçirdiği.
  10. Devicetree Overlay Notes — overlay'lerin çalışma anında nasıl uygulandığı.
  11. Device Tree Reference (eLinux) — örneklerle toparlanmış topluluk kaynağı.

Yorumlar