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 <[email protected]> --- 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

