#!/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)
