Merge patch series "firmware: scmi: Support SCMI LMM/CPU protocol for i.MX95"

Peng Fan (OSS) <peng.fan@oss.nxp.com> says:

i.MX95 System Manager(SM) implements Logical Machine Management(LMM) and
CPU protocol to manage Logical Machine(LM) and CPUs(eg, M7).

To manage M7 in a separate LM or in same LM as U-Boot/Linux itself. LMM
APIs and CPU APIs are needed.

When M7 is in LM 'lm-m7', and this LM is managable by 'uboot-lm', U-Boot
could use LMM_BOOT, LMM_SHUTDOWN and etc to manage 'lm-m7'.

If in same LM, use CPU_START, CPU_STOP, CPU_RESET_VECTOR_SET and etc to
manage M7.

Both LMM/CPU APIs will be used by remoteproc driver.

The documentation could be found in Linux Kernel:
drivers/firmware/arm_scmi/vendors/imx/imx95.rst

Link: https://lore.kernel.org/r/20251017-scmi-lmm-v1-0-9fd41e7a5ac0@nxp.com
This commit is contained in:
Tom Rini 2025-10-24 12:09:02 -06:00
commit fd976ff3a2
10 changed files with 552 additions and 4 deletions

View File

@ -41,3 +41,11 @@ config SCMI_AGENT_OPTEE
help
Enable the SCMI communication channel based on OP-TEE transport
for compatible "linaro,scmi-optee".
config SCMI_ID_VENDOR_80
bool
config SCMI_ID_VENDOR_82
bool
source "drivers/firmware/scmi/vendors/imx/Kconfig"

View File

@ -6,3 +6,4 @@ obj-$(CONFIG_SCMI_AGENT_MAILBOX) += mailbox_agent.o
obj-$(CONFIG_SCMI_AGENT_OPTEE) += optee_agent.o
obj-$(CONFIG_SCMI_POWER_DOMAIN) += pwdom.o
obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o sandbox-scmi_devices.o
obj-y += vendors/imx/

View File

@ -86,21 +86,41 @@ struct udevice *scmi_get_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_BASE:
proto = priv->base_dev;
break;
#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
case SCMI_PROTOCOL_ID_POWER_DOMAIN:
proto = priv->pwdom_dev;
break;
#endif
#if IS_ENABLED(CONFIG_CLK_SCMI)
case SCMI_PROTOCOL_ID_CLOCK:
proto = priv->clock_dev;
break;
#endif
#if IS_ENABLED(CONFIG_RESET_SCMI)
case SCMI_PROTOCOL_ID_RESET_DOMAIN:
proto = priv->resetdom_dev;
break;
#endif
#if IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
proto = priv->voltagedom_dev;
break;
#endif
#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI)
case SCMI_PROTOCOL_ID_PINCTRL:
proto = priv->pinctrl_dev;
break;
#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_80)
case SCMI_PROTOCOL_ID_VENDOR_80:
proto = priv->vendor_dev_80;
break;
#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_82)
case SCMI_PROTOCOL_ID_VENDOR_82:
proto = priv->vendor_dev_82;
break;
#endif
default:
dev_err(dev, "Protocol not supported\n");
proto = NULL;
@ -139,21 +159,41 @@ static int scmi_add_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_BASE:
priv->base_dev = proto;
break;
#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
case SCMI_PROTOCOL_ID_POWER_DOMAIN:
priv->pwdom_dev = proto;
break;
#endif
#if IS_ENABLED(CONFIG_CLK_SCMI)
case SCMI_PROTOCOL_ID_CLOCK:
priv->clock_dev = proto;
break;
#endif
#if IS_ENABLED(CONFIG_RESET_SCMI)
case SCMI_PROTOCOL_ID_RESET_DOMAIN:
priv->resetdom_dev = proto;
break;
#endif
#if IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
priv->voltagedom_dev = proto;
break;
#endif
#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI)
case SCMI_PROTOCOL_ID_PINCTRL:
priv->pinctrl_dev = proto;
break;
#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_80)
case SCMI_PROTOCOL_ID_VENDOR_80:
priv->vendor_dev_80 = proto;
break;
#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_82)
case SCMI_PROTOCOL_ID_VENDOR_82:
priv->vendor_dev_82 = proto;
break;
#endif
default:
dev_err(dev, "Protocol not supported\n");
return -EPROTO;

