162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright 2021 NXP */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <dt-bindings/firmware/imx/rsrc.h>
562306a36Sopenharmony_ci#include <linux/arm-smccc.h>
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/err.h>
862306a36Sopenharmony_ci#include <linux/firmware.h>
962306a36Sopenharmony_ci#include <linux/firmware/imx/sci.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/mailbox_client.h>
1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_reserved_mem.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/pm_domain.h>
1962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2062306a36Sopenharmony_ci#include <linux/regmap.h>
2162306a36Sopenharmony_ci#include <linux/remoteproc.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "imx_rproc.h"
2562306a36Sopenharmony_ci#include "remoteproc_elf_helpers.h"
2662306a36Sopenharmony_ci#include "remoteproc_internal.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DSP_RPROC_CLK_MAX			5
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * Module parameters
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cistatic unsigned int no_mailboxes;
3462306a36Sopenharmony_cimodule_param_named(no_mailboxes, no_mailboxes, int, 0644);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(no_mailboxes,
3662306a36Sopenharmony_ci		 "There is no mailbox between cores, so ignore remote proc reply after start, default is 0 (off).");
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define REMOTE_IS_READY				BIT(0)
3962306a36Sopenharmony_ci#define REMOTE_READY_WAIT_MAX_RETRIES		500
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* att flags */
4262306a36Sopenharmony_ci/* DSP own area */
4362306a36Sopenharmony_ci#define ATT_OWN					BIT(31)
4462306a36Sopenharmony_ci/* DSP instruction area */
4562306a36Sopenharmony_ci#define ATT_IRAM				BIT(30)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Definitions for i.MX8MP */
4862306a36Sopenharmony_ci/* DAP registers */
4962306a36Sopenharmony_ci#define IMX8M_DAP_DEBUG				0x28800000
5062306a36Sopenharmony_ci#define IMX8M_DAP_DEBUG_SIZE			(64 * 1024)
5162306a36Sopenharmony_ci#define IMX8M_DAP_PWRCTL			(0x4000 + 0x3020)
5262306a36Sopenharmony_ci#define IMX8M_PWRCTL_CORERESET			BIT(16)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* DSP audio mix registers */
5562306a36Sopenharmony_ci#define IMX8M_AudioDSP_REG0			0x100
5662306a36Sopenharmony_ci#define IMX8M_AudioDSP_REG1			0x104
5762306a36Sopenharmony_ci#define IMX8M_AudioDSP_REG2			0x108
5862306a36Sopenharmony_ci#define IMX8M_AudioDSP_REG3			0x10c
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define IMX8M_AudioDSP_REG2_RUNSTALL		BIT(5)
6162306a36Sopenharmony_ci#define IMX8M_AudioDSP_REG2_PWAITMODE		BIT(1)
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Definitions for i.MX8ULP */
6462306a36Sopenharmony_ci#define IMX8ULP_SIM_LPAV_REG_SYSCTRL0		0x8
6562306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_DBG_RST		BIT(25)
6662306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_PLAT_CLK_EN	BIT(19)
6762306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_PBCLK_EN		BIT(18)
6862306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_CLK_EN		BIT(17)
6962306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_RST		BIT(16)
7062306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_OCD_HALT		BIT(14)
7162306a36Sopenharmony_ci#define IMX8ULP_SYSCTRL0_DSP_STALL		BIT(13)
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define IMX8ULP_SIP_HIFI_XRDC			0xc200000e
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * enum - Predefined Mailbox Messages
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * @RP_MBOX_SUSPEND_SYSTEM: system suspend request for the remote processor
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * @RP_MBOX_SUSPEND_ACK: successful response from remote processor for a
8162306a36Sopenharmony_ci * suspend request
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * @RP_MBOX_RESUME_SYSTEM: system resume request for the remote processor
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * @RP_MBOX_RESUME_ACK: successful response from remote processor for a
8662306a36Sopenharmony_ci * resume request
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cienum imx_dsp_rp_mbox_messages {
8962306a36Sopenharmony_ci	RP_MBOX_SUSPEND_SYSTEM			= 0xFF11,
9062306a36Sopenharmony_ci	RP_MBOX_SUSPEND_ACK			= 0xFF12,
9162306a36Sopenharmony_ci	RP_MBOX_RESUME_SYSTEM			= 0xFF13,
9262306a36Sopenharmony_ci	RP_MBOX_RESUME_ACK			= 0xFF14,
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/**
9662306a36Sopenharmony_ci * struct imx_dsp_rproc - DSP remote processor state
9762306a36Sopenharmony_ci * @regmap: regmap handler
9862306a36Sopenharmony_ci * @rproc: rproc handler
9962306a36Sopenharmony_ci * @dsp_dcfg: device configuration pointer
10062306a36Sopenharmony_ci * @clks: clocks needed by this device
10162306a36Sopenharmony_ci * @cl: mailbox client to request the mailbox channel
10262306a36Sopenharmony_ci * @cl_rxdb: mailbox client to request the mailbox channel for doorbell
10362306a36Sopenharmony_ci * @tx_ch: mailbox tx channel handle
10462306a36Sopenharmony_ci * @rx_ch: mailbox rx channel handle
10562306a36Sopenharmony_ci * @rxdb_ch: mailbox rx doorbell channel handle
10662306a36Sopenharmony_ci * @pd_dev: power domain device
10762306a36Sopenharmony_ci * @pd_dev_link: power domain device link
10862306a36Sopenharmony_ci * @ipc_handle: System Control Unit ipc handle
10962306a36Sopenharmony_ci * @rproc_work: work for processing virtio interrupts
11062306a36Sopenharmony_ci * @pm_comp: completion primitive to sync for suspend response
11162306a36Sopenharmony_ci * @num_domains: power domain number
11262306a36Sopenharmony_ci * @flags: control flags
11362306a36Sopenharmony_ci */
11462306a36Sopenharmony_cistruct imx_dsp_rproc {
11562306a36Sopenharmony_ci	struct regmap				*regmap;
11662306a36Sopenharmony_ci	struct rproc				*rproc;
11762306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg		*dsp_dcfg;
11862306a36Sopenharmony_ci	struct clk_bulk_data			clks[DSP_RPROC_CLK_MAX];
11962306a36Sopenharmony_ci	struct mbox_client			cl;
12062306a36Sopenharmony_ci	struct mbox_client			cl_rxdb;
12162306a36Sopenharmony_ci	struct mbox_chan			*tx_ch;
12262306a36Sopenharmony_ci	struct mbox_chan			*rx_ch;
12362306a36Sopenharmony_ci	struct mbox_chan			*rxdb_ch;
12462306a36Sopenharmony_ci	struct device				**pd_dev;
12562306a36Sopenharmony_ci	struct device_link			**pd_dev_link;
12662306a36Sopenharmony_ci	struct imx_sc_ipc			*ipc_handle;
12762306a36Sopenharmony_ci	struct work_struct			rproc_work;
12862306a36Sopenharmony_ci	struct completion			pm_comp;
12962306a36Sopenharmony_ci	int					num_domains;
13062306a36Sopenharmony_ci	u32					flags;
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci/**
13462306a36Sopenharmony_ci * struct imx_dsp_rproc_dcfg - DSP remote processor configuration
13562306a36Sopenharmony_ci * @dcfg: imx_rproc_dcfg handler
13662306a36Sopenharmony_ci * @reset: reset callback function
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistruct imx_dsp_rproc_dcfg {
13962306a36Sopenharmony_ci	const struct imx_rproc_dcfg		*dcfg;
14062306a36Sopenharmony_ci	int (*reset)(struct imx_dsp_rproc *priv);
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic const struct imx_rproc_att imx_dsp_rproc_att_imx8qm[] = {
14462306a36Sopenharmony_ci	/* dev addr , sys addr  , size	    , flags */
14562306a36Sopenharmony_ci	{ 0x596e8000, 0x556e8000, 0x00008000, ATT_OWN },
14662306a36Sopenharmony_ci	{ 0x596f0000, 0x556f0000, 0x00008000, ATT_OWN },
14762306a36Sopenharmony_ci	{ 0x596f8000, 0x556f8000, 0x00000800, ATT_OWN | ATT_IRAM},
14862306a36Sopenharmony_ci	{ 0x55700000, 0x55700000, 0x00070000, ATT_OWN },
14962306a36Sopenharmony_ci	/* DDR (Data) */
15062306a36Sopenharmony_ci	{ 0x80000000, 0x80000000, 0x60000000, 0},
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic const struct imx_rproc_att imx_dsp_rproc_att_imx8qxp[] = {
15462306a36Sopenharmony_ci	/* dev addr , sys addr  , size	    , flags */
15562306a36Sopenharmony_ci	{ 0x596e8000, 0x596e8000, 0x00008000, ATT_OWN },
15662306a36Sopenharmony_ci	{ 0x596f0000, 0x596f0000, 0x00008000, ATT_OWN },
15762306a36Sopenharmony_ci	{ 0x596f8000, 0x596f8000, 0x00000800, ATT_OWN | ATT_IRAM},
15862306a36Sopenharmony_ci	{ 0x59700000, 0x59700000, 0x00070000, ATT_OWN },
15962306a36Sopenharmony_ci	/* DDR (Data) */
16062306a36Sopenharmony_ci	{ 0x80000000, 0x80000000, 0x60000000, 0},
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const struct imx_rproc_att imx_dsp_rproc_att_imx8mp[] = {
16462306a36Sopenharmony_ci	/* dev addr , sys addr  , size	    , flags */
16562306a36Sopenharmony_ci	{ 0x3b6e8000, 0x3b6e8000, 0x00008000, ATT_OWN },
16662306a36Sopenharmony_ci	{ 0x3b6f0000, 0x3b6f0000, 0x00008000, ATT_OWN },
16762306a36Sopenharmony_ci	{ 0x3b6f8000, 0x3b6f8000, 0x00000800, ATT_OWN | ATT_IRAM},
16862306a36Sopenharmony_ci	{ 0x3b700000, 0x3b700000, 0x00040000, ATT_OWN },
16962306a36Sopenharmony_ci	/* DDR (Data) */
17062306a36Sopenharmony_ci	{ 0x40000000, 0x40000000, 0x80000000, 0},
17162306a36Sopenharmony_ci};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic const struct imx_rproc_att imx_dsp_rproc_att_imx8ulp[] = {
17462306a36Sopenharmony_ci	/* dev addr , sys addr  , size	    , flags */
17562306a36Sopenharmony_ci	{ 0x21170000, 0x21170000, 0x00010000, ATT_OWN | ATT_IRAM},
17662306a36Sopenharmony_ci	{ 0x21180000, 0x21180000, 0x00010000, ATT_OWN },
17762306a36Sopenharmony_ci	/* DDR (Data) */
17862306a36Sopenharmony_ci	{ 0x0c000000, 0x80000000, 0x10000000, 0},
17962306a36Sopenharmony_ci	{ 0x30000000, 0x90000000, 0x10000000, 0},
18062306a36Sopenharmony_ci};
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* Initialize the mailboxes between cores, if exists */
18362306a36Sopenharmony_cistatic int (*imx_dsp_rproc_mbox_init)(struct imx_dsp_rproc *priv);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/* Reset function for DSP on i.MX8MP */
18662306a36Sopenharmony_cistatic int imx8mp_dsp_reset(struct imx_dsp_rproc *priv)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	void __iomem *dap = ioremap_wc(IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
18962306a36Sopenharmony_ci	int pwrctl;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Put DSP into reset and stall */
19262306a36Sopenharmony_ci	pwrctl = readl(dap + IMX8M_DAP_PWRCTL);
19362306a36Sopenharmony_ci	pwrctl |= IMX8M_PWRCTL_CORERESET;
19462306a36Sopenharmony_ci	writel(pwrctl, dap + IMX8M_DAP_PWRCTL);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Keep reset asserted for 10 cycles */
19762306a36Sopenharmony_ci	usleep_range(1, 2);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	regmap_update_bits(priv->regmap, IMX8M_AudioDSP_REG2,
20062306a36Sopenharmony_ci			   IMX8M_AudioDSP_REG2_RUNSTALL,
20162306a36Sopenharmony_ci			   IMX8M_AudioDSP_REG2_RUNSTALL);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Take the DSP out of reset and keep stalled for FW loading */
20462306a36Sopenharmony_ci	pwrctl = readl(dap + IMX8M_DAP_PWRCTL);
20562306a36Sopenharmony_ci	pwrctl &= ~IMX8M_PWRCTL_CORERESET;
20662306a36Sopenharmony_ci	writel(pwrctl, dap + IMX8M_DAP_PWRCTL);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	iounmap(dap);
20962306a36Sopenharmony_ci	return 0;
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/* Reset function for DSP on i.MX8ULP */
21362306a36Sopenharmony_cistatic int imx8ulp_dsp_reset(struct imx_dsp_rproc *priv)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct arm_smccc_res res;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Put DSP into reset and stall */
21862306a36Sopenharmony_ci	regmap_update_bits(priv->regmap, IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
21962306a36Sopenharmony_ci			   IMX8ULP_SYSCTRL0_DSP_RST, IMX8ULP_SYSCTRL0_DSP_RST);
22062306a36Sopenharmony_ci	regmap_update_bits(priv->regmap, IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
22162306a36Sopenharmony_ci			   IMX8ULP_SYSCTRL0_DSP_STALL,
22262306a36Sopenharmony_ci			   IMX8ULP_SYSCTRL0_DSP_STALL);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* Configure resources of DSP through TFA */
22562306a36Sopenharmony_ci	arm_smccc_smc(IMX8ULP_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &res);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Take the DSP out of reset and keep stalled for FW loading */
22862306a36Sopenharmony_ci	regmap_update_bits(priv->regmap, IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
22962306a36Sopenharmony_ci			   IMX8ULP_SYSCTRL0_DSP_RST, 0);
23062306a36Sopenharmony_ci	regmap_update_bits(priv->regmap, IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
23162306a36Sopenharmony_ci			   IMX8ULP_SYSCTRL0_DSP_DBG_RST, 0);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/* Specific configuration for i.MX8MP */
23762306a36Sopenharmony_cistatic const struct imx_rproc_dcfg dsp_rproc_cfg_imx8mp = {
23862306a36Sopenharmony_ci	.src_reg	= IMX8M_AudioDSP_REG2,
23962306a36Sopenharmony_ci	.src_mask	= IMX8M_AudioDSP_REG2_RUNSTALL,
24062306a36Sopenharmony_ci	.src_start	= 0,
24162306a36Sopenharmony_ci	.src_stop	= IMX8M_AudioDSP_REG2_RUNSTALL,
24262306a36Sopenharmony_ci	.att		= imx_dsp_rproc_att_imx8mp,
24362306a36Sopenharmony_ci	.att_size	= ARRAY_SIZE(imx_dsp_rproc_att_imx8mp),
24462306a36Sopenharmony_ci	.method		= IMX_RPROC_MMIO,
24562306a36Sopenharmony_ci};
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8mp = {
24862306a36Sopenharmony_ci	.dcfg		= &dsp_rproc_cfg_imx8mp,
24962306a36Sopenharmony_ci	.reset          = imx8mp_dsp_reset,
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/* Specific configuration for i.MX8ULP */
25362306a36Sopenharmony_cistatic const struct imx_rproc_dcfg dsp_rproc_cfg_imx8ulp = {
25462306a36Sopenharmony_ci	.src_reg	= IMX8ULP_SIM_LPAV_REG_SYSCTRL0,
25562306a36Sopenharmony_ci	.src_mask	= IMX8ULP_SYSCTRL0_DSP_STALL,
25662306a36Sopenharmony_ci	.src_start	= 0,
25762306a36Sopenharmony_ci	.src_stop	= IMX8ULP_SYSCTRL0_DSP_STALL,
25862306a36Sopenharmony_ci	.att		= imx_dsp_rproc_att_imx8ulp,
25962306a36Sopenharmony_ci	.att_size	= ARRAY_SIZE(imx_dsp_rproc_att_imx8ulp),
26062306a36Sopenharmony_ci	.method		= IMX_RPROC_MMIO,
26162306a36Sopenharmony_ci};
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8ulp = {
26462306a36Sopenharmony_ci	.dcfg		= &dsp_rproc_cfg_imx8ulp,
26562306a36Sopenharmony_ci	.reset          = imx8ulp_dsp_reset,
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci/* Specific configuration for i.MX8QXP */
26962306a36Sopenharmony_cistatic const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qxp = {
27062306a36Sopenharmony_ci	.att		= imx_dsp_rproc_att_imx8qxp,
27162306a36Sopenharmony_ci	.att_size	= ARRAY_SIZE(imx_dsp_rproc_att_imx8qxp),
27262306a36Sopenharmony_ci	.method		= IMX_RPROC_SCU_API,
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qxp = {
27662306a36Sopenharmony_ci	.dcfg		= &dsp_rproc_cfg_imx8qxp,
27762306a36Sopenharmony_ci};
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/* Specific configuration for i.MX8QM */
28062306a36Sopenharmony_cistatic const struct imx_rproc_dcfg dsp_rproc_cfg_imx8qm = {
28162306a36Sopenharmony_ci	.att		= imx_dsp_rproc_att_imx8qm,
28262306a36Sopenharmony_ci	.att_size	= ARRAY_SIZE(imx_dsp_rproc_att_imx8qm),
28362306a36Sopenharmony_ci	.method		= IMX_RPROC_SCU_API,
28462306a36Sopenharmony_ci};
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic const struct imx_dsp_rproc_dcfg imx_dsp_rproc_cfg_imx8qm = {
28762306a36Sopenharmony_ci	.dcfg		= &dsp_rproc_cfg_imx8qm,
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int imx_dsp_rproc_ready(struct rproc *rproc)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
29362306a36Sopenharmony_ci	int i;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!priv->rxdb_ch)
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	for (i = 0; i < REMOTE_READY_WAIT_MAX_RETRIES; i++) {
29962306a36Sopenharmony_ci		if (priv->flags & REMOTE_IS_READY)
30062306a36Sopenharmony_ci			return 0;
30162306a36Sopenharmony_ci		usleep_range(100, 200);
30262306a36Sopenharmony_ci	}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return -ETIMEDOUT;
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci/*
30862306a36Sopenharmony_ci * Start function for rproc_ops
30962306a36Sopenharmony_ci *
31062306a36Sopenharmony_ci * There is a handshake for start procedure: when DSP starts, it
31162306a36Sopenharmony_ci * will send a doorbell message to this driver, then the
31262306a36Sopenharmony_ci * REMOTE_IS_READY flags is set, then driver will kick
31362306a36Sopenharmony_ci * a message to DSP.
31462306a36Sopenharmony_ci */
31562306a36Sopenharmony_cistatic int imx_dsp_rproc_start(struct rproc *rproc)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
31862306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
31962306a36Sopenharmony_ci	const struct imx_rproc_dcfg *dcfg = dsp_dcfg->dcfg;
32062306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
32162306a36Sopenharmony_ci	int ret;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	switch (dcfg->method) {
32462306a36Sopenharmony_ci	case IMX_RPROC_MMIO:
32562306a36Sopenharmony_ci		ret = regmap_update_bits(priv->regmap,
32662306a36Sopenharmony_ci					 dcfg->src_reg,
32762306a36Sopenharmony_ci					 dcfg->src_mask,
32862306a36Sopenharmony_ci					 dcfg->src_start);
32962306a36Sopenharmony_ci		break;
33062306a36Sopenharmony_ci	case IMX_RPROC_SCU_API:
33162306a36Sopenharmony_ci		ret = imx_sc_pm_cpu_start(priv->ipc_handle,
33262306a36Sopenharmony_ci					  IMX_SC_R_DSP,
33362306a36Sopenharmony_ci					  true,
33462306a36Sopenharmony_ci					  rproc->bootaddr);
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	default:
33762306a36Sopenharmony_ci		return -EOPNOTSUPP;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (ret)
34162306a36Sopenharmony_ci		dev_err(dev, "Failed to enable remote core!\n");
34262306a36Sopenharmony_ci	else
34362306a36Sopenharmony_ci		ret = imx_dsp_rproc_ready(rproc);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return ret;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci/*
34962306a36Sopenharmony_ci * Stop function for rproc_ops
35062306a36Sopenharmony_ci * It clears the REMOTE_IS_READY flags
35162306a36Sopenharmony_ci */
35262306a36Sopenharmony_cistatic int imx_dsp_rproc_stop(struct rproc *rproc)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
35562306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
35662306a36Sopenharmony_ci	const struct imx_rproc_dcfg *dcfg = dsp_dcfg->dcfg;
35762306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
35862306a36Sopenharmony_ci	int ret = 0;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	if (rproc->state == RPROC_CRASHED) {
36162306a36Sopenharmony_ci		priv->flags &= ~REMOTE_IS_READY;
36262306a36Sopenharmony_ci		return 0;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	switch (dcfg->method) {
36662306a36Sopenharmony_ci	case IMX_RPROC_MMIO:
36762306a36Sopenharmony_ci		ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask,
36862306a36Sopenharmony_ci					 dcfg->src_stop);
36962306a36Sopenharmony_ci		break;
37062306a36Sopenharmony_ci	case IMX_RPROC_SCU_API:
37162306a36Sopenharmony_ci		ret = imx_sc_pm_cpu_start(priv->ipc_handle,
37262306a36Sopenharmony_ci					  IMX_SC_R_DSP,
37362306a36Sopenharmony_ci					  false,
37462306a36Sopenharmony_ci					  rproc->bootaddr);
37562306a36Sopenharmony_ci		break;
37662306a36Sopenharmony_ci	default:
37762306a36Sopenharmony_ci		return -EOPNOTSUPP;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (ret)
38162306a36Sopenharmony_ci		dev_err(dev, "Failed to stop remote core\n");
38262306a36Sopenharmony_ci	else
38362306a36Sopenharmony_ci		priv->flags &= ~REMOTE_IS_READY;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return ret;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/**
38962306a36Sopenharmony_ci * imx_dsp_rproc_sys_to_da() - internal memory translation helper
39062306a36Sopenharmony_ci * @priv: private data pointer
39162306a36Sopenharmony_ci * @sys: system address (DDR address)
39262306a36Sopenharmony_ci * @len: length of the memory buffer
39362306a36Sopenharmony_ci * @da: device address to translate
39462306a36Sopenharmony_ci *
39562306a36Sopenharmony_ci * Convert system address (DDR address) to device address (DSP)
39662306a36Sopenharmony_ci * for there may be memory remap for device.
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_cistatic int imx_dsp_rproc_sys_to_da(struct imx_dsp_rproc *priv, u64 sys,
39962306a36Sopenharmony_ci				   size_t len, u64 *da)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
40262306a36Sopenharmony_ci	const struct imx_rproc_dcfg *dcfg = dsp_dcfg->dcfg;
40362306a36Sopenharmony_ci	int i;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	/* Parse address translation table */
40662306a36Sopenharmony_ci	for (i = 0; i < dcfg->att_size; i++) {
40762306a36Sopenharmony_ci		const struct imx_rproc_att *att = &dcfg->att[i];
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		if (sys >= att->sa && sys + len <= att->sa + att->size) {
41062306a36Sopenharmony_ci			unsigned int offset = sys - att->sa;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci			*da = att->da + offset;
41362306a36Sopenharmony_ci			return 0;
41462306a36Sopenharmony_ci		}
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return -ENOENT;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/* Main virtqueue message work function
42162306a36Sopenharmony_ci *
42262306a36Sopenharmony_ci * This function is executed upon scheduling of the i.MX DSP remoteproc
42362306a36Sopenharmony_ci * driver's workqueue. The workqueue is scheduled by the mailbox rx
42462306a36Sopenharmony_ci * handler.
42562306a36Sopenharmony_ci *
42662306a36Sopenharmony_ci * This work function processes both the Tx and Rx virtqueue indices on
42762306a36Sopenharmony_ci * every invocation. The rproc_vq_interrupt function can detect if there
42862306a36Sopenharmony_ci * are new unprocessed messages or not (returns IRQ_NONE vs IRQ_HANDLED),
42962306a36Sopenharmony_ci * but there is no need to check for these return values. The index 0
43062306a36Sopenharmony_ci * triggering will process all pending Rx buffers, and the index 1 triggering
43162306a36Sopenharmony_ci * will process all newly available Tx buffers and will wakeup any potentially
43262306a36Sopenharmony_ci * blocked senders.
43362306a36Sopenharmony_ci *
43462306a36Sopenharmony_ci * NOTE:
43562306a36Sopenharmony_ci *    The current logic is based on an inherent design assumption of supporting
43662306a36Sopenharmony_ci *    only 2 vrings, but this can be changed if needed.
43762306a36Sopenharmony_ci */
43862306a36Sopenharmony_cistatic void imx_dsp_rproc_vq_work(struct work_struct *work)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = container_of(work, struct imx_dsp_rproc,
44162306a36Sopenharmony_ci						  rproc_work);
44262306a36Sopenharmony_ci	struct rproc *rproc = priv->rproc;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	mutex_lock(&rproc->lock);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (rproc->state != RPROC_RUNNING)
44762306a36Sopenharmony_ci		goto unlock_mutex;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	rproc_vq_interrupt(priv->rproc, 0);
45062306a36Sopenharmony_ci	rproc_vq_interrupt(priv->rproc, 1);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ciunlock_mutex:
45362306a36Sopenharmony_ci	mutex_unlock(&rproc->lock);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/**
45762306a36Sopenharmony_ci * imx_dsp_rproc_rx_tx_callback() - inbound mailbox message handler
45862306a36Sopenharmony_ci * @cl: mailbox client pointer used for requesting the mailbox channel
45962306a36Sopenharmony_ci * @data: mailbox payload
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci * This handler is invoked by mailbox driver whenever a mailbox
46262306a36Sopenharmony_ci * message is received. Usually, the SUSPEND and RESUME related messages
46362306a36Sopenharmony_ci * are handled in this function, other messages are handled by remoteproc core
46462306a36Sopenharmony_ci */
46562306a36Sopenharmony_cistatic void imx_dsp_rproc_rx_tx_callback(struct mbox_client *cl, void *data)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(cl->dev);
46862306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
46962306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
47062306a36Sopenharmony_ci	u32 message = (u32)(*(u32 *)data);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	dev_dbg(dev, "mbox msg: 0x%x\n", message);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	switch (message) {
47562306a36Sopenharmony_ci	case RP_MBOX_SUSPEND_ACK:
47662306a36Sopenharmony_ci		complete(&priv->pm_comp);
47762306a36Sopenharmony_ci		break;
47862306a36Sopenharmony_ci	case RP_MBOX_RESUME_ACK:
47962306a36Sopenharmony_ci		complete(&priv->pm_comp);
48062306a36Sopenharmony_ci		break;
48162306a36Sopenharmony_ci	default:
48262306a36Sopenharmony_ci		schedule_work(&priv->rproc_work);
48362306a36Sopenharmony_ci		break;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci/**
48862306a36Sopenharmony_ci * imx_dsp_rproc_rxdb_callback() - inbound mailbox message handler
48962306a36Sopenharmony_ci * @cl: mailbox client pointer used for requesting the mailbox channel
49062306a36Sopenharmony_ci * @data: mailbox payload
49162306a36Sopenharmony_ci *
49262306a36Sopenharmony_ci * For doorbell, there is no message specified, just set REMOTE_IS_READY
49362306a36Sopenharmony_ci * flag.
49462306a36Sopenharmony_ci */
49562306a36Sopenharmony_cistatic void imx_dsp_rproc_rxdb_callback(struct mbox_client *cl, void *data)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(cl->dev);
49862306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* Remote is ready after firmware is loaded and running */
50162306a36Sopenharmony_ci	priv->flags |= REMOTE_IS_READY;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * imx_dsp_rproc_mbox_alloc() - request mailbox channels
50662306a36Sopenharmony_ci * @priv: private data pointer
50762306a36Sopenharmony_ci *
50862306a36Sopenharmony_ci * Request three mailbox channels (tx, rx, rxdb).
50962306a36Sopenharmony_ci */
51062306a36Sopenharmony_cistatic int imx_dsp_rproc_mbox_alloc(struct imx_dsp_rproc *priv)
51162306a36Sopenharmony_ci{
51262306a36Sopenharmony_ci	struct device *dev = priv->rproc->dev.parent;
51362306a36Sopenharmony_ci	struct mbox_client *cl;
51462306a36Sopenharmony_ci	int ret;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (!of_get_property(dev->of_node, "mbox-names", NULL))
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	cl = &priv->cl;
52062306a36Sopenharmony_ci	cl->dev = dev;
52162306a36Sopenharmony_ci	cl->tx_block = true;
52262306a36Sopenharmony_ci	cl->tx_tout = 100;
52362306a36Sopenharmony_ci	cl->knows_txdone = false;
52462306a36Sopenharmony_ci	cl->rx_callback = imx_dsp_rproc_rx_tx_callback;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* Channel for sending message */
52762306a36Sopenharmony_ci	priv->tx_ch = mbox_request_channel_byname(cl, "tx");
52862306a36Sopenharmony_ci	if (IS_ERR(priv->tx_ch)) {
52962306a36Sopenharmony_ci		ret = PTR_ERR(priv->tx_ch);
53062306a36Sopenharmony_ci		dev_dbg(cl->dev, "failed to request tx mailbox channel: %d\n",
53162306a36Sopenharmony_ci			ret);
53262306a36Sopenharmony_ci		return ret;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	/* Channel for receiving message */
53662306a36Sopenharmony_ci	priv->rx_ch = mbox_request_channel_byname(cl, "rx");
53762306a36Sopenharmony_ci	if (IS_ERR(priv->rx_ch)) {
53862306a36Sopenharmony_ci		ret = PTR_ERR(priv->rx_ch);
53962306a36Sopenharmony_ci		dev_dbg(cl->dev, "failed to request rx mailbox channel: %d\n",
54062306a36Sopenharmony_ci			ret);
54162306a36Sopenharmony_ci		goto free_channel_tx;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	cl = &priv->cl_rxdb;
54562306a36Sopenharmony_ci	cl->dev = dev;
54662306a36Sopenharmony_ci	cl->rx_callback = imx_dsp_rproc_rxdb_callback;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/*
54962306a36Sopenharmony_ci	 * RX door bell is used to receive the ready signal from remote
55062306a36Sopenharmony_ci	 * after firmware loaded.
55162306a36Sopenharmony_ci	 */
55262306a36Sopenharmony_ci	priv->rxdb_ch = mbox_request_channel_byname(cl, "rxdb");
55362306a36Sopenharmony_ci	if (IS_ERR(priv->rxdb_ch)) {
55462306a36Sopenharmony_ci		ret = PTR_ERR(priv->rxdb_ch);
55562306a36Sopenharmony_ci		dev_dbg(cl->dev, "failed to request mbox chan rxdb, ret %d\n",
55662306a36Sopenharmony_ci			ret);
55762306a36Sopenharmony_ci		goto free_channel_rx;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cifree_channel_rx:
56362306a36Sopenharmony_ci	mbox_free_channel(priv->rx_ch);
56462306a36Sopenharmony_cifree_channel_tx:
56562306a36Sopenharmony_ci	mbox_free_channel(priv->tx_ch);
56662306a36Sopenharmony_ci	return ret;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/*
57062306a36Sopenharmony_ci * imx_dsp_rproc_mbox_no_alloc()
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * Empty function for no mailbox between cores
57362306a36Sopenharmony_ci *
57462306a36Sopenharmony_ci * Always return 0
57562306a36Sopenharmony_ci */
57662306a36Sopenharmony_cistatic int imx_dsp_rproc_mbox_no_alloc(struct imx_dsp_rproc *priv)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	return 0;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic void imx_dsp_rproc_free_mbox(struct imx_dsp_rproc *priv)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	mbox_free_channel(priv->tx_ch);
58462306a36Sopenharmony_ci	mbox_free_channel(priv->rx_ch);
58562306a36Sopenharmony_ci	mbox_free_channel(priv->rxdb_ch);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci/**
58962306a36Sopenharmony_ci * imx_dsp_rproc_add_carveout() - request mailbox channels
59062306a36Sopenharmony_ci * @priv: private data pointer
59162306a36Sopenharmony_ci *
59262306a36Sopenharmony_ci * This function registers specified memory entry in @rproc carveouts list
59362306a36Sopenharmony_ci * The carveouts can help to mapping the memory address for DSP.
59462306a36Sopenharmony_ci */
59562306a36Sopenharmony_cistatic int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
59862306a36Sopenharmony_ci	const struct imx_rproc_dcfg *dcfg = dsp_dcfg->dcfg;
59962306a36Sopenharmony_ci	struct rproc *rproc = priv->rproc;
60062306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
60162306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
60262306a36Sopenharmony_ci	struct of_phandle_iterator it;
60362306a36Sopenharmony_ci	struct rproc_mem_entry *mem;
60462306a36Sopenharmony_ci	struct reserved_mem *rmem;
60562306a36Sopenharmony_ci	void __iomem *cpu_addr;
60662306a36Sopenharmony_ci	int a;
60762306a36Sopenharmony_ci	u64 da;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	/* Remap required addresses */
61062306a36Sopenharmony_ci	for (a = 0; a < dcfg->att_size; a++) {
61162306a36Sopenharmony_ci		const struct imx_rproc_att *att = &dcfg->att[a];
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		if (!(att->flags & ATT_OWN))
61462306a36Sopenharmony_ci			continue;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		if (imx_dsp_rproc_sys_to_da(priv, att->sa, att->size, &da))
61762306a36Sopenharmony_ci			return -EINVAL;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		cpu_addr = devm_ioremap_wc(dev, att->sa, att->size);
62062306a36Sopenharmony_ci		if (!cpu_addr) {
62162306a36Sopenharmony_ci			dev_err(dev, "failed to map memory %p\n", &att->sa);
62262306a36Sopenharmony_ci			return -ENOMEM;
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		/* Register memory region */
62662306a36Sopenharmony_ci		mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)att->sa,
62762306a36Sopenharmony_ci					   att->size, da, NULL, NULL, "dsp_mem");
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		if (mem)
63062306a36Sopenharmony_ci			rproc_coredump_add_segment(rproc, da, att->size);
63162306a36Sopenharmony_ci		else
63262306a36Sopenharmony_ci			return -ENOMEM;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		rproc_add_carveout(rproc, mem);
63562306a36Sopenharmony_ci	}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	of_phandle_iterator_init(&it, np, "memory-region", NULL, 0);
63862306a36Sopenharmony_ci	while (of_phandle_iterator_next(&it) == 0) {
63962306a36Sopenharmony_ci		/*
64062306a36Sopenharmony_ci		 * Ignore the first memory region which will be used vdev buffer.
64162306a36Sopenharmony_ci		 * No need to do extra handlings, rproc_add_virtio_dev will handle it.
64262306a36Sopenharmony_ci		 */
64362306a36Sopenharmony_ci		if (!strcmp(it.node->name, "vdev0buffer"))
64462306a36Sopenharmony_ci			continue;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		rmem = of_reserved_mem_lookup(it.node);
64762306a36Sopenharmony_ci		if (!rmem) {
64862306a36Sopenharmony_ci			of_node_put(it.node);
64962306a36Sopenharmony_ci			dev_err(dev, "unable to acquire memory-region\n");
65062306a36Sopenharmony_ci			return -EINVAL;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		if (imx_dsp_rproc_sys_to_da(priv, rmem->base, rmem->size, &da)) {
65462306a36Sopenharmony_ci			of_node_put(it.node);
65562306a36Sopenharmony_ci			return -EINVAL;
65662306a36Sopenharmony_ci		}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		cpu_addr = devm_ioremap_wc(dev, rmem->base, rmem->size);
65962306a36Sopenharmony_ci		if (!cpu_addr) {
66062306a36Sopenharmony_ci			of_node_put(it.node);
66162306a36Sopenharmony_ci			dev_err(dev, "failed to map memory %p\n", &rmem->base);
66262306a36Sopenharmony_ci			return -ENOMEM;
66362306a36Sopenharmony_ci		}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci		/* Register memory region */
66662306a36Sopenharmony_ci		mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)rmem->base,
66762306a36Sopenharmony_ci					   rmem->size, da, NULL, NULL, it.node->name);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci		if (mem) {
67062306a36Sopenharmony_ci			rproc_coredump_add_segment(rproc, da, rmem->size);
67162306a36Sopenharmony_ci		} else {
67262306a36Sopenharmony_ci			of_node_put(it.node);
67362306a36Sopenharmony_ci			return -ENOMEM;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci		rproc_add_carveout(rproc, mem);
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci/* Prepare function for rproc_ops */
68362306a36Sopenharmony_cistatic int imx_dsp_rproc_prepare(struct rproc *rproc)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
68662306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
68762306a36Sopenharmony_ci	struct rproc_mem_entry *carveout;
68862306a36Sopenharmony_ci	int ret;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	ret = imx_dsp_rproc_add_carveout(priv);
69162306a36Sopenharmony_ci	if (ret) {
69262306a36Sopenharmony_ci		dev_err(dev, "failed on imx_dsp_rproc_add_carveout\n");
69362306a36Sopenharmony_ci		return ret;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	pm_runtime_get_sync(dev);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	/*
69962306a36Sopenharmony_ci	 * Clear buffers after pm rumtime for internal ocram is not
70062306a36Sopenharmony_ci	 * accessible if power and clock are not enabled.
70162306a36Sopenharmony_ci	 */
70262306a36Sopenharmony_ci	list_for_each_entry(carveout, &rproc->carveouts, node) {
70362306a36Sopenharmony_ci		if (carveout->va)
70462306a36Sopenharmony_ci			memset(carveout->va, 0, carveout->len);
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return  0;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci/* Unprepare function for rproc_ops */
71162306a36Sopenharmony_cistatic int imx_dsp_rproc_unprepare(struct rproc *rproc)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	pm_runtime_put_sync(rproc->dev.parent);
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	return  0;
71662306a36Sopenharmony_ci}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci/* Kick function for rproc_ops */
71962306a36Sopenharmony_cistatic void imx_dsp_rproc_kick(struct rproc *rproc, int vqid)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
72262306a36Sopenharmony_ci	struct device *dev = rproc->dev.parent;
72362306a36Sopenharmony_ci	int err;
72462306a36Sopenharmony_ci	__u32 mmsg;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	if (!priv->tx_ch) {
72762306a36Sopenharmony_ci		dev_err(dev, "No initialized mbox tx channel\n");
72862306a36Sopenharmony_ci		return;
72962306a36Sopenharmony_ci	}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/*
73262306a36Sopenharmony_ci	 * Send the index of the triggered virtqueue as the mu payload.
73362306a36Sopenharmony_ci	 * Let remote processor know which virtqueue is used.
73462306a36Sopenharmony_ci	 */
73562306a36Sopenharmony_ci	mmsg = vqid;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	err = mbox_send_message(priv->tx_ch, (void *)&mmsg);
73862306a36Sopenharmony_ci	if (err < 0)
73962306a36Sopenharmony_ci		dev_err(dev, "%s: failed (%d, err:%d)\n", __func__, vqid, err);
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci/*
74362306a36Sopenharmony_ci * Custom memory copy implementation for i.MX DSP Cores
74462306a36Sopenharmony_ci *
74562306a36Sopenharmony_ci * The IRAM is part of the HiFi DSP.
74662306a36Sopenharmony_ci * According to hw specs only 32-bits writes are allowed.
74762306a36Sopenharmony_ci */
74862306a36Sopenharmony_cistatic int imx_dsp_rproc_memcpy(void *dst, const void *src, size_t size)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	void __iomem *dest = (void __iomem *)dst;
75162306a36Sopenharmony_ci	const u8 *src_byte = src;
75262306a36Sopenharmony_ci	const u32 *source = src;
75362306a36Sopenharmony_ci	u32 affected_mask;
75462306a36Sopenharmony_ci	int i, q, r;
75562306a36Sopenharmony_ci	u32 tmp;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* destination must be 32bit aligned */
75862306a36Sopenharmony_ci	if (!IS_ALIGNED((uintptr_t)dest, 4))
75962306a36Sopenharmony_ci		return -EINVAL;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	q = size / 4;
76262306a36Sopenharmony_ci	r = size % 4;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	/* copy data in units of 32 bits at a time */
76562306a36Sopenharmony_ci	for (i = 0; i < q; i++)
76662306a36Sopenharmony_ci		writel(source[i], dest + i * 4);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (r) {
76962306a36Sopenharmony_ci		affected_mask = GENMASK(8 * r, 0);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci		/*
77262306a36Sopenharmony_ci		 * first read the 32bit data of dest, then change affected
77362306a36Sopenharmony_ci		 * bytes, and write back to dest.
77462306a36Sopenharmony_ci		 * For unaffected bytes, it should not be changed
77562306a36Sopenharmony_ci		 */
77662306a36Sopenharmony_ci		tmp = readl(dest + q * 4);
77762306a36Sopenharmony_ci		tmp &= ~affected_mask;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		/* avoid reading after end of source */
78062306a36Sopenharmony_ci		for (i = 0; i < r; i++)
78162306a36Sopenharmony_ci			tmp |= (src_byte[q * 4 + i] << (8 * i));
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci		writel(tmp, dest + q * 4);
78462306a36Sopenharmony_ci	}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return 0;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci/*
79062306a36Sopenharmony_ci * Custom memset implementation for i.MX DSP Cores
79162306a36Sopenharmony_ci *
79262306a36Sopenharmony_ci * The IRAM is part of the HiFi DSP.
79362306a36Sopenharmony_ci * According to hw specs only 32-bits writes are allowed.
79462306a36Sopenharmony_ci */
79562306a36Sopenharmony_cistatic int imx_dsp_rproc_memset(void *addr, u8 value, size_t size)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	void __iomem *tmp_dst = (void __iomem *)addr;
79862306a36Sopenharmony_ci	u32 tmp_val = value;
79962306a36Sopenharmony_ci	u32 affected_mask;
80062306a36Sopenharmony_ci	int q, r;
80162306a36Sopenharmony_ci	u32 tmp;
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* destination must be 32bit aligned */
80462306a36Sopenharmony_ci	if (!IS_ALIGNED((uintptr_t)addr, 4))
80562306a36Sopenharmony_ci		return -EINVAL;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	tmp_val |= tmp_val << 8;
80862306a36Sopenharmony_ci	tmp_val |= tmp_val << 16;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	q = size / 4;
81162306a36Sopenharmony_ci	r = size % 4;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	while (q--)
81462306a36Sopenharmony_ci		writel(tmp_val, tmp_dst++);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if (r) {
81762306a36Sopenharmony_ci		affected_mask = GENMASK(8 * r, 0);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci		/*
82062306a36Sopenharmony_ci		 * first read the 32bit data of addr, then change affected
82162306a36Sopenharmony_ci		 * bytes, and write back to addr.
82262306a36Sopenharmony_ci		 * For unaffected bytes, it should not be changed
82362306a36Sopenharmony_ci		 */
82462306a36Sopenharmony_ci		tmp = readl(tmp_dst);
82562306a36Sopenharmony_ci		tmp &= ~affected_mask;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		tmp |= (tmp_val & affected_mask);
82862306a36Sopenharmony_ci		writel(tmp, tmp_dst);
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return 0;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/*
83562306a36Sopenharmony_ci * imx_dsp_rproc_elf_load_segments() - load firmware segments to memory
83662306a36Sopenharmony_ci * @rproc: remote processor which will be booted using these fw segments
83762306a36Sopenharmony_ci * @fw: the ELF firmware image
83862306a36Sopenharmony_ci *
83962306a36Sopenharmony_ci * This function loads the firmware segments to memory, where the remote
84062306a36Sopenharmony_ci * processor expects them.
84162306a36Sopenharmony_ci *
84262306a36Sopenharmony_ci * Return: 0 on success and an appropriate error code otherwise
84362306a36Sopenharmony_ci */
84462306a36Sopenharmony_cistatic int imx_dsp_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci	struct device *dev = &rproc->dev;
84762306a36Sopenharmony_ci	const void *ehdr, *phdr;
84862306a36Sopenharmony_ci	int i, ret = 0;
84962306a36Sopenharmony_ci	u16 phnum;
85062306a36Sopenharmony_ci	const u8 *elf_data = fw->data;
85162306a36Sopenharmony_ci	u8 class = fw_elf_get_class(fw);
85262306a36Sopenharmony_ci	u32 elf_phdr_get_size = elf_size_of_phdr(class);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	ehdr = elf_data;
85562306a36Sopenharmony_ci	phnum = elf_hdr_get_e_phnum(class, ehdr);
85662306a36Sopenharmony_ci	phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr);
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	/* go through the available ELF segments */
85962306a36Sopenharmony_ci	for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) {
86062306a36Sopenharmony_ci		u64 da = elf_phdr_get_p_paddr(class, phdr);
86162306a36Sopenharmony_ci		u64 memsz = elf_phdr_get_p_memsz(class, phdr);
86262306a36Sopenharmony_ci		u64 filesz = elf_phdr_get_p_filesz(class, phdr);
86362306a36Sopenharmony_ci		u64 offset = elf_phdr_get_p_offset(class, phdr);
86462306a36Sopenharmony_ci		u32 type = elf_phdr_get_p_type(class, phdr);
86562306a36Sopenharmony_ci		void *ptr;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci		if (type != PT_LOAD || !memsz)
86862306a36Sopenharmony_ci			continue;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n",
87162306a36Sopenharmony_ci			type, da, memsz, filesz);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci		if (filesz > memsz) {
87462306a36Sopenharmony_ci			dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n",
87562306a36Sopenharmony_ci				filesz, memsz);
87662306a36Sopenharmony_ci			ret = -EINVAL;
87762306a36Sopenharmony_ci			break;
87862306a36Sopenharmony_ci		}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci		if (offset + filesz > fw->size) {
88162306a36Sopenharmony_ci			dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n",
88262306a36Sopenharmony_ci				offset + filesz, fw->size);
88362306a36Sopenharmony_ci			ret = -EINVAL;
88462306a36Sopenharmony_ci			break;
88562306a36Sopenharmony_ci		}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci		if (!rproc_u64_fit_in_size_t(memsz)) {
88862306a36Sopenharmony_ci			dev_err(dev, "size (%llx) does not fit in size_t type\n",
88962306a36Sopenharmony_ci				memsz);
89062306a36Sopenharmony_ci			ret = -EOVERFLOW;
89162306a36Sopenharmony_ci			break;
89262306a36Sopenharmony_ci		}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci		/* grab the kernel address for this device address */
89562306a36Sopenharmony_ci		ptr = rproc_da_to_va(rproc, da, memsz, NULL);
89662306a36Sopenharmony_ci		if (!ptr) {
89762306a36Sopenharmony_ci			dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da,
89862306a36Sopenharmony_ci				memsz);
89962306a36Sopenharmony_ci			ret = -EINVAL;
90062306a36Sopenharmony_ci			break;
90162306a36Sopenharmony_ci		}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci		/* put the segment where the remote processor expects it */
90462306a36Sopenharmony_ci		if (filesz) {
90562306a36Sopenharmony_ci			ret = imx_dsp_rproc_memcpy(ptr, elf_data + offset, filesz);
90662306a36Sopenharmony_ci			if (ret) {
90762306a36Sopenharmony_ci				dev_err(dev, "memory copy failed for da 0x%llx memsz 0x%llx\n",
90862306a36Sopenharmony_ci					da, memsz);
90962306a36Sopenharmony_ci				break;
91062306a36Sopenharmony_ci			}
91162306a36Sopenharmony_ci		}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		/* zero out remaining memory for this segment */
91462306a36Sopenharmony_ci		if (memsz > filesz) {
91562306a36Sopenharmony_ci			ret = imx_dsp_rproc_memset(ptr + filesz, 0, memsz - filesz);
91662306a36Sopenharmony_ci			if (ret) {
91762306a36Sopenharmony_ci				dev_err(dev, "memset failed for da 0x%llx memsz 0x%llx\n",
91862306a36Sopenharmony_ci					da, memsz);
91962306a36Sopenharmony_ci				break;
92062306a36Sopenharmony_ci			}
92162306a36Sopenharmony_ci		}
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	return ret;
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
92862306a36Sopenharmony_ci{
92962306a36Sopenharmony_ci	if (rproc_elf_load_rsc_table(rproc, fw))
93062306a36Sopenharmony_ci		dev_warn(&rproc->dev, "no resource table found for this firmware\n");
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	return 0;
93362306a36Sopenharmony_ci}
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_cistatic const struct rproc_ops imx_dsp_rproc_ops = {
93662306a36Sopenharmony_ci	.prepare	= imx_dsp_rproc_prepare,
93762306a36Sopenharmony_ci	.unprepare	= imx_dsp_rproc_unprepare,
93862306a36Sopenharmony_ci	.start		= imx_dsp_rproc_start,
93962306a36Sopenharmony_ci	.stop		= imx_dsp_rproc_stop,
94062306a36Sopenharmony_ci	.kick		= imx_dsp_rproc_kick,
94162306a36Sopenharmony_ci	.load		= imx_dsp_rproc_elf_load_segments,
94262306a36Sopenharmony_ci	.parse_fw	= imx_dsp_rproc_parse_fw,
94362306a36Sopenharmony_ci	.sanity_check	= rproc_elf_sanity_check,
94462306a36Sopenharmony_ci	.get_boot_addr	= rproc_elf_get_boot_addr,
94562306a36Sopenharmony_ci};
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci/**
94862306a36Sopenharmony_ci * imx_dsp_attach_pm_domains() - attach the power domains
94962306a36Sopenharmony_ci * @priv: private data pointer
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci * On i.MX8QM and i.MX8QXP there is multiple power domains
95262306a36Sopenharmony_ci * required, so need to link them.
95362306a36Sopenharmony_ci */
95462306a36Sopenharmony_cistatic int imx_dsp_attach_pm_domains(struct imx_dsp_rproc *priv)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	struct device *dev = priv->rproc->dev.parent;
95762306a36Sopenharmony_ci	int ret, i;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	priv->num_domains = of_count_phandle_with_args(dev->of_node,
96062306a36Sopenharmony_ci						       "power-domains",
96162306a36Sopenharmony_ci						       "#power-domain-cells");
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* If only one domain, then no need to link the device */
96462306a36Sopenharmony_ci	if (priv->num_domains <= 1)
96562306a36Sopenharmony_ci		return 0;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	priv->pd_dev = devm_kmalloc_array(dev, priv->num_domains,
96862306a36Sopenharmony_ci					  sizeof(*priv->pd_dev),
96962306a36Sopenharmony_ci					  GFP_KERNEL);
97062306a36Sopenharmony_ci	if (!priv->pd_dev)
97162306a36Sopenharmony_ci		return -ENOMEM;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	priv->pd_dev_link = devm_kmalloc_array(dev, priv->num_domains,
97462306a36Sopenharmony_ci					       sizeof(*priv->pd_dev_link),
97562306a36Sopenharmony_ci					       GFP_KERNEL);
97662306a36Sopenharmony_ci	if (!priv->pd_dev_link)
97762306a36Sopenharmony_ci		return -ENOMEM;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	for (i = 0; i < priv->num_domains; i++) {
98062306a36Sopenharmony_ci		priv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
98162306a36Sopenharmony_ci		if (IS_ERR(priv->pd_dev[i])) {
98262306a36Sopenharmony_ci			ret = PTR_ERR(priv->pd_dev[i]);
98362306a36Sopenharmony_ci			goto detach_pm;
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		/*
98762306a36Sopenharmony_ci		 * device_link_add will check priv->pd_dev[i], if it is
98862306a36Sopenharmony_ci		 * NULL, then will break.
98962306a36Sopenharmony_ci		 */
99062306a36Sopenharmony_ci		priv->pd_dev_link[i] = device_link_add(dev,
99162306a36Sopenharmony_ci						       priv->pd_dev[i],
99262306a36Sopenharmony_ci						       DL_FLAG_STATELESS |
99362306a36Sopenharmony_ci						       DL_FLAG_PM_RUNTIME);
99462306a36Sopenharmony_ci		if (!priv->pd_dev_link[i]) {
99562306a36Sopenharmony_ci			dev_pm_domain_detach(priv->pd_dev[i], false);
99662306a36Sopenharmony_ci			ret = -EINVAL;
99762306a36Sopenharmony_ci			goto detach_pm;
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	return 0;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cidetach_pm:
100462306a36Sopenharmony_ci	while (--i >= 0) {
100562306a36Sopenharmony_ci		device_link_del(priv->pd_dev_link[i]);
100662306a36Sopenharmony_ci		dev_pm_domain_detach(priv->pd_dev[i], false);
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	return ret;
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic int imx_dsp_detach_pm_domains(struct imx_dsp_rproc *priv)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	int i;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	if (priv->num_domains <= 1)
101762306a36Sopenharmony_ci		return 0;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	for (i = 0; i < priv->num_domains; i++) {
102062306a36Sopenharmony_ci		device_link_del(priv->pd_dev_link[i]);
102162306a36Sopenharmony_ci		dev_pm_domain_detach(priv->pd_dev[i], false);
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return 0;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci/**
102862306a36Sopenharmony_ci * imx_dsp_rproc_detect_mode() - detect DSP control mode
102962306a36Sopenharmony_ci * @priv: private data pointer
103062306a36Sopenharmony_ci *
103162306a36Sopenharmony_ci * Different platform has different control method for DSP, which depends
103262306a36Sopenharmony_ci * on how the DSP is integrated in platform.
103362306a36Sopenharmony_ci *
103462306a36Sopenharmony_ci * For i.MX8QXP and i.MX8QM, DSP should be started and stopped by System
103562306a36Sopenharmony_ci * Control Unit.
103662306a36Sopenharmony_ci * For i.MX8MP and i.MX8ULP, DSP should be started and stopped by system
103762306a36Sopenharmony_ci * integration module.
103862306a36Sopenharmony_ci */
103962306a36Sopenharmony_cistatic int imx_dsp_rproc_detect_mode(struct imx_dsp_rproc *priv)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
104262306a36Sopenharmony_ci	struct device *dev = priv->rproc->dev.parent;
104362306a36Sopenharmony_ci	struct regmap *regmap;
104462306a36Sopenharmony_ci	int ret = 0;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	switch (dsp_dcfg->dcfg->method) {
104762306a36Sopenharmony_ci	case IMX_RPROC_SCU_API:
104862306a36Sopenharmony_ci		ret = imx_scu_get_handle(&priv->ipc_handle);
104962306a36Sopenharmony_ci		if (ret)
105062306a36Sopenharmony_ci			return ret;
105162306a36Sopenharmony_ci		break;
105262306a36Sopenharmony_ci	case IMX_RPROC_MMIO:
105362306a36Sopenharmony_ci		regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,dsp-ctrl");
105462306a36Sopenharmony_ci		if (IS_ERR(regmap)) {
105562306a36Sopenharmony_ci			dev_err(dev, "failed to find syscon\n");
105662306a36Sopenharmony_ci			return PTR_ERR(regmap);
105762306a36Sopenharmony_ci		}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci		priv->regmap = regmap;
106062306a36Sopenharmony_ci		break;
106162306a36Sopenharmony_ci	default:
106262306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
106362306a36Sopenharmony_ci		break;
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	return ret;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic const char *imx_dsp_clks_names[DSP_RPROC_CLK_MAX] = {
107062306a36Sopenharmony_ci	/* DSP clocks */
107162306a36Sopenharmony_ci	"core", "ocram", "debug", "ipg", "mu",
107262306a36Sopenharmony_ci};
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_cistatic int imx_dsp_rproc_clk_get(struct imx_dsp_rproc *priv)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct device *dev = priv->rproc->dev.parent;
107762306a36Sopenharmony_ci	struct clk_bulk_data *clks = priv->clks;
107862306a36Sopenharmony_ci	int i;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	for (i = 0; i < DSP_RPROC_CLK_MAX; i++)
108162306a36Sopenharmony_ci		clks[i].id = imx_dsp_clks_names[i];
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	return devm_clk_bulk_get_optional(dev, DSP_RPROC_CLK_MAX, clks);
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_cistatic int imx_dsp_rproc_probe(struct platform_device *pdev)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg;
108962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
109062306a36Sopenharmony_ci	struct imx_dsp_rproc *priv;
109162306a36Sopenharmony_ci	struct rproc *rproc;
109262306a36Sopenharmony_ci	const char *fw_name;
109362306a36Sopenharmony_ci	int ret;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	dsp_dcfg = of_device_get_match_data(dev);
109662306a36Sopenharmony_ci	if (!dsp_dcfg)
109762306a36Sopenharmony_ci		return -ENODEV;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	ret = rproc_of_parse_firmware(dev, 0, &fw_name);
110062306a36Sopenharmony_ci	if (ret) {
110162306a36Sopenharmony_ci		dev_err(dev, "failed to parse firmware-name property, ret = %d\n",
110262306a36Sopenharmony_ci			ret);
110362306a36Sopenharmony_ci		return ret;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	rproc = rproc_alloc(dev, "imx-dsp-rproc", &imx_dsp_rproc_ops, fw_name,
110762306a36Sopenharmony_ci			    sizeof(*priv));
110862306a36Sopenharmony_ci	if (!rproc)
110962306a36Sopenharmony_ci		return -ENOMEM;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	priv = rproc->priv;
111262306a36Sopenharmony_ci	priv->rproc = rproc;
111362306a36Sopenharmony_ci	priv->dsp_dcfg = dsp_dcfg;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	if (no_mailboxes)
111662306a36Sopenharmony_ci		imx_dsp_rproc_mbox_init = imx_dsp_rproc_mbox_no_alloc;
111762306a36Sopenharmony_ci	else
111862306a36Sopenharmony_ci		imx_dsp_rproc_mbox_init = imx_dsp_rproc_mbox_alloc;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	dev_set_drvdata(dev, rproc);
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	INIT_WORK(&priv->rproc_work, imx_dsp_rproc_vq_work);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	ret = imx_dsp_rproc_detect_mode(priv);
112562306a36Sopenharmony_ci	if (ret) {
112662306a36Sopenharmony_ci		dev_err(dev, "failed on imx_dsp_rproc_detect_mode\n");
112762306a36Sopenharmony_ci		goto err_put_rproc;
112862306a36Sopenharmony_ci	}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	/* There are multiple power domains required by DSP on some platform */
113162306a36Sopenharmony_ci	ret = imx_dsp_attach_pm_domains(priv);
113262306a36Sopenharmony_ci	if (ret) {
113362306a36Sopenharmony_ci		dev_err(dev, "failed on imx_dsp_attach_pm_domains\n");
113462306a36Sopenharmony_ci		goto err_put_rproc;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci	/* Get clocks */
113762306a36Sopenharmony_ci	ret = imx_dsp_rproc_clk_get(priv);
113862306a36Sopenharmony_ci	if (ret) {
113962306a36Sopenharmony_ci		dev_err(dev, "failed on imx_dsp_rproc_clk_get\n");
114062306a36Sopenharmony_ci		goto err_detach_domains;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	init_completion(&priv->pm_comp);
114462306a36Sopenharmony_ci	rproc->auto_boot = false;
114562306a36Sopenharmony_ci	ret = rproc_add(rproc);
114662306a36Sopenharmony_ci	if (ret) {
114762306a36Sopenharmony_ci		dev_err(dev, "rproc_add failed\n");
114862306a36Sopenharmony_ci		goto err_detach_domains;
114962306a36Sopenharmony_ci	}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	pm_runtime_enable(dev);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	return 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cierr_detach_domains:
115662306a36Sopenharmony_ci	imx_dsp_detach_pm_domains(priv);
115762306a36Sopenharmony_cierr_put_rproc:
115862306a36Sopenharmony_ci	rproc_free(rproc);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return ret;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_cistatic void imx_dsp_rproc_remove(struct platform_device *pdev)
116462306a36Sopenharmony_ci{
116562306a36Sopenharmony_ci	struct rproc *rproc = platform_get_drvdata(pdev);
116662306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
116962306a36Sopenharmony_ci	rproc_del(rproc);
117062306a36Sopenharmony_ci	imx_dsp_detach_pm_domains(priv);
117162306a36Sopenharmony_ci	rproc_free(rproc);
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci/* pm runtime functions */
117562306a36Sopenharmony_cistatic int imx_dsp_runtime_resume(struct device *dev)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(dev);
117862306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
117962306a36Sopenharmony_ci	const struct imx_dsp_rproc_dcfg *dsp_dcfg = priv->dsp_dcfg;
118062306a36Sopenharmony_ci	int ret;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	/*
118362306a36Sopenharmony_ci	 * There is power domain attached with mailbox, if setup mailbox
118462306a36Sopenharmony_ci	 * in probe(), then the power of mailbox is always enabled,
118562306a36Sopenharmony_ci	 * the power can't be saved.
118662306a36Sopenharmony_ci	 * So move setup of mailbox to runtime resume.
118762306a36Sopenharmony_ci	 */
118862306a36Sopenharmony_ci	ret = imx_dsp_rproc_mbox_init(priv);
118962306a36Sopenharmony_ci	if (ret) {
119062306a36Sopenharmony_ci		dev_err(dev, "failed on imx_dsp_rproc_mbox_init\n");
119162306a36Sopenharmony_ci		return ret;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	ret = clk_bulk_prepare_enable(DSP_RPROC_CLK_MAX, priv->clks);
119562306a36Sopenharmony_ci	if (ret) {
119662306a36Sopenharmony_ci		dev_err(dev, "failed on clk_bulk_prepare_enable\n");
119762306a36Sopenharmony_ci		return ret;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/* Reset DSP if needed */
120162306a36Sopenharmony_ci	if (dsp_dcfg->reset)
120262306a36Sopenharmony_ci		dsp_dcfg->reset(priv);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	return 0;
120562306a36Sopenharmony_ci}
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic int imx_dsp_runtime_suspend(struct device *dev)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(dev);
121062306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	clk_bulk_disable_unprepare(DSP_RPROC_CLK_MAX, priv->clks);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	imx_dsp_rproc_free_mbox(priv);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	return 0;
121762306a36Sopenharmony_ci}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_cistatic void imx_dsp_load_firmware(const struct firmware *fw, void *context)
122062306a36Sopenharmony_ci{
122162306a36Sopenharmony_ci	struct rproc *rproc = context;
122262306a36Sopenharmony_ci	int ret;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	/*
122562306a36Sopenharmony_ci	 * Same flow as start procedure.
122662306a36Sopenharmony_ci	 * Load the ELF segments to memory firstly.
122762306a36Sopenharmony_ci	 */
122862306a36Sopenharmony_ci	ret = rproc_load_segments(rproc, fw);
122962306a36Sopenharmony_ci	if (ret)
123062306a36Sopenharmony_ci		goto out;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/* Start the remote processor */
123362306a36Sopenharmony_ci	ret = rproc->ops->start(rproc);
123462306a36Sopenharmony_ci	if (ret)
123562306a36Sopenharmony_ci		goto out;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	rproc->ops->kick(rproc, 0);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ciout:
124062306a36Sopenharmony_ci	release_firmware(fw);
124162306a36Sopenharmony_ci}
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_cistatic int imx_dsp_suspend(struct device *dev)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(dev);
124662306a36Sopenharmony_ci	struct imx_dsp_rproc *priv = rproc->priv;
124762306a36Sopenharmony_ci	__u32 mmsg = RP_MBOX_SUSPEND_SYSTEM;
124862306a36Sopenharmony_ci	int ret;
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	if (rproc->state != RPROC_RUNNING)
125162306a36Sopenharmony_ci		goto out;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	reinit_completion(&priv->pm_comp);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* Tell DSP that suspend is happening */
125662306a36Sopenharmony_ci	ret = mbox_send_message(priv->tx_ch, (void *)&mmsg);
125762306a36Sopenharmony_ci	if (ret < 0) {
125862306a36Sopenharmony_ci		dev_err(dev, "PM mbox_send_message failed: %d\n", ret);
125962306a36Sopenharmony_ci		return ret;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/*
126362306a36Sopenharmony_ci	 * DSP need to save the context at suspend.
126462306a36Sopenharmony_ci	 * Here waiting the response for DSP, then power can be disabled.
126562306a36Sopenharmony_ci	 */
126662306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&priv->pm_comp, msecs_to_jiffies(100)))
126762306a36Sopenharmony_ci		return -EBUSY;
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ciout:
127062306a36Sopenharmony_ci	/*
127162306a36Sopenharmony_ci	 * The power of DSP is disabled in suspend, so force pm runtime
127262306a36Sopenharmony_ci	 * to be suspend, then we can reenable the power and clocks at
127362306a36Sopenharmony_ci	 * resume stage.
127462306a36Sopenharmony_ci	 */
127562306a36Sopenharmony_ci	return pm_runtime_force_suspend(dev);
127662306a36Sopenharmony_ci}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cistatic int imx_dsp_resume(struct device *dev)
127962306a36Sopenharmony_ci{
128062306a36Sopenharmony_ci	struct rproc *rproc = dev_get_drvdata(dev);
128162306a36Sopenharmony_ci	int ret = 0;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	ret = pm_runtime_force_resume(dev);
128462306a36Sopenharmony_ci	if (ret)
128562306a36Sopenharmony_ci		return ret;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	if (rproc->state != RPROC_RUNNING)
128862306a36Sopenharmony_ci		return 0;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	/*
129162306a36Sopenharmony_ci	 * The power of DSP is disabled at suspend, the memory of dsp
129262306a36Sopenharmony_ci	 * is reset, the image segments are lost. So need to reload
129362306a36Sopenharmony_ci	 * firmware and restart the DSP if it is in running state.
129462306a36Sopenharmony_ci	 */
129562306a36Sopenharmony_ci	ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
129662306a36Sopenharmony_ci				      rproc->firmware, dev, GFP_KERNEL,
129762306a36Sopenharmony_ci				      rproc, imx_dsp_load_firmware);
129862306a36Sopenharmony_ci	if (ret < 0) {
129962306a36Sopenharmony_ci		dev_err(dev, "load firmware failed: %d\n", ret);
130062306a36Sopenharmony_ci		goto err;
130162306a36Sopenharmony_ci	}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	return 0;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_cierr:
130662306a36Sopenharmony_ci	pm_runtime_force_suspend(dev);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return ret;
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic const struct dev_pm_ops imx_dsp_rproc_pm_ops = {
131262306a36Sopenharmony_ci	SYSTEM_SLEEP_PM_OPS(imx_dsp_suspend, imx_dsp_resume)
131362306a36Sopenharmony_ci	RUNTIME_PM_OPS(imx_dsp_runtime_suspend, imx_dsp_runtime_resume, NULL)
131462306a36Sopenharmony_ci};
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_cistatic const struct of_device_id imx_dsp_rproc_of_match[] = {
131762306a36Sopenharmony_ci	{ .compatible = "fsl,imx8qxp-hifi4", .data = &imx_dsp_rproc_cfg_imx8qxp },
131862306a36Sopenharmony_ci	{ .compatible = "fsl,imx8qm-hifi4",  .data = &imx_dsp_rproc_cfg_imx8qm },
131962306a36Sopenharmony_ci	{ .compatible = "fsl,imx8mp-hifi4",  .data = &imx_dsp_rproc_cfg_imx8mp },
132062306a36Sopenharmony_ci	{ .compatible = "fsl,imx8ulp-hifi4", .data = &imx_dsp_rproc_cfg_imx8ulp },
132162306a36Sopenharmony_ci	{},
132262306a36Sopenharmony_ci};
132362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, imx_dsp_rproc_of_match);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_cistatic struct platform_driver imx_dsp_rproc_driver = {
132662306a36Sopenharmony_ci	.probe = imx_dsp_rproc_probe,
132762306a36Sopenharmony_ci	.remove_new = imx_dsp_rproc_remove,
132862306a36Sopenharmony_ci	.driver = {
132962306a36Sopenharmony_ci		.name = "imx-dsp-rproc",
133062306a36Sopenharmony_ci		.of_match_table = imx_dsp_rproc_of_match,
133162306a36Sopenharmony_ci		.pm = pm_ptr(&imx_dsp_rproc_pm_ops),
133262306a36Sopenharmony_ci	},
133362306a36Sopenharmony_ci};
133462306a36Sopenharmony_cimodule_platform_driver(imx_dsp_rproc_driver);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
133762306a36Sopenharmony_ciMODULE_DESCRIPTION("i.MX HiFi Core Remote Processor Control Driver");
133862306a36Sopenharmony_ciMODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
1339