Files
linux/drivers/remoteproc/imx_rproc.c
Jiafei Pan 2e7a8ad798 remoteproc: imx_rproc: Use device node name as processor name
As currently there are maybe multiple remote processors, so change from
using fixed name to using device node name as remote processor name in
order to make them can be distinguished by through of name in sys
filesystem.

Signed-off-by: Jiafei Pan <Jiafei.Pan@nxp.com>
Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Link: https://lore.kernel.org/r/20260508032016.27716-1-Jiafei.Pan@nxp.com
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
2026-05-26 08:33:43 -06:00

1587 lines
43 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
*/
#include <dt-bindings/firmware/imx/rsrc.h>
#include <linux/arm-smccc.h>
#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/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.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"
#include "remoteproc_internal.h"
#define IMX7D_SRC_SCR 0x0C
#define IMX7D_ENABLE_M4 BIT(3)
#define IMX7D_SW_M4P_RST BIT(2)
#define IMX7D_SW_M4C_RST BIT(1)
#define IMX7D_SW_M4C_NON_SCLR_RST BIT(0)
#define IMX7D_M4_RST_MASK (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
| IMX7D_SW_M4C_RST \
| IMX7D_SW_M4C_NON_SCLR_RST)
#define IMX7D_M4_START (IMX7D_ENABLE_M4 | IMX7D_SW_M4P_RST \
| IMX7D_SW_M4C_RST)
#define IMX7D_M4_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST | \
IMX7D_SW_M4C_NON_SCLR_RST)
#define IMX8M_M7_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST)
#define IMX8M_M7_POLL IMX7D_ENABLE_M4
#define IMX8M_GPR22 0x58
#define IMX8M_GPR22_CM7_CPUWAIT BIT(0)
/* Address: 0x020D8000 */
#define IMX6SX_SRC_SCR 0x00
#define IMX6SX_ENABLE_M4 BIT(22)
#define IMX6SX_SW_M4P_RST BIT(12)
#define IMX6SX_SW_M4C_NON_SCLR_RST BIT(4)
#define IMX6SX_SW_M4C_RST BIT(3)
#define IMX6SX_M4_START (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
| IMX6SX_SW_M4C_RST)
#define IMX6SX_M4_STOP (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4C_RST | \
IMX6SX_SW_M4C_NON_SCLR_RST)
#define IMX6SX_M4_RST_MASK (IMX6SX_ENABLE_M4 | IMX6SX_SW_M4P_RST \
| IMX6SX_SW_M4C_NON_SCLR_RST \
| IMX6SX_SW_M4C_RST)
#define IMX_RPROC_MEM_MAX 32
#define IMX_SIP_RPROC 0xC2000005
#define IMX_SIP_RPROC_START 0x00
#define IMX_SIP_RPROC_STARTED 0x01
#define IMX_SIP_RPROC_STOP 0x02
#define IMX_SC_IRQ_GROUP_REBOOTED 5
/**
* struct imx_rproc_mem - slim internal memory structure
* @cpu_addr: MPU virtual address of the memory region
* @sys_addr: Bus address used to access the memory region
* @size: Size of the memory region
*/
struct imx_rproc_mem {
void __iomem *cpu_addr;
phys_addr_t sys_addr;
size_t size;
};
/* att flags: lower 16 bits specifying core, higher 16 bits for flags */
/* M4 own area. Can be mapped at probe */
#define ATT_OWN BIT(31)
#define ATT_IOMEM BIT(30)
#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;
static const struct imx_rproc_plat_ops imx_rproc_ops_sm_cpu;
struct imx_rproc {
struct device *dev;
struct regmap *regmap;
struct regmap *gpr;
struct rproc *rproc;
const struct imx_rproc_dcfg *dcfg;
struct imx_rproc_mem mem[IMX_RPROC_MEM_MAX];
struct clk *clk;
struct mbox_client cl;
struct mbox_chan *tx_ch;
struct mbox_chan *rx_ch;
struct work_struct rproc_work;
struct workqueue_struct *workqueue;
void __iomem *rsc_table;
struct imx_sc_ipc *ipc_handle;
struct notifier_block rproc_nb;
u32 rproc_pt; /* partition id */
u32 rsrc_id; /* resource id */
u32 entry; /* cpu start address */
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_imx95_m7[] = {
/* dev addr , sys addr , size , flags */
/* TCM CODE NON-SECURE */
{ 0x00000000, 0x203C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM SYS NON-SECURE*/
{ 0x20000000, 0x20400000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* DDR */
{ 0x80000000, 0x80000000, 0x50000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx94_m70[] = {
/* dev addr , sys addr , size , flags */
/* TCM CODE NON-SECURE */
{ 0x00000000, 0x203C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM SYS NON-SECURE*/
{ 0x20000000, 0x20400000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* DDR */
{ 0x80000000, 0x80000000, 0x10000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx94_m71[] = {
/* dev addr , sys addr , size , flags */
/* TCM CODE NON-SECURE */
{ 0x00000000, 0x202C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM SYS NON-SECURE*/
{ 0x20000000, 0x20300000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* DDR */
{ 0x80000000, 0x80000000, 0x10000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx94_m33s[] = {
/* dev addr , sys addr , size , flags */
/* TCM CODE NON-SECURE */
{ 0x0FFC0000, 0x209C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM SYS NON-SECURE */
{ 0x20000000, 0x20A00000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* M33S OCRAM NON-SECURE */
{ 0x20800000, 0x20800000, 0x180000, ATT_OWN | ATT_IOMEM },
/* DDR */
{ 0x80000000, 0x80000000, 0x10000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx93[] = {
/* dev addr , sys addr , size , flags */
/* TCM CODE NON-SECURE */
{ 0x0FFC0000, 0x201C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM CODE SECURE */
{ 0x1FFC0000, 0x201C0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM SYS NON-SECURE*/
{ 0x20000000, 0x20200000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* TCM SYS SECURE*/
{ 0x30000000, 0x20200000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* DDR */
{ 0x80000000, 0x80000000, 0x10000000, 0 },
{ 0x90000000, 0x80000000, 0x10000000, 0 },
{ 0xC0000000, 0xC0000000, 0x10000000, 0 },
{ 0xD0000000, 0xC0000000, 0x10000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx8qm[] = {
/* dev addr , sys addr , size , flags */
{ 0x08000000, 0x08000000, 0x10000000, 0},
/* TCML */
{ 0x1FFE0000, 0x34FE0000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(0)},
{ 0x1FFE0000, 0x38FE0000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(1)},
/* TCMU */
{ 0x20000000, 0x35000000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(0)},
{ 0x20000000, 0x39000000, 0x00020000, ATT_OWN | ATT_IOMEM | ATT_CORE(1)},
/* DDR (Data) */
{ 0x80000000, 0x80000000, 0x60000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx8qxp[] = {
{ 0x08000000, 0x08000000, 0x10000000, 0 },
/* TCML/U */
{ 0x1FFE0000, 0x34FE0000, 0x00040000, ATT_OWN | ATT_IOMEM },
/* OCRAM(Low 96KB) */
{ 0x21000000, 0x00100000, 0x00018000, 0 },
/* OCRAM */
{ 0x21100000, 0x00100000, 0x00040000, 0 },
/* DDR (Data) */
{ 0x80000000, 0x80000000, 0x60000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx8mn[] = {
/* dev addr , sys addr , size , flags */
/* ITCM */
{ 0x00000000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM },
/* OCRAM_S */
{ 0x00180000, 0x00180000, 0x00009000, 0 },
/* OCRAM */
{ 0x00900000, 0x00900000, 0x00020000, 0 },
/* OCRAM */
{ 0x00920000, 0x00920000, 0x00020000, 0 },
/* OCRAM */
{ 0x00940000, 0x00940000, 0x00050000, 0 },
/* QSPI Code - alias */
{ 0x08000000, 0x08000000, 0x08000000, 0 },
/* DDR (Code) - alias */
{ 0x10000000, 0x40000000, 0x0FFE0000, 0 },
/* DTCM */
{ 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM },
/* OCRAM_S - alias */
{ 0x20180000, 0x00180000, 0x00008000, ATT_OWN },
/* OCRAM */
{ 0x20200000, 0x00900000, 0x00020000, ATT_OWN },
/* OCRAM */
{ 0x20220000, 0x00920000, 0x00020000, ATT_OWN },
/* OCRAM */
{ 0x20240000, 0x00940000, 0x00040000, ATT_OWN },
/* DDR (Data) */
{ 0x40000000, 0x40000000, 0x80000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx8mq[] = {
/* dev addr , sys addr , size , flags */
/* TCML - alias */
{ 0x00000000, 0x007e0000, 0x00020000, ATT_IOMEM},
/* OCRAM_S */
{ 0x00180000, 0x00180000, 0x00008000, 0 },
/* OCRAM */
{ 0x00900000, 0x00900000, 0x00020000, 0 },
/* OCRAM */
{ 0x00920000, 0x00920000, 0x00020000, 0 },
/* QSPI Code - alias */
{ 0x08000000, 0x08000000, 0x08000000, 0 },
/* DDR (Code) - alias */
{ 0x10000000, 0x40000000, 0x0FFE0000, 0 },
/* TCML/U */
{ 0x1FFE0000, 0x007E0000, 0x00040000, ATT_OWN | ATT_IOMEM},
/* OCRAM_S */
{ 0x20180000, 0x00180000, 0x00008000, ATT_OWN },
/* OCRAM */
{ 0x20200000, 0x00900000, 0x00020000, ATT_OWN },
/* OCRAM */
{ 0x20220000, 0x00920000, 0x00020000, ATT_OWN },
/* DDR (Data) */
{ 0x40000000, 0x40000000, 0x80000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx8ulp[] = {
{0x1FFC0000, 0x1FFC0000, 0xC0000, ATT_OWN},
{0x21000000, 0x21000000, 0x10000, ATT_OWN},
{0x80000000, 0x80000000, 0x60000000, 0}
};
static const struct imx_rproc_att imx_rproc_att_imx7ulp[] = {
{0x1FFD0000, 0x1FFD0000, 0x30000, ATT_OWN},
{0x20000000, 0x20000000, 0x10000, ATT_OWN},
{0x2F000000, 0x2F000000, 0x20000, ATT_OWN},
{0x2F020000, 0x2F020000, 0x20000, ATT_OWN},
{0x60000000, 0x60000000, 0x40000000, 0}
};
static const struct imx_rproc_att imx_rproc_att_imx7d[] = {
/* dev addr , sys addr , size , flags */
/* OCRAM_S (M4 Boot code) - alias */
{ 0x00000000, 0x00180000, 0x00008000, 0 },
/* OCRAM_S (Code) */
{ 0x00180000, 0x00180000, 0x00008000, ATT_OWN },
/* OCRAM (Code) - alias */
{ 0x00900000, 0x00900000, 0x00020000, 0 },
/* OCRAM_EPDC (Code) - alias */
{ 0x00920000, 0x00920000, 0x00020000, 0 },
/* OCRAM_PXP (Code) - alias */
{ 0x00940000, 0x00940000, 0x00008000, 0 },
/* TCML (Code) */
{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN | ATT_IOMEM },
/* DDR (Code) - alias, first part of DDR (Data) */
{ 0x10000000, 0x80000000, 0x0FFF0000, 0 },
/* TCMU (Data) */
{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN | ATT_IOMEM },
/* OCRAM (Data) */
{ 0x20200000, 0x00900000, 0x00020000, 0 },
/* OCRAM_EPDC (Data) */
{ 0x20220000, 0x00920000, 0x00020000, 0 },
/* OCRAM_PXP (Data) */
{ 0x20240000, 0x00940000, 0x00008000, 0 },
/* DDR (Data) */
{ 0x80000000, 0x80000000, 0x60000000, 0 },
};
static const struct imx_rproc_att imx_rproc_att_imx6sx[] = {
/* dev addr , sys addr , size , flags */
/* TCML (M4 Boot Code) - alias */
{ 0x00000000, 0x007F8000, 0x00008000, ATT_IOMEM },
/* OCRAM_S (Code) */
{ 0x00180000, 0x008F8000, 0x00004000, 0 },
/* OCRAM_S (Code) - alias */
{ 0x00180000, 0x008FC000, 0x00004000, 0 },
/* TCML (Code) */
{ 0x1FFF8000, 0x007F8000, 0x00008000, ATT_OWN | ATT_IOMEM },
/* DDR (Code) - alias, first part of DDR (Data) */
{ 0x10000000, 0x80000000, 0x0FFF8000, 0 },
/* TCMU (Data) */
{ 0x20000000, 0x00800000, 0x00008000, ATT_OWN | ATT_IOMEM },
/* OCRAM_S (Data) - alias? */
{ 0x208F8000, 0x008F8000, 0x00004000, 0 },
/* DDR (Data) */
{ 0x80000000, 0x80000000, 0x60000000, 0 },
};
static int imx_rproc_arm_smc_start(struct rproc *rproc)
{
struct arm_smccc_res res;
arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_START, 0, 0, 0, 0, 0, 0, &res);
return res.a0;
}
static int imx_rproc_mmio_start(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
if (priv->gpr)
return regmap_clear_bits(priv->gpr, dcfg->gpr_reg, dcfg->gpr_wait);
return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_start);
}
static int imx_rproc_scu_api_start(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry);
}
static u64 imx_rproc_sm_get_reset_vector(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
u32 reset_vector_mask = priv->dcfg->reset_vector_mask ?: GENMASK(31, 0);
/*
* The hardware fetches the first two words from reset_vectors
* (hardware reset address) and populates SP and PC using the first
* two words. Execution proceeds from PC. The ELF entry point does
* not always match the hardware reset address.
* To derive the correct hardware reset address, the lower address
* bits must be masked off before programming the reset vector.
*/
return rproc->bootaddr & reset_vector_mask;
}
static int imx_rproc_sm_cpu_start(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
u64 reset_vector;
int ret;
reset_vector = imx_rproc_sm_get_reset_vector(rproc);
ret = scmi_imx_cpu_reset_vector_set(dcfg->cpuid, reset_vector, true, false, false);
if (ret) {
dev_err(priv->dev, "Failed to set reset vector cpuid(%u): %d\n", dcfg->cpuid, ret);
return ret;
}
return scmi_imx_cpu_start(dcfg->cpuid, true);
}
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;
u64 reset_vector;
int ret;
reset_vector = imx_rproc_sm_get_reset_vector(rproc);
/*
* 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, reset_vector);
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;
struct device *dev = priv->dev;
int ret;
ret = imx_rproc_xtr_mbox_init(rproc, true);
if (ret)
return ret;
if (!priv->ops || !priv->ops->start)
return -EOPNOTSUPP;
ret = priv->ops->start(rproc);
if (ret)
dev_err(dev, "Failed to enable remote core!\n");
return ret;
}
static int imx_rproc_arm_smc_stop(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
struct arm_smccc_res res;
arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STOP, 0, 0, 0, 0, 0, 0, &res);
if (res.a1)
dev_info(priv->dev, "Not in wfi, force stopped\n");
return res.a0;
}
static int imx_rproc_mmio_stop(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
int ret;
if (priv->gpr) {
ret = regmap_set_bits(priv->gpr, dcfg->gpr_reg, dcfg->gpr_wait);
if (ret) {
dev_err(priv->dev, "Failed to quiescence M4 platform!\n");
return ret;
}
}
return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_stop);
}
static int imx_rproc_scu_api_stop(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_cpu_stop(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
return scmi_imx_cpu_start(dcfg->cpuid, false);
}
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;
struct device *dev = priv->dev;
int ret;
if (!priv->ops || !priv->ops->stop)
return -EOPNOTSUPP;
ret = priv->ops->stop(rproc);
if (ret)
dev_err(dev, "Failed to stop remote core\n");
else
imx_rproc_free_mbox(rproc);
return ret;
}
static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da,
size_t len, u64 *sys, bool *is_iomem)
{
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
int i;
/* parse address translation table */
for (i = 0; i < dcfg->att_size; i++) {
const struct imx_rproc_att *att = &dcfg->att[i];
/*
* Ignore entries not belong to current core:
* i.MX8QM has dual general M4_[0,1] cores, M4_0's own entries
* has "ATT_CORE(0) & BIT(0)" true, M4_1's own entries has
* "ATT_CORE(1) & BIT(1)" true.
*/
if (att->flags & ATT_CORE_MASK) {
if (!((BIT(priv->core_index)) & (att->flags & ATT_CORE_MASK)))
continue;
}
if (da >= att->da && da + len < att->da + att->size) {
unsigned int offset = da - att->da;
*sys = att->sa + offset;
if (is_iomem)
*is_iomem = att->flags & ATT_IOMEM;
return 0;
}
}
dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%zx\n",
da, len);
return -ENOENT;
}
static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
{
struct imx_rproc *priv = rproc->priv;
void *va = NULL;
u64 sys;
int i;
if (len == 0)
return NULL;
/*
* On device side we have many aliases, so we need to convert device
* address (M4) to system bus address first.
*/
if (imx_rproc_da_to_sys(priv, da, len, &sys, is_iomem))
return NULL;
for (i = 0; i < IMX_RPROC_MEM_MAX; i++) {
if (sys >= priv->mem[i].sys_addr && sys + len <
priv->mem[i].sys_addr + priv->mem[i].size) {
unsigned int offset = sys - priv->mem[i].sys_addr;
/* __force to make sparse happy with type conversion */
va = (__force void *)(priv->mem[i].cpu_addr + offset);
break;
}
}
dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%p\n",
da, len, va);
return va;
}
static int imx_rproc_mem_alloc(struct rproc *rproc,
struct rproc_mem_entry *mem)
{
struct device *dev = rproc->dev.parent;
void *va;
dev_dbg(dev, "map memory: %p+%zx\n", &mem->dma, mem->len);
va = ioremap_wc(mem->dma, mem->len);
if (IS_ERR_OR_NULL(va)) {
dev_err(dev, "Unable to map memory region: %p+%zx\n",
&mem->dma, mem->len);
return -ENOMEM;
}
/* Update memory entry va */
mem->va = va;
return 0;
}
static int imx_rproc_mem_release(struct rproc *rproc,
struct rproc_mem_entry *mem)
{
dev_dbg(rproc->dev.parent, "unmap memory: %pa\n", &mem->dma);
iounmap(mem->va);
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;
struct device_node *np = priv->dev->of_node;
struct rproc_mem_entry *mem;
int i = 0;
u32 da;
/* Register associated reserved memory regions */
while (1) {
int err;
struct resource res;
err = of_reserved_mem_region_to_resource(np, i++, &res);
if (err)
break;
/*
* Ignore the first memory region which will be used vdev buffer.
* No need to do extra handlings, rproc_add_virtio_dev will handle it.
*/
if (strstarts(res.name, "vdev0buffer"))
continue;
if (strstarts(res.name, "rsc-table"))
continue;
/* No need to translate pa to da, i.MX use same map */
da = res.start;
/* Register memory region */
mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start,
resource_size(&res), da,
imx_rproc_mem_alloc, imx_rproc_mem_release,
"%.*s", strchrnul(res.name, '@') - res.name,
res.name);
if (!mem)
return -ENOMEM;
rproc_coredump_add_segment(rproc, da, resource_size(&res));
rproc_add_carveout(rproc, mem);
}
if (priv->ops && priv->ops->prepare)
return priv->ops->prepare(rproc);
return 0;
}
static int imx_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
{
int ret;
ret = rproc_elf_load_rsc_table(rproc, fw);
if (ret)
dev_info(&rproc->dev, "No resource table in elf\n");
return 0;
}
static void imx_rproc_kick(struct rproc *rproc, int vqid)
{
struct imx_rproc *priv = rproc->priv;
int err;
__u32 mmsg;
if (!priv->tx_ch) {
dev_err(priv->dev, "No initialized mbox tx channel\n");
return;
}
/*
* Send the index of the triggered virtqueue as the mu payload.
* Let remote processor know which virtqueue is used.
*/
mmsg = vqid << 16;
err = mbox_send_message(priv->tx_ch, (void *)&mmsg);
if (err < 0)
dev_err(priv->dev, "%s: failed (%d, err:%d)\n",
__func__, vqid, err);
}
static int imx_rproc_attach(struct rproc *rproc)
{
return imx_rproc_xtr_mbox_init(rproc, true);
}
static int imx_rproc_scu_api_detach(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id))
return -EOPNOTSUPP;
imx_rproc_free_mbox(rproc);
return 0;
}
static int imx_rproc_detach(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
if (!priv->ops || !priv->ops->detach)
return -EOPNOTSUPP;
return priv->ops->detach(rproc);
}
static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz)
{
struct imx_rproc *priv = rproc->priv;
/* The resource table has already been mapped in imx_rproc_addr_init */
if (!priv->rsc_table)
return NULL;
*table_sz = SZ_1K;
return (struct resource_table *)priv->rsc_table;
}
static struct resource_table *
imx_rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
{
struct imx_rproc *priv = rproc->priv;
/* No resource table in the firmware */
if (!rproc->table_ptr)
return NULL;
if (priv->rsc_table)
return (struct resource_table *)priv->rsc_table;
return rproc_elf_find_loaded_rsc_table(rproc, fw);
}
static const struct rproc_ops imx_rproc_ops = {
.prepare = imx_rproc_prepare,
.attach = imx_rproc_attach,
.detach = imx_rproc_detach,
.start = imx_rproc_start,
.stop = imx_rproc_stop,
.kick = imx_rproc_kick,
.da_to_va = imx_rproc_da_to_va,
.load = rproc_elf_load_segments,
.parse_fw = imx_rproc_parse_fw,
.find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table,
.get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table,
.sanity_check = rproc_elf_sanity_check,
.get_boot_addr = rproc_elf_get_boot_addr,
};
static int imx_rproc_addr_init(struct imx_rproc *priv,
struct platform_device *pdev)
{
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
int a, b = 0, err, nph;
/* remap required addresses */
for (a = 0; a < dcfg->att_size; a++) {
const struct imx_rproc_att *att = &dcfg->att[a];
if (!(att->flags & ATT_OWN))
continue;
if (b >= IMX_RPROC_MEM_MAX)
break;
if (att->flags & ATT_IOMEM)
priv->mem[b].cpu_addr = devm_ioremap(&pdev->dev,
att->sa, att->size);
else
priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev,
att->sa, att->size);
if (!priv->mem[b].cpu_addr) {
dev_err(dev, "failed to remap %#x bytes from %#x\n", att->size, att->sa);
return -ENOMEM;
}
priv->mem[b].sys_addr = att->sa;
priv->mem[b].size = att->size;
b++;
}
/* memory-region is optional property */
nph = of_reserved_mem_region_count(np);
if (nph <= 0)
return 0;
/* remap optional addresses */
for (a = 0; a < nph; a++) {
struct resource res;
err = of_reserved_mem_region_to_resource(np, a, &res);
if (err) {
dev_err(dev, "unable to resolve memory region\n");
return err;
}
/* Not map vdevbuffer, vdevring region */
if (strstarts(res.name, "vdev"))
continue;
if (b >= IMX_RPROC_MEM_MAX)
break;
/* Not use resource version, because we might share region */
priv->mem[b].cpu_addr = devm_ioremap_resource_wc(&pdev->dev, &res);
if (IS_ERR(priv->mem[b].cpu_addr)) {
dev_err(dev, "failed to remap %pr\n", &res);
return -ENOMEM;
}
priv->mem[b].sys_addr = res.start;
priv->mem[b].size = resource_size(&res);
if (strstarts(res.name, "rsc-table"))
priv->rsc_table = priv->mem[b].cpu_addr;
b++;
}
return 0;
}
static int imx_rproc_notified_idr_cb(int id, void *ptr, void *data)
{
struct rproc *rproc = data;
rproc_vq_interrupt(rproc, id);
return 0;
}
static void imx_rproc_vq_work(struct work_struct *work)
{
struct imx_rproc *priv = container_of(work, struct imx_rproc,
rproc_work);
struct rproc *rproc = priv->rproc;
idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc);
}
static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg)
{
struct rproc *rproc = dev_get_drvdata(cl->dev);
struct imx_rproc *priv = rproc->priv;
queue_work(priv->workqueue, &priv->rproc_work);
}
static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block)
{
struct imx_rproc *priv = rproc->priv;
struct device *dev = priv->dev;
struct mbox_client *cl;
/*
* stop() and detach() will free the mbox channels, so need
* to request mbox channels in start() and attach().
*
* Because start() and attach() not able to handle mbox defer
* probe, imx_rproc_xtr_mbox_init is also called in probe().
* The check is to avoid request mbox again when start() or
* attach() after probe() returns success.
*/
if (priv->tx_ch && priv->rx_ch)
return 0;
if (!of_property_present(dev->of_node, "mbox-names"))
return 0;
cl = &priv->cl;
cl->dev = dev;
cl->tx_block = tx_block;
cl->tx_tout = 100;
cl->knows_txdone = false;
cl->rx_callback = imx_rproc_rx_callback;
priv->tx_ch = mbox_request_channel_byname(cl, "tx");
if (IS_ERR(priv->tx_ch))
return dev_err_probe(cl->dev, PTR_ERR(priv->tx_ch),
"failed to request tx mailbox channel\n");
priv->rx_ch = mbox_request_channel_byname(cl, "rx");
if (IS_ERR(priv->rx_ch)) {
mbox_free_channel(priv->tx_ch);
return dev_err_probe(cl->dev, PTR_ERR(priv->rx_ch),
"failed to request rx mailbox channel\n");
}
return 0;
}
static void imx_rproc_free_mbox(void *data)
{
struct rproc *rproc = data;
struct imx_rproc *priv = rproc->priv;
if (priv->tx_ch) {
mbox_free_channel(priv->tx_ch);
priv->tx_ch = NULL;
}
if (priv->rx_ch) {
mbox_free_channel(priv->rx_ch);
priv->rx_ch = NULL;
}
}
static void imx_rproc_put_scu(void *data)
{
struct imx_rproc *priv = data;
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) {
dev_pm_domain_detach_list(priv->pd_list);
return;
}
imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), false);
imx_scu_irq_unregister_notifier(&priv->rproc_nb);
}
static int imx_rproc_partition_notify(struct notifier_block *nb,
unsigned long event, void *group)
{
struct imx_rproc *priv = container_of(nb, struct imx_rproc, rproc_nb);
/* Ignore other irqs */
if (!((event & BIT(priv->rproc_pt)) && (*(u8 *)group == IMX_SC_IRQ_GROUP_REBOOTED)))
return 0;
rproc_report_crash(priv->rproc, RPROC_WATCHDOG);
pr_info("Partition%d reset!\n", priv->rproc_pt);
return 0;
}
static int imx_rproc_attach_pd(struct imx_rproc *priv)
{
struct device *dev = priv->dev;
int ret, i;
bool detached = true;
/*
* If there is only one power-domain entry, the platform driver framework
* will handle it, no need handle it in this driver.
*/
if (dev->pm_domain)
return 0;
ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list);
if (ret < 0)
return ret;
/*
* If all the power domain devices are already turned on, the remote
* core is already powered up and running when the kernel booted (e.g.,
* started by U-Boot's bootaux command). In this case attach to it.
*/
for (i = 0; i < ret; i++) {
if (!dev_pm_genpd_is_on(priv->pd_list->pd_devs[i])) {
detached = false;
break;
}
}
if (detached)
priv->rproc->state = RPROC_DETACHED;
return 0;
}
static int imx_rproc_arm_smc_detect_mode(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
struct arm_smccc_res res;
arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STARTED, 0, 0, 0, 0, 0, 0, &res);
if (res.a0)
priv->rproc->state = RPROC_DETACHED;
return 0;
}
static int imx_rproc_mmio_detect_mode(struct rproc *rproc)
{
const struct regmap_config config = { .name = "imx-rproc" };
struct imx_rproc *priv = rproc->priv;
const struct imx_rproc_dcfg *dcfg = priv->dcfg;
struct device *dev = priv->dev;
struct regmap *regmap;
u32 val;
int ret;
priv->gpr = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,iomuxc-gpr");
if (IS_ERR(priv->gpr))
priv->gpr = NULL;
regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
if (IS_ERR(regmap)) {
dev_err(dev, "failed to find syscon\n");
return PTR_ERR(regmap);
}
priv->regmap = regmap;
ret = regmap_attach_dev(dev, regmap, &config);
if (ret) {
dev_err(dev, "regmap attach failed\n");
return ret;
}
if (priv->gpr) {
ret = regmap_read(priv->gpr, dcfg->gpr_reg, &val);
if (val & dcfg->gpr_wait) {
/*
* After cold boot, the CM indicates its in wait
* state, but not fully powered off. Power it off
* fully so firmware can be loaded into it.
*/
imx_rproc_stop(priv->rproc);
return 0;
}
}
ret = regmap_read(regmap, dcfg->src_reg, &val);
if (ret) {
dev_err(dev, "Failed to read src\n");
return ret;
}
if ((val & dcfg->src_mask) != dcfg->src_stop)
priv->rproc->state = RPROC_DETACHED;
return 0;
}
static int imx_rproc_scu_api_detect_mode(struct rproc *rproc)
{
struct imx_rproc *priv = rproc->priv;
struct device *dev = priv->dev;
int ret;
u8 pt;
ret = imx_scu_get_handle(&priv->ipc_handle);
if (ret)
return ret;
ret = of_property_read_u32(dev->of_node, "fsl,resource-id", &priv->rsrc_id);
if (ret) {
dev_err(dev, "No fsl,resource-id property\n");
return ret;
}
if (priv->rsrc_id == IMX_SC_R_M4_1_PID0)
priv->core_index = 1;
else
priv->core_index = 0;
ret = devm_add_action_or_reset(dev, imx_rproc_put_scu, priv);
if (ret)
return dev_err_probe(dev, ret, "Failed to add action for put scu\n");
/*
* If Mcore resource is not owned by Acore partition, It is kicked by ROM,
* and Linux could only do IPC with Mcore and nothing else.
*/
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) {
if (of_property_read_u32(dev->of_node, "fsl,entry-address", &priv->entry))
return -EINVAL;
return imx_rproc_attach_pd(priv);
}
priv->rproc->state = RPROC_DETACHED;
priv->rproc->recovery_disabled = false;
rproc_set_feature(priv->rproc, RPROC_FEAT_ATTACH_ON_RECOVERY);
/* Get partition id and enable irq in SCFW */
ret = imx_sc_rm_get_resource_owner(priv->ipc_handle, priv->rsrc_id, &pt);
if (ret) {
dev_err(dev, "not able to get resource owner\n");
return ret;
}
priv->rproc_pt = pt;
priv->rproc_nb.notifier_call = imx_rproc_partition_notify;
ret = imx_scu_irq_register_notifier(&priv->rproc_nb);
if (ret) {
dev_err(dev, "register scu notifier failed, %d\n", ret);
return ret;
}
ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt),
true);
if (ret) {
imx_scu_irq_unregister_notifier(&priv->rproc_nb);
dev_err(dev, "Enable irq failed, %d\n", ret);
return ret;
}
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) {
priv->ops = &imx_rproc_ops_sm_cpu;
dev_info(dev, "Using CPU Protocol OPS\n");
return 0;
}
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)
{
/*
* To i.MX{7,8} ULP, Linux is under control of RTOS, no need
* priv->ops or priv->ops->detect_mode, it is state RPROC_DETACHED.
*/
if (!priv->ops || !priv->ops->detect_mode) {
priv->rproc->state = RPROC_DETACHED;
return 0;
}
return priv->ops->detect_mode(priv->rproc);
}
static int imx_rproc_sys_off_handler(struct sys_off_data *data)
{
struct rproc *rproc = data->cb_data;
int ret;
imx_rproc_free_mbox(rproc);
ret = imx_rproc_xtr_mbox_init(rproc, false);
if (ret) {
dev_err(&rproc->dev, "Failed to request non-blocking mbox\n");
return NOTIFY_BAD;
}
return NOTIFY_DONE;
}
static void imx_rproc_destroy_workqueue(void *data)
{
struct workqueue_struct *workqueue = data;
destroy_workqueue(workqueue);
}
static int imx_rproc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct imx_rproc *priv;
struct rproc *rproc;
const struct imx_rproc_dcfg *dcfg;
int ret;
rproc = devm_rproc_alloc(dev, np->name, &imx_rproc_ops,
NULL, sizeof(*priv));
if (!rproc)
return -ENOMEM;
dcfg = of_device_get_match_data(dev);
if (!dcfg)
return -EINVAL;
priv = rproc->priv;
priv->rproc = rproc;
priv->dcfg = dcfg;
priv->dev = dev;
if (dcfg->ops)
priv->ops = dcfg->ops;
dev_set_drvdata(dev, rproc);
priv->workqueue = create_workqueue(dev_name(dev));
if (!priv->workqueue) {
dev_err(dev, "cannot create workqueue\n");
return -ENOMEM;
}
ret = devm_add_action_or_reset(dev, imx_rproc_destroy_workqueue, priv->workqueue);
if (ret)
return dev_err_probe(dev, ret, "Failed to add devm destroy workqueue action\n");
INIT_WORK(&priv->rproc_work, imx_rproc_vq_work);
ret = imx_rproc_xtr_mbox_init(rproc, true);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, imx_rproc_free_mbox, rproc);
if (ret)
return dev_err_probe(dev, ret,
"Failed to add devm free mbox action: %d\n", ret);
ret = imx_rproc_addr_init(priv, pdev);
if (ret)
return dev_err_probe(dev, ret, "failed on imx_rproc_addr_init\n");
ret = imx_rproc_detect_mode(priv);
if (ret)
return dev_err_probe(dev, ret, "failed on detect mode\n");
/*
* Handle clocks when remote core is under control of Linux AND the
* clocks are not managed by system firmware.
*/
if (dcfg->flags & IMX_RPROC_NEED_CLKS) {
priv->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock\n");
}
if (rproc->state != RPROC_DETACHED)
rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot");
if (dcfg->flags & IMX_RPROC_NEED_SYSTEM_OFF) {
/*
* setup mailbox to non-blocking mode in
* [SYS_OFF_MODE_POWER_OFF_PREPARE, SYS_OFF_MODE_RESTART_PREPARE]
* phase before invoking [SYS_OFF_MODE_POWER_OFF, SYS_OFF_MODE_RESTART]
* atomic chain, see kernel/reboot.c.
*/
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF_PREPARE,
SYS_OFF_PRIO_DEFAULT,
imx_rproc_sys_off_handler, rproc);
if (ret)
return dev_err_probe(dev, ret, "register power off handler failure\n");
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART_PREPARE,
SYS_OFF_PRIO_DEFAULT,
imx_rproc_sys_off_handler, rproc);
if (ret)
return dev_err_probe(dev, ret, "register restart handler failure\n");
}
pm_runtime_enable(dev);
ret = pm_runtime_resume_and_get(dev);
if (ret)
return dev_err_probe(dev, ret, "pm_runtime get failed\n");
ret = devm_rproc_add(dev, rproc);
if (ret) {
dev_err(dev, "rproc_add failed\n");
goto err_put_pm;
}
return 0;
err_put_pm:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
return ret;
}
static void imx_rproc_remove(struct platform_device *pdev)
{
struct rproc *rproc = platform_get_drvdata(pdev);
struct imx_rproc *priv = rproc->priv;
pm_runtime_disable(priv->dev);
pm_runtime_put_noidle(priv->dev);
}
static const struct imx_rproc_plat_ops imx_rproc_ops_arm_smc = {
.start = imx_rproc_arm_smc_start,
.stop = imx_rproc_arm_smc_stop,
.detect_mode = imx_rproc_arm_smc_detect_mode,
};
static const struct imx_rproc_plat_ops imx_rproc_ops_mmio = {
.start = imx_rproc_mmio_start,
.stop = imx_rproc_mmio_stop,
.detect_mode = imx_rproc_mmio_detect_mode,
};
static const struct imx_rproc_plat_ops imx_rproc_ops_scu_api = {
.start = imx_rproc_scu_api_start,
.stop = imx_rproc_scu_api_stop,
.detach = imx_rproc_scu_api_detach,
.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_plat_ops imx_rproc_ops_sm_cpu = {
.detect_mode = imx_rproc_sm_detect_mode,
.start = imx_rproc_sm_cpu_start,
.stop = imx_rproc_sm_cpu_stop,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = {
.src_reg = IMX7D_SRC_SCR,
.src_mask = IMX7D_M4_RST_MASK,
.src_start = IMX7D_M4_START,
.src_stop = IMX8M_M7_STOP,
.gpr_reg = IMX8M_GPR22,
.gpr_wait = IMX8M_GPR22_CM7_CPUWAIT,
.att = imx_rproc_att_imx8mn,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8mn),
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = {
.att = imx_rproc_att_imx8mn,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8mn),
.ops = &imx_rproc_ops_arm_smc,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = {
.src_reg = IMX7D_SRC_SCR,
.src_mask = IMX7D_M4_RST_MASK,
.src_start = IMX7D_M4_START,
.src_stop = IMX7D_M4_STOP,
.att = imx_rproc_att_imx8mq,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8mq),
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qm = {
.att = imx_rproc_att_imx8qm,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8qm),
.ops = &imx_rproc_ops_scu_api,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qxp = {
.att = imx_rproc_att_imx8qxp,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8qxp),
.ops = &imx_rproc_ops_scu_api,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx8ulp = {
.att = imx_rproc_att_imx8ulp,
.att_size = ARRAY_SIZE(imx_rproc_att_imx8ulp),
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = {
.att = imx_rproc_att_imx7ulp,
.att_size = ARRAY_SIZE(imx_rproc_att_imx7ulp),
.flags = IMX_RPROC_NEED_SYSTEM_OFF,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = {
.src_reg = IMX7D_SRC_SCR,
.src_mask = IMX7D_M4_RST_MASK,
.src_start = IMX7D_M4_START,
.src_stop = IMX7D_M4_STOP,
.att = imx_rproc_att_imx7d,
.att_size = ARRAY_SIZE(imx_rproc_att_imx7d),
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = {
.src_reg = IMX6SX_SRC_SCR,
.src_mask = IMX6SX_M4_RST_MASK,
.src_start = IMX6SX_M4_START,
.src_stop = IMX6SX_M4_STOP,
.att = imx_rproc_att_imx6sx,
.att_size = ARRAY_SIZE(imx_rproc_att_imx6sx),
.ops = &imx_rproc_ops_mmio,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = {
.att = imx_rproc_att_imx93,
.att_size = ARRAY_SIZE(imx_rproc_att_imx93),
.ops = &imx_rproc_ops_arm_smc,
.flags = IMX_RPROC_NEED_CLKS,
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx94_m70 = {
.att = imx_rproc_att_imx94_m70,
.att_size = ARRAY_SIZE(imx_rproc_att_imx94_m70),
.ops = &imx_rproc_ops_sm_lmm,
.cpuid = 1,
.lmid = 2,
.reset_vector_mask = GENMASK_U32(31, 16),
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx94_m71 = {
.att = imx_rproc_att_imx94_m71,
.att_size = ARRAY_SIZE(imx_rproc_att_imx94_m71),
.ops = &imx_rproc_ops_sm_lmm,
.cpuid = 7,
.lmid = 3,
.reset_vector_mask = GENMASK_U32(31, 16),
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx94_m33s = {
.att = imx_rproc_att_imx94_m33s,
.att_size = ARRAY_SIZE(imx_rproc_att_imx94_m33s),
.ops = &imx_rproc_ops_sm_lmm,
.cpuid = 8,
.lmid = 1,
.reset_vector_mask = GENMASK_U32(31, 16),
};
static const struct imx_rproc_dcfg imx_rproc_cfg_imx95_m7 = {
.att = imx_rproc_att_imx95_m7,
.att_size = ARRAY_SIZE(imx_rproc_att_imx95_m7),
.ops = &imx_rproc_ops_sm_lmm,
/* Must align with System Manager Firmware */
.cpuid = 1, /* Use 1 as cpu id for M7 core */
.lmid = 1, /* Use 1 as Logical Machine ID where M7 resides */
.reset_vector_mask = GENMASK_U32(31, 16),
};
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 },
{ .compatible = "fsl,imx6sx-cm4", .data = &imx_rproc_cfg_imx6sx },
{ .compatible = "fsl,imx8mq-cm4", .data = &imx_rproc_cfg_imx8mq },
{ .compatible = "fsl,imx8mm-cm4", .data = &imx_rproc_cfg_imx8mq },
{ .compatible = "fsl,imx8mn-cm7", .data = &imx_rproc_cfg_imx8mn },
{ .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mn },
{ .compatible = "fsl,imx8mn-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio },
{ .compatible = "fsl,imx8mp-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio },
{ .compatible = "fsl,imx8qxp-cm4", .data = &imx_rproc_cfg_imx8qxp },
{ .compatible = "fsl,imx8qm-cm4", .data = &imx_rproc_cfg_imx8qm },
{ .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp },
{ .compatible = "fsl,imx93-cm33", .data = &imx_rproc_cfg_imx93 },
{ .compatible = "fsl,imx94-cm70", .data = &imx_rproc_cfg_imx94_m70 },
{ .compatible = "fsl,imx94-cm71", .data = &imx_rproc_cfg_imx94_m71 },
{ .compatible = "fsl,imx94-cm33s", .data = &imx_rproc_cfg_imx94_m33s },
{ .compatible = "fsl,imx95-cm7", .data = &imx_rproc_cfg_imx95_m7 },
{},
};
MODULE_DEVICE_TABLE(of, imx_rproc_of_match);
static struct platform_driver imx_rproc_driver = {
.probe = imx_rproc_probe,
.remove = imx_rproc_remove,
.driver = {
.name = "imx-rproc",
.of_match_table = imx_rproc_of_match,
},
};
module_platform_driver(imx_rproc_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("i.MX remote processor control driver");
MODULE_AUTHOR("Oleksij Rempel <o.rempel@pengutronix.de>");