On Thu, Dec 18, 2025 at 01:17:38PM +0800, Peng Fan (OSS) wrote: > From: Peng Fan <[email protected]> > > i.MX95 features a Cortex-M33 core, six Cortex-A55 cores, and > one Cortex-M7 core. The System Control Management Interface(SCMI) > firmware runs on the M33 core. The i.MX95 SCMI firmware named System > Manager(SM) includes vendor extension protocols, Logical Machine > Management(LMM) protocol and CPU protocol and etc. > > Depending on SM configuration, M7 can be used as follows: > (1) M7 in a separate Logical Machine (LM) from A55 cores, that Linux > can't control > (2) M7 in a separate LM from A55 cores that Linux can control using LMM > protocol. > (3) M7 runs in same Logical Machine as A55 cores, so Linux can control it > using CPU protocol > > So extend the driver to using LMM and CPU protocol to manage the M7 core. > - 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 equals M7 LM ID(linux and M7 in same LM), use CPU > protocol to start/stop. Otherwise, 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_detect_mode, use scmi_imx_cpu_started to check the > status of M7. > - For above case (1) and (2), Use SCMI_IMX_LMM_POWER_ON 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 to save power only when M7 LM is going to be started by > remoteproc framework. Otherwise bypass SCMI_IMX_LMM_SHUTDOWN API if > M7 LM is started before booting Linux. > > Current setup relies on pre-Linux software(U-Boot) to do M7 TCM ECC > initialization. In future, we could add the support in Linux to decouple > U-Boot and Linux. > > Reviewed-by: Daniel Baluta <[email protected]> > Signed-off-by: Peng Fan <[email protected]>
Reviewed-by: Frank Li <[email protected]> > --- > drivers/remoteproc/Kconfig | 2 + > drivers/remoteproc/imx_rproc.c | 198 > +++++++++++++++++++++++++++++++++++++++++ > drivers/remoteproc/imx_rproc.h | 3 + > 3 files changed, 203 insertions(+) > > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > index > 48a0d3a69ed08057716f1e7ea950899f60bbe0cf..ee54436fea5ad08a9c198ce74d44ce7a9aa206de > 100644 > --- a/drivers/remoteproc/Kconfig > +++ b/drivers/remoteproc/Kconfig > @@ -27,6 +27,8 @@ config IMX_REMOTEPROC > tristate "i.MX remoteproc support" > depends on ARCH_MXC > depends on HAVE_ARM_SMCCC > + depends on IMX_SCMI_CPU_DRV || !IMX_SCMI_CPU_DRV > + depends on IMX_SCMI_LMM_DRV || !IMX_SCMI_LMM_DRV > select MAILBOX > help > Say y here to support iMX's remote processors via the remote > diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c > index > b0857a1a9660503ee7cd5473c06dacb5262286b8..ae3183ad996462ad0815889a2291d320ce7a1e8f > 100644 > --- a/drivers/remoteproc/imx_rproc.c > +++ b/drivers/remoteproc/imx_rproc.c > @@ -8,6 +8,7 @@ > #include <linux/clk.h> > #include <linux/err.h> > #include <linux/firmware/imx/sci.h> > +#include <linux/firmware/imx/sm.h> > #include <linux/interrupt.h> > #include <linux/kernel.h> > #include <linux/mailbox_client.h> > @@ -22,6 +23,7 @@ > #include <linux/reboot.h> > #include <linux/regmap.h> > #include <linux/remoteproc.h> > +#include <linux/scmi_imx_protocol.h> > #include <linux/workqueue.h> > > #include "imx_rproc.h" > @@ -92,9 +94,16 @@ struct imx_rproc_mem { > #define ATT_CORE_MASK 0xffff > #define ATT_CORE(I) BIT((I)) > > +/* Linux has permission to handle the Logical Machine of remote cores */ > +#define IMX_RPROC_FLAGS_SM_LMM_AVAIL BIT(0) > + > static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block); > static void imx_rproc_free_mbox(void *data); > > +/* Forward declarations for platform operations */ > +static const struct imx_rproc_plat_ops imx_rproc_ops_sm_lmm; > +static const struct imx_rproc_plat_ops imx_rproc_ops_sm_cpu; > + > struct imx_rproc { > struct device *dev; > struct regmap *regmap; > @@ -117,6 +126,11 @@ struct imx_rproc { > u32 core_index; > struct dev_pm_domain_list *pd_list; > const struct imx_rproc_plat_ops *ops; > + /* > + * For i.MX System Manager based systems > + * BIT 0: IMX_RPROC_FLAGS_SM_LMM_AVAIL(RPROC LM is under Linux control ) > + */ > + u32 flags; > }; > > static const struct imx_rproc_att imx_rproc_att_imx93[] = { > @@ -313,6 +327,44 @@ static int imx_rproc_scu_api_start(struct rproc *rproc) > return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, > priv->entry); > } > > +static int imx_rproc_sm_cpu_start(struct rproc *rproc) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + int ret; > + > + ret = scmi_imx_cpu_reset_vector_set(dcfg->cpuid, 0, true, false, false); > + if (ret) { > + dev_err(priv->dev, "Failed to set reset vector cpuid(%u): > %d\n", dcfg->cpuid, ret); > + return ret; > + } > + > + return scmi_imx_cpu_start(dcfg->cpuid, true); > +} > + > +static int imx_rproc_sm_lmm_start(struct rproc *rproc) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + struct device *dev = priv->dev; > + int ret; > + > + ret = scmi_imx_lmm_reset_vector_set(dcfg->lmid, dcfg->cpuid, 0, 0); > + 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_operation(dcfg->lmid, SCMI_IMX_LMM_BOOT, 0); > + if (ret) { > + dev_err(dev, "Failed to boot lmm(%d): %d\n", dcfg->lmid, ret); > + return ret; > + } > + > + return 0; > +} > + > static int imx_rproc_start(struct rproc *rproc) > { > struct imx_rproc *priv = rproc->priv; > @@ -369,6 +421,25 @@ static int imx_rproc_scu_api_stop(struct rproc *rproc) > return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, > priv->entry); > } > > +static int imx_rproc_sm_cpu_stop(struct rproc *rproc) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + > + return scmi_imx_cpu_start(dcfg->cpuid, false); > +} > + > +static int imx_rproc_sm_lmm_stop(struct rproc *rproc) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + > + if (!(priv->flags & IMX_RPROC_FLAGS_SM_LMM_AVAIL)) > + return -EACCES; > + > + return scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_SHUTDOWN, 0); > +} > + > static int imx_rproc_stop(struct rproc *rproc) > { > struct imx_rproc *priv = rproc->priv; > @@ -485,6 +556,33 @@ static int imx_rproc_mem_release(struct rproc *rproc, > return 0; > } > > +static int imx_rproc_sm_lmm_prepare(struct rproc *rproc) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + int ret; > + > + /* > + * IMX_RPROC_FLAGS_SM_LMM_AVAIL not set indicates Linux is not able > + * to start/stop rproc LM, then if rproc is not in detached state, > + * prepare should fail. If in detached state, this is in rproc_attach() > + * path. > + */ > + if (!(priv->flags & IMX_RPROC_FLAGS_SM_LMM_AVAIL)) > + return rproc->state == RPROC_DETACHED ? 0 : -EACCES; > + > + /* Power on the Logical Machine to make sure TCM is available. */ > + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_POWER_ON, 0); > + if (ret) { > + dev_err(priv->dev, "Failed to power on lmm(%d): %d\n", > dcfg->lmid, ret); > + return ret; > + } > + > + dev_info(priv->dev, "lmm(%d) powered on by Linux\n", dcfg->lmid); > + > + return 0; > +} > + > static int imx_rproc_prepare(struct rproc *rproc) > { > struct imx_rproc *priv = rproc->priv; > @@ -980,6 +1078,93 @@ static int imx_rproc_scu_api_detect_mode(struct rproc > *rproc) > return 0; > } > > +static int imx_rproc_sm_lmm_check(struct rproc *rproc, bool started) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + struct device *dev = priv->dev; > + int ret; > + > + /* > + * Use power on to do permission check. If rproc is in different LM, > + * and linux has permission to handle the LM, set > IMX_RPROC_FLAGS_SM_LMM_AVAIL. > + */ > + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_POWER_ON, 0); > + if (ret) { > + if (ret == -EACCES) { > + /* > + * rproc LM is booted before Linux and not under Linux > Control, so only > + * do IPC between RPROC and Linux, not return failure > + */ > + dev_info(dev, "lmm(%d) not under Linux Control\n", > dcfg->lmid); > + return 0; > + } > + > + dev_err(dev, "power on lmm(%d) failed: %d\n", dcfg->lmid, ret); > + return ret; > + } > + > + > + /* rproc was started before boot Linux and under control of Linux, > directly return */ > + if (started) { > + priv->flags |= IMX_RPROC_FLAGS_SM_LMM_AVAIL; > + return 0; > + } > + > + /* else shutdown the LM to save power */ > + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_SHUTDOWN, 0); > + if (ret) { > + dev_err(dev, "shutdown lmm(%d) failed: %d\n", dcfg->lmid, ret); > + return ret; > + } > + > + priv->flags |= IMX_RPROC_FLAGS_SM_LMM_AVAIL; > + > + return 0; > +} > + > +static int imx_rproc_sm_detect_mode(struct rproc *rproc) > +{ > + struct imx_rproc *priv = rproc->priv; > + const struct imx_rproc_dcfg *dcfg = priv->dcfg; > + struct device *dev = priv->dev; > + struct scmi_imx_lmm_info info; > + bool started = false; > + int ret; > + > + ret = scmi_imx_cpu_started(dcfg->cpuid, &started); > + if (ret) { > + dev_err(dev, "Failed to detect cpu(%d) status: %d\n", > dcfg->cpuid, ret); > + return ret; > + } > + > + if (started) > + priv->rproc->state = RPROC_DETACHED; > + > + /* Get current Linux Logical Machine ID */ > + ret = scmi_imx_lmm_info(LMM_ID_DISCOVER, &info); > + if (ret) { > + dev_err(dev, "Failed to get current LMM ID err: %d\n", ret); > + return ret; > + } > + > + /* > + * Check whether rproc is in the same LM as host core(running Linux) > + * If yes, use CPU protocol API to manage rproc. > + * If no, use Logical Machine API to manage rproc. > + */ > + if (dcfg->lmid == info.lmid) { > + priv->ops = &imx_rproc_ops_sm_cpu; > + dev_info(dev, "Using CPU Protocol OPS\n"); > + return 0; > + } > + > + priv->ops = &imx_rproc_ops_sm_lmm; > + dev_info(dev, "Using LMM Protocol OPS\n"); > + > + return imx_rproc_sm_lmm_check(rproc, started); > +} > + > static int imx_rproc_detect_mode(struct imx_rproc *priv) > { > /* > @@ -1155,6 +1340,19 @@ static const struct imx_rproc_plat_ops > imx_rproc_ops_scu_api = { > .detect_mode = imx_rproc_scu_api_detect_mode, > }; > > +static const struct imx_rproc_plat_ops imx_rproc_ops_sm_lmm = { > + .detect_mode = imx_rproc_sm_detect_mode, > + .prepare = imx_rproc_sm_lmm_prepare, > + .start = imx_rproc_sm_lmm_start, > + .stop = imx_rproc_sm_lmm_stop, > +}; > + > +static const struct imx_rproc_plat_ops imx_rproc_ops_sm_cpu = { > + .detect_mode = imx_rproc_sm_detect_mode, > + .start = imx_rproc_sm_cpu_start, > + .stop = imx_rproc_sm_cpu_stop, > +}; > + > static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = { > .src_reg = IMX7D_SRC_SCR, > .src_mask = IMX7D_M4_RST_MASK, > diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h > index > 37417568a0ade2ae4d6a4e3d0f139ea52b185254..d37e6f90548cec727b4aeb874680b42af85bdbb4 > 100644 > --- a/drivers/remoteproc/imx_rproc.h > +++ b/drivers/remoteproc/imx_rproc.h > @@ -38,6 +38,9 @@ struct imx_rproc_dcfg { > size_t att_size; > u32 flags; > const struct imx_rproc_plat_ops *ops; > + /* For System Manager(SM) based SoCs */ > + u32 cpuid; /* ID of the remote core */ > + u32 lmid; /* ID of the Logcial Machine */ > }; > > #endif /* _IMX_RPROC_H */ > > -- > 2.37.1 >

