Tag: crust
Orange Pi 3: Warum der Warmstart hängt – und wie ein Device-Tree-Umbau den PMIC zurückholt
by carsten on Oct.29, 2026, under Armbian, Debian, Hardware, Linux, SBC
TL;DR: Auf meinem Orange Pi 3 (Allwinner H6, Armbian) bootete das System nach einem reboot nicht mehr – nur Strom ziehen half. Ursache: Die Firmware (ATF/Crust) spricht den Power-Management-Chip AXP805 über den RSB-Bus an und schaltet ihn dabei in den RSB-Modus. Der überlebt einen Warmreset. Armbians Device Tree lässt Linux den PMIC aber über I2C ansprechen – nach dem Warmstart antwortet er dort nicht mehr, ohne PMIC gibt es keine Spannungsregler, ohne Regler keine SD-Karte, und der Kernel wartet ewig auf sein Root-Dateisystem. Lösung: ein DTB, in dem der PMIC unter rsb@7083000 hängt, plus ein apt-Hook, der diesen DTB nach Kernel-Updates neu erzeugt.
Ausgangslage
Der Orange Pi 3 läuft bei mir als kleiner Infrastruktur-Server im Keller (Pi-hole, DNS, VPN & Co.). Eines Tages kam er nach einem Ansible-Update-Lauf nicht mehr hoch. Das interne eMMC meldete sich als verschlissen (life_time 0x0B, „Card stuck being busy“), also: Backup auf eine SD-Karte, davon booten – läuft.
Bis auf ein Detail: Ein reboot endete jedes Mal im Nichts. Kein Ping, kein SSH. Nur ein Power-Cycle brachte das Board zurück. Naheliegender Verdacht: das kaputte eMMC stört den Boot.
Werkzeug: serielle Konsole aus der Ferne
Ohne Konsole rät man nur. Also kam ein USB-TTL-Adapter an den Debug-UART des OPi und an einen Raspberry Pi daneben (115200 8N1; Klassiker: TX/RX zuerst vertauscht). Auf dem Raspberry Pi läuft eine screen-Sitzung mit Logfile, dazu zwei kleine Skripte:
- Autoboot-Stopper: wartet im Log auf „U-Boot“ und sendet dann alle 100 ms ein Leerzeichen, bis der Prompt
=>erscheint. - Boot-Helfer: setzt am Prompt Variablen (
extraargs,fdtfile) perscreen -X stuffund prüft jeweils das Echo, dannboot.
Kleine Falle am Rande: screen schreibt sein Log erst bei Zeilenende. Der Prompt => taucht deshalb erst auf, nachdem man ein CR hinterhergeschickt hat.
Schritt 1: Ist es das eMMC?
Erster Versuch: das eMMC (mmc2) per Device-Tree-Overlay komplett abschalten.
/dts-v1/;
/plugin/;
/ {
compatible = "xunlong,orangepi-3", "allwinner,sun50i-h6";
fragment@0 {
target = <&mmc2>;
__overlay__ { status = "disabled"; };
};
};
Das funktioniert (u-boot meldet „Applying user provided DT overlay“, die eMMC-Fehler sind weg) – aber der Warmstart hängt weiterhin. Das eMMC war also nur ein zufällig gleichzeitiger Defekt.
Schritt 2: Warm gegen kalt vergleichen
Mit der seriellen Konsole lassen sich Warm- und Kaltstart Zeile für Zeile vergleichen. Ergebnis: SPL, ATF (BL31), Crust und u-boot laufen identisch durch, bis Starting kernel .... Danach: Stille.
Das liegt aber erst einmal nur daran, dass Armbian mit loglevel=1 startet und /dev/console auf tty1 (HDMI) liegt. Also am u-boot-Prompt, ohne Dateien zu ändern:
=> setenv extraargs "earlycon=uart8250,mmio32,0x05000000 ignore_loglevel keep_bootcon"
=> boot
Falle:
earlycon=uart8250,mmio32,0x05000000,115200n8sieht richtiger aus, liefert aber Zeichensalat. Mit Baudrate rechnet der Kernel mit einem angenommenen UART-Takt von 1,8432 MHz statt der tatsächlichen 24 MHz. Ohne Baudrate übernimmt er einfach die Einstellung von u-boot.
Schritt 3: Der Kernel spricht
Der erste Lauf mit Debug-Ausgabe hing bei clk: Disabling unused clocks. Mit clk_ignore_unused kam der Kernel weiter, hing dann aber beim Warten auf das Root-Dateisystem. Ein Blick weiter oben im Log zeigte den eigentlichen Fehler:
axp20x-i2c 0-0036: AXP20x variant AXP806 found
axp20x-i2c 0-0036: Failed to set masks in 0x40: -6
axp20x-i2c 0-0036: failed to add irq chip: -6
...
platform 4020000.mmc: deferred probe pending: platform: wait for supplier /soc/i2c@7081400/pmic@36/regulators/cldo1
platform 5020000.ethernet: deferred probe pending: platform: wait for supplier /soc/i2c@7081400/pmic@36/regulators/aldo2
Beim Kaltstart steht an derselben Stelle AXP20X driver loaded.
Die Kette ist damit klar: Ohne PMIC-Treiber gibt es keine Spannungsregler. Ohne Regler bleiben SD-Controller, Pin-Controller und Ethernet im „deferred probe“, und rootwait wartet für immer. Der Hänger bei den Clocks war nur eine Folge davon.
Falle Nr. 2: „AXP806 found“ klingt, als hätte der Kernel den Chip erfolgreich gelesen. Tatsächlich stammt die Meldung aus dem Abgleich mit dem Device Tree, ohne jeden Buszugriff. Der erste echte Zugriff ist das Schreiben der IRQ-Masken, und der scheitert mit
-6(ENXIO). Beim I2C-Treiber heißt das: NAK, unter Adresse 0x36 antwortet niemand.
Schritt 4: Warum antwortet der PMIC nicht mehr?
Ein Warmreset setzt nur den SoC zurück, nicht den PMIC. Der AXP805 behält also den Zustand, den die Software vor dem Reset hinterlassen hat. Die Frage war: Wer fasst den PMIC zwischen reboot und dem nächsten Linux an?
Die Bootkette des H6 sieht so aus:
BROM → u-boot SPL → TF-A (BL31) → Crust (SCP-Firmware auf dem AR100-Kern) → u-boot → Linux
Ein reboot läuft so ab: Linux ruft PSCI SYSTEM_RESET auf, TF-A leitet das per SCPI an Crust weiter, und erst die Firmware führt den Reset aus.
Die Strings im Armbian-Bootloader-Binary (u-boot-sunxi-with-spl.bin) sind aufschlussreich:
PMIC: Probing AXP805 on RSB
RSB: set device to RSB
RSB: set run-time address
PSCI: SCPI %s failed: %d
Die Firmware kann den PMIC also über RSB ansprechen, Allwinners „Reduced Serial Bus“. Der AXP805 startet nach dem Einschalten im I2C-Modus. Ein „set device to RSB“ schaltet ihn um, und zurück geht es erst mit einem Stromtrennen.
Der Device Tree von Armbian hängt den PMIC dagegen an den I2C-Bus:
i2c@7081400 { /* r_i2c */
status = "okay";
pmic@36 {
compatible = "x-powers,axp805", "x-powers,axp806";
reg = <0x36>;
...
};
};
rsb@7083000 {
status = "disabled";
};
Genau dieses Szenario wurde 2021 schon auf der Kernel-Mailingliste diskutiert. Samuel Holland stellte den H6-PMIC im Mainline-Kernel auf RSB um, getestet unter anderem auf dem Orange Pi 3 (Patch-Serie, DT-Patch). In der Review wies Chen-Yu Tsai auf das Risiko hin: Firmware wie Crust, die RSB benutzt, schaltet den Chip nicht zurück auf I2C, und dann erreicht Linux ihn nicht mehr.
Ehrlicherweise: Den Moment der Umschaltung habe ich nicht direkt beobachtet. TF-A meldet beim Boot kein eigenes PMIC-Setup, weil Crust vorhanden ist. Am wahrscheinlichsten passiert sie daher im Reboot-Pfad von Crust. Belegt ist aber das Wichtige: Nach dem Warmreset gibt der PMIC auf I2C ein NAK, über RSB ist er erreichbar. Das zeigt der nächste Schritt.
Warum lief das früher? Der Wechsel auf den aktuellen Kernel und die aktuelle Firmware kam mit einem Update Anfang September. Der erste Warmstart danach war ausgerechnet der Update-Reboot, nach dem das System „tot“ war. Das gleichzeitig sterbende eMMC hat die Spur zusätzlich verwischt.
Schritt 5: Der Beweis ohne Risiko
Statt gleich etwas fest einzutragen, habe ich einen alternativen DTB gebaut und ihn nur für einen einzigen Boot am u-boot-Prompt ausgewählt. Armbians armbianEnv.txt setzt kein fdtfile, die Variable aus dem Prompt bleibt also erhalten:
=> setenv fdtfile allwinner/sun50i-h6-orangepi-3-rsb.dtb
=> boot
Ergebnis, nach einem Warmstart in genau dem Zustand, der vorher fünfmal gescheitert war:
sunxi-rsb 7083000.rsb: RSB running at 4000000 Hz
axp20x-rsb sunxi-rsb-745: AXP20x variant AXP806 found
axp20x-rsb sunxi-rsb-745: AXP20X driver loaded
mmcblk0: mmc0:59b4 SN32G 29.7 GiB
Das System kam vollständig hoch. Wäre es schiefgegangen, hätte ein Power-Cycle gereicht, und das Board hätte wieder mit dem Original-DTB gestartet.
Warum funktioniert das beim Kalt- und beim Warmstart? Der Linux-RSB-Treiber schickt beim Start selbst „set device to RSB“. Für ihn spielt es keine Rolle, ob der PMIC noch im I2C-Modus ist oder schon umgeschaltet wurde.
Der Umbau des Device Trees
Ein Overlay reicht hier nicht. Overlays können Knoten nicht verschieben, und alle Verbraucher (SD-Controller, CPU, GPU, Ethernet …) verweisen per Phandle auf die Regulator-Knoten unter pmic@36. Also wird der komplette DTB umgebaut:
- Mit
dtc -I dtb -O dtsdekompilieren. - Den Knoten
pmic@36ausi2c@7081400herausschneiden. - Ihn als
pmic@745(die RSB-Laufzeitadresse) mitreg = <0x745>inrsb@7083000einsetzen. rsb@7083000aufokaystellen,i2c@7081400aufdisabled. Der PMIC war das einzige Gerät anr_i2c, und beide Busse teilen sich die Pins PL0/PL1.- Die Pfade in
__symbols__umschreiben (/soc/i2c@7081400/pmic@36→/soc/rsb@7083000/pmic@745), damit Overlays weiter auflösen, etwa das eMMC-Overlay. - Mit
dtc -@neu kompilieren.
Die Phandle-Werte wandern mit dem Knoten mit, deshalb stimmen alle Verweise weiterhin. Ob der Kernel die nötigen Treiber mitbringt, zeigt ein kurzer Blick in die Config: CONFIG_SUNXI_RSB=y, CONFIG_MFD_AXP20X_RSB=y.
Dauerhaft machen und gegen Updates absichern
In /boot/armbianEnv.txt:
fdtfile=allwinner/sun50i-h6-orangepi-3-rsb.dtb
Falle Nr. 3: Armbian legt die DTBs pro Kernelversion ab (
/boot/dtb-6.18.44-current-sunxi64/),/boot/dtbist nur ein Symlink. Nach dem nächsten Kernel-Update zeigt der Link auf ein neues Verzeichnis ohne den RSB-DTB. u-boot hätte dann gar keinen DTB, und das Board startet überhaupt nicht mehr.
Deshalb gibt es einen Hook. /usr/local/sbin/opi3-rsb-dtb läuft per DPkg::Post-Invoke nach jedem apt-Lauf, also auch bei Ansible und unattended-upgrades:
- Es erzeugt den RSB-DTB in jedem
/boot/dtb-*/neu, sobald sich die Prüfsumme des Original-DTB ändert. - Es prüft das Ergebnis mit
fdtget: RSB an, PMIC-Adresse 0x745, Symbole umgehängt. - Bringt der Upstream-DTB den PMIC irgendwann selbst unter RSB, übernimmt es ihn unverändert.
- Bei jedem Fehler entfernt es
fdtfileausarmbianEnv.txt. Dann bootet das Board sicher mit dem Original-DTB, nur der Warmstart hängt wieder. Den Fehler meldet es ins Syslog und legt eine Markierungsdatei an.
// /etc/apt/apt.conf.d/99-opi3-rsb-dtb
DPkg::Post-Invoke { "if [ -x /usr/local/sbin/opi3-rsb-dtb ]; then /usr/local/sbin/opi3-rsb-dtb >/dev/null || true; fi"; };
Dazu kommt ein NRPE-Check check_opi3_bootfix. Er schlägt Alarm, wenn fdtfile nicht mehr auf den RSB-DTB zeigt, die Datei fehlt, sie nicht zum aktuellen Original passt oder der Hook fehlgeschlagen ist.
Testmatrix
| Test | Szenario | Ergebnis |
|---|---|---|
| 1–5 | Warmstart, Original-DTB (PMIC an I2C), mit und ohne Debug-Optionen | hängt: PMIC gibt NAK |
| A | Warmstart, RSB-DTB nur am u-boot-Prompt gewählt | ✅ |
| B | normaler reboot, RSB-DTB fest eingetragen | ✅ |
| C | Kaltstart (Strom getrennt) | ✅ |
| D | normaler reboot nach weiterem Aufräumen, Stunden später | ✅ |
Bonus: Was das eMMC wirklich umgebracht hat
Bei der Gelegenheit habe ich nachgemessen, wer eigentlich so viel schreibt. Die neue SD-Karte hatte nach zwei Tagen schon über 300 GB auf dem Zähler (/sys/fs/ext4/mmcblk0p1/lifetime_write_kbytes). Hauptverursacher war Pi-hole v6 mit database.forceDisk = true. Damit liegt die Kurzzeit-Datenbank, die normalerweise im RAM gehalten wird, auf der Karte, und SQLite schreibt bei vielen kleinen Einfügungen etwa das Hundertfache dessen, was die Datenbank tatsächlich wächst. Ergebnis: 30 bis über 100 GiB pro Tag. Für ein 8-GB-eMMC aus 2018 ist das nach ein paar Monaten das Lebensende. Die Datenbank zieht deshalb auf eine USB-SSD um. Außerdem gibt es einen zweiten NRPE-Check, der Schreibmenge und Schreibrate der SD-Karte überwacht, weil die Karte selbst keine Gesundheitsdaten liefert (CMD56 liefert nur Nullen).
Was ich mitnehme
- Ohne serielle Konsole keine Diagnose. Ein 3-Euro-USB-TTL-Adapter und ein Raspberry Pi daneben machen aus „startet nicht“ einen nachvollziehbaren Ablauf.
- Warm- und Kaltstart sind auf ARM-Boards zwei verschiedene Welten. Alles, was den SoC-Reset überlebt (PMIC, RTC-Domäne, SCP-Firmware), kann Zustand mitschleppen.
- Fehlercodes ernst nehmen:
-6heißt bei I2C NAK und nicht etwa Timeout. Das hat direkt auf den Busmodus gezeigt. - Meldungen nicht trauen, die nach Erfolg klingen: „AXP806 found“ war nie ein Hardware-Zugriff.
- Erst flüchtig testen, dann festschreiben.
setenvam u-boot-Prompt ist der risikoärmste Weg, einen DTB auszuprobieren. - Workarounds absichern: Ein Fix, den das nächste Kernel-Update stillschweigend aushebelt (oder der das Board gar nicht mehr starten lässt), ist schlimmer als keiner. Daher Hook, Rückfall und Monitoring.
