[PATCH 4/5] firmware: scmi: Add i.MX95 SCMI LMM protocol driver

Peng Fan (OSS) peng.fan at oss.nxp.com
Fri Oct 17 11:32:33 CEST 2025


From: Peng Fan <peng.fan at nxp.com>

Add Logical Machine Management(LMM) protocol which is intended for boot,
shutdown, and reset of other logical machines (LM). It is usually used to
allow one LM to manager another used as an offload or accelerator engine.

Following Linux Kernel, created a separate folder for holding vendor
protocol drivers.

Signed-off-by: Peng Fan <peng.fan at nxp.com>
---
 drivers/firmware/scmi/Kconfig                  |   2 +
 drivers/firmware/scmi/Makefile                 |   1 +
 drivers/firmware/scmi/vendors/imx/Kconfig      |   7 +
 drivers/firmware/scmi/vendors/imx/Makefile     |   7 +
 drivers/firmware/scmi/vendors/imx/imx-sm-lmm.c | 213 +++++++++++++++++++++++++
 include/scmi_nxp_protocols.h                   |  45 ++++++
 6 files changed, 275 insertions(+)

diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig
index dca8fed9bd25a0555233a7bead3a326ef2f05344..33e089c460ba74084507b023bb1dd5037b73b60d 100644
--- a/drivers/firmware/scmi/Kconfig
+++ b/drivers/firmware/scmi/Kconfig
@@ -47,3 +47,5 @@ config SCMI_ID_VENDOR_80
 
 config SCMI_ID_VENDOR_82
 	bool
+
+source "drivers/firmware/scmi/vendors/imx/Kconfig"
diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile
index dae42863589a9b8a84c1789610b950837ff73db1..6129726f81735fd8a531af18f9099cd5e1ea487e 100644
--- a/drivers/firmware/scmi/Makefile
+++ b/drivers/firmware/scmi/Makefile
@@ -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/
diff --git a/drivers/firmware/scmi/vendors/imx/Kconfig b/drivers/firmware/scmi/vendors/imx/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..211bb1f2244629096a50b06ee58006b5ee1a2dde
--- /dev/null
+++ b/drivers/firmware/scmi/vendors/imx/Kconfig
@@ -0,0 +1,7 @@
+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.
diff --git a/drivers/firmware/scmi/vendors/imx/Makefile b/drivers/firmware/scmi/vendors/imx/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..6031ad805209eda3313f53f9eee4ec96c9a4fe88
--- /dev/null
+++ b/drivers/firmware/scmi/vendors/imx/Makefile
@@ -0,0 +1,7 @@
+#
+# Copyright 2025 NXP
+#
+# SPDX-License-Identifier:      GPL-2.0+
+#
+
+obj-$(CONFIG_IMX_SM_LMM) += imx-sm-lmm.o
diff --git a/drivers/firmware/scmi/vendors/imx/imx-sm-lmm.c b/drivers/firmware/scmi/vendors/imx/imx-sm-lmm.c
new file mode 100644
index 0000000000000000000000000000000000000000..2fd791ca85315090b2c4706c8a3ff05fcd1fad1f
--- /dev/null
+++ b/drivers/firmware/scmi/vendors/imx/imx-sm-lmm.c
@@ -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);
diff --git a/include/scmi_nxp_protocols.h b/include/scmi_nxp_protocols.h
index 7e2143b16ead05a2662d9b88d2d348ab16a5750f..1c79bc2282b6efb02bcfc4843c1b4a0e83fe93fa 100644
--- a/include/scmi_nxp_protocols.h
+++ b/include/scmi_nxp_protocols.h
@@ -9,6 +9,7 @@
 #include <asm/types.h>
 #include <linux/bitops.h>
 
+#define SCMI_PROTOCOL_ID_IMX_LMM	0x80
 #define SCMI_PROTOCOL_ID_IMX_MISC	0x84
 
 #define SCMI_PAYLOAD_LEN	100
@@ -50,4 +51,48 @@ 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
 #endif

-- 
2.35.3



More information about the U-Boot mailing list