Files
Arch-R/scripts/pmic-poweroff
Douglas Teles 18c229177a First successful build — 11 pipeline gaps fixed, repo organized
Build pipeline now produces a working bootable image. Found and fixed
11 gaps between the manually-built working SD and build-all.sh output:

Boot partition:
- extlinux.conf as primary boot method (U-Boot loads first)
- PanCho removed from boot.ini and build-image.sh
- Stale uInitrd removed (caused wrong boot path)
- logo.bmp (U-Boot native BMP) replaces broken splash-1.raw
- fstab uses LABEL=ROMS instead of /dev/mmcblk1p3
- Only R36S DTB copied (no extra r35s/rg351mp-linux)

Root filesystem:
- emulationstation.service created and enabled
- getty@tty1 disabled (ES takes over tty1)
- archr-boot-setup: Before=emulationstation.service, simplified
- All services use After=local-fs.target (not removed getty)
- boot-timing captures ES profiling data

New files added to repo:
- build-mesa.sh, build-retroarch.sh (were untracked)
- Custom DTS, ALSA config, controller autoconfig
- Runtime scripts (retroarch-launch, pmic-poweroff, hotkeys)
- VLC stub source, timezone data

Repo cleanup:
- README.md rewritten with build instructions + architecture
- .gitignore expanded (test scripts, failed approaches, logs)
- splash-show.sh removed (failed splash approach)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 14:01:13 -03:00

137 lines
4.1 KiB
Python

#!/usr/bin/python3
"""
Arch R - RK817 PMIC Direct Power-Off via I2C
Two-stage approach:
Stage 1: SLPPIN_DN_FUN (standard kernel method — needs SLEEP pin)
Stage 2: Disable all power rails (POWER_EN registers — guaranteed)
RK817 does NOT have a DEV_OFF bit like RK805/RK808.
Power-off is via SLPPIN mechanism, but requires the SLEEP pin
to be physically driven (via pinctrl — which crashes on RK3326).
Fallback: Disable all DCDC/LDO outputs via POWER_EN registers.
This cuts power to CPU, RAM, and all peripherals → system dies.
RK817 I2C: bus=0, addr=0x20
Register map (from include/linux/mfd/rk808.h):
POWER_EN_REG(0) = 0xb1 — DCDC1-4 + BOOST enable
POWER_EN_REG(1) = 0xb2 — LDO1-4 enable
POWER_EN_REG(2) = 0xb3 — LDO5-8 enable
POWER_EN_REG(3) = 0xb4 — LDO9 enable
SYS_CFG(3) = 0xf4 — SLPPIN function + polarity
INT_STS_MSK_REG0 = 0xf9 — interrupt mask 0
INT_STS_MSK_REG1 = 0xfb — interrupt mask 1
INT_STS_MSK_REG2 = 0xfd — interrupt mask 2
Uses raw I2C via /dev/i2c-0 — no i2c-tools dependency.
"""
import os
import sys
import fcntl
import time
I2C_SLAVE_FORCE = 0x0706
I2C_BUS = '/dev/i2c-0'
PMIC_ADDR = 0x20
# RK817 registers
SYS_CFG3 = 0xf4
INT_STS_MSK_REG0 = 0xf9
INT_STS_MSK_REG1 = 0xfb
INT_STS_MSK_REG2 = 0xfd
# Power enable registers — controls DCDC/LDO rail outputs
POWER_EN_REG0 = 0xb1 # DCDC1-4, BOOST
POWER_EN_REG1 = 0xb2 # LDO1-4
POWER_EN_REG2 = 0xb3 # LDO5-8
POWER_EN_REG3 = 0xb4 # LDO9+
# SYS_CFG(3) bit definitions
SLPPIN_FUNC_MSK = 0x18
SLPPIN_DN_FUN = 0x10
SLPPOL_MSK = 0x20
SLPPOL_H = 0x20
def i2c_read_reg(fd, reg):
os.write(fd, bytes([reg]))
return os.read(fd, 1)[0]
def i2c_write_reg(fd, reg, val):
os.write(fd, bytes([reg, val]))
def i2c_update_bits(fd, reg, mask, val):
cur = i2c_read_reg(fd, reg)
new = (cur & ~mask) | (val & mask)
i2c_write_reg(fd, reg, new)
LOG = "/boot/pmic-poweroff.log"
def log(msg):
try:
with open(LOG, "a") as f:
f.write(f"{msg}\n")
except Exception:
pass
try:
log(f"=== PMIC power-off started ===")
# Sync filesystems before cutting power
os.sync()
log("Filesystems synced")
fd = os.open(I2C_BUS, os.O_RDWR)
fcntl.ioctl(fd, I2C_SLAVE_FORCE, PMIC_ADDR)
# Read current state for diagnostics
cfg3 = i2c_read_reg(fd, SYS_CFG3)
en0 = i2c_read_reg(fd, POWER_EN_REG0)
log(f"SYS_CFG3=0x{cfg3:02x} POWER_EN0=0x{en0:02x}")
# === Stage 1: Mask ALL interrupts ===
# Prevents any IRQ from firing during shutdown (kernel panic cause)
i2c_write_reg(fd, INT_STS_MSK_REG0, 0xff)
i2c_write_reg(fd, INT_STS_MSK_REG1, 0xff)
i2c_write_reg(fd, INT_STS_MSK_REG2, 0xff)
log("All interrupts masked")
# === Stage 2: Try SLPPIN_DN_FUN (standard method) ===
i2c_update_bits(fd, SYS_CFG3, SLPPIN_FUNC_MSK, 0x00) # Clear
i2c_update_bits(fd, SYS_CFG3, SLPPOL_MSK, SLPPOL_H) # Polarity HIGH
i2c_update_bits(fd, SYS_CFG3, SLPPIN_FUNC_MSK, SLPPIN_DN_FUN) # Power down
time.sleep(0.2) # 200ms — generous wait for SLPPIN to take effect
log("SLPPIN_DN_FUN set, still alive after 200ms")
# === Stage 3: Kill all power rails (guaranteed shutdown) ===
# RK817 has no DEV_OFF bit — SLPPIN needs physical pin drive.
# Without pinctrl (crashes kernel), SLEEP pin is never toggled.
# Solution: Disable all DCDC/LDO outputs directly.
# Order: LDOs first (peripherals), DCDCs last (core power).
# Once vdd_arm (DCDC2) is cut, CPU dies instantly.
log("Disabling all power rails...")
# Disable LDOs (peripherals: LCD, SD, BL, PMU, 1V8, 1V0)
i2c_write_reg(fd, POWER_EN_REG3, 0x00) # LDO9+
i2c_write_reg(fd, POWER_EN_REG2, 0x00) # LDO5-8
i2c_write_reg(fd, POWER_EN_REG1, 0x00) # LDO1-4 (includes vcc_1v8)
# Disable DCDCs — this kills CPU power (vdd_arm=DCDC2, vdd_logic=DCDC1)
# After this write, the CPU has no power and the system is dead.
i2c_write_reg(fd, POWER_EN_REG0, 0x00) # DCDC1-4 + BOOST
# Should never reach here — CPU is dead
time.sleep(5)
os.close(fd)
except Exception as e:
log(f"PMIC power-off FAILED: {e}")
print(f"PMIC power-off failed: {e}", file=sys.stderr)
sys.exit(1)