Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

remoteproc: imx_rproc: Add support for System Manager LMM API

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 is not same as M7 LM ID(linux and M7 in same LM), use
LMM protocol to start/stop. CPU protocol support will be added in the
following patch. 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 <daniel.baluta@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20260109-imx95-rproc-2026-1-8-v6-4-d2fefb36263d@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>

authored by

Peng Fan and committed by
Mathieu Poirier
d8ab94fa edd2a995

+171
+2
drivers/remoteproc/Kconfig
··· 27 27 tristate "i.MX remoteproc support" 28 28 depends on ARCH_MXC 29 29 depends on HAVE_ARM_SMCCC 30 + depends on IMX_SCMI_CPU_DRV || !IMX_SCMI_CPU_DRV 31 + depends on IMX_SCMI_LMM_DRV || !IMX_SCMI_LMM_DRV 30 32 select MAILBOX 31 33 help 32 34 Say y here to support iMX's remote processors via the remote
+166
drivers/remoteproc/imx_rproc.c
··· 8 8 #include <linux/clk.h> 9 9 #include <linux/err.h> 10 10 #include <linux/firmware/imx/sci.h> 11 + #include <linux/firmware/imx/sm.h> 11 12 #include <linux/interrupt.h> 12 13 #include <linux/kernel.h> 13 14 #include <linux/mailbox_client.h> ··· 23 22 #include <linux/reboot.h> 24 23 #include <linux/regmap.h> 25 24 #include <linux/remoteproc.h> 25 + #include <linux/scmi_imx_protocol.h> 26 26 #include <linux/workqueue.h> 27 27 28 28 #include "imx_rproc.h" ··· 94 92 #define ATT_CORE_MASK 0xffff 95 93 #define ATT_CORE(I) BIT((I)) 96 94 95 + /* Linux has permission to handle the Logical Machine of remote cores */ 96 + #define IMX_RPROC_FLAGS_SM_LMM_CTRL BIT(0) 97 + 97 98 static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block); 98 99 static void imx_rproc_free_mbox(void *data); 100 + 101 + /* Forward declarations for platform operations */ 102 + static const struct imx_rproc_plat_ops imx_rproc_ops_sm_lmm; 99 103 100 104 struct imx_rproc { 101 105 struct device *dev; ··· 125 117 u32 core_index; 126 118 struct dev_pm_domain_list *pd_list; 127 119 const struct imx_rproc_plat_ops *ops; 120 + /* 121 + * For i.MX System Manager based systems 122 + * BIT 0: IMX_RPROC_FLAGS_SM_LMM_CTRL(RPROC LM is under Linux control ) 123 + */ 124 + u32 flags; 128 125 }; 129 126 130 127 static const struct imx_rproc_att imx_rproc_att_imx93[] = { ··· 326 313 return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); 327 314 } 328 315 316 + static int imx_rproc_sm_lmm_start(struct rproc *rproc) 317 + { 318 + struct imx_rproc *priv = rproc->priv; 319 + const struct imx_rproc_dcfg *dcfg = priv->dcfg; 320 + struct device *dev = priv->dev; 321 + int ret; 322 + 323 + /* 324 + * If the remoteproc core can't start the M7, it will already be 325 + * handled in imx_rproc_sm_lmm_prepare(). 326 + */ 327 + ret = scmi_imx_lmm_reset_vector_set(dcfg->lmid, dcfg->cpuid, 0, 0); 328 + if (ret) { 329 + dev_err(dev, "Failed to set reset vector lmid(%u), cpuid(%u): %d\n", 330 + dcfg->lmid, dcfg->cpuid, ret); 331 + return ret; 332 + } 333 + 334 + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_BOOT, 0); 335 + if (ret) { 336 + dev_err(dev, "Failed to boot lmm(%d): %d\n", dcfg->lmid, ret); 337 + return ret; 338 + } 339 + 340 + return 0; 341 + } 342 + 329 343 static int imx_rproc_start(struct rproc *rproc) 330 344 { 331 345 struct imx_rproc *priv = rproc->priv; ··· 407 367 struct imx_rproc *priv = rproc->priv; 408 368 409 369 return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, priv->entry); 370 + } 371 + 372 + static int imx_rproc_sm_lmm_stop(struct rproc *rproc) 373 + { 374 + struct imx_rproc *priv = rproc->priv; 375 + const struct imx_rproc_dcfg *dcfg = priv->dcfg; 376 + 377 + if (!(priv->flags & IMX_RPROC_FLAGS_SM_LMM_CTRL)) 378 + return -EACCES; 379 + 380 + return scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_SHUTDOWN, 0); 410 381 } 411 382 412 383 static int imx_rproc_stop(struct rproc *rproc) ··· 532 481 { 533 482 dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma); 534 483 iounmap(mem->va); 484 + 485 + return 0; 486 + } 487 + 488 + static int imx_rproc_sm_lmm_prepare(struct rproc *rproc) 489 + { 490 + struct imx_rproc *priv = rproc->priv; 491 + const struct imx_rproc_dcfg *dcfg = priv->dcfg; 492 + int ret; 493 + 494 + /* 495 + * IMX_RPROC_FLAGS_SM_LMM_CTRL not set indicates Linux is not able 496 + * to start/stop M7, then if rproc is not in detached state, 497 + * prepare should fail. If in detached state, this is in rproc_attach() 498 + * path. 499 + */ 500 + if (rproc->state == RPROC_DETACHED) 501 + return 0; 502 + 503 + if (!(priv->flags & IMX_RPROC_FLAGS_SM_LMM_CTRL)) 504 + return -EACCES; 505 + 506 + /* Power on the Logical Machine to make sure TCM is available. */ 507 + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_POWER_ON, 0); 508 + if (ret) { 509 + dev_err(priv->dev, "Failed to power on lmm(%d): %d\n", dcfg->lmid, ret); 510 + return ret; 511 + } 512 + 513 + dev_info(priv->dev, "lmm(%d) powered on by Linux\n", dcfg->lmid); 535 514 536 515 return 0; 537 516 } ··· 1061 980 return 0; 1062 981 } 1063 982 983 + /* Check whether remoteproc core is responsible for M7 lifecycle */ 984 + static int imx_rproc_sm_lmm_check(struct rproc *rproc, bool started) 985 + { 986 + struct imx_rproc *priv = rproc->priv; 987 + const struct imx_rproc_dcfg *dcfg = priv->dcfg; 988 + struct device *dev = priv->dev; 989 + int ret; 990 + 991 + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_POWER_ON, 0); 992 + if (ret) { 993 + if (ret == -EACCES) { 994 + /* 995 + * M7 is booted before Linux and not under Linux Control, so only 996 + * do IPC between RPROC and Linux, not return failure 997 + */ 998 + dev_info(dev, "lmm(%d) not under Linux Control\n", dcfg->lmid); 999 + return 0; 1000 + } 1001 + 1002 + dev_err(dev, "power on lmm(%d) failed: %d\n", dcfg->lmid, ret); 1003 + return ret; 1004 + } 1005 + 1006 + /* Shutdown remote processor if not started */ 1007 + if (!started) { 1008 + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_SHUTDOWN, 0); 1009 + if (ret) { 1010 + dev_err(dev, "shutdown lmm(%d) failed: %d\n", dcfg->lmid, ret); 1011 + return ret; 1012 + } 1013 + } 1014 + 1015 + priv->flags |= IMX_RPROC_FLAGS_SM_LMM_CTRL; 1016 + 1017 + return 0; 1018 + } 1019 + 1020 + static int imx_rproc_sm_detect_mode(struct rproc *rproc) 1021 + { 1022 + struct imx_rproc *priv = rproc->priv; 1023 + const struct imx_rproc_dcfg *dcfg = priv->dcfg; 1024 + struct device *dev = priv->dev; 1025 + struct scmi_imx_lmm_info info; 1026 + bool started = false; 1027 + int ret; 1028 + 1029 + ret = scmi_imx_cpu_started(dcfg->cpuid, &started); 1030 + if (ret) { 1031 + dev_err(dev, "Failed to detect cpu(%d) status: %d\n", dcfg->cpuid, ret); 1032 + return ret; 1033 + } 1034 + 1035 + if (started) 1036 + priv->rproc->state = RPROC_DETACHED; 1037 + 1038 + /* Get current Linux Logical Machine ID */ 1039 + ret = scmi_imx_lmm_info(LMM_ID_DISCOVER, &info); 1040 + if (ret) { 1041 + dev_err(dev, "Failed to get current LMM ID err: %d\n", ret); 1042 + return ret; 1043 + } 1044 + 1045 + /* 1046 + * Check whether M7 is in the same LM as host core(running Linux) 1047 + * If yes, use CPU protocol API to manage M7. 1048 + * If no, use Logical Machine API to manage M7. 1049 + */ 1050 + if (dcfg->lmid == info.lmid) { 1051 + dev_err(dev, "CPU Protocol OPS is not supported\n"); 1052 + return -EOPNOTSUPP; 1053 + } 1054 + 1055 + priv->ops = &imx_rproc_ops_sm_lmm; 1056 + dev_info(dev, "Using LMM Protocol OPS\n"); 1057 + 1058 + return imx_rproc_sm_lmm_check(rproc, started); 1059 + } 1060 + 1064 1061 static int imx_rproc_detect_mode(struct imx_rproc *priv) 1065 1062 { 1066 1063 /* ··· 1312 1153 .stop = imx_rproc_scu_api_stop, 1313 1154 .detach = imx_rproc_scu_api_detach, 1314 1155 .detect_mode = imx_rproc_scu_api_detect_mode, 1156 + }; 1157 + 1158 + static const struct imx_rproc_plat_ops imx_rproc_ops_sm_lmm = { 1159 + .detect_mode = imx_rproc_sm_detect_mode, 1160 + .prepare = imx_rproc_sm_lmm_prepare, 1161 + .start = imx_rproc_sm_lmm_start, 1162 + .stop = imx_rproc_sm_lmm_stop, 1315 1163 }; 1316 1164 1317 1165 static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = {
+3
drivers/remoteproc/imx_rproc.h
··· 38 38 size_t att_size; 39 39 u32 flags; 40 40 const struct imx_rproc_plat_ops *ops; 41 + /* For System Manager(SM) based SoCs */ 42 + u32 cpuid; /* ID of the remote core */ 43 + u32 lmid; /* ID of the Logcial Machine */ 41 44 }; 42 45 43 46 #endif /* _IMX_RPROC_H */