[PATCH 2/4] remoteproc: imx: Add i.MX95 support

Peng Fan (OSS) peng.fan at oss.nxp.com
Thu Jan 29 11:24:58 CET 2026


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

i.MX95 uses System Manager(sm) API to start/stop logical machine or cpu.
There are two modes:
 M7 in a dedicated logical machine, use LMM API
 M7 and A55 in same logical machine, use CPU API

Extend the driver to using LMM and CPU protocol to manage the M7 core:
 - Detect using LMM or CPU API in probe using API scmi_imx_lmm_info().
 - Compare linux LM ID(got using scmi_imx_lmm_info) and M7 LM ID(the ID
   is fixed as 1 in SM firmware if M7 is in a separate LM),
   if Linux LM ID is not same as M7 LM ID(linux and M7 in same LM), use
   LMM protocol to start/stop. Whether using CPU or LMM protocol to
   start/stop, the M7 status detection could use CPU protocol to detect
   started or not. So in imx_rproc_is_running, use scmi_imx_cpu_started to
   check the status of M7.
 - For above case (2), Use scmi_imx_lmm_power_boot to detect whether
   the M7 LM is under control of A55 LM.
 - For above case , after using SCMI_IMX_LMM_POWER_ON to check
   permission, scmi_imx_lmm_shutdown API should be called to shutdown
   the M7 LM.
 - Add a new ops imx_rproc_ops_sm.

Signed-off-by: Peng Fan <peng.fan at nxp.com>
---
 drivers/remoteproc/imx_rproc.c | 168 +++++++++++++++++++++++++++++++++++++++--
 drivers/remoteproc/imx_rproc.h |   3 +
 2 files changed, 166 insertions(+), 5 deletions(-)

diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 9eecbe70f0b..3f8cea127ec 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -11,6 +11,7 @@
 #include <linux/types.h>
 #include <regmap.h>
 #include <remoteproc.h>
+#include <scmi_nxp_protocols.h>
 #include <syscon.h>
 
 #include "imx_rproc.h"
@@ -37,9 +38,25 @@
 #define IMX_SIP_RPROC_STARTED		0x01
 #define IMX_SIP_RPROC_STOP		0x02
 
+/* Must align with System Manager Firmware */
+#define IMX95_M7_CPUID			1
+#define IMX95_M7_LMID			1
+
+/* Logical Machine API Operation */
+#define IMX_RPROC_FLAGS_SM_LMM_OP	BIT(0)
+/* CPU API Operation */
+#define IMX_RPROC_FLAGS_SM_CPU_OP	BIT(1)
+/* Linux has permission to handle the Logical Machine of remote cores */
+#define IMX_RPROC_FLAGS_SM_LMM_AVAIL	BIT(2)
+
 struct imx_rproc {
 	const struct imx_rproc_dcfg	*dcfg;
 	struct regmap *regmap;
+	u32				flags;
+	/* For System Manager based system */
+	struct udevice			*lmm_dev;
+	struct udevice			*cpu_dev;
+	ulong				reset_vector;
 };
 
 /* att flags: lower 16 bits specifying core, higher 16 bits for flags  */
@@ -65,6 +82,41 @@ static int imx_rproc_mmio_start(struct udevice *dev)
 	return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_start);
 }
 
+static int imx_rproc_sm_start(struct udevice *dev)
+{
+	struct imx_rproc *priv = dev_get_priv(dev);
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+	int ret;
+
+	if (priv->flags & IMX_RPROC_FLAGS_SM_CPU_OP) {
+		ret = scmi_imx_cpu_reset_vector_set(priv->cpu_dev, dcfg->cpuid, 0,
+						    priv->reset_vector, true, false, false);
+		if (ret) {
+			dev_err(dev, "Failed to set reset vector cpuid(%u): %d\n",
+				dcfg->cpuid, ret);
+			return ret;
+		}
+
+		return scmi_imx_cpu_start(priv->cpu_dev, dcfg->cpuid, true);
+	}
+
+	ret = scmi_imx_lmm_reset_vector_set(priv->lmm_dev, dcfg->lmid, dcfg->cpuid, 0,
+					    priv->reset_vector);
+	if (ret) {
+		dev_err(dev, "Failed to set reset vector lmid(%u), cpuid(%u): %d\n",
+			dcfg->lmid, dcfg->cpuid, ret);
+		return ret;
+	}
+
+	ret = scmi_imx_lmm_power_boot(priv->lmm_dev, dcfg->lmid, true);
+	if (ret) {
+		dev_err(dev, "Failed to boot lmm(%d): %d\n", ret, dcfg->lmid);
+		return ret;
+	}
+
+	return 0;
+}
+
 static int imx_rproc_start(struct udevice *dev)
 {
 	struct imx_rproc *priv = dev_get_priv(dev);
@@ -100,6 +152,17 @@ static int imx_rproc_mmio_stop(struct udevice *dev)
 	return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_stop);
 }
 
