[PATCH v2 0/2] Add RISC-V RPMI device power service support
From: Joshua Yeong
Date: Sun Aug 30 2026 - 11:28:59 EST
The RISC-V Platform Management Interface (RPMI) specification defines a
modular and extensible messaging protocol between the supervisor software
and a platform microcontroller (PuC). Among the service groups it defines
is the device power service group (service group ID 0x00009), which allows
the supervisor to enumerate the power domains of platform devices managed
by the PuC, query their attributes, and get/set their power state.
This series adds supervisor-side support for that service group:
- DT bindings for the power domain controller exposed to the supervisor
("riscv,rpmi-device-power") and for the SBI MPXY channel that the SBI
implementation uses to expose the service group to the supervisor
("riscv,rpmi-mpxy-device-power").
- A generic power domain (genpd) provider driver under
drivers/pmdomain/riscv/ which talks to the PuC over an SBI MPXY
mailbox channel. At probe it queries GET_NUM_DOMAINS, then for each
domain queries GET_ATTRS for the name and transition latency and
GET_STATE for the initial state, and registers the whole set as a
onecell genpd provider. Domain power on/off is driven through
SET_STATE with the generic ON/OFF power state parameters, so devices
can simply reference a domain through the "power-domains" property.
The series is based on the existing RISC-V RPMI/MPXY infrastructure
already present in the tree (drivers/mailbox/riscv-sbi-mpxy-mbox.c and
include/linux/mailbox/riscv-rpmi-message.h), and only adds the device
power service group definitions on top of it.
Changes in v2:
- Serialize RPMI transactions on the shared mailbox channel with a
mutex. The channel is used in non-blocking mode with client signalled
txdone, so mbox_send_message() only submits a message when the
channel is idle and silently queues it otherwise, returning the ring
buffer index. A concurrent caller would therefore see success, return
with an untouched response buffer, and leave its stack allocated
struct rpmi_mbox_message referenced by the mailbox ring.
- Unwind the already initialised power domains with pm_genpd_remove()
when of_genpd_add_provider_onecell() fails, so that devres does not
free memory still linked into the global gpd_list.
- Set .suppress_bind_attrs and drop the .remove() callback. The domains
are handed out to consumer devices and pm_genpd_remove() cannot fail
safely from a void remove(), so unbinding is now refused outright.
Use builtin_platform_driver() to match, since the driver is bool only;
this also makes a later switch to tristate fail to build rather than
silently reintroducing the problem. Both match other genpd providers,
e.g. drivers/pmdomain/starfive/jh71xx-pmu.c.
- Use __le32 for the RPMI message fields and convert the responses with
le32_to_cpu(), matching drivers/clk/clk-rpmi.c. The requests were
already converted with cpu_to_le32() but stored into plain u32.
- Zero initialise the response structures and reject responses shorter
than expected with -EPROTO, so a short reply from the PuC can no
longer leak uninitialised stack into a power domain name. The length
is checked after the status field so that a legitimate error reply,
which carries only STATUS, is still reported as the RPMI error.
The v1 review also asked for a sync_state callback, on the grounds that
domains initialised in the ON state would never be powered off. That one
is not addressed, because genpd already handles it: for a provider node
with a bound device, of_genpd_add_provider_onecell() installs its own
genpd_sync_state() through dev_set_drv_sync_state(), which clears
->stay_on and powers off the unclaimed domains once all consumers have
probed. A driver supplied .sync_state would in fact be refused, since
dev_set_drv_sync_state() returns -EBUSY rather than overwriting an
existing callback. This was confirmed under QEMU: domains reported as ON
by the PuC with no consumer do end up powered off, while the domain held
by the RTC stays on.
v1: https://lore.kernel.org/r/20260829205520.1691-1-joshua.yeong@xxxxxxxxxxxxxxxx
Testing
=======
The series was tested under QEMU with the RPMI device power service
implemented in firmware.
Components:
- OpenSBI: latest master branch
https://github.com/riscv-software-src/opensbi
- QEMU: the RPMI-enabled tree at
https://github.com/yeongjoshua/qemu/tree/rpmi-v11.1.0
Kernel config: enable CONFIG_RISCV_RPMI_DEVICE_POWER (default y on RISC-V
when MAILBOX is enabled) along with the SBI MPXY mailbox driver.
Run with:
qemu-system-riscv64 \
-M virt -m 2G -smp 4 \
-bios fw_dynamic.bin \
-kernel Image \
-M rpmi=true \
-nographic \
-initrd rootfs-busybox.cpio \
-append "root=/dev/ram rw console=ttyS0,115200 no_console_suspend mem=2048M earlycon=uart8250,mmio,0x10000000"
The RPMI device power domains advertised by the emulated platform
microcontroller show up as generic power domains and can be inspected
through /sys/kernel/debug/pm_genpd/. The series was additionally booted
with CONFIG_PROVE_LOCKING, CONFIG_DEBUG_MUTEXES and
CONFIG_DEBUG_ATOMIC_SLEEP enabled, with no lockdep reports.
Joshua Yeong (2):
dt-bindings: power: Add RPMI device power service bindings
pmdomain: riscv: Add RPMI device power service
.../power/riscv,rpmi-device-power.yaml | 65 +++
.../power/riscv,rpmi-mpxy-device-power.yaml | 65 +++
MAINTAINERS | 4 +
drivers/pmdomain/Kconfig | 1 +
drivers/pmdomain/Makefile | 1 +
drivers/pmdomain/riscv/Kconfig | 13 +
drivers/pmdomain/riscv/Makefile | 3 +
.../pmdomain/riscv/riscv-rpmi-device-power.c | 466 ++++++++++++++++++
include/linux/mailbox/riscv-rpmi-message.h | 11 +
9 files changed, 629 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/riscv,rpmi-device-power.yaml
create mode 100644 Documentation/devicetree/bindings/power/riscv,rpmi-mpxy-device-power.yaml
create mode 100644 drivers/pmdomain/riscv/Kconfig
create mode 100644 drivers/pmdomain/riscv/Makefile
create mode 100644 drivers/pmdomain/riscv/riscv-rpmi-device-power.c
base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
--
2.43.0