#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>
#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"
#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;
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[] = {
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;
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;
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;
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)
{
/*
.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,