Appendix · reference

RK3399 power domain controller

Per-block power gating for GPU, VPU, ISP, RGA, and the suspend path

Identity

PartRK3399 PMU power domain controller
RoleIndependent power gating for GPU, VPU, ISP0/1, RGA, VIO, HDCP, GMAC, SDIOAUDIO, and the SDRAM PD; required for S2RAM and for clean GPU/VPU power-on
Bus / addressPMU MMIO 0xff310000 (power-control registers PMU_PWRDN_CON 0x14, PMU_PWRDN_ST 0x18, PMU_BUS_IDLE_REQ 0x60, PMU_BUS_IDLE_ST 0x64, PMU_BUS_IDLE_ACK 0x68)
GPIO / IRQnone
DatasheetRK3399 TRM Part 1 chapter 6 (PMU)
Pine64 wikiPinePhone Pro main page
Schematicon-SoC (no external schematic)

Status — ◐ partial

A first-cut sysctl-only rk_power_domain driver landed in 227e58b rk_power_domain: sysctl-only RK3399 PMU power-domain controller (with build-pipeline fixups in 42dbab2 files.arm64: fix hunk count after rk_power_domain addition + 84175f0 rk_power_domain: use OF_decode_addr instead of fdtbus_bs_tag ). It binds to rockchip,rk3399-power-controller, walks up to its parent PMU node via OF_decode_addr, and exposes every one of the 27 RK3399 power domains under dev.rk_power_domain.0.<name>.{power,idle}_state (read-write) plus _status (read-only mirror of the live PMU register). The actual register sequence — bus-idle handshake before power-off, power-on before idle release — is implemented as rk_pd_set_idle() + rk_pd_set_power(), mirroring Linux’s rockchip_pmu_set_idle_request() / rockchip_do_pmu_set_power_domain().

Verified live on 2026-05-03 (kernel #144):

$ sysctl dev.rk_power_domain.0.rga.power_state_status
dev.rk_power_domain.0.rga.power_state_status: 1     # boot state: on
$ sudo sysctl dev.rk_power_domain.0.rga.power_state=0
dev.rk_power_domain.0.rga.power_state: 1 -> 0
$ sysctl dev.rk_power_domain.0.rga.power_state_status \
        dev.rk_power_domain.0.rga.idle_state_status
dev.rk_power_domain.0.rga.power_state_status: 0     # off, idle latched
dev.rk_power_domain.0.rga.idle_state_status: 1
$ sudo sysctl dev.rk_power_domain.0.rga.power_state=1
dev.rk_power_domain.0.rga.power_state: 0 -> 1
$ sysctl dev.rk_power_domain.0.rga.power_state_status \
        dev.rk_power_domain.0.rga.idle_state_status
dev.rk_power_domain.0.rga.power_state_status: 1     # back on, un-idled
dev.rk_power_domain.0.rga.idle_state_status: 0

All 27 domains start pwr=1, idle=0 — U-Boot really does leave everything on. The full handshake (set idle req → poll ack + st → set pwr → poll st, and the reverse for power-on) completes within the 100 ms timeout for every safe domain we’ve round-tripped.

That proof is not the current shipping state. 929e3e5 PINEPHONE_PRO: temporarily disable rk_power_domain disabled device rk_power_domain in PINEPHONE_PRO after the sysctl-only first cut correlated with the phone refusing to boot after one successful boot. The kernel-config comment is the operative truth: the driver attaches as an EARLY_DRIVER_MODULE, maps the PMU MMIO via OF_decode_addr, and appeared to leave persistent state across warm reboot even without attach-time writes. Until that is isolated, the code stays in tree but out of the phone kernel.

What remains for full integration

The driver does the register dance correctly but does not yet plug into a power-domain framework — FreeBSD has no equivalent of Linux’s genpd / pwr_domain_if.m, no power-domains = <&power N> consumer parsing in simplebus, and no consumer driver in tree expects the framework. Work to close that gap:

  1. Define a pwr_domain_if.m interface modeled on clk_if.m / hwreset_if.m. Methods: pwr_domain_request(handle), pwr_domain_release(handle), plus pwr_domain_get_by_ofw_property().
  2. Move away from EARLY_DRIVER_MODULE and prove clean reboot behavior before re-enabling the driver in PINEPHONE_PRO.
  3. Teach simplebus to parse power-domains properties at child enumeration and call into the controller’s request method before device_attach() runs on the consumer.
  4. Add power-domains = <&power RK3399_PD_GPU> (etc.) to the PinePhone Pro DTS overlay for the consumers that actually want it (panfrost, eventually VPU / ISP).
  5. Validate the request-counting so multiple consumers sharing a domain don’t trigger a power-off when one releases.

S2RAM is downstream of all the above plus CPU cluster power-down, DDR self-refresh, and PMIC suspend support (RK818).

Driver

The Linux driver is small (~500 lines core) and well-defined: a PMU register set per domain plus an idle/req/ack handshake.