![]() |
RT-Thread RTOS
An open source embedded real-time operating system
|
On AArch64 platforms, secure firmware (TF-A, SCP, vendor EL3) often owns clocks, regulators, resets, and power domains. The OS talks to that firmware through SCMI instead of touching the same hardware directly.
RT-Thread splits the stack into:
| Layer | Location | Role |
|---|---|---|
| Message / protocol IDs | components/drivers/include/drivers/scmi.h | Payload structs, SCMI_PROTOCOL_ID_*, RT_SCMI_MSG_* helpers |
| SCMI bus | components/drivers/firmware/arm_scmi/bus.c | Match rt_scmi_driver ↔ rt_scmi_device by protocol (and optional name) |
| Agent + transport | components/drivers/firmware/arm_scmi/ | Platform driver arm-scmi, shared memory, mailbox / SMC / virtio |
| Protocol providers | *-scmi.c under clk, regulator, reset, … | probe on SCMI bus → register normal DM consumers (rt_clk_*, rt_regulator_*, …) |
Transport details: SCMI agent transports.
Kconfig: RT_FIRMWARE_ARM_SCMI (needs RT_USING_FIRMWARE, RT_USING_OFW, Cortex-A / ARMv8). Enable mailbox and/or SMC transport options as your DT uses.
Application and most drivers never call SCMI directly; they use the same APIs as MMIO backends (rt_clk_get_by_index, rt_regulator_get, rt_reset_control_get, …).
Bus match (bus.c): for each ids[] entry, id->protocol_id == device->protocol_id, and if id->name is set it must equal device->name (from OFW protocol-name or similar on the channel node).
| Export / register | Level | What runs |
|---|---|---|
scmi_bus_init | INIT_CORE_EXPORT | rt_bus_register(&scmi_bus) |
scmi_drv_register (arm-scmi platform driver) | INIT_SUBSYS_EXPORT | Agent ready before bulk DT probe |
platform_ofw_device_probe | INIT_PLATFORM_EXPORT | Creates arm,scmi platform device → scmi_probe |
RT_SCMI_DRIVER_EXPORT(...) | INIT_DEVICE_EXPORT | Protocol driver registers → probes pending rt_scmi_devices |
scmi_clk_drv_register | INIT_SUBSYS_EXPORT | Exception: clk-scmi.c calls rt_scmi_driver_register() manually (earlier than RT_SCMI_DRIVER_EXPORT) |
If a protocol driver registers after its rt_scmi_device already exists, rt_bus_add_driver walks devices and runs probe (same pattern as platform bus).
All transports funnel through:
Build payloads with typed structs in scmi.h and:
| Helper | Use |
|---|---|
RT_SCMI_MSG_IN_OUT(id, in, out) | Fixed-size in + out |
RT_SCMI_MSG_IN(id, in) | Command with input only |
RT_SCMI_MSG_OUT(id, out) | Command with output only |
RT_SCMI_MSG_RAW(...) | Variable buffer sizes |
Firmware returns **SCMI_SUCCESS** (0) or negative **SCMI_ERR_*** in message status fields; rt_scmi_strerror() maps those codes to short names (agent.c).
Threading: process_msg is blocking for mailbox/SMC paths; do not call from ISR unless your transport is explicitly IRQ-safe.
Example mailbox transport (simplified):
compatible | Transport (agent.c scmi_ofw_ids) |
|---|---|
arm,scmi | Mailbox (agent-mailbox.c) |
arm,scmi-smc, arm,scmi-smc-param, qcom,scmi-smc | SMC (agent-smc.c) |
arm,scmi-virtio | Virtio (virtio-scmi.c, if RT_FIRMWARE_ARM_SCMI_TRANSPORT_VIRTIO) |
Child **reg** is the protocol ID (same values as SCMI_PROTOCOL_ID_* in scmi.h). The agent registers one **rt_scmi_device** per child; protocol drivers bind to that node’s parent.ofw_node for provider-specific subnodes (e.g. regulators under voltage protocol).
| Protocol ID | ids[].name | Source | Registers |
|---|---|---|---|
0x14 CLOCK | "clocks" | clk/clk-scmi.c | rt_clk provider |
0x17 VOLTAGE | "regulator" | regulator/regulator-scmi.c | rt_regulator_register |
0x16 RESET | (see driver) | reset/reset-scmi.c | reset controller |
0x11 POWER | "genpd" | pmdomain/pm-domain-scmi.c | power domain proxy |
0x19 PINCTRL | (see driver) | pinctrl/pinctrl-scmi.c | pin config via SCMI |
| SENSOR / thermal | (see driver) | thermal/thermal-scmi.c | thermal zone |
Consumers in DT stay unchanged: e.g. clocks = <&scmi_clk 42>;, resets = <&scmi_reset 3>;, power-domains = <&scmi_pd 1>; — see Clock framework (CLK), Regulator, Reset controller, Power domain, Pin control (pinctrl).
probe(struct rt_scmi_device *sdev) — use sdev->parent.ofw_node and sdev->agent is already set.rt_scmi_process_msg(sdev, &msg).Match **protocol-name** on the channel node to ids[].name when the table entry sets name non-NULL.
arm-scmi probe** and child reg values in logs (DBG_TAG scmi.agent, scmi.bus).shmem.c), 30-tick timeout on rt_mbox_send.arm,smc-id, IRQ completion, page/offset for arm,scmi-smc-param.SCMI_COM_MSG_VERSION if commands return SCMI_ERR_PROTOCOL.arm,scmicomponents/drivers/include/drivers/scmi.hcomponents/drivers/firmware/arm_scmi/agent.c, bus.c