Identity
| Part | RK3399 PMU power domain controller |
| Role | Independent 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 / address | PMU 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 / IRQ | none |
| Datasheet | RK3399 TRM Part 1 chapter 6 (PMU) |
| Pine64 wiki | PinePhone Pro main page |
| Schematic | on-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:
- Define a
pwr_domain_if.minterface modeled onclk_if.m/hwreset_if.m. Methods:pwr_domain_request(handle),pwr_domain_release(handle), pluspwr_domain_get_by_ofw_property(). - Move away from
EARLY_DRIVER_MODULEand prove clean reboot behavior before re-enabling the driver inPINEPHONE_PRO. - Teach
simplebusto parsepower-domainsproperties at child enumeration and call into the controller’s request method beforedevice_attach()runs on the consumer. - 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). - 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
- Our tree:
src/sys/arm64/rockchip/rk_power_domain.c. - Linux mainline:
drivers/pmdomain/rockchip/pm-domains.c— reference implementation; theRK3399_PD_*constants list every gateable block. - FreeBSD upstream: none — no Rockchip power-domain driver exists in
sys/.
The Linux driver is small (~500 lines core) and well-defined: a PMU register set per domain plus an idle/req/ack handshake.
Related
- What’s next — power management roadmap.
- Cross-driver audit 2026-04-30 — gap analysis vs upstream Rockchip drivers.
- Component: RK3399 CRU — power-domain sequencing depends on CRU clock gating.