[PATCH v2 2/5] drivers: remoteproc: imx: add support for Cortex-A Core

Jiafei Pan
2026-04-29T03:08:31Z

Add Cortex-A Core remoteproc support, it use PSCI and SIP SMC call
to manage Cortex-A Core to be on or off.

Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>
---
drivers/remoteproc/imx_rproc.c | 134 +++++++++++++++++++++++++++++++++
drivers/remoteproc/imx_rproc.h | 2 +
2 files changed, 136 insertions(+)

diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 0dd80e688b0e..8a3de27c96b7 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -6,6 +6,7 @@
#include <dt-bindings/firmware/imx/rsrc.h>
#include <linux/arm-smccc.h>
#include <linux/clk.h>
+#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/firmware/imx/sci.h>
#include <linux/firmware/imx/sm.h>
@@ -20,12 +21,17 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/psci.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/remoteproc.h>
#include <linux/scmi_imx_protocol.h>
#include <linux/workqueue.h>

+#include <uapi/linux/psci.h>
+
+#include <asm/smp_plat.h>
+
#include "imx_rproc.h"
#include "remoteproc_internal.h"

@@ -72,6 +78,8 @@
#define IMX_SIP_RPROC_STARTED 0x01
#define IMX_SIP_RPROC_STOP 0x02

+#define IMX_SIP_CPU_OFF 0xC2000012
+
#define IMX_SC_IRQ_GROUP_REBOOTED 5

/**
@@ -131,6 +139,9 @@ struct imx_rproc {
* BIT 0: IMX_RPROC_FLAGS_SM_LMM_CTRL(RPROC LM is under Linux control )
*/
u32 flags;
+ /* used by Cortex-A Core remoteproc to manage all CPU Cores */
+ cpumask_t cpus;
+ cpumask_t offlined_cpus;
};

static const struct imx_rproc_att imx_rproc_att_imx95_m7[] = {
@@ -381,6 +392,45 @@ static int imx_rproc_sm_lmm_start(struct rproc *rproc)
return 0;
}

+static int imx_rproc_psci_start(struct rproc *rproc)
+{
+ struct imx_rproc *priv = rproc->priv;
+ struct device *dev = priv->dev;
+ unsigned int cpu;
+ int ret;
+
+ if (cpumask_empty(&priv->cpus)) {
+ dev_err(dev, "No CPU Core assigned!\n");
+ return -ENODEV;
+ }
+
+ for_each_cpu(cpu, &priv->cpus) {
+ if (cpu_online(cpu)) {
+ ret = remove_cpu(cpu);
+ if (ret)
+ goto err;
+ cpumask_set_cpu(cpu, &priv->offlined_cpus);
+ }
+ }
+
+ cpu = cpumask_first(&priv->cpus);
+ ret = psci_ops.cpu_on(cpu_logical_map(cpu), rproc->bootaddr);
+ if (ret) {
+ dev_err(dev, "Boot failed on CPU Core %d\n", cpu);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ for_each_cpu(cpu, &priv->cpus) {
+ if (!cpu_online(cpu) && add_cpu(cpu) == 0)
+ cpumask_clear_cpu(cpu, &priv->offlined_cpus);
+ }
+
+ return ret;
+}
+
static int imx_rproc_start(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
@@ -456,6 +506,50 @@ static int imx_rproc_sm_lmm_stop(struct rproc *rproc)
return scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_SHUTDOWN, 0);
}