+static int imx_rproc_sm_stop(struct udevice *dev)
+{
+	struct imx_rproc *priv = dev_get_priv(dev);
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+
+	if (priv->flags & IMX_RPROC_FLAGS_SM_CPU_OP)
+		return scmi_imx_cpu_start(priv->cpu_dev, dcfg->cpuid, false);
+
+	return scmi_imx_lmm_shutdown(priv->lmm_dev, dcfg->lmid, 0);
+}
+
 static int imx_rproc_stop(struct udevice *dev)
 {
 	struct imx_rproc *priv = dev_get_priv(dev);
@@ -146,6 +209,20 @@ static int imx_rproc_mmio_is_running(struct udevice *dev)
 	return 1;
 }
 
+static int imx_rproc_sm_is_running(struct udevice *dev)
+{
+	struct imx_rproc *priv = dev_get_priv(dev);
+	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
+	int ret;
+	bool started = false;
+
+	ret = scmi_imx_cpu_started(priv->cpu_dev, dcfg->cpuid, &started);
+	if (ret || !started)
+		return 1;
+
+	return 0;
+}
+
 static int imx_rproc_is_running(struct udevice *dev)
 {
 	struct imx_rproc *priv = dev_get_priv(dev);
@@ -207,6 +284,14 @@ static int imx_rproc_load(struct udevice *dev, ulong addr, ulong size)
 	const struct imx_rproc_dcfg *dcfg = priv->dcfg;
 	int i;
 
+	if (IS_ENABLED(CONFIG_IMX_SM_LMM)) {
+		if (!(priv->flags & (IMX_RPROC_FLAGS_SM_LMM_AVAIL | IMX_RPROC_FLAGS_SM_CPU_OP)))
+			return -EACCES;
+	}
+
+	/* Only used for SM based System */
+	priv->reset_vector = rproc_elf_get_boot_addr(dev, addr) & GENMASK(31, 16);
+
 	/*
 	 * Before loading elf, need do ECC initialization by clearing the memory
 	 * region, if ATT_ECC is set.
@@ -239,19 +324,65 @@ static int imx_rproc_probe(struct udevice *dev)
 {
 	struct imx_rproc *priv = dev_get_priv(dev);
 	struct imx_rproc_dcfg *dcfg = (struct imx_rproc_dcfg *)dev_get_driver_data(dev);
+	struct scmi_imx_lmm_info info;
 	ofnode node;
+	int ret;
 
 	node = dev_ofnode(dev);
 
 	priv->dcfg = dcfg;
 
-	if (dcfg->method != IMX_RPROC_MMIO)
+	if (dcfg->method == IMX_RPROC_MMIO) {
+		priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon");
+		if (IS_ERR(priv->regmap)) {
+			dev_err(dev, "No syscon: %ld\n", PTR_ERR(priv->regmap));
+			return PTR_ERR(priv->regmap);
+		}
 		return 0;
+	} else {
+		if (IS_ENABLED(CONFIG_IMX_SM_LMM)) {
+			struct udevice *lmm_dev;
+
+			ret = uclass_get_device_by_name(UCLASS_SCMI_BASE, "protocol at 80", &lmm_dev);
+			if (ret) {
+				dev_err(dev, "Failed to get SM LMM protocol dev\n");
+				return ret;
+			}
+
+			priv->lmm_dev = lmm_dev;
+
+			ret = scmi_imx_lmm_info(lmm_dev, LMM_ID_DISCOVER, &info);
+			if (ret) {
+				dev_err(dev, "Failed to get lmm info\n");
+				return ret;
+			}
+
+			if (dcfg->lmid != info.lmid) {
+				ret = scmi_imx_lmm_power_boot(lmm_dev, dcfg->lmid, false);
+				if (ret == -EACCES) {
+					dev_err(dev, "Remoteproc not under U-boot control: only support detect running\n");
+				} else if (ret) {
+					dev_err(dev, "power on lmm fail:%d\n", ret);
+					return ret;
+				} else {
+					priv->flags |= IMX_RPROC_FLAGS_SM_LMM_AVAIL;
+				}
+			} else {
+				priv->flags |= IMX_RPROC_FLAGS_SM_CPU_OP;
+			}
+		}
+
+		if (IS_ENABLED(CONFIG_IMX_SM_CPU)) {
+			struct udevice *cpu_dev;
 
-	priv->regmap = syscon_regmap_lookup_by_phandle(dev, "syscon");
-	if (IS_ERR(priv->regmap)) {
-		dev_err(dev, "No syscon: %ld\n", PTR_ERR(priv->regmap));
-		return PTR_ERR(priv->regmap);
+			ret = uclass_get_device_by_name(UCLASS_SCMI_BASE, "protocol at 82", &cpu_dev);
+			if (ret) {
+				dev_err(dev, "Failed to get SM LMM protocol dev\n");
+				return ret;
+			}
+
+			priv->cpu_dev = cpu_dev;
+		}
 	}
 
 	return 0;
@@ -372,12 +503,39 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = {
 	.ops		= &imx_rproc_ops_arm_smc,
 };
 
+static const struct imx_rproc_att imx_rproc_att_imx95_m7[] = {
+	/* dev addr , sys addr  , size	    , flags */
+	/* TCM CODE NON-SECURE */
+	{ 0x00000000, 0x203C0000, 0x00040000, ATT_OWN | ATT_IOMEM | ATT_ECC },
+
+	/* TCM SYS NON-SECURE*/
+	{ 0x20000000, 0x20400000, 0x00040000, ATT_OWN | ATT_IOMEM | ATT_ECC },
+
+	/* DDR */
+	{ 0x80000000, 0x80000000, 0x50000000, 0 },
+};
+
+static const struct imx_rproc_plat_ops imx_rproc_ops_sm = {
+	.start		= imx_rproc_sm_start,
+	.stop		= imx_rproc_sm_stop,
+	.is_running	= imx_rproc_sm_is_running,
+};
+
+static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = {
+	.att		= imx_rproc_att_imx95_m7,
+	.att_size	= ARRAY_SIZE(imx_rproc_att_imx95_m7),
+	.ops		= &imx_rproc_ops_sm,
+	.cpuid		= IMX95_M7_CPUID,
+	.lmid		= IMX95_M7_LMID,
+};
+
 static const struct udevice_id imx_rproc_ids[] = {
 	{ .compatible = "fsl,imx8mm-cm4", .data = (ulong)&imx_rproc_cfg_imx8mq },
 	{ .compatible = "fsl,imx8mn-cm7", .data = (ulong)&imx_rproc_cfg_imx8mn, },
 	{ .compatible = "fsl,imx8mp-cm7", .data = (ulong)&imx_rproc_cfg_imx8mn, },
 	{ .compatible = "fsl,imx8mq-cm4", .data = (ulong)&imx_rproc_cfg_imx8mq },
 	{ .compatible = "fsl,imx93-cm33", .data = (ulong)&imx_rproc_cfg_imx93 },
+	{ .compatible = "fsl,imx95-cm7", .data = (ulong)&imx_rproc_cfg_imx95_m7 },
 	{}
 };
 
diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h
index 7a82dc4a195..1629bc7f6e7 100644
--- a/drivers/remoteproc/imx_rproc.h
+++ b/drivers/remoteproc/imx_rproc.h
@@ -51,6 +51,9 @@ struct imx_rproc_dcfg {
 	enum imx_rproc_method		method;
 	u32				flags;
 	const struct imx_rproc_plat_ops	*ops;
+	/* For System Manager(SM) based SoCs, the IDs are from SM firmware */
+	u32				cpuid;
+	u32				lmid;
 };
 
 #endif /* _IMX_RPROC_H */

-- 
2.51.0



More information about the U-Boot mailing list