diff options
25 files changed, 710 insertions, 229 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml b/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml index 11cb42a3fdd1..3100cb870170 100644 --- a/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/amlogic,meson-mx-ao-arc.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/remoteproc/amlogic,meson-mx-ao-arc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/remoteproc/amlogic,meson-mx-ao-arc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Amlogic Meson AO ARC Remote Processor diff --git a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml index ae2eab4452dd..0c3910f152d1 100644 --- a/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/fsl,imx-rproc.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/remoteproc/fsl,imx-rproc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/remoteproc/fsl,imx-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: NXP i.MX Co-Processor diff --git a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml index 85b1e43cab08..8b55dbd909b0 100644 --- a/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ingenic,vpu.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/remoteproc/ingenic,vpu.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/remoteproc/ingenic,vpu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Ingenic Video Processing Unit diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml index 15e6851e1ff8..7b43ad3daa56 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,glink-edge.yaml @@ -15,7 +15,7 @@ description: properties: $nodename: - const: "glink-edge" + const: glink-edge apr: $ref: /schemas/soc/qcom/qcom,apr.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml index 7ec8a6b6682c..02c85b420c1a 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml +++ b/Documentation/devicetree/bindings/remoteproc/qcom,smd-edge.yaml @@ -21,7 +21,7 @@ description: properties: $nodename: - const: "smd-edge" + const: smd-edge apr: $ref: /schemas/soc/qcom/qcom,apr.yaml# diff --git a/Documentation/devicetree/bindings/remoteproc/renesas,rcar-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/renesas,rcar-rproc.yaml index 7e0275d31a3c..4bea679a0f61 100644 --- a/Documentation/devicetree/bindings/remoteproc/renesas,rcar-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/renesas,rcar-rproc.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/remoteproc/renesas,rcar-rproc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/remoteproc/renesas,rcar-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Renesas R-Car remote processor controller diff --git a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml index 66b1e3efdaa3..959a56f1b6c7 100644 --- a/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/st,stm32-rproc.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/remoteproc/st,stm32-rproc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/remoteproc/st,stm32-rproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: STMicroelectronics STM32 remote processor controller @@ -29,7 +29,7 @@ properties: st,syscfg-holdboot: description: remote processor reset hold boot - $ref: "/schemas/types.yaml#/definitions/phandle-array" + $ref: /schemas/types.yaml#/definitions/phandle-array items: - items: - description: Phandle of syscon block @@ -39,7 +39,7 @@ properties: st,syscfg-tz: description: Reference to the system configuration which holds the RCC trust zone mode - $ref: "/schemas/types.yaml#/definitions/phandle-array" + $ref: /schemas/types.yaml#/definitions/phandle-array items: - items: - description: Phandle of syscon block @@ -72,9 +72,9 @@ properties: ready for shutdown - description: | A channel (d) used by the local proc to notify the remote proc that it - has to stop interprocessor communnication. + has to stop interprocessor communication. Unidirectional channel: - - from local to remote, where ACK from the remote means that communnication + - from local to remote, where ACK from the remote means that communication as been stopped on the remote side. minItems: 1 @@ -95,7 +95,7 @@ properties: (see ../reserved-memory/reserved-memory.txt) st,syscfg-pdds: - $ref: "/schemas/types.yaml#/definitions/phandle-array" + $ref: /schemas/types.yaml#/definitions/phandle-array description: | Reference to the system configuration which holds the remote items: @@ -105,7 +105,7 @@ properties: - description: The field mask of the PDDS selection st,syscfg-m4-state: - $ref: "/schemas/types.yaml#/definitions/phandle-array" + $ref: /schemas/types.yaml#/definitions/phandle-array description: | Reference to the tamp register which exposes the Cortex-M4 state. items: @@ -115,7 +115,7 @@ properties: - description: The field mask of the Cortex-M4 state st,syscfg-rsc-tbl: - $ref: "/schemas/types.yaml#/definitions/phandle-array" + $ref: /schemas/types.yaml#/definitions/phandle-array description: | Reference to the tamp register which references the Cortex-M4 resource table address. diff --git a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml index fb9605f0655b..fcc3db97fe8f 100644 --- a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml +++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml @@ -21,6 +21,9 @@ description: | called "Single-CPU" mode, where only Core0 is used, but with ability to use Core1's TCMs as well. + AM62 SoC family support a single R5F core only which runs Device Manager + firmware and can also be used as a remote processor with IPC communication. + Each Dual-Core R5F sub-system is represented as a single DTS node representing the cluster, with a pair of child DT nodes representing the individual R5F cores. Each node has a number of required or optional @@ -34,10 +37,11 @@ properties: compatible: enum: + - ti,am62-r5fss + - ti,am64-r5fss - ti,am654-r5fss - - ti,j721e-r5fss - ti,j7200-r5fss - - ti,am64-r5fss + - ti,j721e-r5fss - ti,j721s2-r5fss power-domains: @@ -64,10 +68,17 @@ properties: $ref: /schemas/types.yaml#/definitions/uint32 description: | Configuration Mode for the Dual R5F cores within the R5F cluster. - Should be either a value of 1 (LockStep mode) or 0 (Split mode) on + For most SoCs (AM65x, J721E, J7200, J721s2), + It should be either a value of 1 (LockStep mode) or 0 (Split mode) on most SoCs (AM65x, J721E, J7200, J721s2), default is LockStep mode if - omitted; and should be either a value of 0 (Split mode) or 2 - (Single-CPU mode) on AM64x SoCs, default is Split mode if omitted. + omitted. + For AM64x SoCs, + It should be either a value of 0 (Split mode) or 2 (Single-CPU mode) and + default is Split mode if omitted. + For AM62x SoCs, + It should be set as 3 (Single-Core mode) which is also the default if + omitted. + # R5F Processor Child Nodes: # ========================== @@ -80,7 +91,9 @@ patternProperties: node representing a TI instantiation of the Arm Cortex R5F core. There are some specific integration differences for the IP like the usage of a Region Address Translator (RAT) for translating the larger SoC bus - addresses into a 32-bit address space for the processor. + addresses into a 32-bit address space for the processor. For AM62x, + the R5F Sub-System device node should only define one R5F child node + as it has only one core available. Each R5F core has an associated 64 KB of Tightly-Coupled Memory (TCM) internal memories split between two banks - TCMA and TCMB (further @@ -100,10 +113,11 @@ patternProperties: properties: compatible: enum: + - ti,am62-r5f + - ti,am64-r5f - ti,am654-r5f - - ti,j721e-r5f - ti,j7200-r5f - - ti,am64-r5f + - ti,j721e-r5f - ti,j721s2-r5f reg: @@ -208,19 +222,39 @@ patternProperties: unevaluatedProperties: false -if: - properties: - compatible: - enum: - - ti,am64-r5fss -then: - properties: - ti,cluster-mode: - enum: [0, 2] -else: - properties: - ti,cluster-mode: - enum: [0, 1] +allOf: + - if: + properties: + compatible: + enum: + - ti,am64-r5fss + then: + properties: + ti,cluster-mode: + enum: [0, 2] + + - if: + properties: + compatible: + enum: + - ti,am654-r5fss + - ti,j7200-r5fss + - ti,j721e-r5fss + - ti,j721s2-r5fss + then: + properties: + ti,cluster-mode: + enum: [0, 1] + + - if: + properties: + compatible: + enum: + - ti,am62-r5fss + then: + properties: + ti,cluster-mode: + enum: [3] required: - compatible diff --git a/drivers/mailbox/zynqmp-ipi-mailbox.c b/drivers/mailbox/zynqmp-ipi-mailbox.c index a4c8d23c76e2..d097f45b0e5f 100644 --- a/drivers/mailbox/zynqmp-ipi-mailbox.c +++ b/drivers/mailbox/zynqmp-ipi-mailbox.c @@ -152,7 +152,7 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) struct zynqmp_ipi_message *msg; u64 arg0, arg3; struct arm_smccc_res res; - int ret, i; + int ret, i, status = IRQ_NONE; (void)irq; arg0 = SMC_IPI_MAILBOX_STATUS_ENQUIRY; @@ -170,11 +170,11 @@ static irqreturn_t zynqmp_ipi_interrupt(int irq, void *data) memcpy_fromio(msg->data, mchan->req_buf, msg->len); mbox_chan_received_data(chan, (void *)msg); - return IRQ_HANDLED; + status = IRQ_HANDLED; } } } - return IRQ_NONE; + return status; } /** @@ -634,7 +634,12 @@ static int zynqmp_ipi_probe(struct platform_device *pdev) struct zynqmp_ipi_mbox *mbox; int num_mboxes, ret = -EINVAL; - num_mboxes = of_get_child_count(np); + num_mboxes = of_get_available_child_count(np); + if (num_mboxes == 0) { + dev_err(dev, "mailbox nodes not available\n"); + return -EINVAL; + } + pdata = devm_kzalloc(dev, struct_size(pdata, ipi_mboxes, num_mboxes), GFP_KERNEL); if (!pdata) 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); diff --git a/include/linux/mailbox/zynqmp-ipi-message.h b/include/linux/mailbox/zynqmp-ipi-message.h index 35ce84c8ca02..31d8046d945e 100644 --- a/include/linux/mailbox/zynqmp-ipi-message.h +++ b/include/linux/mailbox/zynqmp-ipi-message.h @@ -9,7 +9,7 @@ * @data: message payload * * This is the structure for data used in mbox_send_message - * the maximum length of data buffer is fixed to 12 bytes. + * the maximum length of data buffer is fixed to 32 bytes. * Client is supposed to be aware of this. */ struct zynqmp_ipi_message { |