summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/remoteproc/Kconfig2
-rw-r--r--drivers/remoteproc/imx_rproc.c166
-rw-r--r--drivers/remoteproc/imx_rproc.h3
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 */