View File

@ -0,0 +1,15 @@
config IMX_SM_CPU
bool "Enable i.MX System Manager CPU API"
depends on ARCH_IMX9 && SCMI_FIRMWARE
select SCMI_ID_VENDOR_82
help
If you say Y here to enable i.MX System Manager CPU
API to work on some NXP i.MX processors.
config IMX_SM_LMM
bool "Enable i.MX System Manager Logical Machine API"
depends on ARCH_IMX9 && SCMI_FIRMWARE
select SCMI_ID_VENDOR_80
help
If you say Y here to enable i.MX System Manager Logical Machine
API to work on some NXP i.MX processors.

View File

@ -0,0 +1,8 @@
#
# Copyright 2025 NXP
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_IMX_SM_CPU) += imx-sm-cpu.o
obj-$(CONFIG_IMX_SM_LMM) += imx-sm-lmm.o

View File

@ -0,0 +1,179 @@
// SPDX-License-Identifier: GPL-2.0
/*
* i.MX SCMI CPU protocol
*
* Copyright 2025 NXP
*/
#include <compiler.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <misc.h>
#include <scmi_agent.h>
#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <scmi_nxp_protocols.h>
enum scmi_imx_cpu_protocol_cmd {
SCMI_IMX_CPU_ATTRIBUTES = 0x3,
SCMI_IMX_CPU_START = 0x4,
SCMI_IMX_CPU_STOP = 0x5,
SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6,
SCMI_IMX_CPU_INFO_GET = 0xC,
};
struct scmi_imx_cpu_priv {
u32 nr_cpu;
};
struct scmi_imx_cpu_reset_vector_set_in {
__le32 cpuid;
#define CPU_VEC_FLAGS_RESUME BIT(31)
#define CPU_VEC_FLAGS_START BIT(30)
#define CPU_VEC_FLAGS_BOOT BIT(29)
__le32 flags;
__le32 resetvectorlow;
__le32 resetvectorhigh;
};
struct scmi_imx_cpu_info_get_out {
__le32 status;
#define CPU_RUN_MODE_START 0
#define CPU_RUN_MODE_HOLD 1
#define CPU_RUN_MODE_STOP 2
#define CPU_RUN_MODE_SLEEP 3
__le32 runmode;
__le32 sleepmode;
__le32 resetvectorlow;
__le32 resetvectorhigh;
};
int scmi_imx_cpu_start(struct udevice *dev, u32 cpuid, bool start)
{
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_82,
.message_id = SCMI_IMX_CPU_STOP,
.in_msg = (u8 *)&cpuid,
.in_msg_sz = sizeof(cpuid),
.out_msg = (u8 *)&status,
.out_msg_sz = sizeof(status),
};
int ret;
if (!dev)
return -EINVAL;
if (start)
msg.message_id = SCMI_IMX_CPU_START;
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
if (status)
return scmi_to_linux_errno(status);
return 0;
}
int scmi_imx_cpu_reset_vector_set(struct udevice *dev, u32 cpuid, u32 flags, u64 vector,
bool start, bool boot, bool resume)
{
struct scmi_imx_cpu_reset_vector_set_in in;
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_82,
.message_id = SCMI_IMX_CPU_RESET_VECTOR_SET,
.in_msg = (u8 *)&in,
.in_msg_sz = sizeof(in),
.out_msg = (u8 *)&status,
.out_msg_sz = sizeof(status),
};
int ret;
if (!dev)
return -EINVAL;
in.cpuid = cpu_to_le32(cpuid);
in.flags = cpu_to_le32(0);
if (start)
in.flags |= CPU_VEC_FLAGS_START;
if (boot)
in.flags |= CPU_VEC_FLAGS_BOOT;
if (resume)
in.flags |= CPU_VEC_FLAGS_RESUME;
in.resetvectorlow = cpu_to_le32(lower_32_bits(vector));
in.resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
if (status)
return scmi_to_linux_errno(status);
return 0;
}
int scmi_imx_cpu_started(struct udevice *dev, u32 cpuid, bool *started)
{
struct scmi_imx_cpu_info_get_out out;
u32 mode;
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_82,
.message_id = SCMI_IMX_CPU_INFO_GET,
.in_msg = (u8 *)&cpuid,
.in_msg_sz = sizeof(cpuid),
.out_msg = (u8 *)&out,
.out_msg_sz = sizeof(out),
};
int ret;
if (!dev)
return -EINVAL;
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
status = cpu_to_le32(out.status);
if (status)
return scmi_to_linux_errno(status);
mode = le32_to_cpu(out.runmode);
if (mode == CPU_RUN_MODE_START || mode == CPU_RUN_MODE_SLEEP)
*started = true;
return 0;
}
static int scmi_imx_cpu_probe(struct udevice *dev)
{
int ret;
ret = devm_scmi_of_get_channel(dev);
if (ret) {
dev_err(dev, "failed to get channel (%d)\n", ret);
return ret;
}
return 0;
}
U_BOOT_DRIVER(scmi_imx_cpu) = {
.name = "scmi_imx_cpu",
.id = UCLASS_SCMI_BASE,
.probe = scmi_imx_cpu_probe,
.priv_auto = sizeof(struct scmi_imx_cpu_priv),
};
static struct scmi_proto_match match[] = {
{ .proto_id = SCMI_PROTOCOL_ID_VENDOR_82},
{ /* Sentinel */ }
};
U_BOOT_SCMI_PROTO_DRIVER(scmi_imx_cpu, match);

