From 251dd6bf0e89a587710fa7bfa320088617f8854e Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 17 Oct 2025 17:32:30 +0800 Subject: firmware: scmi: Conditionally compile protocol support Add conditional compilation for SCMI protocol support in scmi_get_protocol() and scmi_add_protocol() based on corresponding Kconfig options. This ensures that only the enabled protocols are compiled and accessed, and reducing binary size. Signed-off-by: Peng Fan Reviewed-by: Alice Guo --- drivers/firmware/scmi/scmi_agent-uclass.c | 20 ++++++++++++++++++++ include/scmi_agent-uclass.h | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index 69a277e8786..f48c2fdb27b 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -86,21 +86,31 @@ 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 default: dev_err(dev, "Protocol not supported\n"); proto = NULL; @@ -139,21 +149,31 @@ 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 default: dev_err(dev, "Protocol not supported\n"); return -EPROTO; diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index d6586eb3ff9..ccedd42f367 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -40,11 +40,21 @@ 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 }; static inline u32 scmi_version(struct udevice *dev) -- cgit v1.2.3 From d1f1c98d8468f5f76c2c8c3acb31f83d736d34ce Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 17 Oct 2025 17:32:31 +0800 Subject: firmware: scmi: Cleanup the SCMI MISC ID SCMI_IMX_PROTOCOL_ID_MISC was never used, so drop it. And move SCMI_PROTOCOL_ID_IMX_MISC out of enum scmi_std_protocol to scmi_nxp_protocols.h, because it is i.MX specific and following Linux Kernel style, use macro definition. Signed-off-by: Peng Fan Reviewed-by: Alice Guo --- include/scmi_nxp_protocols.h | 4 +--- include/scmi_protocols.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/scmi_nxp_protocols.h b/include/scmi_nxp_protocols.h index fe6ecd6a7cf..7e2143b16ea 100644 --- a/include/scmi_nxp_protocols.h +++ b/include/scmi_nxp_protocols.h @@ -9,9 +9,7 @@ #include #include -enum scmi_imx_protocol { - SCMI_IMX_PROTOCOL_ID_MISC = 0x84, -}; +#define SCMI_PROTOCOL_ID_IMX_MISC 0x84 #define SCMI_PAYLOAD_LEN 100 diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 95e0c3cce3b..5c9516d5595 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -25,7 +25,6 @@ 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, }; enum scmi_status_code { -- cgit v1.2.3 From 7830ccc77a13dd2a9880a942734bb5687416c4d8 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 17 Oct 2025 17:32:32 +0800 Subject: firmware: scmi: Support probe vendor ID 0x80 and 0x82 Preparing to add i.MX LMM and CPU protocol driver, support probe SCMI vendor ID 0x80(i.MX SCMI LMM ID) and 0x82(i.MX SCMI CPU ID). And use Kconfig option to support conditional compilation. Signed-off-by: Peng Fan Reviewed-by: Alice Guo --- drivers/firmware/scmi/Kconfig | 6 ++++++ drivers/firmware/scmi/scmi_agent-uclass.c | 20 ++++++++++++++++++++ include/scmi_agent-uclass.h | 6 ++++++ include/scmi_protocols.h | 2 ++ 4 files changed, 34 insertions(+) diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index 8cf85f0d7a1..dca8fed9bd2 100644 --- a/drivers/firmware/scmi/Kconfig +++ b/drivers/firmware/scmi/Kconfig @@ -41,3 +41,9 @@ 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 diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index f48c2fdb27b..ad825d66da2 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -110,6 +110,16 @@ struct udevice *scmi_get_protocol(struct udevice *dev, 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"); @@ -173,6 +183,16 @@ static int scmi_add_protocol(struct udevice *dev, 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"); diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index ccedd42f367..9b36d3ae67b 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -55,6 +55,12 @@ struct scmi_agent_priv { #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) diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index 5c9516d5595..bb74a57f79a 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -25,6 +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_VENDOR_80 = 0x80, + SCMI_PROTOCOL_ID_VENDOR_82 = 0x82, }; enum scmi_status_code { -- cgit v1.2.3 From 3f20ea3675c466ab16761c9b280e6c382bae02c3 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 17 Oct 2025 17:32:33 +0800 Subject: 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 Reviewed-by: Alice Guo --- 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(+) create mode 100644 drivers/firmware/scmi/vendors/imx/Kconfig create mode 100644 drivers/firmware/scmi/vendors/imx/Makefile create mode 100644 drivers/firmware/scmi/vendors/imx/imx-sm-lmm.c diff --git a/drivers/firmware/scmi/Kconfig b/drivers/firmware/scmi/Kconfig index dca8fed9bd2..33e089c460b 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 dae42863589..6129726f817 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 00000000000..211bb1f2244 --- /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 00000000000..6031ad80520 --- /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 00000000000..2fd791ca853 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 7e2143b16ea..1c79bc2282b 100644 --- a/include/scmi_nxp_protocols.h +++ b/include/scmi_nxp_protocols.h @@ -9,6 +9,7 @@ #include #include +#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 -- cgit v1.2.3 From ac9b02dd1028d14b6326970c93dfc3c50daa16f1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Fri, 17 Oct 2025 17:32:34 +0800 Subject: firmware: scmi: Add i.MX95 SCMI CPU Protocol This protocol allows an agent to start, stop a CPU or set reset vector. It is used to manage auxiliary CPUs in an LM (e.g. additional cores in an AP cluster). Signed-off-by: Peng Fan Reviewed-by: Alice Guo --- drivers/firmware/scmi/vendors/imx/Kconfig | 8 ++ drivers/firmware/scmi/vendors/imx/Makefile | 1 + drivers/firmware/scmi/vendors/imx/imx-sm-cpu.c | 179 +++++++++++++++++++++++++ include/scmi_nxp_protocols.h | 24 ++++ 4 files changed, 212 insertions(+) create mode 100644 drivers/firmware/scmi/vendors/imx/imx-sm-cpu.c diff --git a/drivers/firmware/scmi/vendors/imx/Kconfig b/drivers/firmware/scmi/vendors/imx/Kconfig index 211bb1f2244..16850502bbb 100644 --- a/drivers/firmware/scmi/vendors/imx/Kconfig +++ b/drivers/firmware/scmi/vendors/imx/Kconfig @@ -1,3 +1,11 @@ +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 diff --git a/drivers/firmware/scmi/vendors/imx/Makefile b/drivers/firmware/scmi/vendors/imx/Makefile index 6031ad80520..59ff8640dc1 100644 --- a/drivers/firmware/scmi/vendors/imx/Makefile +++ b/drivers/firmware/scmi/vendors/imx/Makefile @@ -4,4 +4,5 @@ # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_IMX_SM_CPU) += imx-sm-cpu.o obj-$(CONFIG_IMX_SM_LMM) += imx-sm-lmm.o diff --git a/drivers/firmware/scmi/vendors/imx/imx-sm-cpu.c b/drivers/firmware/scmi/vendors/imx/imx-sm-cpu.c new file mode 100644 index 00000000000..28dfac642ec --- /dev/null +++ b/drivers/firmware/scmi/vendors/imx/imx-sm-cpu.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * i.MX SCMI CPU protocol + * + * Copyright 2025 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); diff --git a/include/scmi_nxp_protocols.h b/include/scmi_nxp_protocols.h index 1c79bc2282b..c17f3663eba 100644 --- a/include/scmi_nxp_protocols.h +++ b/include/scmi_nxp_protocols.h @@ -10,6 +10,7 @@ #include #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 @@ -95,4 +96,27 @@ static inline int scmi_imx_lmm_shutdown(struct udevice *dev, u32 lmid, bool flag 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 -- cgit v1.2.3