Device-Tree-Tutorial (Zynq)

Zusammenfassung

Fünfteiliges Tutorial von Xillybus zur Linux Device Tree: Beschreibung der Hardware für den Kernel über Daten statt Code. Geschrieben für den Xilinx Zynq-7000 (ARM Cortex-A9 + FPGA), aber die Konzepte gelten allgemein für ARM-Embedded-Linux. Behandelt DTS-Syntax, dtc-Kompilierung, Treiber-Matching über compatible und die Ressourcen-API im Probe-Pfad.

Inhalt

Teil I – Motivation und Werkzeuge

  • Problem: ARM-Chips haben keine BIOS-artige Selbstbeschreibung; pro Board eigene Header/Patches führten zu einem „unmaintainable pile of hacks”. Lösung: Device Tree (Open Firmware / Flattened Device Tree, FDT) als Bytestruktur, die der Bootloader vor dem Sprung in den Kernel an eine bekannte RAM-Adresse legt.
  • Drei Formen: .dts (Quelle) → .dtb (Binary Blob) → /proc/device-tree (Dateisystem im laufenden System zum Debuggen).
  • Kompilieren: scripts/dtc/dtc -I dts -O dtb -o my-tree.dtb my-tree.dts; Rückübersetzen: -I dtb -O dts; effektiven Tree auslesen: dtc -I fs -O dts ~/effective.dts /proc/device-tree/.
  • Modularisierung per #include von .dtsi: prozessor­abhängige Peripherie dort mit status = "disabled", board-DTS überschreibt gezielt mit "okay". Da dtc kein #include kennt: kbuild-Makefile mit always := mydevicetree.dtb (kombinierbar mit obj-m für den Treiber).

Teil II – Struktur des Baums

  • Wurzel / trägt #address-cells, #size-cells, compatible = "xlnx,zynq-zed", interrupt-parent; typische Kinder: chosen (bootargs, linux,stdout-path), cpus, memory@0 (reg = <0x0 0x20000000>), Bus axi@0 mit compatible = "...", "simple-bus".
  • Analogie Dateisystem: Knoten = Verzeichnisse, Zuweisungen = Dateien; /proc/device-tree/#size-cells zeigt Integer Big Endian. Labels (ps7_ddr_0:) stehen nicht im DTB.
  • Boot-Kommandozeile aus drei Quellen möglich: CONFIG_CMDLINE, Bootloader, chosen/bootargs.

Teil III – Matching zwischen Tree und Treiber

  • Faustregel des Autors: „Never write a device driver” – existierenden ähnlichen Treiber kopieren/anpassen.
  • Vier Anforderungen: Treiber wird geladen, kennt physische Adressen, kennt Interrupts, liest applikationsspezifische Parameter.
  • Matching über compatible (reiner String-Vergleich):
static struct of_device_id xillybus_of_match[] __devinitdata = {
    { .compatible = "xlnx,xillybus-1.00.a", },
    {}
};
MODULE_DEVICE_TABLE(of, xillybus_of_match);
 
static struct platform_driver xillybus_platform_driver = {
    .probe  = xilly_drv_probe,
    .remove = xilly_drv_remove,
    .driver = {
        .name           = "xillybus",
        .owner          = THIS_MODULE,
        .of_match_table = xillybus_of_match,
    },
};
  • Laden: eingebaute Treiber beim Boot, Module später via modprobe (erfordert depmod -a nach Kopieren nach /lib/modules/<ver>/kernel/drivers/). Mehrere Treiber können matchen; Probe-Reihenfolge undefiniert.

Teil IV – Register und Interrupts im Probe

match = of_match_device(xillybus_of_match, &op->dev);
of_address_to_resource(&op->dev.of_node, 0, &res);      /* reg -> res.start/end */
request_mem_region(res.start, resource_size(&res), "xillybus");
registers = of_iomap(op->dev.of_node, 0);               /* ≙ ioremap(res) */
  • Zugriff nur via iowrite32()/ioread32() – niemals Pointer direkt oder volatile (Cache-Kohärenz auf ARM!).
  • Interrupts: irq = irq_of_parse_and_map(op->dev.of_node, 0); request_irq(irq, isr, 0, "xillybus", dev);
  • interrupts = <0 59 1>: (1) SPI-Flag (Konvention: 0), (2) IRQ-Nummer − 32 (GIC-Nummer aus XPS), (3) Typ: 0 = unverändert, 1 = rising edge, 4 = level/high; falling edge/active low werden von der Hardware nicht unterstützt.

Teil V – Eigene Properties

  • Custom-Parameter wie xlnx,slv-awidth = <0x20>; auslesen: ptr = of_get_property(op->dev.of_node, "xlnx,slv-awidth", NULL); value = be32_to_cpup(ptr); (Big Endian → CPU-Endianness; weitere API in drivers/of/base.c).
  • Checkliste für ein eigenes IP-Peripherie-Entry: compatible-String wählen, reg aus XPS-Adressmap eintragen, interrupt-parent = <&gic>, interrupts setzen, Custom-Parameter ergänzen.

Kernkonzepte

  • compatible verbindet Hardware-Knoten mit Treiber; reg/interrupts beschreiben Ressourcen; #address-cells/#size-cells steuern das Adressformat.
  • dtc übersetzt DTS ⇄ DTB; /proc/device-tree macht den aktiven Baum sichtbar.

Quellen

  • raw/Device-Tree I.pdfraw/Device-Tree V.pdf – Tutorial-Teile 1–5 (Xillybus)

Verwandte Artikel