View File

@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0
/*
* i.MX SCMI LMM protocol
*
* Copyright 2025 NXP
*/
#include <compiler.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <linux/types.h>
#include <misc.h>
#include <scmi_agent.h>
#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <scmi_nxp_protocols.h>
enum scmi_imx_lmm_protocol_cmd {
SCMI_IMX_LMM_ATTRIBUTES = 0x3,
SCMI_IMX_LMM_BOOT = 0x4,
SCMI_IMX_LMM_RESET = 0x5,
SCMI_IMX_LMM_SHUTDOWN = 0x6,
SCMI_IMX_LMM_WAKE = 0x7,
SCMI_IMX_LMM_SUSPEND = 0x8,
SCMI_IMX_LMM_NOTIFY = 0x9,
SCMI_IMX_LMM_RESET_REASON = 0xA,
SCMI_IMX_LMM_POWER_ON = 0xB,
SCMI_IMX_LMM_RESET_VECTOR_SET = 0xC,
};
struct scmi_imx_lmm_priv {
u32 nr_lmm;
};
struct scmi_msg_imx_lmm_attributes_out {
__le32 status;
__le32 lmid;
__le32 attributes;
__le32 state;
__le32 errstatus;
u8 name[LMM_MAX_NAME];
};
struct scmi_imx_lmm_reset_vector_set_in {
__le32 lmid;
__le32 cpuid;
__le32 flags; /* reserved for future extension */
__le32 resetvectorlow;
__le32 resetvectorhigh;
};
struct scmi_imx_lmm_shutdown_in {
__le32 lmid;
#define SCMI_IMX_LMM_SHUTDOWN_GRACEFUL BIT(0)
__le32 flags;
};
int scmi_imx_lmm_info(struct udevice *dev, u32 lmid, struct scmi_imx_lmm_info *info)
{
struct scmi_msg_imx_lmm_attributes_out out;
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_80,
.message_id = SCMI_IMX_LMM_ATTRIBUTES,
.in_msg = (u8 *)&lmid,
.in_msg_sz = sizeof(lmid),
.out_msg = (u8 *)&out,
.out_msg_sz = sizeof(out),
};
int ret;
if (!dev)
return -EINVAL;
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
status = cpu_to_le32(out.status);
if (status)
return scmi_to_linux_errno(status);
info->lmid = le32_to_cpu(out.lmid);
info->state = le32_to_cpu(out.state);
info->errstatus = le32_to_cpu(out.errstatus);
strcpy(info->name, out.name);
dev_dbg(dev, "i.MX LMM: Logical Machine(%d), name: %s\n",
info->lmid, info->name);
return ret;
}
int scmi_imx_lmm_power_boot(struct udevice *dev, u32 lmid, bool boot)
{
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_80,
.message_id = SCMI_IMX_LMM_POWER_ON,
.in_msg = (u8 *)&lmid,
.in_msg_sz = sizeof(lmid),
.out_msg = (u8 *)&status,
.out_msg_sz = sizeof(status),
};
int ret;
if (!dev)
return -EINVAL;
if (boot)
msg.message_id = SCMI_IMX_LMM_BOOT;
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
if (status)
return scmi_to_linux_errno(status);
return 0;
}
int scmi_imx_lmm_reset_vector_set(struct udevice *dev, u32 lmid, u32 cpuid, u32 flags, u64 vector)
{
struct scmi_imx_lmm_reset_vector_set_in in;
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_80,
.message_id = SCMI_IMX_LMM_RESET_VECTOR_SET,
.in_msg = (u8 *)&in,
.in_msg_sz = sizeof(in),
.out_msg = (u8 *)&status,
.out_msg_sz = sizeof(status),
};
int ret;
if (!dev)
return -EINVAL;
in.lmid = lmid;
in.cpuid = cpuid;
in.flags = flags;
in.resetvectorlow = vector & 0xFFFFFFFF;
in.resetvectorhigh = vector >> 32;
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
if (status)
return scmi_to_linux_errno(status);
return 0;
}
int scmi_imx_lmm_shutdown(struct udevice *dev, u32 lmid, bool flags)
{
struct scmi_imx_lmm_shutdown_in in;
s32 status;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_VENDOR_80,
.message_id = SCMI_IMX_LMM_SHUTDOWN,
.in_msg = (u8 *)&in,
.in_msg_sz = sizeof(in),
.out_msg = (u8 *)&status,
.out_msg_sz = sizeof(status),
};
int ret;
if (!dev)
return -EINVAL;
in.lmid = lmid;
if (flags & SCMI_IMX_LMM_SHUTDOWN_GRACEFUL)
in.flags = cpu_to_le32(SCMI_IMX_LMM_SHUTDOWN_GRACEFUL);
else
in.flags = cpu_to_le32(0);
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
return ret;
if (status)
return scmi_to_linux_errno(status);
return 0;
}
static int scmi_imx_lmm_probe(struct udevice *dev)
{
int ret;
ret = devm_scmi_of_get_channel(dev);
if (ret) {
dev_err(dev, "failed to get channel (%d)\n", ret);
return ret;
}
return 0;
}
U_BOOT_DRIVER(scmi_imx_lmm) = {
.name = "scmi_imx_lmm",
.id = UCLASS_SCMI_BASE,
.probe = scmi_imx_lmm_probe,
.priv_auto = sizeof(struct scmi_imx_lmm_priv),
};
static struct scmi_proto_match match[] = {
{ .proto_id = SCMI_PROTOCOL_ID_VENDOR_80},
{ /* Sentinel */ }
};
U_BOOT_SCMI_PROTO_DRIVER(scmi_imx_lmm, match);