+static int imx_rproc_psci_stop(struct rproc *rproc)
+{
+ struct imx_rproc *priv = rproc->priv;
+ struct device *dev = priv->dev;
+ struct arm_smccc_res res;
+ unsigned int cpu;
+ unsigned long start, end;
+ int err;
+
+ for_each_cpu(cpu, &priv->cpus) {
+ /* Check CPU status */
+ err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+ if (err == PSCI_0_2_AFFINITY_LEVEL_OFF)
+ continue;
+
+ /* Bring CPU to be off */
+ arm_smccc_smc(IMX_SIP_CPU_OFF, cpu, 0,
+ 0, 0, 0, 0, 0, &res);
+ start = jiffies;
+ end = start + msecs_to_jiffies(100);
+ do {
+ err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
+ if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
+ pr_info("CPU%d is killed (polled %d ms)\n", cpu,
+ jiffies_to_msecs(jiffies - start));
+ break;
+ }
+
+ usleep_range(100, 1000);
+ } while (time_before(jiffies, end));
+ }
+
+ /* Return back freed CPU Core to Linux kernel */
+ for_each_cpu(cpu, &priv->cpus) {
+ if (cpumask_test_cpu(cpu, &priv->offlined_cpus)) {
+ if (add_cpu(cpu) != 0)
+ dev_err(dev, "Failed to bring CPU %d back to be online", cpu);
+ cpumask_clear_cpu(cpu, &priv->offlined_cpus);
+ }
+ }
+
+ return 0;
+}
+
static int imx_rproc_stop(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
@@ -480,6 +574,12 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
int i;

+ /* No need to translate for Cortex-A Core */
+ if (dcfg->flags & IMX_RPROC_NO_ADDR_TRANS) {
+ *sys = da;
+ return 0;
+ }
+
/* parse address translation table */
for (i = 0; i < dcfg->att_size; i++) {
const struct imx_rproc_att *att = &dcfg->att[i];
@@ -1184,6 +1284,13 @@ static int imx_rproc_sm_detect_mode(struct rproc *rproc)
return imx_rproc_sm_lmm_check(rproc, started);
}

+static int imx_rproc_psci_detect_mode(struct rproc *rproc)
+{
+ rproc->state = RPROC_OFFLINE;
+
+ return 0;
+}
+
static int imx_rproc_detect_mode(struct imx_rproc *priv)
{
/*
@@ -1228,6 +1335,8 @@ static int imx_rproc_probe(struct platform_device *pdev)
struct imx_rproc *priv;
struct rproc *rproc;
const struct imx_rproc_dcfg *dcfg;
+ unsigned int cpus;
+ unsigned long cpus_bits;
int ret;

/* set some other name then imx */
@@ -1274,6 +1383,17 @@ static int imx_rproc_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "failed on imx_rproc_addr_init\n");

+ ret = of_property_read_u32(dev->of_node, "fsl,cpus-bits", &cpus);
+ if (ret) {
+ cpumask_clear(&priv->cpus);
+ } else {
+ cpus_bits = cpus;
+ bitmap_copy(cpumask_bits(&priv->cpus), &cpus_bits,
+ min((unsigned int)nr_cpumask_bits,
+ (unsigned int)sizeof(unsigned long)));
+ rproc->auto_boot = false;
+ }
+
ret = imx_rproc_detect_mode(priv);
if (ret)
return dev_err_probe(dev, ret, "failed on detect mode\n");
@@ -1372,6 +1492,12 @@ static const struct imx_rproc_plat_ops imx_rproc_ops_sm_cpu = {
.stop = imx_rproc_sm_cpu_stop,
};

+static const struct imx_rproc_plat_ops imx_rproc_ops_psci = {
+ .start = imx_rproc_psci_start,
+ .stop = imx_rproc_psci_stop,
+ .detect_mode = imx_rproc_psci_detect_mode,
+};
+
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = {
.src_reg = IMX7D_SRC_SCR,
.src_mask = IMX7D_M4_RST_MASK,
@@ -1464,6 +1590,13 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = {
.lmid = 1, /* Use 1 as Logical Machine ID where M7 resides */
};

+static const struct imx_rproc_dcfg imx_rproc_cfg_psci = {
+ .att = NULL,
+ .att_size = 0,
+ .ops = &imx_rproc_ops_psci,
+ .flags = IMX_RPROC_NO_ADDR_TRANS,
+};
+
static const struct of_device_id imx_rproc_of_match[] = {
{ .compatible = "fsl,imx7ulp-cm4", .data = &imx_rproc_cfg_imx7ulp },
{ .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d },
@@ -1479,6 +1612,7 @@ static const struct of_device_id imx_rproc_of_match[] = {
{ .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp },
{ .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 },
{ .compatible = "fsl,imx95-cm7", .data = &imx_rproc_cfg_imx95_m7 },
+ { .compatible = "fsl,imx-rproc-psci", .data = &imx_rproc_cfg_psci },
{},
};
MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h
index d37e6f90548c..be6709971042 100644
--- a/drivers/remoteproc/imx_rproc.h
+++ b/drivers/remoteproc/imx_rproc.h
@@ -18,6 +18,8 @@ struct imx_rproc_att {
/* dcfg flags */
#define IMX_RPROC_NEED_SYSTEM_OFF BIT(0)
#define IMX_RPROC_NEED_CLKS BIT(1)
+/* No need address translation */
+#define IMX_RPROC_NO_ADDR_TRANS BIT(2)

struct imx_rproc_plat_ops {
int (*start)(struct rproc *rproc);
--
2.43.0