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