diff options
| -rw-r--r-- | drivers/remoteproc/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/remoteproc/imx_rproc.c | 166 | ||||
| -rw-r--r-- | drivers/remoteproc/imx_rproc.h | 3 |
3 files changed, 171 insertions, 0 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 48a0d3a69ed0..ee54436fea5a 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 b0857a1a9660..b254045d45ea 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,15 @@ 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_CTRL 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; + struct imx_rproc { struct device *dev; struct regmap *regmap; @@ -117,6 +125,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_CTRL(RPROC LM is under Linux control ) + */ + u32 flags; }; static const struct imx_rproc_att imx_rproc_att_imx93[] = { @@ -313,6 +326,33 @@ 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_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; + + /* + * If the remoteproc core can't start the M7, it will already be + * handled in imx_rproc_sm_lmm_prepare(). + */ + 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 +409,17 @@ 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_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_CTRL)) + 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 +536,36 @@ 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_CTRL not set indicates Linux is not able + * to start/stop M7, then if rproc is not in detached state, + * prepare should fail. If in detached state, this is in rproc_attach() + * path. + */ + if (rproc->state == RPROC_DETACHED) + return 0; + + if (!(priv->flags & IMX_RPROC_FLAGS_SM_LMM_CTRL)) + return -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 +1061,84 @@ static int imx_rproc_scu_api_detect_mode(struct rproc *rproc) return 0; } +/* Check whether remoteproc core is responsible for M7 lifecycle */ +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; + + ret = scmi_imx_lmm_operation(dcfg->lmid, SCMI_IMX_LMM_POWER_ON, 0); + if (ret) { + if (ret == -EACCES) { + /* + * M7 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; + } + + /* Shutdown remote processor if not started */ + if (!started) { + 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_CTRL; + + 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 M7 is in the same LM as host core(running Linux) + * If yes, use CPU protocol API to manage M7. + * If no, use Logical Machine API to manage M7. + */ + if (dcfg->lmid == info.lmid) { + dev_err(dev, "CPU Protocol OPS is not supported\n"); + return -EOPNOTSUPP; + } + + 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 +1314,13 @@ 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_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 37417568a0ad..d37e6f90548c 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 */ |
