firmware: scmi: Add i.MX95 SCMI LMM protocol driver
authorPeng Fan <peng.fan@nxp.com>
Fri, 17 Oct 2025 09:32:33 +0000 (17:32 +0800)
committerTom Rini <trini@konsulko.com>
Fri, 24 Oct 2025 19:47:50 +0000 (13:47 -0600)
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@nxp.com>
Reviewed-by: Alice Guo <alice.guo@nxp.com>
drivers/firmware/scmi/Kconfig
drivers/firmware/scmi/Makefile
drivers/firmware/scmi/vendors/imx/Kconfig [new file with mode: 0644]
drivers/firmware/scmi/vendors/imx/Makefile [new file with mode: 0644]
drivers/firmware/scmi/vendors/imx/imx-sm-lmm.c [new file with mode: 0644]
include/scmi_nxp_protocols.h

index dca8fed..33e089c 100644 (file)
@@ -47,3 +47,5 @@ config SCMI_ID_VENDOR_80
 
 config SCMI_ID_VENDOR_82
        bool
+
+source "drivers/firmware/scmi/vendors/imx/Kconfig"
index dae4286..6129726 100644 (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/
diff --git a/drivers/firmware/scmi/vendors/imx/Kconfig b/drivers/firmware/scmi/vendors/imx/Kconfig
new file mode 100644 (file)
index 0000000..211bb1f
--- /dev/null
@@ -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 (file)
index 0000000..6031ad8
--- /dev/null
@@ -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 (file)
index 0000000..2fd791c
--- /dev/null
@@ -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);
index 7e2143b..1c79bc2 100644 (file)
@@ -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