Linux Devicetree na Raspberry Pi


Wstęp

Devicetree jest przekazywane do kernela przez bootloader podczas procesu bootowania linuxa. W nim zawiera się mapa sprzętu podłączonego do procesora, dzięki temu kernel wie jak zainicjalizować sprzęt i jakie załadować sterowniki.

UWAGA: Materiał video uzupełniający ten wpis znajdziesz na YouTube:

Najpierw wyjaśnijmy po co zostało w ogóle stworzone devicetree. Na początku, cały opis podłączonego sprzętu zawierał się w kernelu, był w nim zahardkodowany. I miało to jedną, bardzo dużą wadę – na każdą wersję produktu trzeba było przygotować osobny obraz kernela, co było bardzo pracochłonne. Na przykładzie Raspberry pi – firma która je produkuje wspiera wiele różnych wersji. Raspberry Pi 3, 4, 5 poza tym są jeszcze różne wersje compute module i kilka innych. I na każde z tych urządzeń trzeba wykonać podobne kroki i przebudowywać kernel za każdym razem.

Obrazuje to powyższy diagram – każda wersja produkty wymaga przygotowania i utrzymania osobnego obrazu kernela.

Natomiast gdy mamy devicetree, możemy mieć jednego kernela i kilka różnych konfiguracji, które są wczytywane w zależności od tego, jaki mamy sprzęt.

Ważne skróty

DTS – Device Tree Structure

DTSI – Device Tree Structure Include

DTB – Device Tree Blob (skompilowane devicetree <DTS>)

DTBO – Device Tree Overlay

Modyfikacja DTS na Raspberry Pi

Co jeśli chcemy zmodyfikować istniejące devicetree, np. dla Raspberry Pi? Można zmienić kod konfiguracji bezpośrednio w źródłach, ale to bardziej skomplikowany sposób i częściej używany jeśli tworzymy własne płytki i musimy dostosować do nich devicetree.

Warto również pamiętać, że DT to standardowy mechanizm w systemach opartych na Linuxie i instrukcje dotyczące konkretniej platformy sprzętowej będą przydatne również na innych.

Raspberry Pi Foudation (twórcy płytek) wspiera wiele różnych opcji HW. Na przykład moduł uart jest dzielony między moduł bluetooth a działanie jako zwykła konsola. I żeby nie musieć za każdym razem zmieniać devicetree, to stosuje się „patche” na devicetree, które modyfikują tylko niektóre fragmenty głównego drzewa. Nazywa się je devicetree overlay, czyli dosłownie „nakładki na devicetree”.

Przykładowy overlay możesz znaleźć pod tym linkiem: https://github.com/raspberrypi/linux/blob/rpi-6.6.y/arch/arm/boot/dts/overlays/adafruit-st7735r-overlay.dts

Stworzymy teraz własny devicetree overlay, który włączy dodatkową diodę podłączoną do jednego z pinów GPIO, i ustawi jej tryb świecenia w heartbeat.

Kod devicetree overlay

Poniżej znajdziesz kod DT overlay:

/dts-v1/;
/plugin/;

/ {
	compatible = "brcm,bcm2835";

	fragment@0 {
		// Configure the gpio pin controller
		target = <&gpio>;
		__overlay__ {
            led_pin1: led_pins@23 {
				brcm,pins = <23>; // gpio number
				brcm,function = <1>; // 0 = input, 1 = output
				brcm,pull = <0>; // 0 = none, 1 = pull down, 2 = pull up
			};
		};
	};
    
	fragment@1 {
		target-path = "/";
		__overlay__ {
            leds1: leds@1 {
				compatible = "gpio-leds";
				pinctrl-names = "default";
				pinctrl-0 = <&led_pin1>;
				status = "okay";

				led1: led {
			                label = "karol";
					        gpios = <&gpio 23 0>;
			                linux,default-trigger = "heartbeat";
				};
			};
		};
	};
};

Najważniejsze właściwości overlaya:

  • /dts-v1/; – definiuje wersję składni devicetree. opis w specyfikacji,
  • / – węzeł główny, czyli root node. opisuje ogólnie naszą platformę
  • Węzły i właściwości, czyli nodes i properties:
    • Węzły – reprezentują hierarchiczną strukturę drzewa, to są peryferia i magistrale podłączone do procesora i obecne na platformie.
    • Właściwości – opisują węzły. Przykładowo property o nazwie compatible definiuje jaki sterownik będzie przyporządkowany danemy nodowi. np regulator-gpio.
  • Definiując nody po prostu piszemy ich nazwę, jeśli chcemy się do nich odnieść to używamy &
  • Najważniejsze properties:
    • compatible – definicja zgodności, jaki sterownik będzie obsługiwał dany sprzęt
    • status – oznacza czy sprzęt jest operacyjny na urządzeniu. okay – włączony, disabled – wyłączony, ale może zostać właczony jeśli trzeba (jest obecny w systemie)
    • reg – jaki obszar pamięci będzie zajmował. większość peryferiów jest mapowana do pamięci i tutaj właśnie jest to definiowane
  • /chosen – ważny parametr, to nie jest definicja sprzętowa, a oznacza jakie parametry będą przekazane w trakcie runtime do firmware’u, tutaj jest to parametr dla konsoli szeregowej. Musi to być child node root noda

Kompilacja

Stworzyliśmy nasz overlay – teraz musimy go skompilować. Można to zrobić za pomocą komendy:

dtc -@ -Hepapr -I dts -O dtb -o my_overlay.dtbo led_overlay.dts 

Następnie plik wynikowy, my_overlay.dtbo przenosimy na partycję bootfs/ do katalogu overlays/

Następnie w pliku config.txt dodajemy:

dtoverlay=my_overlay

Po podłączeniu płytki do zasilania dioda podłączona o pinu GPIO23 zacznie migać.

Dokumentacja

Gdzie szukać informacji o DT i overlayach? Najlepiej w oficjalnej dokumentacji. Większość informacji jest zawarta w tzw. dt-bindings. Są to dokumenty utworzone dla każdego subsystemu kernela, gdzie znajduje się opis w jaki sposób opisywać dany sprzęt w devicetree.

Przykładowy dt-binding dla LEDów: https://github.com/raspberrypi/linux/tree/rpi-6.6.y/Documentation/devicetree/bindings/leds


Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *