diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-25 17:38:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-04-25 17:38:25 -0700 |
commit | 3361e9a4ea957b09c5d6242613360c415194dbb5 (patch) | |
tree | 3af326023edaa8fc12053d3821d8eba2a1926f4c /drivers/remoteproc | |
parent | fe89e9b1af14794a4df9d45b958ff1adf0b98dc1 (diff) | |
parent | 1f6fa392a9942e4a2bd3122913baeb33e987ccd9 (diff) |
Merge tag 'rproc-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux
Pull remoteproc updates from Bjorn Andersson:
- Unnecessary type casts from the 'void *' rproc->priv pointer are
dropped throughout the subsystem.
- A kernel-doc error is corrected in the Mediatek SCPI IPI
implementation
- The firmware loading onto the IMX DSP remote processors is reworked
to avoid non-32bit memory operations. A module parameter is
introduced to assist development of firmware without communication
abilities in place. Error paths in imx_dsp_rproc_mbox_alloc() is
cleaned up
- The cluster configuration handling in the TI K3 R5 driver is
corrected and support for the single-R5 core found in the TI AM62x
SoC family is introduced
- The TI PRU driver device- to virtual-address translation is updated
to avoid compiler warning about the unsigned device-address always
being larger than 0
- The ST remoteproc driver is transitioned to use of_property_present()
- Issues with kicks arriving after the STM32 remote processor has been
shut down are mitigated by checking the processor's state before
handling them.
- Support for mailbox channels for communication with the remote
processors are added to the Xilinx R5 remoteproc driver. The naming
of carveouts are corrected and their parsing is reworked. For this a
couple of fixes targeting the mailbox subsystem are picked up here as
well.
- Reference counting of of_nodes are corrected in the ST, STM32, RCAR
and IMX remoteproc drivers
* tag 'rproc-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/remoteproc/linux: (24 commits)
remoteproc: st: Use of_property_present() for testing DT property presence
dt-bindings: remoteproc: Drop unneeded quotes
remoteproc: imx_dsp_rproc: Fix kernel test robot sparse warning
remoteproc: imx_dsp_rproc: Improve exception handling in imx_dsp_rproc_mbox_alloc()
remoteproc: pru: Remove always true check positive unsigned value
dt-bindings: remoteproc: stm32-rproc: Typo fix
remoteproc: stm32_rproc: Add mutex protection for workqueue
remoteproc: Remove unnecessary (void*) conversions
remoteproc: imx_dsp_rproc: Call of_node_put() on iteration error
remoteproc: imx_rproc: Call of_node_put() on iteration error
remoteproc: rcar_rproc: Call of_node_put() on iteration error
remoteproc: st: Call of_node_put() on iteration error
remoteproc: stm32: Call of_node_put() on iteration error
remoteproc: k3-r5: Use separate compatible string for TI AM62x SoC family
dt-bindings: remoteproc: ti: Add new compatible for AM62 SoC family
remoteproc: k3-r5: Simplify cluster mode setting usage
remoteproc/mtk_scpi_ipi: Fix one kernel-doc comment
remoteproc: xilinx: Add mailbox channels for rpmsg
drivers: remoteproc: xilinx: Fix carveout names
mailbox: zynqmp: Fix typo in IPI documentation
...
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r-- | drivers/remoteproc/da8xx_remoteproc.c | 12 | ||||
-rw-r--r-- | drivers/remoteproc/imx_dsp_rproc.c | 249 | ||||
-rw-r--r-- | drivers/remoteproc/imx_rproc.c | 7 | ||||
-rw-r--r-- | drivers/remoteproc/mtk_scp.c | 12 | ||||
-rw-r--r-- | drivers/remoteproc/mtk_scp_ipi.c | 2 | ||||
-rw-r--r-- | drivers/remoteproc/pru_rproc.c | 5 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_adsp.c | 10 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_mss.c | 8 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_pas.c | 14 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_wcnss.c | 10 | ||||
-rw-r--r-- | drivers/remoteproc/rcar_rproc.c | 9 | ||||
-rw-r--r-- | drivers/remoteproc/st_remoteproc.c | 7 | ||||
-rw-r--r-- | drivers/remoteproc/stm32_rproc.c | 14 | ||||
-rw-r--r-- | drivers/remoteproc/ti_k3_r5_remoteproc.c | 127 | ||||
-rw-r--r-- | drivers/remoteproc/xlnx_r5_remoteproc.c | 324 |
15 files changed, 626 insertions, 184 deletions
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 98e0be9476a4..768217f0f5cd 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -84,7 +84,7 @@ struct da8xx_rproc { */ static irqreturn_t handle_event(int irq, void *p) { - struct rproc *rproc = (struct rproc *)p; + struct rproc *rproc = p; /* Process incoming buffers on all our vrings */ rproc_vq_interrupt(rproc, 0); @@ -104,8 +104,8 @@ static irqreturn_t handle_event(int irq, void *p) */ static irqreturn_t da8xx_rproc_callback(int irq, void *p) { - struct rproc *rproc = (struct rproc *)p; - struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; + struct rproc *rproc = p; + struct da8xx_rproc *drproc = rproc->priv; u32 chipsig; chipsig = readl(drproc->chipsig); @@ -133,7 +133,7 @@ static irqreturn_t da8xx_rproc_callback(int irq, void *p) static int da8xx_rproc_start(struct rproc *rproc) { struct device *dev = rproc->dev.parent; - struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; + struct da8xx_rproc *drproc = rproc->priv; struct clk *dsp_clk = drproc->dsp_clk; struct reset_control *dsp_reset = drproc->dsp_reset; int ret; @@ -183,7 +183,7 @@ static int da8xx_rproc_stop(struct rproc *rproc) /* kick a virtqueue */ static void da8xx_rproc_kick(struct rproc *rproc, int vqid) { - struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; + struct da8xx_rproc *drproc = rproc->priv; /* Interrupt remote proc */ writel(SYSCFG_CHIPSIG2, drproc->chipsig); @@ -360,7 +360,7 @@ free_mem: static int da8xx_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); - struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; + struct da8xx_rproc *drproc = rproc->priv; struct device *dev = &pdev->dev; /* diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 95da1cbefacf..cab06dbf37fb 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -28,6 +28,14 @@ #define DSP_RPROC_CLK_MAX 5 +/* + * Module parameters + */ +static unsigned int no_mailboxes; +module_param_named(no_mailboxes, no_mailboxes, int, 0644); +MODULE_PARM_DESC(no_mailboxes, + "There is no mailbox between cores, so ignore remote proc reply after start, default is 0 (off)."); + #define REMOTE_IS_READY BIT(0) #define REMOTE_READY_WAIT_MAX_RETRIES 500 @@ -172,6 +180,9 @@ static const struct imx_rproc_att imx_dsp_rproc_att_imx8ulp[] = { { 0x30000000, 0x90000000, 0x10000000, 0}, }; +/* Initialize the mailboxes between cores, if exists */ +static int (*imx_dsp_rproc_mbox_init)(struct imx_dsp_rproc *priv); + /* Reset function for DSP on i.MX8MP */ static int imx8mp_dsp_reset(struct imx_dsp_rproc *priv) { @@ -492,12 +503,12 @@ static void imx_dsp_rproc_rxdb_callback(struct mbox_client *cl, void *data) } /** - * imx_dsp_rproc_mbox_init() - request mailbox channels + * imx_dsp_rproc_mbox_alloc() - request mailbox channels * @priv: private data pointer * * Request three mailbox channels (tx, rx, rxdb). */ -static int imx_dsp_rproc_mbox_init(struct imx_dsp_rproc *priv) +static int imx_dsp_rproc_mbox_alloc(struct imx_dsp_rproc *priv) { struct device *dev = priv->rproc->dev.parent; struct mbox_client *cl; @@ -519,7 +530,7 @@ static int imx_dsp_rproc_mbox_init(struct imx_dsp_rproc *priv) ret = PTR_ERR(priv->tx_ch); dev_dbg(cl->dev, "failed to request tx mailbox channel: %d\n", ret); - goto err_out; + return ret; } /* Channel for receiving message */ @@ -528,7 +539,7 @@ static int imx_dsp_rproc_mbox_init(struct imx_dsp_rproc *priv) ret = PTR_ERR(priv->rx_ch); dev_dbg(cl->dev, "failed to request rx mailbox channel: %d\n", ret); - goto err_out; + goto free_channel_tx; } cl = &priv->cl_rxdb; @@ -544,22 +555,30 @@ static int imx_dsp_rproc_mbox_init(struct imx_dsp_rproc *priv) ret = PTR_ERR(priv->rxdb_ch); dev_dbg(cl->dev, "failed to request mbox chan rxdb, ret %d\n", ret); - goto err_out; + goto free_channel_rx; } return 0; -err_out: - if (!IS_ERR(priv->tx_ch)) - mbox_free_channel(priv->tx_ch); - if (!IS_ERR(priv->rx_ch)) - mbox_free_channel(priv->rx_ch); - if (!IS_ERR(priv->rxdb_ch)) - mbox_free_channel(priv->rxdb_ch); - +free_channel_rx: + mbox_free_channel(priv->rx_ch); +free_channel_tx: + mbox_free_channel(priv->tx_ch); return ret; } +/* + * imx_dsp_rproc_mbox_no_alloc() + * + * Empty function for no mailbox between cores + * + * Always return 0 + */ +static int imx_dsp_rproc_mbox_no_alloc(struct imx_dsp_rproc *priv) +{ + return 0; +} + static void imx_dsp_rproc_free_mbox(struct imx_dsp_rproc *priv) { mbox_free_channel(priv->tx_ch); @@ -627,15 +646,19 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) rmem = of_reserved_mem_lookup(it.node); if (!rmem) { + of_node_put(it.node); dev_err(dev, "unable to acquire memory-region\n"); return -EINVAL; } - if (imx_dsp_rproc_sys_to_da(priv, rmem->base, rmem->size, &da)) + if (imx_dsp_rproc_sys_to_da(priv, rmem->base, rmem->size, &da)) { + of_node_put(it.node); return -EINVAL; + } cpu_addr = devm_ioremap_wc(dev, rmem->base, rmem->size); if (!cpu_addr) { + of_node_put(it.node); dev_err(dev, "failed to map memory %p\n", &rmem->base); return -ENOMEM; } @@ -644,10 +667,12 @@ static int imx_dsp_rproc_add_carveout(struct imx_dsp_rproc *priv) mem = rproc_mem_entry_init(dev, (void __force *)cpu_addr, (dma_addr_t)rmem->base, rmem->size, da, NULL, NULL, it.node->name); - if (mem) + if (mem) { rproc_coredump_add_segment(rproc, da, rmem->size); - else + } else { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, mem); } @@ -715,6 +740,191 @@ static void imx_dsp_rproc_kick(struct rproc *rproc, int vqid) dev_err(dev, "%s: failed (%d, err:%d)\n", __func__, vqid, err); } +/* + * Custom memory copy implementation for i.MX DSP Cores + * + * The IRAM is part of the HiFi DSP. + * According to hw specs only 32-bits writes are allowed. + */ +static int imx_dsp_rproc_memcpy(void *dst, const void *src, size_t size) +{ + void __iomem *dest = (void __iomem *)dst; + const u8 *src_byte = src; + const u32 *source = src; + u32 affected_mask; + int i, q, r; + u32 tmp; + + /* destination must be 32bit aligned */ + if (!IS_ALIGNED((uintptr_t)dest, 4)) + return -EINVAL; + + q = size / 4; + r = size % 4; + + /* copy data in units of 32 bits at a time */ + for (i = 0; i < q; i++) + writel(source[i], dest + i * 4); + + if (r) { + affected_mask = GENMASK(8 * r, 0); + + /* + * first read the 32bit data of dest, then change affected + * bytes, and write back to dest. + * For unaffected bytes, it should not be changed + */ + tmp = readl(dest + q * 4); + tmp &= ~affected_mask; + + /* avoid reading after end of source */ + for (i = 0; i < r; i++) + tmp |= (src_byte[q * 4 + i] << (8 * i)); + + writel(tmp, dest + q * 4); + } + + return 0; +} + +/* + * Custom memset implementation for i.MX DSP Cores + * + * The IRAM is part of the HiFi DSP. + * According to hw specs only 32-bits writes are allowed. + */ +static int imx_dsp_rproc_memset(void *addr, u8 value, size_t size) +{ + void __iomem *tmp_dst = (void __iomem *)addr; + u32 tmp_val = value; + u32 affected_mask; + int q, r; + u32 tmp; + + /* destination must be 32bit aligned */ + if (!IS_ALIGNED((uintptr_t)addr, 4)) + return -EINVAL; + + tmp_val |= tmp_val << 8; + tmp_val |= tmp_val << 16; + + q = size / 4; + r = size % 4; + + while (q--) + writel(tmp_val, tmp_dst++); + + if (r) { + affected_mask = GENMASK(8 * r, 0); + + /* + * first read the 32bit data of addr, then change affected + * bytes, and write back to addr. + * For unaffected bytes, it should not be changed + */ + tmp = readl(tmp_dst); + tmp &= ~affected_mask; + + tmp |= (tmp_val & affected_mask); + writel(tmp, tmp_dst); + } + + return 0; +} + +/* + * imx_dsp_rproc_elf_load_segments() - load firmware segments to memory + * @rproc: remote processor which will be booted using these fw segments + * @fw: the ELF firmware image + * + * This function loads the firmware segments to memory, where the remote + * processor expects them. + * + * Return: 0 on success and an appropriate error code otherwise + */ +static int imx_dsp_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = &rproc->dev; + const void *ehdr, *phdr; + int i, ret = 0; + u16 phnum; + const u8 *elf_data = fw->data; + u8 class = fw_elf_get_class(fw); + u32 elf_phdr_get_size = elf_size_of_phdr(class); + + ehdr = elf_data; + phnum = elf_hdr_get_e_phnum(class, ehdr); + phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr); + + /* go through the available ELF segments */ + for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) { + u64 da = elf_phdr_get_p_paddr(class, phdr); + u64 memsz = elf_phdr_get_p_memsz(class, phdr); + u64 filesz = elf_phdr_get_p_filesz(class, phdr); + u64 offset = elf_phdr_get_p_offset(class, phdr); + u32 type = elf_phdr_get_p_type(class, phdr); + void *ptr; + + if (type != PT_LOAD || !memsz) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n", + type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > fw->size) { + dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n", + offset + filesz, fw->size); + ret = -EINVAL; + break; + } + + if (!rproc_u64_fit_in_size_t(memsz)) { + dev_err(dev, "size (%llx) does not fit in size_t type\n", + memsz); + ret = -EOVERFLOW; + break; + } + + /* grab the kernel address for this device address */ + ptr = rproc_da_to_va(rproc, da, memsz, NULL); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da, + memsz); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (filesz) { + ret = imx_dsp_rproc_memcpy(ptr, elf_data + offset, filesz); + if (ret) { + dev_err(dev, "memory copy failed for da 0x%llx memsz 0x%llx\n", + da, memsz); + break; + } + } + + /* zero out remaining memory for this segment */ + if (memsz > filesz) { + ret = imx_dsp_rproc_memset(ptr + filesz, 0, memsz - filesz); + if (ret) { + dev_err(dev, "memset failed for da 0x%llx memsz 0x%llx\n", + da, memsz); + break; + } + } + } + + return ret; +} + static int imx_dsp_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) { if (rproc_elf_load_rsc_table(rproc, fw)) @@ -729,7 +939,7 @@ static const struct rproc_ops imx_dsp_rproc_ops = { .start = imx_dsp_rproc_start, .stop = imx_dsp_rproc_stop, .kick = imx_dsp_rproc_kick, - .load = rproc_elf_load_segments, + .load = imx_dsp_rproc_elf_load_segments, .parse_fw = imx_dsp_rproc_parse_fw, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, @@ -903,6 +1113,11 @@ static int imx_dsp_rproc_probe(struct platform_device *pdev) priv->rproc = rproc; priv->dsp_dcfg = dsp_dcfg; + if (no_mailboxes) + imx_dsp_rproc_mbox_init = imx_dsp_rproc_mbox_no_alloc; + else + imx_dsp_rproc_mbox_init = imx_dsp_rproc_mbox_alloc; + dev_set_drvdata(dev, rproc); INIT_WORK(&priv->rproc_work, imx_dsp_rproc_vq_work); diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 9fc978e0393c..0ab840dc7e97 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -541,6 +541,7 @@ static int imx_rproc_prepare(struct rproc *rproc) rmem = of_reserved_mem_lookup(it.node); if (!rmem) { + of_node_put(it.node); dev_err(priv->dev, "unable to acquire memory-region\n"); return -EINVAL; } @@ -553,10 +554,12 @@ static int imx_rproc_prepare(struct rproc *rproc) imx_rproc_mem_alloc, imx_rproc_mem_release, it.node->name); - if (mem) + if (mem) { rproc_coredump_add_segment(rproc, da, rmem->size); - else + } else { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, mem); } diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 0861b76f185f..e1d93e63d7df 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -74,8 +74,8 @@ static void scp_wdt_handler(struct mtk_scp *scp, u32 scp_to_host) static void scp_init_ipi_handler(void *data, unsigned int len, void *priv) { - struct mtk_scp *scp = (struct mtk_scp *)priv; - struct scp_run *run = (struct scp_run *)data; + struct mtk_scp *scp = priv; + struct scp_run *run = data; scp->run.signaled = run->signaled; strscpy(scp->run.fw_ver, run->fw_ver, SCP_FW_VER_LEN); @@ -498,7 +498,7 @@ static int scp_parse_fw(struct rproc *rproc, const struct firmware *fw) static int scp_start(struct rproc *rproc) { - struct mtk_scp *scp = (struct mtk_scp *)rproc->priv; + struct mtk_scp *scp = rproc->priv; struct device *dev = scp->dev; struct scp_run *run = &scp->run; int ret; @@ -587,7 +587,7 @@ static void *mt8192_scp_da_to_va(struct mtk_scp *scp, u64 da, size_t len) static void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { - struct mtk_scp *scp = (struct mtk_scp *)rproc->priv; + struct mtk_scp *scp = rproc->priv; return scp->data->scp_da_to_va(scp, da, len); } @@ -627,7 +627,7 @@ static void mt8195_scp_stop(struct mtk_scp *scp) static int scp_stop(struct rproc *rproc) { - struct mtk_scp *scp = (struct mtk_scp *)rproc->priv; + struct mtk_scp *scp = rproc->priv; int ret; ret = clk_prepare_enable(scp->clk); @@ -829,7 +829,7 @@ static int scp_probe(struct platform_device *pdev) if (!rproc) return dev_err_probe(dev, -ENOMEM, "unable to allocate remoteproc\n"); - scp = (struct mtk_scp *)rproc->priv; + scp = rproc->priv; scp->rproc = rproc; scp->dev = dev; scp->data = of_device_get_match_data(dev); diff --git a/drivers/remoteproc/mtk_scp_ipi.c b/drivers/remoteproc/mtk_scp_ipi.c index fc55df649b40..9c7c17b9d181 100644 --- a/drivers/remoteproc/mtk_scp_ipi.c +++ b/drivers/remoteproc/mtk_scp_ipi.c @@ -125,7 +125,7 @@ void scp_ipi_lock(struct mtk_scp *scp, u32 id) EXPORT_SYMBOL_GPL(scp_ipi_lock); /** - * scp_ipi_lock() - Unlock after operations of an IPI ID + * scp_ipi_unlock() - Unlock after operations of an IPI ID * * @scp: mtk_scp structure * @id: IPI ID diff --git a/drivers/remoteproc/pru_rproc.c b/drivers/remoteproc/pru_rproc.c index b76db7fa693d..095f66130f48 100644 --- a/drivers/remoteproc/pru_rproc.c +++ b/drivers/remoteproc/pru_rproc.c @@ -657,7 +657,7 @@ static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, size_t len) swap(dram0, dram1); shrd_ram = pruss->mem_regions[PRUSS_MEM_SHRD_RAM2]; - if (da >= PRU_PDRAM_DA && da + len <= PRU_PDRAM_DA + dram0.size) { + if (da + len <= PRU_PDRAM_DA + dram0.size) { offset = da - PRU_PDRAM_DA; va = (__force void *)(dram0.va + offset); } else if (da >= PRU_SDRAM_DA && @@ -706,8 +706,7 @@ static void *pru_i_da_to_va(struct pru_rproc *pru, u32 da, size_t len) */ da &= 0xfffff; - if (da >= PRU_IRAM_DA && - da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { + if (da + len <= PRU_IRAM_DA + pru->mem_regions[PRU_IOMEM_IRAM].size) { offset = da - PRU_IRAM_DA; va = (__force void *)(pru->mem_regions[PRU_IOMEM_IRAM].va + offset); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 08d8dad22ca7..d546ab9dc141 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -321,7 +321,7 @@ reset: static int adsp_load(struct rproc *rproc, const struct firmware *fw) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int ret; ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, 0, @@ -379,7 +379,7 @@ static int adsp_map_carveout(struct rproc *rproc) static int adsp_start(struct rproc *rproc) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int ret; unsigned int val; @@ -469,7 +469,7 @@ static void qcom_adsp_pil_handover(struct qcom_q6v5 *q6v5) static int adsp_stop(struct rproc *rproc) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int handover; int ret; @@ -492,7 +492,7 @@ static int adsp_stop(struct rproc *rproc) static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int offset; offset = da - adsp->mem_reloc; @@ -696,7 +696,7 @@ static int adsp_probe(struct platform_device *pdev) rproc->has_iommu = desc->has_iommu; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); - adsp = (struct qcom_adsp *)rproc->priv; + adsp = rproc->priv; adsp->dev = &pdev->dev; adsp->rproc = rproc; adsp->info_name = desc->sysmon_name; diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 1ba711bc0100..8e15e4f85de1 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1562,7 +1562,7 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, static int q6v5_start(struct rproc *rproc) { - struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + struct q6v5 *qproc = rproc->priv; int xfermemop_ret; int ret; @@ -1604,7 +1604,7 @@ reclaim_mpss: static int q6v5_stop(struct rproc *rproc) { - struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + struct q6v5 *qproc = rproc->priv; int ret; ret = qcom_q6v5_request_stop(&qproc->q6v5, qproc->sysmon); @@ -1662,7 +1662,7 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, static unsigned long q6v5_panic(struct rproc *rproc) { - struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + struct q6v5 *qproc = rproc->priv; return qcom_q6v5_panic(&qproc->q6v5); } @@ -1977,7 +1977,7 @@ static int q6v5_probe(struct platform_device *pdev) rproc->auto_boot = false; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); - qproc = (struct q6v5 *)rproc->priv; + qproc = rproc->priv; qproc->dev = &pdev->dev; qproc->rproc = rproc; qproc->hexagon_mdt_image = "modem.mdt"; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index c99a20542685..e34d82b18a67 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -186,7 +186,7 @@ static int adsp_shutdown_poll_decrypt(struct qcom_adsp *adsp) static int adsp_unprepare(struct rproc *rproc) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; /* * adsp_load() did pass pas_metadata to the SCM driver for storing @@ -203,7 +203,7 @@ static int adsp_unprepare(struct rproc *rproc) static int adsp_load(struct rproc *rproc, const struct firmware *fw) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int ret; /* Store firmware handle to be used in adsp_start() */ @@ -244,7 +244,7 @@ release_dtb_firmware: static int adsp_start(struct rproc *rproc) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int ret; ret = qcom_q6v5_prepare(&adsp->q6v5); @@ -360,7 +360,7 @@ static void qcom_pas_handover(struct qcom_q6v5 *q6v5) static int adsp_stop(struct rproc *rproc) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int handover; int ret; @@ -390,7 +390,7 @@ static int adsp_stop(struct rproc *rproc) static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; int offset; offset = da - adsp->mem_reloc; @@ -405,7 +405,7 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iom static unsigned long adsp_panic(struct rproc *rproc) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + struct qcom_adsp *adsp = rproc->priv; return qcom_q6v5_panic(&adsp->q6v5); } @@ -683,7 +683,7 @@ static int adsp_probe(struct platform_device *pdev) rproc->auto_boot = desc->auto_boot; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); - adsp = (struct qcom_adsp *)rproc->priv; + adsp = rproc->priv; adsp->dev = &pdev->dev; adsp->rproc = rproc; adsp->minidump_id = desc->minidump_id; diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index 9d4d04fff8c6..0fc317265064 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -154,7 +154,7 @@ static const struct wcnss_data pronto_v3_data = { static int wcnss_load(struct rproc *rproc, const struct firmware *fw) { - struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + struct qcom_wcnss *wcnss = rproc->priv; int ret; ret = qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, @@ -227,7 +227,7 @@ static void wcnss_configure_iris(struct qcom_wcnss *wcnss) static int wcnss_start(struct rproc *rproc) { - struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + struct qcom_wcnss *wcnss = rproc->priv; int ret, i; mutex_lock(&wcnss->iris_lock); @@ -293,7 +293,7 @@ release_iris_lock: static int wcnss_stop(struct rproc *rproc) { - struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + struct qcom_wcnss *wcnss = rproc->priv; int ret; if (wcnss->state) { @@ -320,7 +320,7 @@ static int wcnss_stop(struct rproc *rproc) static void *wcnss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { - struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; + struct qcom_wcnss *wcnss = rproc->priv; int offset; offset = da - wcnss->mem_reloc; @@ -566,7 +566,7 @@ static int wcnss_probe(struct platform_device *pdev) } rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); - wcnss = (struct qcom_wcnss *)rproc->priv; + wcnss = rproc->priv; wcnss->dev = &pdev->dev; wcnss->rproc = rproc; platform_set_drvdata(pdev, wcnss); diff --git a/drivers/remoteproc/rcar_rproc.c b/drivers/remoteproc/rcar_rproc.c index aa86154109c7..1ff2a73ade90 100644 --- a/drivers/remoteproc/rcar_rproc.c +++ b/drivers/remoteproc/rcar_rproc.c @@ -62,13 +62,16 @@ static int rcar_rproc_prepare(struct rproc *rproc) rmem = of_reserved_mem_lookup(it.node); if (!rmem) { + of_node_put(it.node); dev_err(&rproc->dev, "unable to acquire memory-region\n"); return -EINVAL; } - if (rmem->base > U32_MAX) + if (rmem->base > U32_MAX) { + of_node_put(it.node); return -EINVAL; + } /* No need to translate pa to da, R-Car use same map */ da = rmem->base; @@ -79,8 +82,10 @@ static int rcar_rproc_prepare(struct rproc *rproc) rcar_rproc_mem_release, it.node->name); - if (!mem) + if (!mem) { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, mem); } diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index a3268d95a50e..3f1b8963639f 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -129,6 +129,7 @@ static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) while (of_phandle_iterator_next(&it) == 0) { rmem = of_reserved_mem_lookup(it.node); if (!rmem) { + of_node_put(it.node); dev_err(dev, "unable to acquire memory-region\n"); return -EINVAL; } @@ -150,8 +151,10 @@ static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) it.node->name); } - if (!mem) + if (!mem) { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, mem); index++; @@ -379,7 +382,7 @@ static int st_rproc_probe(struct platform_device *pdev) clk_set_rate(ddata->clk, ddata->clk_rate); } - if (of_get_property(np, "mbox-names", NULL)) { + if (of_property_present(np, "mbox-names")) { ddata->mbox_client_vq0.dev = dev; ddata->mbox_client_vq0.tx_done = NULL; ddata->mbox_client_vq0.tx_block = false; diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 7d782ed9e589..8746cbb1f168 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -223,11 +223,13 @@ static int stm32_rproc_prepare(struct rproc *rproc) while (of_phandle_iterator_next(&it) == 0) { rmem = of_reserved_mem_lookup(it.node); if (!rmem) { + of_node_put(it.node); dev_err(dev, "unable to acquire memory-region\n"); return -EINVAL; } if (stm32_rproc_pa_to_da(rproc, rmem->base, &da) < 0) { + of_node_put(it.node); dev_err(dev, "memory region not valid %pa\n", &rmem->base); return -EINVAL; @@ -254,8 +256,10 @@ static int stm32_rproc_prepare(struct rproc *rproc) it.node->name); } - if (!mem) + if (!mem) { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, mem); index++; @@ -287,8 +291,16 @@ static void stm32_rproc_mb_vq_work(struct work_struct *work) struct stm32_mbox *mb = container_of(work, struct stm32_mbox, vq_work); struct rproc *rproc = dev_get_drvdata(mb->client.dev); + mutex_lock(&rproc->lock); + + if (rproc->state != RPROC_RUNNING) + goto unlock_mutex; + if (rproc_vq_interrupt(rproc, mb->vq_id) == IRQ_NONE) dev_dbg(&rproc->dev, "no message found in vq%d\n", mb->vq_id); + +unlock_mutex: + mutex_unlock(&rproc->lock); } static void stm32_rproc_mb_callback(struct mbox_client *cl, void *data) diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 0481926c6975..23fe44d4d7a5 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -71,14 +71,16 @@ struct k3_r5_mem { /* * All cluster mode values are not applicable on all SoCs. The following * are the modes supported on various SoCs: - * Split mode : AM65x, J721E, J7200 and AM64x SoCs - * LockStep mode : AM65x, J721E and J7200 SoCs - * Single-CPU mode : AM64x SoCs only + * Split mode : AM65x, J721E, J7200 and AM64x SoCs + * LockStep mode : AM65x, J721E and J7200 SoCs + * Single-CPU mode : AM64x SoCs only + * Single-Core mode : AM62x, AM62A SoCs */ enum cluster_mode { CLUSTER_MODE_SPLIT = 0, CLUSTER_MODE_LOCKSTEP, CLUSTER_MODE_SINGLECPU, + CLUSTER_MODE_SINGLECORE }; /** @@ -86,11 +88,13 @@ enum cluster_mode { * @tcm_is_double: flag to denote the larger unified TCMs in certain modes * @tcm_ecc_autoinit: flag to denote the auto-initialization of TCMs for ECC * @single_cpu_mode: flag to denote if SoC/IP supports Single-CPU mode + * @is_single_core: flag to denote if SoC/IP has only single core R5 */ struct k3_r5_soc_data { bool tcm_is_double; bool tcm_ecc_autoinit; bool single_cpu_mode; + bool is_single_core; }; /** @@ -838,7 +842,8 @@ static int k3_r5_rproc_configure(struct k3_r5_rproc *kproc) core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); if (cluster->mode == CLUSTER_MODE_LOCKSTEP || - cluster->mode == CLUSTER_MODE_SINGLECPU) { + cluster->mode == CLUSTER_MODE_SINGLECPU || + cluster->mode == CLUSTER_MODE_SINGLECORE) { core = core0; } else { core = kproc->core; @@ -852,38 +857,34 @@ static int k3_r5_rproc_configure(struct k3_r5_rproc *kproc) dev_dbg(dev, "boot_vector = 0x%llx, cfg = 0x%x ctrl = 0x%x stat = 0x%x\n", boot_vec, cfg, ctrl, stat); - /* check if only Single-CPU mode is supported on applicable SoCs */ - if (cluster->soc_data->single_cpu_mode) { - single_cpu = - !!(stat & PROC_BOOT_STATUS_FLAG_R5_SINGLECORE_ONLY); - if (single_cpu && cluster->mode == CLUSTER_MODE_SPLIT) { - dev_err(cluster->dev, "split-mode not permitted, force configuring for single-cpu mode\n"); - cluster->mode = CLUSTER_MODE_SINGLECPU; - } - goto config; + single_cpu = !!(stat & PROC_BOOT_STATUS_FLAG_R5_SINGLECORE_ONLY); + lockstep_en = !!(stat & PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED); + + /* Override to single CPU mode if set in status flag */ + if (single_cpu && cluster->mode == CLUSTER_MODE_SPLIT) { + dev_err(cluster->dev, "split-mode not permitted, force configuring for single-cpu mode\n"); + cluster->mode = CLUSTER_MODE_SINGLECPU; } - /* check conventional LockStep vs Split mode configuration */ - lockstep_en = !!(stat & PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED); + /* Override to split mode if lockstep enable bit is not set in status flag */ if (!lockstep_en && cluster->mode == CLUSTER_MODE_LOCKSTEP) { dev_err(cluster->dev, "lockstep mode not permitted, force configuring for split-mode\n"); cluster->mode = CLUSTER_MODE_SPLIT; } -config: /* always enable ARM mode and set boot vector to 0 */ boot_vec = 0x0; if (core == core0) { clr_cfg = PROC_BOOT_CFG_FLAG_R5_TEINIT; - if (cluster->soc_data->single_cpu_mode) { - /* - * Single-CPU configuration bit can only be configured - * on Core0 and system firmware will NACK any requests - * with the bit configured, so program it only on - * permitted cores - */ - if (cluster->mode == CLUSTER_MODE_SINGLECPU) - set_cfg = PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE; + /* + * Single-CPU configuration bit can only be configured + * on Core0 and system firmware will NACK any requests + * with the bit configured, so program it only on + * permitted cores + */ + if (cluster->mode == CLUSTER_MODE_SINGLECPU || + cluster->mode == CLUSTER_MODE_SINGLECORE) { + set_cfg = PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE; } else { /* * LockStep configuration bit is Read-only on Split-mode @@ -1074,6 +1075,7 @@ static void k3_r5_adjust_tcm_sizes(struct k3_r5_rproc *kproc) if (cluster->mode == CLUSTER_MODE_LOCKSTEP || cluster->mode == CLUSTER_MODE_SINGLECPU || + cluster->mode == CLUSTER_MODE_SINGLECORE || !cluster->soc_data->tcm_is_double) return; @@ -1108,12 +1110,12 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) struct k3_r5_cluster *cluster = kproc->cluster; struct k3_r5_core *core = kproc->core; struct device *cdev = core->dev; - bool r_state = false, c_state = false; + bool r_state = false, c_state = false, lockstep_en = false, single_cpu = false; u32 ctrl = 0, cfg = 0, stat = 0, halted = 0; u64 boot_vec = 0; u32 atcm_enable, btcm_enable, loczrama; struct k3_r5_core *core0; - enum cluster_mode mode; + enum cluster_mode mode = cluster->mode; int ret; core0 = list_first_entry(&cluster->cores, struct k3_r5_core, elem); @@ -1147,13 +1149,14 @@ static int k3_r5_rproc_configure_mode(struct k3_r5_rproc *kproc) atcm_enable = cfg & PROC_BOOT_CFG_FLAG_R5_ATCM_EN ? 1 : 0; btcm_enable = cfg & PROC_BOOT_CFG_FLAG_R5_BTCM_EN ? 1 : 0; loczrama = cfg & PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE ? 1 : 0; - if (cluster->soc_data->single_cpu_mode) { - mode = cfg & PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE ? - CLUSTER_MODE_SINGLECPU : CLUSTER_MODE_SPLIT; - } else { - mode = cfg & PROC_BOOT_CFG_FLAG_R5_LOCKSTEP ? - CLUSTER_MODE_LOCKSTEP : CLUSTER_MODE_SPLIT; - } + single_cpu = cfg & PROC_BOOT_CFG_FLAG_R5_SINGLE_CORE ? 1 : 0; + lockstep_en = cfg & PROC_BOOT_CFG_FLAG_R5_LOCKSTEP ? 1 : 0; + + if (single_cpu && mode != CLUSTER_MODE_SINGLECORE) + mode = CLUSTER_MODE_SINGLECPU; + if (lockstep_en) + mode = CLUSTER_MODE_LOCKSTEP; + halted = ctrl & PROC_BOOT_CTRL_FLAG_R5_CORE_HALT; /* @@ -1269,9 +1272,12 @@ init_rmem: goto err_add; } - /* create only one rproc in lockstep mode or single-cpu mode */ + /* create only one rproc in lockstep, single-cpu or + * single core mode + */ if (cluster->mode == CLUSTER_MODE_LOCKSTEP || - cluster->mode == CLUSTER_MODE_SINGLECPU) + cluster->mode == CLUSTER_MODE_SINGLECPU || + cluster->mode == CLUSTER_MODE_SINGLECORE) break; } @@ -1700,12 +1706,6 @@ static int k3_r5_probe(struct platform_device *pdev) return -ENOMEM; cluster->dev = dev; - /* - * default to most common efuse configurations - Split-mode on AM64x - * and LockStep-mode on all others - */ - cluster->mode = data->single_cpu_mode ? - CLUSTER_MODE_SPLIT : CLUSTER_MODE_LOCKSTEP; cluster->soc_data = data; INIT_LIST_HEAD(&cluster->cores); @@ -1716,9 +1716,37 @@ static int k3_r5_probe(struct platform_device *pdev) return ret; } + if (ret == -EINVAL) { + /* + * default to most common efuse configurations - Split-mode on AM64x + * and LockStep-mode on all others + * default to most common efuse configurations - + * Split-mode on AM64x + * Single core on AM62x + * LockStep-mode on all others + */ + if (!data->is_single_core) + cluster->mode = data->single_cpu_mode ? + CLUSTER_MODE_SPLIT : CLUSTER_MODE_LOCKSTEP; + else + cluster->mode = CLUSTER_MODE_SINGLECORE; + } + + if ((cluster->mode == CLUSTER_MODE_SINGLECPU && !data->single_cpu_mode) || + (cluster->mode == CLUSTER_MODE_SINGLECORE && !data->is_single_core)) { + dev_err(dev, "Cluster mode = %d is not supported on this SoC\n", cluster->mode); + return -EINVAL; + } + num_cores = of_get_available_child_count(np); - if (num_cores != 2) { - dev_err(dev, "MCU cluster requires both R5F cores to be enabled, num_cores = %d\n", + if (num_cores != 2 && !data->is_single_core) { + dev_err(dev, "MCU cluster requires both R5F cores to be enabled but num_cores is set to = %d\n", + num_cores); + return -ENODEV; + } + + if (num_cores != 1 && data->is_single_core) { + dev_err(dev, "SoC supports only single core R5 but num_cores is set to %d\n", num_cores); return -ENODEV; } @@ -1760,18 +1788,28 @@ static const struct k3_r5_soc_data am65_j721e_soc_data = { .tcm_is_double = false, .tcm_ecc_autoinit = false, .single_cpu_mode = false, + .is_single_core = false, }; static const struct k3_r5_soc_data j7200_j721s2_soc_data = { .tcm_is_double = true, .tcm_ecc_autoinit = true, .single_cpu_mode = false, + .is_single_core = false, }; static const struct k3_r5_soc_data am64_soc_data = { .tcm_is_double = true, .tcm_ecc_autoinit = true, .single_cpu_mode = true, + .is_single_core = false, +}; + +static const struct k3_r5_soc_data am62_soc_data = { + .tcm_is_double = false, + .tcm_ecc_autoinit = true, + .single_cpu_mode = false, + .is_single_core = true, }; static const struct of_device_id k3_r5_of_match[] = { @@ -1779,6 +1817,7 @@ static const struct of_device_id k3_r5_of_match[] = { { .compatible = "ti,j721e-r5fss", .data = &am65_j721e_soc_data, }, { .compatible = "ti,j7200-r5fss", .data = &j7200_j721s2_soc_data, }, { .compatible = "ti,am64-r5fss", .data = &am64_soc_data, }, + { .compatible = "ti,am62-r5fss", .data = &am62_soc_data, }, { .compatible = "ti,j721s2-r5fss", .data = &j7200_j721s2_soc_data, }, { /* sentinel */ }, }; diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 2db57d394155..feca6de68da2 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -8,16 +8,23 @@ #include <linux/dma-mapping.h> #include <linux/firmware/xlnx-zynqmp.h> #include <linux/kernel.h> +#include <linux/mailbox_client.h> +#include <linux/mailbox/zynqmp-ipi-message.h> #include <linux/module.h> #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/remoteproc.h> -#include <linux/slab.h> #include "remoteproc_internal.h" +/* IPI buffer MAX length */ +#define IPI_BUF_LEN_MAX 32U + +/* RX mailbox client buffer max length */ +#define MBOX_CLIENT_BUF_MAX (IPI_BUF_LEN_MAX + \ + sizeof(struct zynqmp_ipi_message)) /* * settings for RPU cluster mode which * reflects possible values of xlnx,cluster-mode dt-property @@ -43,6 +50,27 @@ struct mem_bank_data { char *bank_name; }; +/** + * struct mbox_info + * + * @rx_mc_buf: to copy data from mailbox rx channel + * @tx_mc_buf: to copy data to mailbox tx channel + * @r5_core: this mailbox's corresponding r5_core pointer + * @mbox_work: schedule work after receiving data from mailbox + * @mbox_cl: mailbox client + * @tx_chan: mailbox tx channel + * @rx_chan: mailbox rx channel + */ +struct mbox_info { + unsigned char rx_mc_buf[MBOX_CLIENT_BUF_MAX]; + unsigned char tx_mc_buf[MBOX_CLIENT_BUF_MAX]; + struct zynqmp_r5_core *r5_core; + struct work_struct mbox_work; + struct mbox_client mbox_cl; + struct mbox_chan *tx_chan; + struct mbox_chan *rx_chan; +}; + /* * Hardcoded TCM bank values. This will be removed once TCM bindings are * accepted for system-dt specifications and upstreamed in linux kernel @@ -61,20 +89,18 @@ static const struct mem_bank_data zynqmp_tcm_banks[] = { * @np: device node of RPU instance * @tcm_bank_count: number TCM banks accessible to this RPU * @tcm_banks: array of each TCM bank data - * @rmem_count: Number of reserved mem regions - * @rmem: reserved memory region nodes from device tree * @rproc: rproc handle * @pm_domain_id: RPU CPU power domain id + * @ipi: pointer to mailbox information */ struct zynqmp_r5_core { struct device *dev; struct device_node *np; int tcm_bank_count; struct mem_bank_data **tcm_banks; - int rmem_count; - struct reserved_mem **rmem; struct rproc *rproc; u32 pm_domain_id; + struct mbox_info *ipi; }; /** @@ -92,6 +118,178 @@ struct zynqmp_r5_cluster { struct zynqmp_r5_core **r5_cores; }; +/** + * event_notified_idr_cb() - callback for vq_interrupt per notifyid + * @id: rproc->notify id + * @ptr: pointer to idr private data + * @data: data passed to idr_for_each callback + * + * Pass notification to remoteproc virtio + * + * Return: 0. having return is to satisfy the idr_for_each() function + * pointer input argument requirement. + **/ +static int event_notified_idr_cb(int id, void *ptr, void *data) +{ + struct rproc *rproc = data; + + if (rproc_vq_interrupt(rproc, id) == IRQ_NONE) + dev_dbg(&rproc->dev, "data not found for vqid=%d\n", id); + + return 0; +} + +/** + * handle_event_notified() - remoteproc notification work function + * @work: pointer to the work structure + * + * It checks each registered remoteproc notify IDs. + */ +static void handle_event_notified(struct work_struct *work) +{ + struct mbox_info *ipi; + struct rproc *rproc; + + ipi = container_of(work, struct mbox_info, mbox_work); + rproc = ipi->r5_core->rproc; + + /* + * We only use IPI for interrupt. The RPU firmware side may or may + * not write the notifyid when it trigger IPI. + * And thus, we scan through all the registered notifyids and + * find which one is valid to get the message. + * Even if message from firmware is NULL, we attempt to get vqid + */ + idr_for_each(&rproc->notifyids, event_notified_idr_cb, rproc); +} + +/** + * zynqmp_r5_mb_rx_cb() - receive channel mailbox callback + * @cl: mailbox client + * @msg: message pointer + * + * Receive data from ipi buffer, ack interrupt and then + * it will schedule the R5 notification work. + */ +static void zynqmp_r5_mb_rx_cb(struct mbox_client *cl, void *msg) +{ + struct zynqmp_ipi_message *ipi_msg, *buf_msg; + struct mbox_info *ipi; + size_t len; + + ipi = container_of(cl, struct mbox_info, mbox_cl); + + /* copy data from ipi buffer to r5_core */ + ipi_msg = (struct zynqmp_ipi_message *)msg; + buf_msg = (struct zynqmp_ipi_message *)ipi->rx_mc_buf; + len = ipi_msg->len; + if (len > IPI_BUF_LEN_MAX) { + dev_warn(cl->dev, "msg size exceeded than %d\n", + IPI_BUF_LEN_MAX); + len = IPI_BUF_LEN_MAX; + } + buf_msg->len = len; + memcpy(buf_msg->data, ipi_msg->data, len); + + /* received and processed interrupt ack */ + if (mbox_send_message(ipi->rx_chan, NULL) < 0) + dev_err(cl->dev, "ack failed to mbox rx_chan\n"); + + schedule_work(&ipi->mbox_work); +} + +/** + * zynqmp_r5_setup_mbox() - Setup mailboxes related properties + * this is used for each individual R5 core + * + * @cdev: child node device + * + * Function to setup mailboxes related properties + * return : NULL if failed else pointer to mbox_info + */ +static struct mbox_info *zynqmp_r5_setup_mbox(struct device *cdev) +{ + struct mbox_client *mbox_cl; + struct mbox_info *ipi; + + ipi = kzalloc(sizeof(*ipi), GFP_KERNEL); + if (!ipi) + return NULL; + + mbox_cl = &ipi->mbox_cl; + mbox_cl->rx_callback = zynqmp_r5_mb_rx_cb; + mbox_cl->tx_block = false; + mbox_cl->knows_txdone = false; + mbox_cl->tx_done = NULL; + mbox_cl->dev = cdev; + + /* Request TX and RX channels */ + ipi->tx_chan = mbox_request_channel_byname(mbox_cl, "tx"); + if (IS_ERR(ipi->tx_chan)) { + ipi->tx_chan = NULL; + kfree(ipi); + dev_warn(cdev, "mbox tx channel request failed\n"); + return NULL; + } + + ipi->rx_chan = mbox_request_channel_byname(mbox_cl, "rx"); + if (IS_ERR(ipi->rx_chan)) { + mbox_free_channel(ipi->tx_chan); + ipi->rx_chan = NULL; + ipi->tx_chan = NULL; + kfree(ipi); + dev_warn(cdev, "mbox rx channel request failed\n"); + return NULL; + } + + INIT_WORK(&ipi->mbox_work, handle_event_notified); + + return ipi; +} + +static void zynqmp_r5_free_mbox(struct mbox_info *ipi) +{ + if (!ipi) + return; + + if (ipi->tx_chan) { + mbox_free_channel(ipi->tx_chan); + ipi->tx_chan = NULL; + } + + if (ipi->rx_chan) { + mbox_free_channel(ipi->rx_chan); + ipi->rx_chan = NULL; + } + + kfree(ipi); +} + +/* + * zynqmp_r5_core_kick() - kick a firmware if mbox is provided + * @rproc: r5 core's corresponding rproc structure + * @vqid: virtqueue ID + */ +static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid) +{ + struct zynqmp_r5_core *r5_core = rproc->priv; + struct device *dev = r5_core->dev; + struct zynqmp_ipi_message *mb_msg; + struct mbox_info *ipi; + int ret; + + ipi = r5_core->ipi; + if (!ipi) + return; + + mb_msg = (struct zynqmp_ipi_message *)ipi->tx_mc_buf; + memcpy(mb_msg->data, &vqid, sizeof(vqid)); + mb_msg->len = sizeof(vqid); + ret = mbox_send_message(ipi->tx_chan, mb_msg); + if (ret < 0) + dev_warn(dev, "failed to send message\n"); +} + /* * zynqmp_r5_set_mode() * @@ -239,21 +437,29 @@ static int add_mem_regions_carveout(struct rproc *rproc) { struct rproc_mem_entry *rproc_mem; struct zynqmp_r5_core *r5_core; + struct of_phandle_iterator it; struct reserved_mem *rmem; - int i, num_mem_regions; + int i = 0; - r5_core = (struct zynqmp_r5_core *)rproc->priv; - num_mem_regions = r5_core->rmem_count; + r5_core = rproc->priv; - for (i = 0; i < num_mem_regions; i++) { - rmem = r5_core->rmem[i]; + /* Register associated reserved memory regions */ + of_phandle_iterator_init(&it, r5_core->np, "memory-region", NULL, 0); - if (!strncmp(rmem->name, "vdev0buffer", strlen("vdev0buffer"))) { + while (of_phandle_iterator_next(&it) == 0) { + rmem = of_reserved_mem_lookup(it.node); + if (!rmem) { + of_node_put(it.node); + dev_err(&rproc->dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + if (!strcmp(it.node->name, "vdev0buffer")) { /* Init reserved memory for vdev buffer */ rproc_mem = rproc_of_resm_mem_entry_init(&rproc->dev, i, rmem->size, rmem->base, - rmem->name); + it.node->name); } else { /* Register associated reserved memory regions */ rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, @@ -261,16 +467,19 @@ static int add_mem_regions_carveout(struct rproc *rproc) rmem->size, rmem->base, zynqmp_r5_mem_region_map, zynqmp_r5_mem_region_unmap, - rmem->name); + it.node->name); } - if (!rproc_mem) + if (!rproc_mem) { + of_node_put(it.node); return -ENOMEM; + } rproc_add_carveout(rproc, rproc_mem); dev_dbg(&rproc->dev, "reserved mem carveout %s addr=%llx, size=0x%llx", - rmem->name, rmem->base, rmem->size); + it.node->name, rmem->base, rmem->size); + i++; } return 0; @@ -363,7 +572,7 @@ static int add_tcm_carveout_split_mode(struct rproc *rproc) size_t bank_size; char *bank_name; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; dev = r5_core->dev; num_banks = r5_core->tcm_bank_count; @@ -432,7 +641,7 @@ static int add_tcm_carveout_lockstep_mode(struct rproc *rproc) u32 pm_domain_id; char *bank_name; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; dev = r5_core->dev; /* Go through zynqmp banks for r5 node */ @@ -502,7 +711,7 @@ static int add_tcm_banks(struct rproc *rproc) struct zynqmp_r5_core *r5_core; struct device *dev; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; if (!r5_core) return -EINVAL; @@ -595,7 +804,7 @@ static int zynqmp_r5_rproc_unprepare(struct rproc *rproc) u32 pm_domain_id; int i; - r5_core = (struct zynqmp_r5_core *)rproc->priv; + r5_core = rproc->priv; for (i = 0; i < r5_core->tcm_bank_count; i++) { pm_domain_id = r5_core->tcm_banks[i]->pm_domain_id; @@ -617,6 +826,7 @@ static const struct rproc_ops zynqmp_r5_rproc_ops = { .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, + .kick = zynqmp_r5_rproc_kick, }; /** @@ -649,7 +859,7 @@ static struct zynqmp_r5_core *zynqmp_r5_add_rproc_core(struct device *cdev) } r5_rproc->auto_boot = false; - r5_core = (struct zynqmp_r5_core *)r5_rproc->priv; + r5_core = r5_rproc->priv; r5_core->dev = cdev; r5_core->np = dev_of_node(cdev); if (!r5_core->np) { @@ -726,59 +936,6 @@ static int zynqmp_r5_get_tcm_node(struct zynqmp_r5_cluster *cluster) return 0; } -/** - * zynqmp_r5_get_mem_region_node() - * parse memory-region property and get reserved mem regions - * - * @r5_core: pointer to zynqmp_r5_core type object - * - * Return: 0 for success and error code for failure. - */ -static int zynqmp_r5_get_mem_region_node(struct zynqmp_r5_core *r5_core) -{ - struct device_node *np, *rmem_np; - struct reserved_mem **rmem; - int res_mem_count, i; - struct device *dev; - - dev = r5_core->dev; - np = r5_core->np; - - res_mem_count = of_property_count_elems_of_size(np, "memory-region", - sizeof(phandle)); - if (res_mem_count <= 0) { - dev_warn(dev, "failed to get memory-region property %d\n", - res_mem_count); - return 0; - } - - rmem = devm_kcalloc(dev, res_mem_count, - sizeof(struct reserved_mem *), GFP_KERNEL); - if (!rmem) - return -ENOMEM; - - for (i = 0; i < res_mem_count; i++) { - rmem_np = of_parse_phandle(np, "memory-region", i); - if (!rmem_np) - goto release_rmem; - - rmem[i] = of_reserved_mem_lookup(rmem_np); - if (!rmem[i]) { - of_node_put(rmem_np); - goto release_rmem; - } - - of_node_put(rmem_np); - } - - r5_core->rmem_count = res_mem_count; - r5_core->rmem = rmem; - return 0; - -release_rmem: - return -EINVAL; -} - /* * zynqmp_r5_core_init() * Create and initialize zynqmp_r5_core type object @@ -806,10 +963,6 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; - ret = zynqmp_r5_get_mem_region_node(r5_core); - if (ret) - dev_warn(dev, "memory-region prop failed %d\n", ret); - /* Initialize r5 cores with power-domains parsed from dts */ ret = of_property_read_u32_index(r5_core->np, "power-domains", 1, &r5_core->pm_domain_id); @@ -849,6 +1002,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) struct device_node *child; enum rpu_tcm_comb tcm_mode; int core_count, ret, i; + struct mbox_info *ipi; ret = of_property_read_u32(dev_node, "xlnx,cluster-mode", &cluster_mode); @@ -929,6 +1083,16 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) } /* + * If mailbox nodes are disabled using "status" property then + * setting up mailbox channels will fail. + */ + ipi = zynqmp_r5_setup_mbox(&child_pdev->dev); + if (ipi) { + r5_cores[i]->ipi = ipi; + ipi->r5_core = r5_cores[i]; + } + + /* * If two child nodes are available in dts in lockstep mode, * then ignore second child node. */ @@ -965,6 +1129,7 @@ release_r5_cores: while (i >= 0) { put_device(child_devs[i]); if (r5_cores[i]) { + zynqmp_r5_free_mbox(r5_cores[i]->ipi); of_reserved_mem_device_release(r5_cores[i]->dev); rproc_del(r5_cores[i]->rproc); rproc_free(r5_cores[i]->rproc); @@ -978,17 +1143,18 @@ release_r5_cores: static void zynqmp_r5_cluster_exit(void *data) { - struct platform_device *pdev = (struct platform_device *)data; + struct platform_device *pdev = data; struct zynqmp_r5_cluster *cluster; struct zynqmp_r5_core *r5_core; int i; - cluster = (struct zynqmp_r5_cluster *)platform_get_drvdata(pdev); + cluster = platform_get_drvdata(pdev); if (!cluster) return; for (i = 0; i < cluster->core_count; i++) { r5_core = cluster->r5_cores[i]; + zynqmp_r5_free_mbox(r5_core->ipi); of_reserved_mem_device_release(r5_core->dev); put_device(r5_core->dev); rproc_del(r5_core->rproc); |