View File

@ -40,11 +40,27 @@ struct scmi_agent_priv {
u8 *agent_name;
u32 agent_id;
struct udevice *base_dev;
#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
struct udevice *pwdom_dev;
#endif
#if IS_ENABLED(CONFIG_CLK_SCMI)
struct udevice *clock_dev;
#endif
#if IS_ENABLED(CONFIG_RESET_SCMI)
struct udevice *resetdom_dev;
#endif
#if IS_ENABLED(CONFIG_DM_REGULATOR_SCMI)
struct udevice *voltagedom_dev;
#endif
#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI)
struct udevice *pinctrl_dev;
#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_80)
struct udevice *vendor_dev_80;
#endif
#if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_82)
struct udevice *vendor_dev_82;
#endif
};
static inline u32 scmi_version(struct udevice *dev)

View File

@ -9,9 +9,9 @@
#include <asm/types.h>
#include <linux/bitops.h>
enum scmi_imx_protocol {
SCMI_IMX_PROTOCOL_ID_MISC = 0x84,
};
#define SCMI_PROTOCOL_ID_IMX_LMM 0x80
#define SCMI_PROTOCOL_ID_IMX_CPU 0x82
#define SCMI_PROTOCOL_ID_IMX_MISC 0x84
#define SCMI_PAYLOAD_LEN 100
@ -52,4 +52,71 @@ struct scmi_imx_misc_reset_reason_out {
u32 extInfo[MISC_MAX_EXTINFO];
};
#define LMM_ID_DISCOVER 0xFFFFFFFFU
#define LMM_MAX_NAME 16
enum scmi_imx_lmm_state {
LMM_STATE_LM_OFF,
LMM_STATE_LM_ON,
LMM_STATE_LM_SUSPEND,
LMM_STATE_LM_POWERED,
};
struct scmi_imx_lmm_info {
u32 lmid;
enum scmi_imx_lmm_state state;
u32 errstatus;
u8 name[LMM_MAX_NAME];
};
#if IS_ENABLED(CONFIG_IMX_SM_LMM)
int scmi_imx_lmm_info(struct udevice *dev, u32 lmid, struct scmi_imx_lmm_info *info);
int scmi_imx_lmm_power_boot(struct udevice *dev, u32 lmid, bool boot);
int scmi_imx_lmm_reset_vector_set(struct udevice *dev, u32 lmid, u32 cpuid, u32 flags, u64 vector);
int scmi_imx_lmm_shutdown(struct udevice *dev, u32 lmid, bool flags);
#else
static inline int scmi_imx_lmm_info(struct udevice *dev, u32 lmid, struct scmi_imx_lmm_info *info)
{
return -EOPNOTSUPP;
}
static inline int scmi_imx_lmm_power_boot(struct udevice *dev, u32 lmid, bool boot)
{
return -EOPNOTSUPP;
}
static inline int
scmi_imx_lmm_reset_vector_set(struct udevice *dev, u32 lmid, u32 cpuid, u32 flags, u64 vector)
{
return -EOPNOTSUPP;
}
static inline int scmi_imx_lmm_shutdown(struct udevice *dev, u32 lmid, bool flags)
{
return -EOPNOTSUPP;
}
#endif
#if IS_ENABLED(CONFIG_IMX_SM_CPU)
int scmi_imx_cpu_started(struct udevice *dev, u32 cpuid, bool *started);
int scmi_imx_cpu_reset_vector_set(struct udevice *dev, u32 cpuid, u32 flags, u64 vector,
bool start, bool boot, bool resume);
int scmi_imx_cpu_start(struct udevice *dev, u32 cpuid, bool start);
#else
static inline int scmi_imx_cpu_started(struct udevice *dev, u32 cpuid, bool *started)
{
return -EOPNOTSUPP;
}
static inline int scmi_imx_cpu_reset_vector_set(struct udevice *dev, u32 cpuid, u32 flags,
u64 vector, bool start, bool boot, bool resume)
{
return -EOPNOTSUPP;
}
static inline int scmi_imx_cpu_start(struct udevice *dev, u32 cpuid, bool start)
{
return -EOPNOTSUPP;
}
#endif
#endif

View File

@ -25,7 +25,8 @@ enum scmi_std_protocol {
SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17,
SCMI_PROTOCOL_ID_PINCTRL = 0x19,
SCMI_PROTOCOL_ID_IMX_MISC = 0x84,
SCMI_PROTOCOL_ID_VENDOR_80 = 0x80,
SCMI_PROTOCOL_ID_VENDOR_82 = 0x82,
};
enum scmi_status_code {