firmware: scmi: Add i.MX95 SCMI LMM protocol driver

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>
This commit is contained in:
Peng Fan
2025-10-17 17:32:33 +08:00
committed by Tom Rini
parent 7830ccc77a
commit 3f20ea3675
6 changed files with 275 additions and 0 deletions

View File

@@ -47,3 +47,5 @@ config SCMI_ID_VENDOR_80
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

@@ -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.

View File

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

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

@@ -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