From 900fc60df22748dbc28e4970838e8f7b8f1013ce Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 5 Mar 2020 01:17:27 +0530 Subject: [PATCH 01/37] remoteproc: qcom_q6v5_mss: Don't reassign mpss region on shutdown Trying to reclaim mpss memory while the mba is not running causes the system to crash on devices with security fuses blown, so leave it assigned to the remote on shutdown and recover it on a subsequent boot. Fixes: 6c5a9dc2481b ("remoteproc: qcom: Make secure world call for mem ownership switch") Cc: stable@vger.kernel.org Signed-off-by: Bjorn Andersson Signed-off-by: Sibi Sankar Tested-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200304194729.27979-2-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 35 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index a1cc9cbe038f..9fed1b1c203d 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1001,11 +1001,6 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); } - ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, - false, qproc->mpss_phys, - qproc->mpss_size); - WARN_ON(ret); - q6v5_reset_assert(qproc); q6v5_clk_disable(qproc->dev, qproc->reset_clks, @@ -1095,6 +1090,14 @@ static int q6v5_mpss_load(struct q6v5 *qproc) max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); } + /** + * In case of a modem subsystem restart on secure devices, the modem + * memory can be reclaimed only after MBA is loaded. For modem cold + * boot this will be a nop + */ + q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, false, + qproc->mpss_phys, qproc->mpss_size); + mpss_reloc = relocate ? min_addr : qproc->mpss_phys; qproc->mpss_reloc = mpss_reloc; /* Load firmware segments */ @@ -1184,8 +1187,16 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, void *ptr = rproc_da_to_va(rproc, segment->da, segment->size); /* Unlock mba before copying segments */ - if (!qproc->dump_mba_loaded) + if (!qproc->dump_mba_loaded) { ret = q6v5_mba_load(qproc); + if (!ret) { + /* Reset ownership back to Linux to copy segments */ + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, + false, + qproc->mpss_phys, + qproc->mpss_size); + } + } if (!ptr || ret) memset(dest, 0xff, segment->size); @@ -1196,8 +1207,14 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, /* Reclaim mba after copying segments */ if (qproc->dump_segment_mask == qproc->dump_complete_mask) { - if (qproc->dump_mba_loaded) + if (qproc->dump_mba_loaded) { + /* Try to reset ownership back to Q6 */ + q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, + true, + qproc->mpss_phys, + qproc->mpss_size); q6v5_mba_reclaim(qproc); + } } } @@ -1237,10 +1254,6 @@ static int q6v5_start(struct rproc *rproc) return 0; reclaim_mpss: - xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, - false, qproc->mpss_phys, - qproc->mpss_size); - WARN_ON(xfermemop_ret); q6v5_mba_reclaim(qproc); return ret; From 715d85251957adeccf437618bbd5c07e173df91a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 5 Mar 2020 01:17:28 +0530 Subject: [PATCH 02/37] remoteproc: qcom_q6v5_mss: Validate each segment during loading The code used to sync with the MBA after each segment loaded and this is still what's done downstream. So reduce the delta towards downstream by switching to a model where the content is iteratively validated. Signed-off-by: Bjorn Andersson Signed-off-by: Sibi Sankar Tested-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200304194729.27979-3-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 83 +++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 9fed1b1c203d..b8a922181de8 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -379,23 +379,33 @@ static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds, } static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, - bool remote_owner, phys_addr_t addr, + bool local, bool remote, phys_addr_t addr, size_t size) { - struct qcom_scm_vmperm next; + struct qcom_scm_vmperm next[2]; + int perms = 0; if (!qproc->need_mem_protection) return 0; - if (remote_owner && *current_perm == BIT(QCOM_SCM_VMID_MSS_MSA)) - return 0; - if (!remote_owner && *current_perm == BIT(QCOM_SCM_VMID_HLOS)) + + if (local == !!(*current_perm & BIT(QCOM_SCM_VMID_HLOS)) && + remote == !!(*current_perm & BIT(QCOM_SCM_VMID_MSS_MSA))) return 0; - next.vmid = remote_owner ? QCOM_SCM_VMID_MSS_MSA : QCOM_SCM_VMID_HLOS; - next.perm = remote_owner ? QCOM_SCM_PERM_RW : QCOM_SCM_PERM_RWX; + if (local) { + next[perms].vmid = QCOM_SCM_VMID_HLOS; + next[perms].perm = QCOM_SCM_PERM_RWX; + perms++; + } + + if (remote) { + next[perms].vmid = QCOM_SCM_VMID_MSS_MSA; + next[perms].perm = QCOM_SCM_PERM_RW; + perms++; + } return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), - current_perm, &next, 1); + current_perm, next, perms); } static int q6v5_load(struct rproc *rproc, const struct firmware *fw) @@ -801,7 +811,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) /* Hypervisor mapping to access metadata by modem */ mdata_perm = BIT(QCOM_SCM_VMID_HLOS); - ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, true, phys, size); + ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, false, true, + phys, size); if (ret) { dev_err(qproc->dev, "assigning Q6 access to metadata failed: %d\n", ret); @@ -819,7 +830,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) dev_err(qproc->dev, "MPSS header authentication failed: %d\n", ret); /* Metadata authentication done, remove modem access */ - xferop_ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, false, phys, size); + xferop_ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, true, false, + phys, size); if (xferop_ret) dev_warn(qproc->dev, "mdt buffer not reclaimed system may become unstable\n"); @@ -906,7 +918,7 @@ static int q6v5_mba_load(struct q6v5 *qproc) } /* Assign MBA image access in DDR to q6 */ - ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, true, qproc->mba_phys, qproc->mba_size); if (ret) { dev_err(qproc->dev, @@ -943,8 +955,8 @@ halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); reclaim_mba: - xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, - qproc->mba_phys, + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, + false, qproc->mba_phys, qproc->mba_size); if (xfermemop_ret) { dev_err(qproc->dev, @@ -1014,7 +1026,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) /* In case of failure or coredump scenario where reclaiming MBA memory * could not happen reclaim it here. */ - ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, false, qproc->mba_phys, qproc->mba_size); WARN_ON(ret); @@ -1041,6 +1053,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) phys_addr_t boot_addr; phys_addr_t min_addr = PHYS_ADDR_MAX; phys_addr_t max_addr = 0; + u32 code_length; bool relocate = false; char *fw_name; size_t fw_name_len; @@ -1095,9 +1108,19 @@ static int q6v5_mpss_load(struct q6v5 *qproc) * memory can be reclaimed only after MBA is loaded. For modem cold * boot this will be a nop */ - q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, false, + q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, false, qproc->mpss_phys, qproc->mpss_size); + /* Share ownership between Linux and MSS, during segment loading */ + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, true, + qproc->mpss_phys, qproc->mpss_size); + if (ret) { + dev_err(qproc->dev, + "assigning Q6 access to mpss memory failed: %d\n", ret); + ret = -EAGAIN; + goto release_firmware; + } + mpss_reloc = relocate ? min_addr : qproc->mpss_phys; qproc->mpss_reloc = mpss_reloc; /* Load firmware segments */ @@ -1146,10 +1169,25 @@ static int q6v5_mpss_load(struct q6v5 *qproc) phdr->p_memsz - phdr->p_filesz); } size += phdr->p_memsz; + + code_length = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); + if (!code_length) { + boot_addr = relocate ? qproc->mpss_phys : min_addr; + writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); + writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); + } + writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); + + ret = readl(qproc->rmb_base + RMB_MBA_STATUS_REG); + if (ret < 0) { + dev_err(qproc->dev, "MPSS authentication failed: %d\n", + ret); + goto release_firmware; + } } /* Transfer ownership of modem ddr region to q6 */ - ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, false, true, qproc->mpss_phys, qproc->mpss_size); if (ret) { dev_err(qproc->dev, @@ -1158,11 +1196,6 @@ static int q6v5_mpss_load(struct q6v5 *qproc) goto release_firmware; } - boot_addr = relocate ? qproc->mpss_phys : min_addr; - writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); - writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); - writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); - ret = q6v5_rmb_mba_wait(qproc, RMB_MBA_AUTH_COMPLETE, 10000); if (ret == -ETIMEDOUT) dev_err(qproc->dev, "MPSS authentication timed out\n"); @@ -1192,7 +1225,7 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, if (!ret) { /* Reset ownership back to Linux to copy segments */ ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, - false, + true, false, qproc->mpss_phys, qproc->mpss_size); } @@ -1210,7 +1243,7 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, if (qproc->dump_mba_loaded) { /* Try to reset ownership back to Q6 */ q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, - true, + false, true, qproc->mpss_phys, qproc->mpss_size); q6v5_mba_reclaim(qproc); @@ -1240,8 +1273,8 @@ static int q6v5_start(struct rproc *rproc) goto reclaim_mpss; } - xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, - qproc->mba_phys, + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, + false, qproc->mba_phys, qproc->mba_size); if (xfermemop_ret) dev_err(qproc->dev, From d96f2571dc84d128cacf1944f4ecc87834c779a6 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Thu, 5 Mar 2020 01:17:29 +0530 Subject: [PATCH 03/37] remoteproc: qcom_q6v5_mss: Reload the mba region on coredump On secure devices after a wdog/fatal interrupt, the mba region has to be refreshed in order to prevent the following errors during mba load. Err Logs: remoteproc remoteproc2: stopped remote processor 4080000.remoteproc qcom-q6v5-mss 4080000.remoteproc: PBL returned unexpected status -284031232 qcom-q6v5-mss 4080000.remoteproc: PBL returned unexpected status -284031232 .... qcom-q6v5-mss 4080000.remoteproc: PBL returned unexpected status -284031232 qcom-q6v5-mss 4080000.remoteproc: MBA booted, loading mpss Fixes: 7dd8ade24dc2a ("remoteproc: qcom: q6v5-mss: Add custom dump function for modem") Cc: stable@vger.kernel.org Signed-off-by: Sibi Sankar Tested-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200304194729.27979-4-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_mss.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index b8a922181de8..d7667418a62f 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1042,6 +1042,23 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) } } +static int q6v5_reload_mba(struct rproc *rproc) +{ + struct q6v5 *qproc = rproc->priv; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, rproc->firmware, qproc->dev); + if (ret < 0) + return ret; + + q6v5_load(rproc, fw); + ret = q6v5_mba_load(qproc); + release_firmware(fw); + + return ret; +} + static int q6v5_mpss_load(struct q6v5 *qproc) { const struct elf32_phdr *phdrs; @@ -1221,7 +1238,7 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, /* Unlock mba before copying segments */ if (!qproc->dump_mba_loaded) { - ret = q6v5_mba_load(qproc); + ret = q6v5_reload_mba(rproc); if (!ret) { /* Reset ownership back to Linux to copy segments */ ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, From 2e7d4c2c4b25e50450666fde6244f580df365bb3 Mon Sep 17 00:00:00 2001 From: Arnaud Pouliquen Date: Wed, 12 Feb 2020 17:19:56 +0100 Subject: [PATCH 04/37] remoteproc: fix kernel-doc warnings Fix the following warnings when documentation is built: drivers/remoteproc/remoteproc_virtio.c:330: warning: Function parameter or member 'id' not described in 'rproc_add_virtio_dev' drivers/remoteproc/remoteproc_core.c:243: warning: Function parameter or member 'name' not described in 'rproc_find_carveout_by_name' drivers/remoteproc/remoteproc_core.c:473: warning: Function parameter or member 'offset' not described in 'rproc_handle_vdev' drivers/remoteproc/remoteproc_core.c:604: warning: Function parameter or member 'offset' not described in 'rproc_handle_trace' drivers/remoteproc/remoteproc_core.c:678: warning: Function parameter or member 'offset' not described in 'rproc_handle_devmem' drivers/remoteproc/remoteproc_core.c:873: warning: Function parameter or member 'offset' not described in 'rproc_handle_carveout' drivers/remoteproc/remoteproc_core.c:1029: warning: cannot understand function prototype: 'rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = ' drivers/remoteproc/remoteproc_core.c:1693: warning: Function parameter or member 'work' not described in 'rproc_crash_handler_work' Acked-by: Randy Dunlap Signed-off-by: Arnaud Pouliquen Link: https://lore.kernel.org/r/20200212161956.10358-1-arnaud.pouliquen@st.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 10 ++++++++-- drivers/remoteproc/remoteproc_virtio.c | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 097f33e4f1f3..3f0026cf67b7 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -224,7 +224,8 @@ EXPORT_SYMBOL(rproc_da_to_va); /** * rproc_find_carveout_by_name() - lookup the carveout region by a name * @rproc: handle of a remote processor - * @name,..: carveout name to find (standard printf format) + * @name: carveout name to find (format string) + * @...: optional parameters matching @name string * * Platform driver has the capability to register some pre-allacoted carveout * (physically contiguous memory regions) before rproc firmware loading and @@ -445,6 +446,7 @@ static void rproc_rvdev_release(struct device *dev) * rproc_handle_vdev() - handle a vdev fw resource * @rproc: the remote processor * @rsc: the vring resource descriptor + * @offset: offset of the resource entry * @avail: size of available data (for sanity checking the image) * * This resource entry requests the host to statically register a virtio @@ -587,6 +589,7 @@ void rproc_vdev_release(struct kref *ref) * rproc_handle_trace() - handle a shared trace buffer resource * @rproc: the remote processor * @rsc: the trace resource descriptor + * @offset: offset of the resource entry * @avail: size of available data (for sanity checking the image) * * In case the remote processor dumps trace logs into memory, @@ -652,6 +655,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, * rproc_handle_devmem() - handle devmem resource entry * @rproc: remote processor handle * @rsc: the devmem resource entry + * @offset: offset of the resource entry * @avail: size of available data (for sanity checking the image) * * Remote processors commonly need to access certain on-chip peripherals. @@ -853,6 +857,7 @@ static int rproc_release_carveout(struct rproc *rproc, * rproc_handle_carveout() - handle phys contig memory allocation requests * @rproc: rproc handle * @rsc: the resource entry + * @offset: offset of the resource entry * @avail: size of available data (for image validation) * * This function will handle firmware requests for allocation of physically @@ -1022,7 +1027,7 @@ rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len, } EXPORT_SYMBOL(rproc_of_resm_mem_entry_init); -/** +/* * A lookup table for resource handlers. The indices are defined in * enum fw_resource_type. */ @@ -1685,6 +1690,7 @@ unlock_mutex: /** * rproc_crash_handler_work() - handle a crash + * @work: work treating the crash * * This function needs to handle everything related to a crash, like cpu * registers and stack dump, information to help to debug the fatal error, etc. diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 8c07cb2ca8ba..eb817132bc5f 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -320,6 +320,7 @@ static void rproc_virtio_dev_release(struct device *dev) /** * rproc_add_virtio_dev() - register an rproc-induced virtio device * @rvdev: the remote vdev + * @id: the device type identification (used to match it with a driver). * * This function registers a virtio device. This vdev's partent is * the rproc device. From 9ce3bf225e5a908756b90b8f7bbc38834427296b Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:38:55 +0100 Subject: [PATCH 05/37] remoteproc: Use size_t type for len in da_to_va With upcoming changes in elf loader for elf64 support, section size will be a u64. When used with da_to_va, this will potentially lead to overflow if using the current "int" type for len argument. Change da_to_va prototype to use a size_t for len and fix all users of this function. Reviewed-by: Bjorn Andersson Reviewed-by: Mathieu Poirier Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-2-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- drivers/remoteproc/imx_rproc.c | 11 ++++++----- drivers/remoteproc/keystone_remoteproc.c | 4 ++-- drivers/remoteproc/qcom_q6v5_adsp.c | 2 +- drivers/remoteproc/qcom_q6v5_mss.c | 2 +- drivers/remoteproc/qcom_q6v5_pas.c | 2 +- drivers/remoteproc/qcom_q6v5_wcss.c | 2 +- drivers/remoteproc/qcom_wcnss.c | 2 +- drivers/remoteproc/remoteproc_core.c | 2 +- drivers/remoteproc/remoteproc_internal.h | 2 +- drivers/remoteproc/st_slim_rproc.c | 4 ++-- drivers/remoteproc/wkup_m3_rproc.c | 4 ++-- include/linux/remoteproc.h | 2 +- 12 files changed, 20 insertions(+), 19 deletions(-) diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 3e72b6f38d4b..8957ed271d20 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -186,7 +186,7 @@ static int imx_rproc_stop(struct rproc *rproc) } static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, - int len, u64 *sys) + size_t len, u64 *sys) { const struct imx_rproc_dcfg *dcfg = priv->dcfg; int i; @@ -203,19 +203,19 @@ static int imx_rproc_da_to_sys(struct imx_rproc *priv, u64 da, } } - dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%x\n", + dev_warn(priv->dev, "Translation failed: da = 0x%llx len = 0x%zx\n", da, len); return -ENOENT; } -static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len) +static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct imx_rproc *priv = rproc->priv; void *va = NULL; u64 sys; int i; - if (len <= 0) + if (len == 0) return NULL; /* @@ -235,7 +235,8 @@ static void *imx_rproc_da_to_va(struct rproc *rproc, u64 da, int len) } } - dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va); + dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%p\n", + da, len, va); return va; } diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 5c4658f00b3d..cd266163a65f 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -246,7 +246,7 @@ static void keystone_rproc_kick(struct rproc *rproc, int vqid) * can be used either by the remoteproc core for loading (when using kernel * remoteproc loader), or by any rpmsg bus drivers. */ -static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len) +static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct keystone_rproc *ksproc = rproc->priv; void __iomem *va = NULL; @@ -255,7 +255,7 @@ static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len) size_t size; int i; - if (len <= 0) + if (len == 0) return NULL; for (i = 0; i < ksproc->num_mems; i++) { diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index e953886b2eb7..2b01f2282062 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -270,7 +270,7 @@ static int adsp_stop(struct rproc *rproc) return ret; } -static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len) +static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; int offset; diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index d7667418a62f..03ffc6db4c68 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -1325,7 +1325,7 @@ static int q6v5_stop(struct rproc *rproc) return 0; } -static void *q6v5_da_to_va(struct rproc *rproc, u64 da, int len) +static void *q6v5_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct q6v5 *qproc = rproc->priv; int offset; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index edf9d0e1a235..a41860d2243a 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -222,7 +222,7 @@ static int adsp_stop(struct rproc *rproc) return ret; } -static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len) +static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; int offset; diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c index f93e1e4a1cc0..f1924b740a10 100644 --- a/drivers/remoteproc/qcom_q6v5_wcss.c +++ b/drivers/remoteproc/qcom_q6v5_wcss.c @@ -406,7 +406,7 @@ static int q6v5_wcss_stop(struct rproc *rproc) return 0; } -static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len) +static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct q6v5_wcss *wcss = rproc->priv; int offset; diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index dc135754bb9c..0c7afd038f0d 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -287,7 +287,7 @@ static int wcnss_stop(struct rproc *rproc) return ret; } -static void *wcnss_da_to_va(struct rproc *rproc, u64 da, int len) +static void *wcnss_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; int offset; diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 3f0026cf67b7..1f20db16a708 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -185,7 +185,7 @@ EXPORT_SYMBOL(rproc_va_to_pa); * here the output of the DMA API for the carveouts, which should be more * correct. */ -void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) +void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct rproc_mem_entry *carveout; void *ptr = NULL; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 493ef9262411..58580210575c 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -50,7 +50,7 @@ void rproc_exit_sysfs(void); void rproc_free_vring(struct rproc_vring *rvring); int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); -void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); +void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len); phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 04492fead3c8..09bcb4d8b9e0 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -174,7 +174,7 @@ static int slim_rproc_stop(struct rproc *rproc) return 0; } -static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) +static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct st_slim_rproc *slim_rproc = rproc->priv; void *va = NULL; @@ -191,7 +191,7 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) } } - dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%pK\n", + dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%zx va = 0x%pK\n", da, len, va); return va; diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c index 3984e585c847..b9349d684258 100644 --- a/drivers/remoteproc/wkup_m3_rproc.c +++ b/drivers/remoteproc/wkup_m3_rproc.c @@ -80,14 +80,14 @@ static int wkup_m3_rproc_stop(struct rproc *rproc) return 0; } -static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len) +static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct wkup_m3_rproc *wkupm3 = rproc->priv; void *va = NULL; int i; u32 offset; - if (len <= 0) + if (len == 0) return NULL; for (i = 0; i < WKUPM3_MEM_MAX; i++) { diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 16ad66683ad0..89215798eaea 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -374,7 +374,7 @@ struct rproc_ops { int (*start)(struct rproc *rproc); int (*stop)(struct rproc *rproc); void (*kick)(struct rproc *rproc, int vqid); - void * (*da_to_va)(struct rproc *rproc, u64 da, int len); + void * (*da_to_va)(struct rproc *rproc, u64 da, size_t len); int (*parse_fw)(struct rproc *rproc, const struct firmware *fw); int (*handle_rsc)(struct rproc *rproc, u32 rsc_type, void *rsc, int offset, int avail); From 096ee78669d2bc8fccc40117de8d4e838a0c80db Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:38:56 +0100 Subject: [PATCH 06/37] remoteproc: Use size_t instead of int for rproc_mem_entry len Now that rproc_da_to_va uses a size_t for length, use a size_t for len field of rproc_mem_entry. Function used to create such structures now takes a size_t instead of int to allow full size range to be handled. Reviewed-by: Bjorn Andersson Reviewed-by: Mathieu Poirier Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-3-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 14 ++++++++------ drivers/remoteproc/remoteproc_debugfs.c | 2 +- include/linux/remoteproc.h | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 1f20db16a708..ebb7213c33b1 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -319,8 +319,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) struct device *dev = &rproc->dev; struct rproc_vring *rvring = &rvdev->vring[i]; struct fw_rsc_vdev *rsc; - int ret, size, notifyid; + int ret, notifyid; struct rproc_mem_entry *mem; + size_t size; /* actual size of vring (in bytes) */ size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); @@ -750,11 +751,12 @@ static int rproc_alloc_carveout(struct rproc *rproc, va = dma_alloc_coherent(dev->parent, mem->len, &dma, GFP_KERNEL); if (!va) { dev_err(dev->parent, - "failed to allocate dma memory: len 0x%x\n", mem->len); + "failed to allocate dma memory: len 0x%zx\n", + mem->len); return -ENOMEM; } - dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", + dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%zx\n", va, &dma, mem->len); if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) { @@ -962,7 +964,7 @@ EXPORT_SYMBOL(rproc_add_carveout); */ struct rproc_mem_entry * rproc_mem_entry_init(struct device *dev, - void *va, dma_addr_t dma, int len, u32 da, + void *va, dma_addr_t dma, size_t len, u32 da, int (*alloc)(struct rproc *, struct rproc_mem_entry *), int (*release)(struct rproc *, struct rproc_mem_entry *), const char *name, ...) @@ -1004,7 +1006,7 @@ EXPORT_SYMBOL(rproc_mem_entry_init); * provided by client. */ struct rproc_mem_entry * -rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len, +rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len, u32 da, const char *name, ...) { struct rproc_mem_entry *mem; @@ -1275,7 +1277,7 @@ static void rproc_resource_cleanup(struct rproc *rproc) unmapped = iommu_unmap(rproc->domain, entry->da, entry->len); if (unmapped != entry->len) { /* nothing much to do besides complaining */ - dev_err(dev, "failed to unmap %u/%zu\n", entry->len, + dev_err(dev, "failed to unmap %zx/%zu\n", entry->len, unmapped); } diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index dd93cf04e17f..82dc34b819df 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -293,7 +293,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p) seq_printf(seq, "\tVirtual address: %pK\n", carveout->va); seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); - seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len); + seq_printf(seq, "\tLength: 0x%zx Bytes\n\n", carveout->len); } return 0; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 89215798eaea..bee559330204 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -329,7 +329,7 @@ struct rproc; struct rproc_mem_entry { void *va; dma_addr_t dma; - int len; + size_t len; u32 da; void *priv; char name[32]; @@ -599,13 +599,13 @@ void rproc_add_carveout(struct rproc *rproc, struct rproc_mem_entry *mem); struct rproc_mem_entry * rproc_mem_entry_init(struct device *dev, - void *va, dma_addr_t dma, int len, u32 da, + void *va, dma_addr_t dma, size_t len, u32 da, int (*alloc)(struct rproc *, struct rproc_mem_entry *), int (*release)(struct rproc *, struct rproc_mem_entry *), const char *name, ...); struct rproc_mem_entry * -rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, int len, +rproc_of_resm_mem_entry_init(struct device *dev, u32 of_resm_idx, size_t len, u32 da, const char *name, ...); int rproc_boot(struct rproc *rproc); From e4ae4b7d01699d0f3ea61bbef119f2d67e5455c0 Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:38:57 +0100 Subject: [PATCH 07/37] remoteproc: Use u64 type for boot_addr elf64 entry is defined as a u64. Since boot_addr is used to store the elf entry point, change boot_addr type to u64 to support both elf32 and elf64. In the same time, fix users that were using this variable. Reviewed-by: Bjorn Andersson Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-4-cleger@kalray.eu [bjorn: Fixes up return type of rproc_get_boot_addr()] Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_elf_loader.c | 2 +- drivers/remoteproc/remoteproc_internal.h | 4 ++-- drivers/remoteproc/st_remoteproc.c | 2 +- include/linux/remoteproc.h | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 606aae166eba..c2a9783cfb9a 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -102,7 +102,7 @@ EXPORT_SYMBOL(rproc_elf_sanity_check); * Note that the boot address is not a configurable property of all remote * processors. Some will always boot at a specific hard-coded address. */ -u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw) +u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw) { struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 58580210575c..23f7a713995f 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -55,7 +55,7 @@ phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); -u32 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw); +u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw); struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc, @@ -73,7 +73,7 @@ int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) } static inline -u32 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw) +u64 rproc_get_boot_addr(struct rproc *rproc, const struct firmware *fw) { if (rproc->ops->get_boot_addr) return rproc->ops->get_boot_addr(rproc, fw); diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index ee13d23b43a9..a3268d95a50e 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -190,7 +190,7 @@ static int st_rproc_start(struct rproc *rproc) } } - dev_info(&rproc->dev, "Started from 0x%x\n", rproc->bootaddr); + dev_info(&rproc->dev, "Started from 0x%llx\n", rproc->bootaddr); return 0; diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index bee559330204..1683d6c386a6 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -382,7 +382,7 @@ struct rproc_ops { struct rproc *rproc, const struct firmware *fw); int (*load)(struct rproc *rproc, const struct firmware *fw); int (*sanity_check)(struct rproc *rproc, const struct firmware *fw); - u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw); + u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw); }; /** @@ -498,7 +498,7 @@ struct rproc { int num_traces; struct list_head carveouts; struct list_head mappings; - u32 bootaddr; + u64 bootaddr; struct list_head rvdevs; struct list_head subdevs; struct idr notifyids; From 73516a33588cde0bfa4742d857f8849da28bddd2 Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:38:58 +0100 Subject: [PATCH 08/37] remoteproc: Add elf helpers to access elf64 and elf32 fields elf32 and elf64 mainly differ by their types. In order to avoid copy/pasting the whole loader code, generate static inline functions which will access values according to the elf class. It allows to keep a common loader basis. In order to accommodate both elf types sizes, the maximum size for a elf header member is chosen using the maximum value of the field for both elf class. Reviewed-by: Bjorn Andersson Reviewed-by: Mathieu Poirier Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-5-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_elf_helpers.h | 96 +++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 drivers/remoteproc/remoteproc_elf_helpers.h diff --git a/drivers/remoteproc/remoteproc_elf_helpers.h b/drivers/remoteproc/remoteproc_elf_helpers.h new file mode 100644 index 000000000000..4b6be7b6bf4d --- /dev/null +++ b/drivers/remoteproc/remoteproc_elf_helpers.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Remote processor elf helpers defines + * + * Copyright (C) 2020 Kalray, Inc. + */ + +#ifndef REMOTEPROC_ELF_LOADER_H +#define REMOTEPROC_ELF_LOADER_H + +#include +#include + +/** + * fw_elf_get_class - Get elf class + * @fw: the ELF firmware image + * + * Note that we use and elf32_hdr to access the class since the start of the + * struct is the same for both elf class + * + * Return: elf class of the firmware + */ +static inline u8 fw_elf_get_class(const struct firmware *fw) +{ + struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; + + return ehdr->e_ident[EI_CLASS]; +} + +static inline void elf_hdr_init_ident(struct elf32_hdr *hdr, u8 class) +{ + memcpy(hdr->e_ident, ELFMAG, SELFMAG); + hdr->e_ident[EI_CLASS] = class; + hdr->e_ident[EI_DATA] = ELFDATA2LSB; + hdr->e_ident[EI_VERSION] = EV_CURRENT; + hdr->e_ident[EI_OSABI] = ELFOSABI_NONE; +} + +/* Generate getter and setter for a specific elf struct/field */ +#define ELF_GEN_FIELD_GET_SET(__s, __field, __type) \ +static inline __type elf_##__s##_get_##__field(u8 class, const void *arg) \ +{ \ + if (class == ELFCLASS32) \ + return (__type) ((const struct elf32_##__s *) arg)->__field; \ + else \ + return (__type) ((const struct elf64_##__s *) arg)->__field; \ +} \ +static inline void elf_##__s##_set_##__field(u8 class, void *arg, \ + __type value) \ +{ \ + if (class == ELFCLASS32) \ + ((struct elf32_##__s *) arg)->__field = (__type) value; \ + else \ + ((struct elf64_##__s *) arg)->__field = (__type) value; \ +} + +ELF_GEN_FIELD_GET_SET(hdr, e_entry, u64) +ELF_GEN_FIELD_GET_SET(hdr, e_phnum, u16) +ELF_GEN_FIELD_GET_SET(hdr, e_shnum, u16) +ELF_GEN_FIELD_GET_SET(hdr, e_phoff, u64) +ELF_GEN_FIELD_GET_SET(hdr, e_shoff, u64) +ELF_GEN_FIELD_GET_SET(hdr, e_shstrndx, u16) +ELF_GEN_FIELD_GET_SET(hdr, e_machine, u16) +ELF_GEN_FIELD_GET_SET(hdr, e_type, u16) +ELF_GEN_FIELD_GET_SET(hdr, e_version, u32) +ELF_GEN_FIELD_GET_SET(hdr, e_ehsize, u32) +ELF_GEN_FIELD_GET_SET(hdr, e_phentsize, u16) + +ELF_GEN_FIELD_GET_SET(phdr, p_paddr, u64) +ELF_GEN_FIELD_GET_SET(phdr, p_vaddr, u64) +ELF_GEN_FIELD_GET_SET(phdr, p_filesz, u64) +ELF_GEN_FIELD_GET_SET(phdr, p_memsz, u64) +ELF_GEN_FIELD_GET_SET(phdr, p_type, u32) +ELF_GEN_FIELD_GET_SET(phdr, p_offset, u64) +ELF_GEN_FIELD_GET_SET(phdr, p_flags, u32) +ELF_GEN_FIELD_GET_SET(phdr, p_align, u64) + +ELF_GEN_FIELD_GET_SET(shdr, sh_size, u64) +ELF_GEN_FIELD_GET_SET(shdr, sh_offset, u64) +ELF_GEN_FIELD_GET_SET(shdr, sh_name, u32) +ELF_GEN_FIELD_GET_SET(shdr, sh_addr, u64) + +#define ELF_STRUCT_SIZE(__s) \ +static inline unsigned long elf_size_of_##__s(u8 class) \ +{ \ + if (class == ELFCLASS32)\ + return sizeof(struct elf32_##__s); \ + else \ + return sizeof(struct elf64_##__s); \ +} + +ELF_STRUCT_SIZE(shdr) +ELF_STRUCT_SIZE(phdr) +ELF_STRUCT_SIZE(hdr) + +#endif /* REMOTEPROC_ELF_LOADER_H */ From 826c339099295312eb9bc207197f3beb6ff19051 Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:38:59 +0100 Subject: [PATCH 09/37] remoteproc: Rename rproc_elf_sanity_check for elf32 Since this function will be modified to support both elf32 and elf64, rename the existing one to elf32 (which is the only supported format at the moment). This will allow not to introduce possible side effect when adding elf64 support (ie: all backends will still support only elf32 if not requested explicitely using rproc_elf_sanity_check). Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-6-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 2 +- drivers/remoteproc/remoteproc_elf_loader.c | 6 +++--- drivers/remoteproc/remoteproc_internal.h | 2 +- drivers/remoteproc/st_remoteproc.c | 2 +- drivers/remoteproc/st_slim_rproc.c | 2 +- drivers/remoteproc/stm32_rproc.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index ebb7213c33b1..0f4a426447ae 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -2061,7 +2061,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, rproc->ops->load = rproc_elf_load_segments; rproc->ops->parse_fw = rproc_elf_load_rsc_table; rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; - rproc->ops->sanity_check = rproc_elf_sanity_check; + rproc->ops->sanity_check = rproc_elf32_sanity_check; rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; } diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index c2a9783cfb9a..5a67745f2638 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -25,13 +25,13 @@ #include "remoteproc_internal.h" /** - * rproc_elf_sanity_check() - Sanity Check ELF firmware image + * rproc_elf_sanity_check() - Sanity Check ELF32 firmware image * @rproc: the remote processor handle * @fw: the ELF firmware image * * Make sure this fw image is sane. */ -int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) +int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw) { const char *name = rproc->firmware; struct device *dev = &rproc->dev; @@ -89,7 +89,7 @@ int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) return 0; } -EXPORT_SYMBOL(rproc_elf_sanity_check); +EXPORT_SYMBOL(rproc_elf32_sanity_check); /** * rproc_elf_get_boot_addr() - Get rproc's boot address. diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 23f7a713995f..85903f5fea5b 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -54,7 +54,7 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len); phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); -int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); +int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw); u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw); diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index a3268d95a50e..a6cbfa452764 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c @@ -233,7 +233,7 @@ static const struct rproc_ops st_rproc_ops = { .parse_fw = st_rproc_parse_fw, .load = rproc_elf_load_segments, .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, - .sanity_check = rproc_elf_sanity_check, + .sanity_check = rproc_elf32_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, }; diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 09bcb4d8b9e0..3cca8b65a8db 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -203,7 +203,7 @@ static const struct rproc_ops slim_rproc_ops = { .da_to_va = slim_rproc_da_to_va, .get_boot_addr = rproc_elf_get_boot_addr, .load = rproc_elf_load_segments, - .sanity_check = rproc_elf_sanity_check, + .sanity_check = rproc_elf32_sanity_check, }; /** diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index a18f88044111..9a8b5f5e2572 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -505,7 +505,7 @@ static struct rproc_ops st_rproc_ops = { .load = rproc_elf_load_segments, .parse_fw = stm32_rproc_parse_fw, .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, - .sanity_check = rproc_elf_sanity_check, + .sanity_check = rproc_elf32_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, }; From f31e339f1b9b765c8c727d635c1b74424c102c1e Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:39:00 +0100 Subject: [PATCH 10/37] remoteproc: Add elf64 support in elf loader In order to support elf64, use macros from remoteproc_elf_helpers.h to access elf headers depending on elf class. To allow new drivers to support elf64, add rproc_elf_sanity_check function which make more sense than adding a elf64 named one since it will support both elf versions. Driver which need to support both elf32/elf64 should use this new function for elf sanity check instead of the elf32 one. Signed-off-by: Clement Leger Tested-by: Arnaud POULIQUEN Link: https://lore.kernel.org/r/20200302093902.27849-7-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- Documentation/remoteproc.txt | 2 +- drivers/remoteproc/remoteproc_elf_loader.c | 182 +++++++++++++++------ drivers/remoteproc/remoteproc_internal.h | 10 ++ 3 files changed, 139 insertions(+), 55 deletions(-) diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt index 03c3d2e568b0..2be1147256e0 100644 --- a/Documentation/remoteproc.txt +++ b/Documentation/remoteproc.txt @@ -230,7 +230,7 @@ in the used rings. Binary Firmware Structure ========================= -At this point remoteproc only supports ELF32 firmware binaries. However, +At this point remoteproc supports ELF32 and ELF64 firmware binaries. However, it is quite expected that other platforms/devices which we'd want to support with this framework will be based on different binary formats. diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 5a67745f2638..4869fb7d8fe4 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -23,20 +23,29 @@ #include #include "remoteproc_internal.h" +#include "remoteproc_elf_helpers.h" /** - * rproc_elf_sanity_check() - Sanity Check ELF32 firmware image + * rproc_elf_sanity_check() - Sanity Check for ELF32/ELF64 firmware image * @rproc: the remote processor handle * @fw: the ELF firmware image * - * Make sure this fw image is sane. + * Make sure this fw image is sane (ie a correct ELF32/ELF64 file). */ -int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw) +int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw) { const char *name = rproc->firmware; struct device *dev = &rproc->dev; + /* + * Elf files are beginning with the same structure. Thus, to simplify + * header parsing, we can use the elf32_hdr one for both elf64 and + * elf32. + */ struct elf32_hdr *ehdr; + u32 elf_shdr_get_size; + u64 phoff, shoff; char class; + u16 phnum; if (!fw) { dev_err(dev, "failed to load %s\n", name); @@ -50,13 +59,22 @@ int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw) ehdr = (struct elf32_hdr *)fw->data; - /* We only support ELF32 at this point */ + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + dev_err(dev, "Image is corrupted (bad magic)\n"); + return -EINVAL; + } + class = ehdr->e_ident[EI_CLASS]; - if (class != ELFCLASS32) { + if (class != ELFCLASS32 && class != ELFCLASS64) { dev_err(dev, "Unsupported class: %d\n", class); return -EINVAL; } + if (class == ELFCLASS64 && fw->size < sizeof(struct elf64_hdr)) { + dev_err(dev, "elf64 header is too small\n"); + return -EINVAL; + } + /* We assume the firmware has the same endianness as the host */ # ifdef __LITTLE_ENDIAN if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { @@ -67,28 +85,52 @@ int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw) return -EINVAL; } - if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) { + phoff = elf_hdr_get_e_phoff(class, fw->data); + shoff = elf_hdr_get_e_shoff(class, fw->data); + phnum = elf_hdr_get_e_phnum(class, fw->data); + elf_shdr_get_size = elf_size_of_shdr(class); + + if (fw->size < shoff + elf_shdr_get_size) { dev_err(dev, "Image is too small\n"); return -EINVAL; } - if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { - dev_err(dev, "Image is corrupted (bad magic)\n"); - return -EINVAL; - } - - if (ehdr->e_phnum == 0) { + if (phnum == 0) { dev_err(dev, "No loadable segments\n"); return -EINVAL; } - if (ehdr->e_phoff > fw->size) { + if (phoff > fw->size) { dev_err(dev, "Firmware size is too small\n"); return -EINVAL; } + dev_dbg(dev, "Firmware is an elf%d file\n", + class == ELFCLASS32 ? 32 : 64); + return 0; } +EXPORT_SYMBOL(rproc_elf_sanity_check); + +/** + * rproc_elf_sanity_check() - Sanity Check ELF32 firmware image + * @rproc: the remote processor handle + * @fw: the ELF32 firmware image + * + * Make sure this fw image is sane. + */ +int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw) +{ + int ret = rproc_elf_sanity_check(rproc, fw); + + if (ret) + return ret; + + if (fw_elf_get_class(fw) == ELFCLASS32) + return 0; + + return -EINVAL; +} EXPORT_SYMBOL(rproc_elf32_sanity_check); /** @@ -104,9 +146,7 @@ EXPORT_SYMBOL(rproc_elf32_sanity_check); */ u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw) { - struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; - - return ehdr->e_entry; + return elf_hdr_get_e_entry(fw_elf_get_class(fw), fw->data); } EXPORT_SYMBOL(rproc_elf_get_boot_addr); @@ -137,53 +177,65 @@ EXPORT_SYMBOL(rproc_elf_get_boot_addr); int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) { struct device *dev = &rproc->dev; - struct elf32_hdr *ehdr; - struct elf32_phdr *phdr; + 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 = (struct elf32_hdr *)elf_data; - phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff); + 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 < ehdr->e_phnum; i++, phdr++) { - u32 da = phdr->p_paddr; - u32 memsz = phdr->p_memsz; - u32 filesz = phdr->p_filesz; - u32 offset = phdr->p_offset; + 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 (phdr->p_type != PT_LOAD) + if (type != PT_LOAD) continue; - dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n", - phdr->p_type, da, memsz, filesz); + 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%x memsz 0x%x\n", + 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%x avail 0x%zx\n", + 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); if (!ptr) { - dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz); + 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 (phdr->p_filesz) - memcpy(ptr, elf_data + phdr->p_offset, filesz); + if (filesz) + memcpy(ptr, elf_data + offset, filesz); /* * Zero out remaining memory for this segment. @@ -200,24 +252,35 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) } EXPORT_SYMBOL(rproc_elf_load_segments); -static struct elf32_shdr * -find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size) +static const void * +find_table(struct device *dev, const struct firmware *fw) { - struct elf32_shdr *shdr; + const void *shdr, *name_table_shdr; int i; const char *name_table; struct resource_table *table = NULL; - const u8 *elf_data = (void *)ehdr; + const u8 *elf_data = (void *)fw->data; + u8 class = fw_elf_get_class(fw); + size_t fw_size = fw->size; + const void *ehdr = elf_data; + u16 shnum = elf_hdr_get_e_shnum(class, ehdr); + u32 elf_shdr_get_size = elf_size_of_shdr(class); + u16 shstrndx = elf_hdr_get_e_shstrndx(class, ehdr); /* look for the resource table and handle it */ - shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff); - name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset; + /* First, get the section header according to the elf class */ + shdr = elf_data + elf_hdr_get_e_shoff(class, ehdr); + /* Compute name table section header entry in shdr array */ + name_table_shdr = shdr + (shstrndx * elf_shdr_get_size); + /* Finally, compute the name table section address in elf */ + name_table = elf_data + elf_shdr_get_sh_offset(class, name_table_shdr); - for (i = 0; i < ehdr->e_shnum; i++, shdr++) { - u32 size = shdr->sh_size; - u32 offset = shdr->sh_offset; + for (i = 0; i < shnum; i++, shdr += elf_shdr_get_size) { + u64 size = elf_shdr_get_sh_size(class, shdr); + u64 offset = elf_shdr_get_sh_offset(class, shdr); + u32 name = elf_shdr_get_sh_name(class, shdr); - if (strcmp(name_table + shdr->sh_name, ".resource_table")) + if (strcmp(name_table + name, ".resource_table")) continue; table = (struct resource_table *)(elf_data + offset); @@ -270,21 +333,21 @@ find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size) */ int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw) { - struct elf32_hdr *ehdr; - struct elf32_shdr *shdr; + const void *shdr; struct device *dev = &rproc->dev; struct resource_table *table = NULL; const u8 *elf_data = fw->data; size_t tablesz; + u8 class = fw_elf_get_class(fw); + u64 sh_offset; - ehdr = (struct elf32_hdr *)elf_data; - - shdr = find_table(dev, ehdr, fw->size); + shdr = find_table(dev, fw); if (!shdr) return -EINVAL; - table = (struct resource_table *)(elf_data + shdr->sh_offset); - tablesz = shdr->sh_size; + sh_offset = elf_shdr_get_sh_offset(class, shdr); + table = (struct resource_table *)(elf_data + sh_offset); + tablesz = elf_shdr_get_sh_size(class, shdr); /* * Create a copy of the resource table. When a virtio device starts @@ -317,13 +380,24 @@ EXPORT_SYMBOL(rproc_elf_load_rsc_table); struct resource_table *rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) { - struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; - struct elf32_shdr *shdr; + const void *shdr; + u64 sh_addr, sh_size; + u8 class = fw_elf_get_class(fw); + struct device *dev = &rproc->dev; - shdr = find_table(&rproc->dev, ehdr, fw->size); + shdr = find_table(&rproc->dev, fw); if (!shdr) return NULL; - return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size); + sh_addr = elf_shdr_get_sh_addr(class, shdr); + sh_size = elf_shdr_get_sh_size(class, shdr); + + if (!rproc_u64_fit_in_size_t(sh_size)) { + dev_err(dev, "size (%llx) does not fit in size_t type\n", + sh_size); + return NULL; + } + + return rproc_da_to_va(rproc, sh_addr, sh_size); } EXPORT_SYMBOL(rproc_elf_find_loaded_rsc_table); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index 85903f5fea5b..b389dc79da81 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -55,6 +55,7 @@ phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); int rproc_elf32_sanity_check(struct rproc *rproc, const struct firmware *fw); +int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); u64 rproc_elf_get_boot_addr(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw); int rproc_elf_load_rsc_table(struct rproc *rproc, const struct firmware *fw); @@ -119,4 +120,13 @@ struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc, return NULL; } +static inline +bool rproc_u64_fit_in_size_t(u64 val) +{ + if (sizeof(size_t) == sizeof(u64)) + return true; + + return (val <= (size_t) -1); +} + #endif /* REMOTEPROC_INTERNAL_H */ From 12677467d6d5897391ef635db97787aef2f574eb Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:39:01 +0100 Subject: [PATCH 11/37] remoteproc: Allow overriding only sanity_check Now that rproc_elf_sanity_check can be used by external drivers, allow to only overwrite the sanity_check member of rproc_ops. This will allow drivers to handle elf32 and elf64 by overwriting sanity_check with rproc_elf_sanity_check function. Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-8-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 0f4a426447ae..aa598f99791a 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -2061,7 +2061,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, rproc->ops->load = rproc_elf_load_segments; rproc->ops->parse_fw = rproc_elf_load_rsc_table; rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; - rproc->ops->sanity_check = rproc_elf32_sanity_check; + if (!rproc->ops->sanity_check) + rproc->ops->sanity_check = rproc_elf32_sanity_check; rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; } From 8f4033507d856be9a7983921ab3d2a1d03b9a093 Mon Sep 17 00:00:00 2001 From: Clement Leger Date: Mon, 2 Mar 2020 10:39:02 +0100 Subject: [PATCH 12/37] remoteproc: Adapt coredump to generate correct elf type Now that remoteproc can load an elf64, coredump elf class should be the same as the loaded elf class. In order to do that, add a elf_class field to rproc with default values. If an elf is loaded successfully, this field will be updated with the loaded elf class. Then, the coredump core code has been modified to use the generic elf macro in order to create an elf file with correct class. Reviewed-by: Mathieu Poirier Reviewed-by: Bjorn Andersson Signed-off-by: Clement Leger Link: https://lore.kernel.org/r/20200302093902.27849-9-cleger@kalray.eu Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 65 +++++++++++----------- drivers/remoteproc/remoteproc_elf_loader.c | 3 + include/linux/remoteproc.h | 1 + 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index aa598f99791a..0a9bb745bd0d 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -38,6 +38,7 @@ #include #include "remoteproc_internal.h" +#include "remoteproc_elf_helpers.h" #define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL @@ -1571,20 +1572,21 @@ EXPORT_SYMBOL(rproc_coredump_add_custom_segment); static void rproc_coredump(struct rproc *rproc) { struct rproc_dump_segment *segment; - struct elf32_phdr *phdr; - struct elf32_hdr *ehdr; + void *phdr; + void *ehdr; size_t data_size; size_t offset; void *data; void *ptr; + u8 class = rproc->elf_class; int phnum = 0; if (list_empty(&rproc->dump_segments)) return; - data_size = sizeof(*ehdr); + data_size = elf_size_of_hdr(class); list_for_each_entry(segment, &rproc->dump_segments, node) { - data_size += sizeof(*phdr) + segment->size; + data_size += elf_size_of_phdr(class) + segment->size; phnum++; } @@ -1595,33 +1597,33 @@ static void rproc_coredump(struct rproc *rproc) ehdr = data; - memset(ehdr, 0, sizeof(*ehdr)); - memcpy(ehdr->e_ident, ELFMAG, SELFMAG); - ehdr->e_ident[EI_CLASS] = ELFCLASS32; - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE; - ehdr->e_type = ET_CORE; - ehdr->e_machine = EM_NONE; - ehdr->e_version = EV_CURRENT; - ehdr->e_entry = rproc->bootaddr; - ehdr->e_phoff = sizeof(*ehdr); - ehdr->e_ehsize = sizeof(*ehdr); - ehdr->e_phentsize = sizeof(*phdr); - ehdr->e_phnum = phnum; + memset(ehdr, 0, elf_size_of_hdr(class)); + /* e_ident field is common for both elf32 and elf64 */ + elf_hdr_init_ident(ehdr, class); + + elf_hdr_set_e_type(class, ehdr, ET_CORE); + elf_hdr_set_e_machine(class, ehdr, EM_NONE); + elf_hdr_set_e_version(class, ehdr, EV_CURRENT); + elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr); + elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class)); + elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class)); + elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class)); + elf_hdr_set_e_phnum(class, ehdr, phnum); + + phdr = data + elf_hdr_get_e_phoff(class, ehdr); + offset = elf_hdr_get_e_phoff(class, ehdr); + offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr); - phdr = data + ehdr->e_phoff; - offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum; list_for_each_entry(segment, &rproc->dump_segments, node) { - memset(phdr, 0, sizeof(*phdr)); - phdr->p_type = PT_LOAD; - phdr->p_offset = offset; - phdr->p_vaddr = segment->da; - phdr->p_paddr = segment->da; - phdr->p_filesz = segment->size; - phdr->p_memsz = segment->size; - phdr->p_flags = PF_R | PF_W | PF_X; - phdr->p_align = 0; + memset(phdr, 0, elf_size_of_phdr(class)); + elf_phdr_set_p_type(class, phdr, PT_LOAD); + elf_phdr_set_p_offset(class, phdr, offset); + elf_phdr_set_p_vaddr(class, phdr, segment->da); + elf_phdr_set_p_paddr(class, phdr, segment->da); + elf_phdr_set_p_filesz(class, phdr, segment->size); + elf_phdr_set_p_memsz(class, phdr, segment->size); + elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X); + elf_phdr_set_p_align(class, phdr, 0); if (segment->dump) { segment->dump(rproc, segment, data + offset); @@ -1637,8 +1639,8 @@ static void rproc_coredump(struct rproc *rproc) } } - offset += phdr->p_filesz; - phdr++; + offset += elf_phdr_get_p_filesz(class, phdr); + phdr += elf_size_of_phdr(class); } dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL); @@ -2037,6 +2039,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, rproc->name = name; rproc->priv = &rproc[1]; rproc->auto_boot = true; + rproc->elf_class = ELFCLASS32; device_initialize(&rproc->dev); rproc->dev.parent = dev; diff --git a/drivers/remoteproc/remoteproc_elf_loader.c b/drivers/remoteproc/remoteproc_elf_loader.c index 4869fb7d8fe4..16e2c496fd45 100644 --- a/drivers/remoteproc/remoteproc_elf_loader.c +++ b/drivers/remoteproc/remoteproc_elf_loader.c @@ -248,6 +248,9 @@ int rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) memset(ptr + filesz, 0, memsz - filesz); } + if (ret == 0) + rproc->elf_class = class; + return ret; } EXPORT_SYMBOL(rproc_elf_load_segments); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 1683d6c386a6..ed127b2d35ca 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -514,6 +514,7 @@ struct rproc { bool auto_boot; struct list_head dump_segments; int nb_vdev; + u8 elf_class; }; /** From e1833b9e0d728fc731cd1e9449af185ebf2877da Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 10 Mar 2020 14:15:14 -0700 Subject: [PATCH 13/37] remoteproc/mediatek: Use size_t type for len in scp_da_to_va Clang errors: drivers/remoteproc/mtk_scp.c:364:14: error: incompatible function pointer types initializing 'void *(*)(struct rproc *, u64, size_t)' (aka 'void *(*)(struct rproc *, unsigned long long, unsigned int)') with an expression of type 'void *(struct rproc *, u64, int)' (aka 'void *(struct rproc *, unsigned long long, int)') [-Werror,-Wincompatible-function-pointer-types] .da_to_va = scp_da_to_va, ^~~~~~~~~~~~ 1 error generated. Make the same change as commit 0fcbb369f052 ("remoteproc: Use size_t type for len in da_to_va"), which was not updated for the acceptance of commit 63c13d61eafe ("remoteproc/mediatek: add SCP support for mt8183"). Fixes: 0fcbb369f052 ("remoteproc: Use size_t type for len in da_to_va") Link: https://github.com/ClangBuiltLinux/linux/issues/927 Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20200310211514.32288-1-natechancellor@gmail.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/mtk_scp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c index 7ccdf64ff3ea..ea3743e7e794 100644 --- a/drivers/remoteproc/mtk_scp.c +++ b/drivers/remoteproc/mtk_scp.c @@ -320,7 +320,7 @@ stop: return ret; } -static void *scp_da_to_va(struct rproc *rproc, u64 da, int len) +static void *scp_da_to_va(struct rproc *rproc, u64 da, size_t len) { struct mtk_scp *scp = (struct mtk_scp *)rproc->priv; int offset; From b1f0fa8659f333700b0ca8df7b2e33dc5d554178 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 24 Feb 2020 18:25:19 +0100 Subject: [PATCH 14/37] remoteproc: stm32: demote warning about optional property absence The mainline device trees lack the "st,syscfg-pdds", so probing the driver always throws the warning. As the property is optional per binding and the driver can deal with its absence, demote the warning to info log level. Signed-off-by: Ahmad Fatoum Link: https://lore.kernel.org/r/20200224172519.15315-1-a.fatoum@pengutronix.de Signed-off-by: Bjorn Andersson --- drivers/remoteproc/stm32_rproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/remoteproc/stm32_rproc.c b/drivers/remoteproc/stm32_rproc.c index 9a8b5f5e2572..6a66dbf2df40 100644 --- a/drivers/remoteproc/stm32_rproc.c +++ b/drivers/remoteproc/stm32_rproc.c @@ -602,7 +602,7 @@ static int stm32_rproc_parse_dt(struct platform_device *pdev) err = stm32_rproc_get_syscon(np, "st,syscfg-pdds", &ddata->pdds); if (err) - dev_warn(dev, "failed to get pdds\n"); + dev_info(dev, "failed to get pdds\n"); rproc->auto_boot = of_property_read_bool(np, "st,auto-boot"); From 0b145574b6cd2b326d53fd2cf8054ffd4ad6103f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 28 Feb 2020 12:33:56 -0600 Subject: [PATCH 15/37] remoteproc: re-check state in rproc_trigger_recovery() Two places call rproc_trigger_recovery(): - rproc_crash_handler_work() sets rproc->state to CRASHED under protection of the mutex, then calls it if recovery is not disabled. This function is called in workqueue context when scheduled in rproc_report_crash(). - rproc_recovery_write() calls it in two spots, both of which the only call it if the rproc->state is CRASHED. The mutex is taken right away in rproc_trigger_recovery(). However, by the time the mutex is acquired, something else might have changed rproc->state to something other than CRASHED. The work that follows that is only appropriate for a remoteproc in CRASHED state. So check the state after acquiring the mutex, and only proceed with the recovery work if the remoteproc is still in CRASHED state. Delay reporting that recovering has begun until after we hold the mutex and we know the remote processor is in CRASHED state. Signed-off-by: Alex Elder Link: https://lore.kernel.org/r/20200228183359.16229-2-elder@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 0a9bb745bd0d..a9ac1d01e09b 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -1662,12 +1662,16 @@ int rproc_trigger_recovery(struct rproc *rproc) struct device *dev = &rproc->dev; int ret; - dev_err(dev, "recovering %s\n", rproc->name); - ret = mutex_lock_interruptible(&rproc->lock); if (ret) return ret; + /* State could have changed before we got the mutex */ + if (rproc->state != RPROC_CRASHED) + goto unlock_mutex; + + dev_err(dev, "recovering %s\n", rproc->name); + ret = rproc_stop(rproc, true); if (ret) goto unlock_mutex; From e138cce3e3736ef0a2772fb963f01c1bafb29c71 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 28 Feb 2020 12:33:57 -0600 Subject: [PATCH 16/37] remoteproc: remoteproc debugfs file fixes Don't bother checking the remoteproc state before calling rproc_trigger_recovery() because that function will verify the state, and the state can only be safely checked while holding the mutex anyway. Make the mode for "recovery" be writable. Signed-off-by: Alex Elder Link: https://lore.kernel.org/r/20200228183359.16229-3-elder@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_debugfs.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 82dc34b819df..b87565a13eb1 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -138,16 +138,14 @@ rproc_recovery_write(struct file *filp, const char __user *user_buf, buf[count - 1] = '\0'; if (!strncmp(buf, "enabled", count)) { + /* change the flag and begin the recovery process if needed */ rproc->recovery_disabled = false; - /* if rproc has crashed, trigger recovery */ - if (rproc->state == RPROC_CRASHED) - rproc_trigger_recovery(rproc); + rproc_trigger_recovery(rproc); } else if (!strncmp(buf, "disabled", count)) { rproc->recovery_disabled = true; } else if (!strncmp(buf, "recover", count)) { - /* if rproc has crashed, trigger recovery */ - if (rproc->state == RPROC_CRASHED) - rproc_trigger_recovery(rproc); + /* begin the recovery process without changing the flag */ + rproc_trigger_recovery(rproc); } return count; @@ -349,7 +347,7 @@ void rproc_create_debug_dir(struct rproc *rproc) debugfs_create_file("name", 0400, rproc->dbg_dir, rproc, &rproc_name_ops); - debugfs_create_file("recovery", 0400, rproc->dbg_dir, + debugfs_create_file("recovery", 0600, rproc->dbg_dir, rproc, &rproc_recovery_ops); debugfs_create_file("crash", 0200, rproc->dbg_dir, rproc, &rproc_crash_ops); From 1f2f65c41034accf9baf684c0dae756b56fac19f Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Fri, 28 Feb 2020 12:33:59 -0600 Subject: [PATCH 17/37] remoteproc: return error for bad "recovery" debugfs input If the value written to the "recovery" debugfs file is not one of the recognized commands return an error to indicate it's invalid. Signed-off-by: Alex Elder Link: https://lore.kernel.org/r/20200228183359.16229-5-elder@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_debugfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index b87565a13eb1..d734cadb16e3 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -146,6 +146,8 @@ rproc_recovery_write(struct file *filp, const char __user *user_buf, } else if (!strncmp(buf, "recover", count)) { /* begin the recovery process without changing the flag */ rproc_trigger_recovery(rproc); + } else { + return -EINVAL; } return count; From 791c13b709dd51eb37330f2a5837434e90c87c27 Mon Sep 17 00:00:00 2001 From: Nikita Shubin Date: Fri, 6 Mar 2020 10:24:53 +0300 Subject: [PATCH 18/37] remoteproc: Fix NULL pointer dereference in rproc_virtio_notify Undefined rproc_ops .kick method in remoteproc driver will result in "Unable to handle kernel NULL pointer dereference" in rproc_virtio_notify, after firmware loading if: 1) .kick method wasn't defined in driver 2) resource_table exists in firmware and has "Virtio device entry" defined Let's refuse to register an rproc-induced virtio device if no kick method was defined for rproc. [ 13.180049][ T415] 8<--- cut here --- [ 13.190558][ T415] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [ 13.212544][ T415] pgd = (ptrval) [ 13.217052][ T415] [00000000] *pgd=00000000 [ 13.224692][ T415] Internal error: Oops: 80000005 [#1] PREEMPT SMP ARM [ 13.231318][ T415] Modules linked in: rpmsg_char imx_rproc virtio_rpmsg_bus rpmsg_core [last unloaded: imx_rproc] [ 13.241687][ T415] CPU: 0 PID: 415 Comm: unload-load.sh Not tainted 5.5.2-00002-g707df13bbbdd #6 [ 13.250561][ T415] Hardware name: Freescale i.MX7 Dual (Device Tree) [ 13.257009][ T415] PC is at 0x0 [ 13.260249][ T415] LR is at rproc_virtio_notify+0x2c/0x54 [ 13.265738][ T415] pc : [<00000000>] lr : [<8050f6b0>] psr: 60010113 [ 13.272702][ T415] sp : b8d47c48 ip : 00000001 fp : bc04de00 [ 13.278625][ T415] r10: bc04c000 r9 : 00000cc0 r8 : b8d46000 [ 13.284548][ T415] r7 : 00000000 r6 : b898f200 r5 : 00000000 r4 : b8a29800 [ 13.291773][ T415] r3 : 00000000 r2 : 990a3ad4 r1 : 00000000 r0 : b8a29800 [ 13.299000][ T415] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none [ 13.306833][ T415] Control: 10c5387d Table: b8b4806a DAC: 00000051 [ 13.313278][ T415] Process unload-load.sh (pid: 415, stack limit = 0x(ptrval)) [ 13.320591][ T415] Stack: (0xb8d47c48 to 0xb8d48000) [ 13.325651][ T415] 7c40: b895b680 00000001 b898f200 803c6430 b895bc80 7f00ae18 [ 13.334531][ T415] 7c60: 00000035 00000000 00000000 b9393200 80b3ed80 00004000 b9393268 bbf5a9a2 [ 13.343410][ T415] 7c80: 00000e00 00000200 00000000 7f00aff0 7f00a014 b895b680 b895b800 990a3ad4 [ 13.352290][ T415] 7ca0: 00000001 b898f210 b898f200 00000000 00000000 7f00e000 00000001 00000000 [ 13.361170][ T415] 7cc0: 00000000 803c62e0 80b2169c 802a0924 b898f210 00000000 00000000 b898f210 [ 13.370049][ T415] 7ce0: 80b9ba44 00000000 80b9ba48 00000000 7f00e000 00000008 80b2169c 80400114 [ 13.378929][ T415] 7d00: 80b2169c 8061fd64 b898f210 7f00e000 80400744 b8d46000 80b21634 80b21634 [ 13.387809][ T415] 7d20: 80b2169c 80400614 80b21634 80400718 7f00e000 00000000 b8d47d7c 80400744 [ 13.396689][ T415] 7d40: b8d46000 80b21634 80b21634 803fe338 b898f254 b80fe76c b8d32e38 990a3ad4 [ 13.405569][ T415] 7d60: fffffff3 b898f210 b8d46000 00000001 b898f254 803ffe7c 80857a90 b898f210 [ 13.414449][ T415] 7d80: 00000001 990a3ad4 b8d46000 b898f210 b898f210 80b17aec b8a29c20 803ff0a4 [ 13.423328][ T415] 7da0: b898f210 00000000 b8d46000 803fb8e0 b898f200 00000000 80b17aec b898f210 [ 13.432209][ T415] 7dc0: b8a29c20 990a3ad4 b895b900 b898f200 8050fb7c 80b17aec b898f210 b8a29c20 [ 13.441088][ T415] 7de0: b8a29800 b895b900 b8a29a04 803c5ec0 b8a29c00 b898f200 b8a29a20 00000007 [ 13.449968][ T415] 7e00: b8a29c20 8050fd78 b8a29800 00000000 b8a29a20 b8a29c04 b8a29820 b8a299d0 [ 13.458848][ T415] 7e20: b895b900 8050e5a4 b8a29800 b8a299d8 b8d46000 b8a299e0 b8a29820 b8a299d0 [ 13.467728][ T415] 7e40: b895b900 8050e008 000041ed 00000000 b8b8c440 b8a299d8 b8a299e0 b8a299d8 [ 13.476608][ T415] 7e60: b8b8c440 990a3ad4 00000000 b8a29820 b8b8c400 00000006 b8a29800 b895b880 [ 13.485487][ T415] 7e80: b8d47f78 00000000 00000000 8050f4b4 00000006 b895b890 b8b8c400 008fbea0 [ 13.494367][ T415] 7ea0: b895b880 8029f530 00000000 00000000 b8d46000 00000006 b8d46000 008fbea0 [ 13.503246][ T415] 7ec0: 8029f434 00000000 b8d46000 00000000 00000000 8021e2e4 0000000a 8061fd0c [ 13.512125][ T415] 7ee0: 0000000a b8af0c00 0000000a b8af0c40 00000001 b8af0c40 00000000 8061f910 [ 13.521005][ T415] 7f00: 0000000a 80240af4 00000002 b8d46000 00000000 8061fd0c 00000002 80232d7c [ 13.529884][ T415] 7f20: 00000000 b8d46000 00000000 990a3ad4 00000000 00000006 b8a62d80 008fbea0 [ 13.538764][ T415] 7f40: b8d47f78 00000000 b8d46000 00000000 00000000 802210c0 b88f2900 00000000 [ 13.547644][ T415] 7f60: b8a62d80 b8a62d80 b8d46000 00000006 008fbea0 80221320 00000000 00000000 [ 13.556524][ T415] 7f80: b8af0c00 990a3ad4 0000006c 008fbea0 76f1cda0 00000004 80101204 00000004 [ 13.565403][ T415] 7fa0: 00000000 80101000 0000006c 008fbea0 00000001 008fbea0 00000006 00000000 [ 13.574283][ T415] 7fc0: 0000006c 008fbea0 76f1cda0 00000004 00000006 00000006 00000000 00000000 [ 13.583162][ T415] 7fe0: 00000004 7ebaf7d0 76eb4c0b 76e3f206 600d0030 00000001 00000000 00000000 [ 13.592056][ T415] [<8050f6b0>] (rproc_virtio_notify) from [<803c6430>] (virtqueue_notify+0x1c/0x34) [ 13.601298][ T415] [<803c6430>] (virtqueue_notify) from [<7f00ae18>] (rpmsg_probe+0x280/0x380 [virtio_rpmsg_bus]) [ 13.611663][ T415] [<7f00ae18>] (rpmsg_probe [virtio_rpmsg_bus]) from [<803c62e0>] (virtio_dev_probe+0x1f8/0x2c4) [ 13.622022][ T415] [<803c62e0>] (virtio_dev_probe) from [<80400114>] (really_probe+0x200/0x450) [ 13.630817][ T415] [<80400114>] (really_probe) from [<80400614>] (driver_probe_device+0x16c/0x1ac) [ 13.639873][ T415] [<80400614>] (driver_probe_device) from [<803fe338>] (bus_for_each_drv+0x84/0xc8) [ 13.649102][ T415] [<803fe338>] (bus_for_each_drv) from [<803ffe7c>] (__device_attach+0xd4/0x164) [ 13.658069][ T415] [<803ffe7c>] (__device_attach) from [<803ff0a4>] (bus_probe_device+0x84/0x8c) [ 13.666950][ T415] [<803ff0a4>] (bus_probe_device) from [<803fb8e0>] (device_add+0x444/0x768) [ 13.675572][ T415] [<803fb8e0>] (device_add) from [<803c5ec0>] (register_virtio_device+0xa4/0xfc) [ 13.684541][ T415] [<803c5ec0>] (register_virtio_device) from [<8050fd78>] (rproc_add_virtio_dev+0xcc/0x1b8) [ 13.694466][ T415] [<8050fd78>] (rproc_add_virtio_dev) from [<8050e5a4>] (rproc_start+0x148/0x200) [ 13.703521][ T415] [<8050e5a4>] (rproc_start) from [<8050e008>] (rproc_boot+0x384/0x5c0) [ 13.711708][ T415] [<8050e008>] (rproc_boot) from [<8050f4b4>] (state_store+0x3c/0xc8) [ 13.719723][ T415] [<8050f4b4>] (state_store) from [<8029f530>] (kernfs_fop_write+0xfc/0x214) [ 13.728348][ T415] [<8029f530>] (kernfs_fop_write) from [<8021e2e4>] (__vfs_write+0x30/0x1cc) [ 13.736971][ T415] [<8021e2e4>] (__vfs_write) from [<802210c0>] (vfs_write+0xac/0x17c) [ 13.744985][ T415] [<802210c0>] (vfs_write) from [<80221320>] (ksys_write+0x64/0xe4) [ 13.752825][ T415] [<80221320>] (ksys_write) from [<80101000>] (ret_fast_syscall+0x0/0x54) [ 13.761178][ T415] Exception stack(0xb8d47fa8 to 0xb8d47ff0) [ 13.766932][ T415] 7fa0: 0000006c 008fbea0 00000001 008fbea0 00000006 00000000 [ 13.775811][ T415] 7fc0: 0000006c 008fbea0 76f1cda0 00000004 00000006 00000006 00000000 00000000 [ 13.784687][ T415] 7fe0: 00000004 7ebaf7d0 76eb4c0b 76e3f206 [ 13.790442][ T415] Code: bad PC value [ 13.839214][ T415] ---[ end trace 1fe21ecfc9f28852 ]--- Reviewed-by: Mathieu Poirier Signed-off-by: Nikita Shubin Fixes: 7a186941626d ("remoteproc: remove the single rpmsg vdev limitation") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20200306072452.24743-1-NShubin@topcon.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_virtio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index eb817132bc5f..e61d738d9b47 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c @@ -335,6 +335,13 @@ int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) struct rproc_mem_entry *mem; int ret; + if (rproc->ops->kick == NULL) { + ret = -EINVAL; + dev_err(dev, ".kick method not defined for %s", + rproc->name); + goto out; + } + /* Try to find dedicated vdev buffer carveout */ mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); if (mem) { From c0abe2ca3605e4c4fb25bf69d0218c63baf71d2b Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 23 Mar 2020 22:29:01 -0700 Subject: [PATCH 19/37] remoteproc: Traverse rproc_list under RCU read lock In order to be able to traverse the mostly read-only rproc_list without locking during panic migrate traversal to be done under rcu_read_lock(). Mutual exclusion for modifications of the list continues to be handled by the rproc_list_mutex and a synchronization point is added before releasing objects that are popped from the list. Reviewed-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324052904.738594-2-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index a9ac1d01e09b..7ee976ee2044 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1868,8 +1869,8 @@ struct rproc *rproc_get_by_phandle(phandle phandle) if (!np) return NULL; - mutex_lock(&rproc_list_mutex); - list_for_each_entry(r, &rproc_list, node) { + rcu_read_lock(); + list_for_each_entry_rcu(r, &rproc_list, node) { if (r->dev.parent && r->dev.parent->of_node == np) { /* prevent underlying implementation from being removed */ if (!try_module_get(r->dev.parent->driver->owner)) { @@ -1882,7 +1883,7 @@ struct rproc *rproc_get_by_phandle(phandle phandle) break; } } - mutex_unlock(&rproc_list_mutex); + rcu_read_unlock(); of_node_put(np); @@ -1939,7 +1940,7 @@ int rproc_add(struct rproc *rproc) /* expose to rproc_get_by_phandle users */ mutex_lock(&rproc_list_mutex); - list_add(&rproc->node, &rproc_list); + list_add_rcu(&rproc->node, &rproc_list); mutex_unlock(&rproc_list_mutex); return 0; @@ -2156,9 +2157,12 @@ int rproc_del(struct rproc *rproc) /* the rproc is downref'ed as soon as it's removed from the klist */ mutex_lock(&rproc_list_mutex); - list_del(&rproc->node); + list_del_rcu(&rproc->node); mutex_unlock(&rproc_list_mutex); + /* Ensure that no readers of rproc_list are still active */ + synchronize_rcu(); + device_del(&rproc->dev); return 0; From dc5192c449368eed3385f4405670aa3ed21d6270 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 23 Mar 2020 22:29:02 -0700 Subject: [PATCH 20/37] remoteproc: Introduce "panic" callback in ops Introduce generic support for handling kernel panics in remoteproc drivers, in order to allow operations needed for aiding in post mortem system debugging, such as flushing caches etc. The function can return a number of milliseconds needed by the remote to "settle" and the core will wait the longest returned duration before returning from the panic handler. Reviewed-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324052904.738594-3-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_core.c | 43 ++++++++++++++++++++++++++++ include/linux/remoteproc.h | 3 ++ 2 files changed, 46 insertions(+) diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 7ee976ee2044..e12a54e67588 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -16,6 +16,7 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ +#include #include #include #include @@ -45,6 +46,7 @@ static DEFINE_MUTEX(rproc_list_mutex); static LIST_HEAD(rproc_list); +static struct notifier_block rproc_panic_nb; typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int offset, int avail); @@ -2236,10 +2238,50 @@ void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type) } EXPORT_SYMBOL(rproc_report_crash); +static int rproc_panic_handler(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + unsigned int longest = 0; + struct rproc *rproc; + unsigned int d; + + rcu_read_lock(); + list_for_each_entry_rcu(rproc, &rproc_list, node) { + if (!rproc->ops->panic || rproc->state != RPROC_RUNNING) + continue; + + d = rproc->ops->panic(rproc); + longest = max(longest, d); + } + rcu_read_unlock(); + + /* + * Delay for the longest requested duration before returning. This can + * be used by the remoteproc drivers to give the remote processor time + * to perform any requested operations (such as flush caches), when + * it's not possible to signal the Linux side due to the panic. + */ + mdelay(longest); + + return NOTIFY_DONE; +} + +static void __init rproc_init_panic(void) +{ + rproc_panic_nb.notifier_call = rproc_panic_handler; + atomic_notifier_chain_register(&panic_notifier_list, &rproc_panic_nb); +} + +static void __exit rproc_exit_panic(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, &rproc_panic_nb); +} + static int __init remoteproc_init(void) { rproc_init_sysfs(); rproc_init_debugfs(); + rproc_init_panic(); return 0; } @@ -2249,6 +2291,7 @@ static void __exit remoteproc_exit(void) { ida_destroy(&rproc_dev_index); + rproc_exit_panic(); rproc_exit_debugfs(); rproc_exit_sysfs(); } diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index ed127b2d35ca..9c07d7958c53 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h @@ -369,6 +369,8 @@ enum rsc_handling_status { * expects to find it * @sanity_check: sanity check the fw image * @get_boot_addr: get boot address to entry point specified in firmware + * @panic: optional callback to react to system panic, core will delay + * panic at least the returned number of milliseconds */ struct rproc_ops { int (*start)(struct rproc *rproc); @@ -383,6 +385,7 @@ struct rproc_ops { int (*load)(struct rproc *rproc, const struct firmware *fw); int (*sanity_check)(struct rproc *rproc, const struct firmware *fw); u64 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw); + unsigned long (*panic)(struct rproc *rproc); }; /** From e9142f5c28e997ab45fac23bc27a3bc01725bb49 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 23 Mar 2020 22:29:03 -0700 Subject: [PATCH 21/37] remoteproc: qcom: q6v5: Add common panic handler Add a common panic handler that invokes a stop request and sleep enough to let the remoteproc flush it's caches etc in order to aid post mortem debugging. For now a hard coded 200ms is returned to the remoteproc core, this value is taken from the downstream kernel. Reviewed-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324052904.738594-4-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5.c | 20 ++++++++++++++++++++ drivers/remoteproc/qcom_q6v5.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c index cb0f4a0be032..111a442c993c 100644 --- a/drivers/remoteproc/qcom_q6v5.c +++ b/drivers/remoteproc/qcom_q6v5.c @@ -15,6 +15,8 @@ #include #include "qcom_q6v5.h" +#define Q6V5_PANIC_DELAY_MS 200 + /** * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start * @q6v5: reference to qcom_q6v5 context to be reinitialized @@ -162,6 +164,24 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5) } EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); +/** + * qcom_q6v5_panic() - panic handler to invoke a stop on the remote + * @q6v5: reference to qcom_q6v5 context + * + * Set the stop bit and sleep in order to allow the remote processor to flush + * its caches etc for post mortem debugging. + * + * Return: 200ms + */ +unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5) +{ + qcom_smem_state_update_bits(q6v5->state, + BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); + + return Q6V5_PANIC_DELAY_MS; +} +EXPORT_SYMBOL_GPL(qcom_q6v5_panic); + /** * qcom_q6v5_init() - initializer of the q6v5 common struct * @q6v5: handle to be initialized diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h index 7ac92c1e0f49..c4ed887c1499 100644 --- a/drivers/remoteproc/qcom_q6v5.h +++ b/drivers/remoteproc/qcom_q6v5.h @@ -42,5 +42,6 @@ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5); int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout); +unsigned long qcom_q6v5_panic(struct qcom_q6v5 *q6v5); #endif From 717c21bad161dd7127f6e6acf86a3571e1102254 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 23 Mar 2020 22:29:04 -0700 Subject: [PATCH 22/37] remoteproc: qcom: Introduce panic handler for PAS and ADSP Make the PAS and ADSP/CDSP remoteproc drivers implement the panic handler that will invoke a stop to prepare the remoteprocs for post mortem debugging. Reviewed-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324052904.738594-5-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_adsp.c | 8 ++++++++ drivers/remoteproc/qcom_q6v5_pas.c | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 2b01f2282062..24a3db961d5e 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -282,12 +282,20 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len) return adsp->mem_region + offset; } +static unsigned long adsp_panic(struct rproc *rproc) +{ + struct qcom_adsp *adsp = rproc->priv; + + return qcom_q6v5_panic(&adsp->q6v5); +} + static const struct rproc_ops adsp_ops = { .start = adsp_start, .stop = adsp_stop, .da_to_va = adsp_da_to_va, .parse_fw = qcom_register_dump_segments, .load = adsp_load, + .panic = adsp_panic, }; static int adsp_init_clock(struct qcom_adsp *adsp, const char **clk_ids) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index a41860d2243a..7a63efb85405 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -234,12 +234,20 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len) return adsp->mem_region + offset; } +static unsigned long adsp_panic(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + + return qcom_q6v5_panic(&adsp->q6v5); +} + static const struct rproc_ops adsp_ops = { .start = adsp_start, .stop = adsp_stop, .da_to_va = adsp_da_to_va, .parse_fw = qcom_register_dump_segments, .load = adsp_load, + .panic = adsp_panic, }; static int adsp_init_clock(struct qcom_adsp *adsp) From c8f70f80b0afd4ef484bbd4e720f14a8403ba613 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:21 +0200 Subject: [PATCH 23/37] dt-bindings: remoteproc: Add OMAP remoteproc bindings Add the device tree bindings document for the IPU and DSP remote processor devices on OMAP4+ SoCs. Cc: Rob Herring Cc: devicetree@vger.kernel.org Signed-off-by: Suman Anna [t-kristo@ti.com: converted to schema] Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20200324110035.29907-2-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- .../remoteproc/ti,omap-remoteproc.yaml | 324 ++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml diff --git a/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml new file mode 100644 index 000000000000..084960a8f17a --- /dev/null +++ b/Documentation/devicetree/bindings/remoteproc/ti,omap-remoteproc.yaml @@ -0,0 +1,324 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/remoteproc/ti,omap-remoteproc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: OMAP4+ Remoteproc Devices + +maintainers: + - Suman Anna + +description: + The OMAP family of SoCs usually have one or more slave processor sub-systems + that are used to offload some of the processor-intensive tasks, or to manage + other hardware accelerators, for achieving various system level goals. + + The processor cores in the sub-system are usually behind an IOMMU, and may + contain additional sub-modules like Internal RAM and/or ROMs, L1 and/or L2 + caches, an Interrupt Controller, a Cache Controller etc. + + The OMAP SoCs usually have a DSP processor sub-system and/or an IPU processor + sub-system. The DSP processor sub-system can contain any of the TI's C64x, + C66x or C67x family of DSP cores as the main execution unit. The IPU processor + sub-system usually contains either a Dual-Core Cortex-M3 or Dual-Core + Cortex-M4 processors. + + Each remote processor sub-system is represented as a single DT node. Each node + has a number of required or optional properties that enable the OS running on + the host processor (MPU) to perform the device management of the remote + processor and to communicate with the remote processor. The various properties + can be classified as constant or variable. The constant properties are + dictated by the SoC and does not change from one board to another having the + same SoC. Examples of constant properties include 'iommus', 'reg'. The + variable properties are dictated by the system integration aspects such as + memory on the board, or configuration used within the corresponding firmware + image. Examples of variable properties include 'mboxes', 'memory-region', + 'timers', 'watchdog-timers' etc. + +properties: + compatible: + enum: + - ti,omap4-dsp + - ti,omap5-dsp + - ti,dra7-dsp + - ti,omap4-ipu + - ti,omap5-ipu + - ti,dra7-ipu + + iommus: + minItems: 1 + maxItems: 2 + description: | + phandles to OMAP IOMMU nodes, that need to be programmed + for this remote processor to access any external RAM memory or + other peripheral device address spaces. This property usually + has only a single phandle. Multiple phandles are used only in + cases where the sub-system has different ports for different + sub-modules within the processor sub-system (eg: DRA7 DSPs), + and need the same programming in both the MMUs. + + mboxes: + minItems: 1 + maxItems: 2 + description: | + OMAP Mailbox specifier denoting the sub-mailbox, to be used for + communication with the remote processor. The specifier format is + as per the bindings, + Documentation/devicetree/bindings/mailbox/omap-mailbox.txt + This property should match with the sub-mailbox node used in + the firmware image. + + clocks: + description: | + Main functional clock for the remote processor + + resets: + description: | + Reset handles for the remote processor + + firmware-name: + description: | + Default name of the firmware to load to the remote processor. + +# Optional properties: +# -------------------- +# Some of these properties are mandatory on some SoCs, and some are optional +# depending on the configuration of the firmware image to be executed on the +# remote processor. The conditions are mentioned for each property. +# +# The following are the optional properties: + + memory-region: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + phandle to the reserved memory node to be associated + with the remoteproc device. The reserved memory node + can be a CMA memory node, and should be defined as + per the bindings, + Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt + + reg: + description: | + Address space for any remoteproc memories present on + the SoC. Should contain an entry for each value in + 'reg-names'. These are mandatory for all DSP and IPU + processors that have them (OMAP4/OMAP5 DSPs do not have + any RAMs) + + reg-names: + description: | + Required names for each of the address spaces defined in + the 'reg' property. Expects the names from the following + list, in the specified order, each representing the corresponding + internal RAM memory region. + minItems: 1 + maxItems: 3 + items: + - const: l2ram + - const: l1pram + - const: l1dram + + ti,bootreg: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + Should be a triple of the phandle to the System Control + Configuration region that contains the boot address + register, the register offset of the boot address + register within the System Control module, and the bit + shift within the register. This property is required for + all the DSP instances on OMAP4, OMAP5 and DRA7xx SoCs. + + ti,autosuspend-delay-ms: + description: | + Custom autosuspend delay for the remoteproc in milliseconds. + Recommended values is preferable to be in the order of couple + of seconds. A negative value can also be used to disable the + autosuspend behavior. + + ti,timers: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + One or more phandles to OMAP DMTimer nodes, that serve + as System/Tick timers for the OS running on the remote + processors. This will usually be a single timer if the + processor sub-system is running in SMP mode, or one per + core in the processor sub-system. This can also be used + to reserve specific timers to be dedicated to the + remote processors. + + This property is mandatory on remote processors requiring + external tick wakeup, and to support Power Management + features. The timers to be used should match with the + timers used in the firmware image. + + ti,watchdog-timers: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + One or more phandles to OMAP DMTimer nodes, used to + serve as Watchdog timers for the processor cores. This + will usually be one per executing processor core, even + if the processor sub-system is running a SMP OS. + + The timers to be used should match with the watchdog + timers used in the firmware image. + +if: + properties: + compatible: + enum: + - ti,dra7-dsp +then: + properties: + reg: + minItems: 3 + maxItems: 3 + required: + - reg + - reg-names + - ti,bootreg + +else: + if: + properties: + compatible: + enum: + - ti,omap4-ipu + - ti,omap5-ipu + - ti,dra7-ipu + then: + properties: + reg: + minItems: 1 + maxItems: 1 + ti,bootreg: false + required: + - reg + - reg-names + + else: + properties: + reg: false + required: + - ti,bootreg + +required: + - compatible + - iommus + - mboxes + - clocks + - resets + - firmware-name + +additionalProperties: false + +examples: + - | + + //Example 1: OMAP4 DSP + + /* DSP Reserved Memory node */ + #include + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + + dsp_memory_region: dsp-memory@98000000 { + compatible = "shared-dma-pool"; + reg = <0x98000000 0x800000>; + reusable; + }; + }; + + /* DSP node */ + ocp { + dsp: dsp { + compatible = "ti,omap4-dsp"; + ti,bootreg = <&scm_conf 0x304 0>; + iommus = <&mmu_dsp>; + mboxes = <&mailbox &mbox_dsp>; + memory-region = <&dsp_memory_region>; + ti,timers = <&timer5>; + ti,watchdog-timers = <&timer6>; + clocks = <&tesla_clkctrl OMAP4_DSP_CLKCTRL 0>; + resets = <&prm_tesla 0>, <&prm_tesla 1>; + firmware-name = "omap4-dsp-fw.xe64T"; + }; + }; + + - |+ + + //Example 2: OMAP5 IPU + + /* IPU Reserved Memory node */ + #include + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + + ipu_memory_region: ipu-memory@95800000 { + compatible = "shared-dma-pool"; + reg = <0 0x95800000 0 0x3800000>; + reusable; + }; + }; + + /* IPU node */ + ocp { + #address-cells = <1>; + #size-cells = <1>; + + ipu: ipu@55020000 { + compatible = "ti,omap5-ipu"; + reg = <0x55020000 0x10000>; + reg-names = "l2ram"; + iommus = <&mmu_ipu>; + mboxes = <&mailbox &mbox_ipu>; + memory-region = <&ipu_memory_region>; + ti,timers = <&timer3>, <&timer4>; + ti,watchdog-timers = <&timer9>, <&timer11>; + clocks = <&ipu_clkctrl OMAP5_MMU_IPU_CLKCTRL 0>; + resets = <&prm_core 2>; + firmware-name = "omap5-ipu-fw.xem4"; + }; + }; + + - |+ + + //Example 3: DRA7xx/AM57xx DSP + + /* DSP1 Reserved Memory node */ + #include + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + + dsp1_memory_region: dsp1-memory@99000000 { + compatible = "shared-dma-pool"; + reg = <0x0 0x99000000 0x0 0x4000000>; + reusable; + }; + }; + + /* DSP1 node */ + ocp { + #address-cells = <1>; + #size-cells = <1>; + + dsp1: dsp@40800000 { + compatible = "ti,dra7-dsp"; + reg = <0x40800000 0x48000>, + <0x40e00000 0x8000>, + <0x40f00000 0x8000>; + reg-names = "l2ram", "l1pram", "l1dram"; + ti,bootreg = <&scm_conf 0x55c 0>; + iommus = <&mmu0_dsp1>, <&mmu1_dsp1>; + mboxes = <&mailbox5 &mbox_dsp1_ipc3x>; + memory-region = <&dsp1_memory_region>; + ti,timers = <&timer5>; + ti,watchdog-timers = <&timer10>; + resets = <&prm_dsp1 0>; + clocks = <&dsp1_clkctrl DRA7_DSP1_MMU0_DSP1_CLKCTRL 0>; + firmware-name = "dra7-dsp1-fw.xe66"; + }; + }; From 75242927014f532b9a04c4c2fb74566e2b0da70a Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:22 +0200 Subject: [PATCH 24/37] remoteproc/omap: Add device tree support OMAP4+ SoCs support device tree boot only. The OMAP remoteproc driver is enhanced to support remoteproc devices created through Device Tree, support for legacy platform devices has been deprecated. The current DT support handles the IPU and DSP processor subsystems on OMAP4 and OMAP5 SoCs. The OMAP remoteproc driver relies on the ti-sysc, reset, and syscon layers for performing clock, reset and boot vector management (DSP remoteprocs only) of the devices, but some of these are limited only to the machine-specific layers in arch/arm. The dependency against control module API for boot vector management of the DSP remoteprocs has now been removed with added logic to parse the boot register from the DT node and program it appropriately directly within the driver. The OMAP remoteproc driver expects the firmware names to be provided via device tree entries (firmware-name.) These are used to load the proper firmware during boot of the remote processor. Cc: Tony Lindgren Signed-off-by: Suman Anna [t-kristo@ti.com: converted to use ti-sysc framework] Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-3-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 177 ++++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 17 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 6398194075aa..d47d5ded651a 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -2,7 +2,7 @@ /* * OMAP Remote Processor driver * - * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011-2020 Texas Instruments Incorporated - http://www.ti.com/ * Copyright (C) 2011 Google, Inc. * * Ohad Ben-Cohen @@ -16,27 +16,51 @@ #include #include #include +#include #include #include #include #include #include - -#include +#include +#include +#include #include "omap_remoteproc.h" #include "remoteproc_internal.h" +/** + * struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs + * @syscon: regmap handle for the system control configuration module + * @boot_reg: boot register offset within the @syscon regmap + */ +struct omap_rproc_boot_data { + struct regmap *syscon; + unsigned int boot_reg; +}; + /** * struct omap_rproc - omap remote processor state * @mbox: mailbox channel handle * @client: mailbox client to request the mailbox channel + * @boot_data: boot data structure for setting processor boot address * @rproc: rproc handle + * @reset: reset handle */ struct omap_rproc { struct mbox_chan *mbox; struct mbox_client client; + struct omap_rproc_boot_data *boot_data; struct rproc *rproc; + struct reset_control *reset; +}; + +/** + * struct omap_rproc_dev_data - device data for the omap remote processor + * @device_name: device name of the remote processor + */ +struct omap_rproc_dev_data { + const char *device_name; }; /** @@ -92,6 +116,21 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) ret); } +/** + * omap_rproc_write_dsp_boot_addr() - set boot address for DSP remote processor + * @rproc: handle of a remote processor + * + * Set boot address for a supported DSP remote processor. + */ +static void omap_rproc_write_dsp_boot_addr(struct rproc *rproc) +{ + struct omap_rproc *oproc = rproc->priv; + struct omap_rproc_boot_data *bdata = oproc->boot_data; + u32 offset = bdata->boot_reg; + + regmap_write(bdata->syscon, offset, rproc->bootaddr); +} + /* * Power up the remote processor. * @@ -103,13 +142,11 @@ static int omap_rproc_start(struct rproc *rproc) { struct omap_rproc *oproc = rproc->priv; struct device *dev = rproc->dev.parent; - struct platform_device *pdev = to_platform_device(dev); - struct omap_rproc_pdata *pdata = pdev->dev.platform_data; int ret; struct mbox_client *client = &oproc->client; - if (pdata->set_bootaddr) - pdata->set_bootaddr(rproc->bootaddr); + if (oproc->boot_data) + omap_rproc_write_dsp_boot_addr(rproc); client->dev = dev; client->tx_done = NULL; @@ -117,7 +154,7 @@ static int omap_rproc_start(struct rproc *rproc) client->tx_block = false; client->knows_txdone = false; - oproc->mbox = omap_mbox_request_channel(client, pdata->mbox_name); + oproc->mbox = mbox_request_channel(client, 0); if (IS_ERR(oproc->mbox)) { ret = -EBUSY; dev_err(dev, "mbox_request_channel failed: %ld\n", @@ -138,9 +175,9 @@ static int omap_rproc_start(struct rproc *rproc) goto put_mbox; } - ret = pdata->device_enable(pdev); + ret = reset_control_deassert(oproc->reset); if (ret) { - dev_err(dev, "omap_device_enable failed: %d\n", ret); + dev_err(dev, "reset control deassert failed: %d\n", ret); goto put_mbox; } @@ -154,13 +191,10 @@ put_mbox: /* power off the remote processor */ static int omap_rproc_stop(struct rproc *rproc) { - struct device *dev = rproc->dev.parent; - struct platform_device *pdev = to_platform_device(dev); - struct omap_rproc_pdata *pdata = pdev->dev.platform_data; struct omap_rproc *oproc = rproc->priv; int ret; - ret = pdata->device_shutdown(pdev); + ret = reset_control_assert(oproc->reset); if (ret) return ret; @@ -175,12 +209,115 @@ static const struct rproc_ops omap_rproc_ops = { .kick = omap_rproc_kick, }; +static const struct omap_rproc_dev_data omap4_dsp_dev_data = { + .device_name = "dsp", +}; + +static const struct omap_rproc_dev_data omap4_ipu_dev_data = { + .device_name = "ipu", +}; + +static const struct omap_rproc_dev_data omap5_dsp_dev_data = { + .device_name = "dsp", +}; + +static const struct omap_rproc_dev_data omap5_ipu_dev_data = { + .device_name = "ipu", +}; + +static const struct of_device_id omap_rproc_of_match[] = { + { + .compatible = "ti,omap4-dsp", + .data = &omap4_dsp_dev_data, + }, + { + .compatible = "ti,omap4-ipu", + .data = &omap4_ipu_dev_data, + }, + { + .compatible = "ti,omap5-dsp", + .data = &omap5_dsp_dev_data, + }, + { + .compatible = "ti,omap5-ipu", + .data = &omap5_ipu_dev_data, + }, + { + /* end */ + }, +}; +MODULE_DEVICE_TABLE(of, omap_rproc_of_match); + +static const char *omap_rproc_get_firmware(struct platform_device *pdev) +{ + const char *fw_name; + int ret; + + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &fw_name); + if (ret) + return ERR_PTR(ret); + + return fw_name; +} + +static int omap_rproc_get_boot_data(struct platform_device *pdev, + struct rproc *rproc) +{ + struct device_node *np = pdev->dev.of_node; + struct omap_rproc *oproc = rproc->priv; + const struct omap_rproc_dev_data *data; + int ret; + + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -ENODEV; + + if (!of_property_read_bool(np, "ti,bootreg")) + return 0; + + oproc->boot_data = devm_kzalloc(&pdev->dev, sizeof(*oproc->boot_data), + GFP_KERNEL); + if (!oproc->boot_data) + return -ENOMEM; + + oproc->boot_data->syscon = + syscon_regmap_lookup_by_phandle(np, "ti,bootreg"); + if (IS_ERR(oproc->boot_data->syscon)) { + ret = PTR_ERR(oproc->boot_data->syscon); + return ret; + } + + if (of_property_read_u32_index(np, "ti,bootreg", 1, + &oproc->boot_data->boot_reg)) { + dev_err(&pdev->dev, "couldn't get the boot register\n"); + return -EINVAL; + } + + return 0; +} + static int omap_rproc_probe(struct platform_device *pdev) { - struct omap_rproc_pdata *pdata = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct omap_rproc *oproc; struct rproc *rproc; + const char *firmware; int ret; + struct reset_control *reset; + + if (!np) { + dev_err(&pdev->dev, "only DT-based devices are supported\n"); + return -ENODEV; + } + + reset = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(reset)) + return PTR_ERR(reset); + + firmware = omap_rproc_get_firmware(pdev); + if (IS_ERR(firmware)) + return PTR_ERR(firmware); ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { @@ -188,16 +325,21 @@ static int omap_rproc_probe(struct platform_device *pdev) return ret; } - rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops, - pdata->firmware, sizeof(*oproc)); + rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &omap_rproc_ops, + firmware, sizeof(*oproc)); if (!rproc) return -ENOMEM; oproc = rproc->priv; oproc->rproc = rproc; + oproc->reset = reset; /* All existing OMAP IPU and DSP processors have an MMU */ rproc->has_iommu = true; + ret = omap_rproc_get_boot_data(pdev, rproc); + if (ret) + goto free_rproc; + platform_set_drvdata(pdev, rproc); ret = rproc_add(rproc); @@ -226,6 +368,7 @@ static struct platform_driver omap_rproc_driver = { .remove = omap_rproc_remove, .driver = { .name = "omap-rproc", + .of_match_table = omap_rproc_of_match, }, }; From feae0300536a9220d2d696798ef3952d612ac73d Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:23 +0200 Subject: [PATCH 25/37] remoteproc/omap: Add a sanity check for DSP boot address alignment The DSP remote processors on OMAP SoCs require a boot register to be programmed with a boot address, and this boot address needs to be on a 1KB boundary. The current code is simply masking the boot address appropriately without performing any sanity checks before releasing the resets. An unaligned boot address results in an undefined execution behavior and can result in various bus errors like MMU Faults or L3 NoC errors. Such errors are hard to debug and can be easily avoided by adding a sanity check for the alignment before booting a DSP remote processor. Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Bjorn Andersson Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-4-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index d47d5ded651a..fe11cb709770 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -121,14 +121,23 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) * @rproc: handle of a remote processor * * Set boot address for a supported DSP remote processor. + * + * Return: 0 on success, or -EINVAL if boot address is not aligned properly */ -static void omap_rproc_write_dsp_boot_addr(struct rproc *rproc) +static int omap_rproc_write_dsp_boot_addr(struct rproc *rproc) { + struct device *dev = rproc->dev.parent; struct omap_rproc *oproc = rproc->priv; struct omap_rproc_boot_data *bdata = oproc->boot_data; u32 offset = bdata->boot_reg; - regmap_write(bdata->syscon, offset, rproc->bootaddr); + if (rproc->bootaddr & (SZ_1K - 1)) { + dev_err(dev, "invalid boot address 0x%llx, must be aligned on a 1KB boundary\n", + rproc->bootaddr); + return -EINVAL; + } + + return regmap_write(bdata->syscon, offset, rproc->bootaddr); } /* @@ -145,8 +154,11 @@ static int omap_rproc_start(struct rproc *rproc) int ret; struct mbox_client *client = &oproc->client; - if (oproc->boot_data) - omap_rproc_write_dsp_boot_addr(rproc); + if (oproc->boot_data) { + ret = omap_rproc_write_dsp_boot_addr(rproc); + if (ret) + return ret; + } client->dev = dev; client->tx_done = NULL; From 4a032199d3f7c59e450ed78aa5306a12226987a7 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:24 +0200 Subject: [PATCH 26/37] remoteproc/omap: Add support to parse internal memories from DT The OMAP remoteproc driver has been enhanced to parse and store the kernel mappings for different internal RAM memories that may be present within each remote processor IP subsystem. Different devices have varying memories present on current SoCs. The current support handles the L2RAM for all IPU devices on OMAP4+ SoCs. The DSPs on OMAP4/OMAP5 only have Unicaches and do not have any L1 or L2 RAM memories. IPUs are expected to have the L2RAM at a fixed device address of 0x20000000, based on the current limitations on Attribute MMU configurations. NOTE: The current logic doesn't handle the parsing of memories for DRA7 remoteproc devices, and will be added alongside the DRA7 support. Signed-off-by: Suman Anna [t-kristo: converted to parse mem names / device addresses from pdata] Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-5-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 94 ++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index fe11cb709770..cdcc9c227b96 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -39,11 +39,27 @@ struct omap_rproc_boot_data { unsigned int boot_reg; }; +/** + * struct omap_rproc_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: bus address used to access the memory region + * @dev_addr: device address of the memory region from DSP view + * @size: size of the memory region + */ +struct omap_rproc_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; +}; + /** * struct omap_rproc - omap remote processor state * @mbox: mailbox channel handle * @client: mailbox client to request the mailbox channel * @boot_data: boot data structure for setting processor boot address + * @mem: internal memory regions data + * @num_mems: number of internal memory regions * @rproc: rproc handle * @reset: reset handle */ @@ -51,16 +67,30 @@ struct omap_rproc { struct mbox_chan *mbox; struct mbox_client client; struct omap_rproc_boot_data *boot_data; + struct omap_rproc_mem *mem; + int num_mems; struct rproc *rproc; struct reset_control *reset; }; +/** + * struct omap_rproc_mem_data - memory definitions for an omap remote processor + * @name: name for this memory entry + * @dev_addr: device address for the memory entry + */ +struct omap_rproc_mem_data { + const char *name; + const u32 dev_addr; +}; + /** * struct omap_rproc_dev_data - device data for the omap remote processor * @device_name: device name of the remote processor + * @mems: memory definitions for this remote processor */ struct omap_rproc_dev_data { const char *device_name; + const struct omap_rproc_mem_data *mems; }; /** @@ -221,12 +251,18 @@ static const struct rproc_ops omap_rproc_ops = { .kick = omap_rproc_kick, }; +static const struct omap_rproc_mem_data ipu_mems[] = { + { .name = "l2ram", .dev_addr = 0x20000000 }, + { }, +}; + static const struct omap_rproc_dev_data omap4_dsp_dev_data = { .device_name = "dsp", }; static const struct omap_rproc_dev_data omap4_ipu_dev_data = { .device_name = "ipu", + .mems = ipu_mems, }; static const struct omap_rproc_dev_data omap5_dsp_dev_data = { @@ -235,6 +271,7 @@ static const struct omap_rproc_dev_data omap5_dsp_dev_data = { static const struct omap_rproc_dev_data omap5_ipu_dev_data = { .device_name = "ipu", + .mems = ipu_mems, }; static const struct of_device_id omap_rproc_of_match[] = { @@ -309,6 +346,59 @@ static int omap_rproc_get_boot_data(struct platform_device *pdev, return 0; } +static int omap_rproc_of_get_internal_memories(struct platform_device *pdev, + struct rproc *rproc) +{ + struct omap_rproc *oproc = rproc->priv; + struct device *dev = &pdev->dev; + const struct omap_rproc_dev_data *data; + struct resource *res; + int num_mems; + int i; + + data = of_device_get_match_data(dev); + if (!data) + return -ENODEV; + + if (!data->mems) + return 0; + + num_mems = of_property_count_elems_of_size(dev->of_node, "reg", + sizeof(u32)) / 2; + + oproc->mem = devm_kcalloc(dev, num_mems, sizeof(*oproc->mem), + GFP_KERNEL); + if (!oproc->mem) + return -ENOMEM; + + for (i = 0; data->mems[i].name; i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + data->mems[i].name); + if (!res) { + dev_err(dev, "no memory defined for %s\n", + data->mems[i].name); + return -ENOMEM; + } + oproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res); + if (IS_ERR(oproc->mem[i].cpu_addr)) { + dev_err(dev, "failed to parse and map %s memory\n", + data->mems[i].name); + return PTR_ERR(oproc->mem[i].cpu_addr); + } + oproc->mem[i].bus_addr = res->start; + oproc->mem[i].dev_addr = data->mems[i].dev_addr; + oproc->mem[i].size = resource_size(res); + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%x va %pK da 0x%x\n", + data->mems[i].name, &oproc->mem[i].bus_addr, + oproc->mem[i].size, oproc->mem[i].cpu_addr, + oproc->mem[i].dev_addr); + } + oproc->num_mems = num_mems; + + return 0; +} + static int omap_rproc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -348,6 +438,10 @@ static int omap_rproc_probe(struct platform_device *pdev) /* All existing OMAP IPU and DSP processors have an MMU */ rproc->has_iommu = true; + ret = omap_rproc_of_get_internal_memories(pdev, rproc); + if (ret) + goto free_rproc; + ret = omap_rproc_get_boot_data(pdev, rproc); if (ret) goto free_rproc; From 530a1b57e8590f2ebbb6a35effa0efa988aabf6c Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:25 +0200 Subject: [PATCH 27/37] remoteproc/omap: Add the rproc ops .da_to_va() implementation An implementation for the rproc ops .da_to_va() has been added that provides the address translation between device addresses to kernel virtual addresses for internal RAMs present on that particular remote processor device. The implementation provides the translations based on the addresses parsed and stored during the probe. This ops gets invoked by the exported rproc_da_to_va() function and allows the remoteproc core's ELF loader to be able to load program data directly into the internal memories. Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-6-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index cdcc9c227b96..fecc68e6d3a8 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -245,10 +245,50 @@ static int omap_rproc_stop(struct rproc *rproc) return 0; } +/** + * omap_rproc_da_to_va() - internal memory translation helper + * @rproc: remote processor to apply the address translation for + * @da: device address to translate + * @len: length of the memory buffer + * + * Custom function implementing the rproc .da_to_va ops to provide address + * translation (device address to kernel virtual address) for internal RAMs + * present in a DSP or IPU device). The translated addresses can be used + * either by the remoteproc core for loading, or by any rpmsg bus drivers. + * + * Return: translated virtual address in kernel memory space on success, + * or NULL on failure. + */ +static void *omap_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len) +{ + struct omap_rproc *oproc = rproc->priv; + int i; + u32 offset; + + if (len <= 0) + return NULL; + + if (!oproc->num_mems) + return NULL; + + for (i = 0; i < oproc->num_mems; i++) { + if (da >= oproc->mem[i].dev_addr && da + len <= + oproc->mem[i].dev_addr + oproc->mem[i].size) { + offset = da - oproc->mem[i].dev_addr; + /* __force to make sparse happy with type conversion */ + return (__force void *)(oproc->mem[i].cpu_addr + + offset); + } + } + + return NULL; +} + static const struct rproc_ops omap_rproc_ops = { .start = omap_rproc_start, .stop = omap_rproc_stop, .kick = omap_rproc_kick, + .da_to_va = omap_rproc_da_to_va, }; static const struct omap_rproc_mem_data ipu_mems[] = { From f4af5bd233657d741ebb317e9abee893dd78a4a0 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:26 +0200 Subject: [PATCH 28/37] remoteproc/omap: Initialize and assign reserved memory node The reserved memory nodes are not assigned to platform devices by default in the driver core to avoid the lookup for every platform device and incur a penalty as the real users are expected to be only a few devices. OMAP remoteproc devices fall into the above category and the OMAP remoteproc driver _requires_ specific CMA pools to be assigned for each device at the moment to align on the location of the vrings and vring buffers in the RTOS-side firmware images. So, use the of_reserved_mem_device_init/release() API appropriately to assign the corresponding reserved memory region to the OMAP remoteproc device. Note that only one region per device is allowed by the framework. Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Bjorn Andersson Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-7-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index fecc68e6d3a8..473d7e3cdef8 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -486,14 +487,23 @@ static int omap_rproc_probe(struct platform_device *pdev) if (ret) goto free_rproc; + ret = of_reserved_mem_device_init(&pdev->dev); + if (ret) { + dev_warn(&pdev->dev, "device does not have specific CMA pool.\n"); + dev_warn(&pdev->dev, "Typically this should be provided,\n"); + dev_warn(&pdev->dev, "only omit if you know what you are doing.\n"); + } + platform_set_drvdata(pdev, rproc); ret = rproc_add(rproc); if (ret) - goto free_rproc; + goto release_mem; return 0; +release_mem: + of_reserved_mem_device_release(&pdev->dev); free_rproc: rproc_free(rproc); return ret; @@ -505,6 +515,7 @@ static int omap_rproc_remove(struct platform_device *pdev) rproc_del(rproc); rproc_free(rproc); + of_reserved_mem_device_release(&pdev->dev); return 0; } From 0aaf19130262059591be51bb745b59788a18c24c Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:27 +0200 Subject: [PATCH 29/37] remoteproc/omap: Add support for DRA7xx remote processors DRA7xx/AM57xx SoCs have two IPU and up to two DSP processor subsystems for offloading different computation algorithms. The IPU processor subsystem contains dual-core ARM Cortex-M4 processors, and is very similar to those on OMAP5. The DSP processor subsystem is based on the TI's standard TMS320C66x DSP CorePac core. Support has been added to the OMAP remoteproc driver through new DRA7xx specific compatibles for properly probing and booting all the different processor subsystem instances on DRA7xx/AM57xx SoCs - IPU1, IPU2, DSP1 & DSP2. A build dependency with SOC_DRA7XX is added to enable the driver to be built in DRA7xx-only configuration. The DSP boot address programming needed enhancement for DRA7xx as the boot register fields are different on DRA7 compared to OMAP4 and OMAP5 SoCs. The register on DRA7xx contains additional fields within the register and the boot address bit-field is right-shifted by 10 bits. The internal memory parsing logic has also been updated to compute the device addresses for the L2 RAM for DSP devices using relative addressing logic, and to parse two additional RAMs at L1 level - L1P and L1D. This allows the remoteproc driver to support loading into these regions for a small subset of firmware images requiring as such. The most common usage would be to use the L1 programmable RAMs as L1 Caches. The firmware lookup logic also has to be adjusted for DRA7xx as there are (can be) more than one instance of both the IPU and DSP remote processors for the first time in OMAP4+ SoCs. Signed-off-by: Suman Anna [t-kristo@ti.com: moved address translation quirks to pdata] Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-8-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/Kconfig | 2 +- drivers/remoteproc/omap_remoteproc.c | 38 +++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index de3862c15fcc..b52abc2268cc 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -35,7 +35,7 @@ config MTK_SCP config OMAP_REMOTEPROC tristate "OMAP remoteproc support" - depends on ARCH_OMAP4 || SOC_OMAP5 + depends on ARCH_OMAP4 || SOC_OMAP5 || SOC_DRA7XX depends on OMAP_IOMMU select MAILBOX select OMAP2PLUS_MBOX diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 473d7e3cdef8..604499275896 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -34,10 +34,13 @@ * struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs * @syscon: regmap handle for the system control configuration module * @boot_reg: boot register offset within the @syscon regmap + * @boot_reg_shift: bit-field shift required for the boot address value in + * @boot_reg */ struct omap_rproc_boot_data { struct regmap *syscon; unsigned int boot_reg; + unsigned int boot_reg_shift; }; /** @@ -161,6 +164,8 @@ static int omap_rproc_write_dsp_boot_addr(struct rproc *rproc) struct omap_rproc *oproc = rproc->priv; struct omap_rproc_boot_data *bdata = oproc->boot_data; u32 offset = bdata->boot_reg; + u32 value; + u32 mask; if (rproc->bootaddr & (SZ_1K - 1)) { dev_err(dev, "invalid boot address 0x%llx, must be aligned on a 1KB boundary\n", @@ -168,7 +173,10 @@ static int omap_rproc_write_dsp_boot_addr(struct rproc *rproc) return -EINVAL; } - return regmap_write(bdata->syscon, offset, rproc->bootaddr); + value = rproc->bootaddr >> bdata->boot_reg_shift; + mask = ~(SZ_1K - 1) >> bdata->boot_reg_shift; + + return regmap_update_bits(bdata->syscon, offset, mask, value); } /* @@ -297,6 +305,13 @@ static const struct omap_rproc_mem_data ipu_mems[] = { { }, }; +static const struct omap_rproc_mem_data dra7_dsp_mems[] = { + { .name = "l2ram", .dev_addr = 0x800000 }, + { .name = "l1pram", .dev_addr = 0xe00000 }, + { .name = "l1dram", .dev_addr = 0xf00000 }, + { }, +}; + static const struct omap_rproc_dev_data omap4_dsp_dev_data = { .device_name = "dsp", }; @@ -315,6 +330,16 @@ static const struct omap_rproc_dev_data omap5_ipu_dev_data = { .mems = ipu_mems, }; +static const struct omap_rproc_dev_data dra7_dsp_dev_data = { + .device_name = "dsp", + .mems = dra7_dsp_mems, +}; + +static const struct omap_rproc_dev_data dra7_ipu_dev_data = { + .device_name = "ipu", + .mems = ipu_mems, +}; + static const struct of_device_id omap_rproc_of_match[] = { { .compatible = "ti,omap4-dsp", @@ -332,6 +357,14 @@ static const struct of_device_id omap_rproc_of_match[] = { .compatible = "ti,omap5-ipu", .data = &omap5_ipu_dev_data, }, + { + .compatible = "ti,dra7-dsp", + .data = &dra7_dsp_dev_data, + }, + { + .compatible = "ti,dra7-ipu", + .data = &dra7_ipu_dev_data, + }, { /* end */ }, @@ -384,6 +417,9 @@ static int omap_rproc_get_boot_data(struct platform_device *pdev, return -EINVAL; } + of_property_read_u32_index(np, "ti,bootreg", 2, + &oproc->boot_data->boot_reg_shift); + return 0; } From 1070f24d4ae90420db342fe54c1ed90ef1129bb5 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Tue, 24 Mar 2020 13:00:28 +0200 Subject: [PATCH 30/37] remoteproc/omap: Remove the platform_data header The platform data header for OMAP remoteproc is no longer used for anything post ti-sysc and DT conversion, so just remove it completely. Signed-off-by: Tero Kristo Acked-by: Suman Anna Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-9-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- include/linux/platform_data/remoteproc-omap.h | 51 ------------------- 1 file changed, 51 deletions(-) delete mode 100644 include/linux/platform_data/remoteproc-omap.h diff --git a/include/linux/platform_data/remoteproc-omap.h b/include/linux/platform_data/remoteproc-omap.h deleted file mode 100644 index 7e3a16097672..000000000000 --- a/include/linux/platform_data/remoteproc-omap.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Remote Processor - omap-specific bits - * - * Copyright (C) 2011 Texas Instruments, Inc. - * Copyright (C) 2011 Google, Inc. - */ - -#ifndef _PLAT_REMOTEPROC_H -#define _PLAT_REMOTEPROC_H - -struct rproc_ops; -struct platform_device; - -/* - * struct omap_rproc_pdata - omap remoteproc's platform data - * @name: the remoteproc's name - * @oh_name: omap hwmod device - * @oh_name_opt: optional, secondary omap hwmod device - * @firmware: name of firmware file to load - * @mbox_name: name of omap mailbox device to use with this rproc - * @ops: start/stop rproc handlers - * @device_enable: omap-specific handler for enabling a device - * @device_shutdown: omap-specific handler for shutting down a device - * @set_bootaddr: omap-specific handler for setting the rproc boot address - */ -struct omap_rproc_pdata { - const char *name; - const char *oh_name; - const char *oh_name_opt; - const char *firmware; - const char *mbox_name; - const struct rproc_ops *ops; - int (*device_enable)(struct platform_device *pdev); - int (*device_shutdown)(struct platform_device *pdev); - void (*set_bootaddr)(u32); -}; - -#if defined(CONFIG_OMAP_REMOTEPROC) || defined(CONFIG_OMAP_REMOTEPROC_MODULE) - -void __init omap_rproc_reserve_cma(void); - -#else - -static inline void __init omap_rproc_reserve_cma(void) -{ -} - -#endif - -#endif /* _PLAT_REMOTEPROC_H */ From 8135d1d28173a7d62c9180a58d37f12f0d69b1c0 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:29 +0200 Subject: [PATCH 31/37] remoteproc/omap: Check for undefined mailbox messages Add some checks in the mailbox callback function to limit any processing in the mailbox callback function to only certain currently valid messages, and drop all the remaining messages. A debug message is added to print any such invalid messages when the appropriate trace control is enabled. Co-developed-by: Subramaniam Chanderashekarapuram Signed-off-by: Subramaniam Chanderashekarapuram Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Bjorn Andersson Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-10-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 6 ++++++ drivers/remoteproc/omap_remoteproc.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 604499275896..5bccb5840a02 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -130,6 +130,12 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data) dev_info(dev, "received echo reply from %s\n", name); break; default: + if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) + return; + if (msg > oproc->rproc->max_notifyid) { + dev_dbg(dev, "dropping unknown message 0x%x", msg); + return; + } /* msg contains the index of the triggered vring */ if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE) dev_dbg(dev, "no message was found in vqid %d\n", msg); diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h index f6d2036d383d..72f656c93caa 100644 --- a/drivers/remoteproc/omap_remoteproc.h +++ b/drivers/remoteproc/omap_remoteproc.h @@ -56,6 +56,12 @@ * * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the * recovery mechanism (to some extent). + * + * Introduce new message definitions if any here. + * + * @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core + * This should be the last definition. + * */ enum omap_rp_mbox_messages { RP_MBOX_READY = 0xFFFFFF00, @@ -64,6 +70,7 @@ enum omap_rp_mbox_messages { RP_MBOX_ECHO_REQUEST = 0xFFFFFF03, RP_MBOX_ECHO_REPLY = 0xFFFFFF04, RP_MBOX_ABORT_REQUEST = 0xFFFFFF05, + RP_MBOX_END_MSG = 0xFFFFFF06, }; #endif /* _OMAP_RPMSG_H */ From e28edc571925ffe0e77aa4ab6082a829690c79f2 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:30 +0200 Subject: [PATCH 32/37] remoteproc/omap: Request a timer(s) for remoteproc usage The remote processors in OMAP4+ SoCs are equipped with internal timers, like the internal SysTick timer in a Cortex M3/M4 NVIC or the CTM timer within Unicache in IPU & DSP. However, these timers are gated when the processor subsystem clock is gated, making them rather difficult to use as OS tick sources. They will not be able to wakeup the processor from any processor-sleep induced clock-gating states. This can be avoided by using an external timer as the tick source, which can be controlled independently by the OMAP remoteproc driver code, but still allowing the processor subsystem clock to be auto-gated when the remoteproc cores are idle. This patch adds the support for OMAP remote processors to request timer(s) to be used by the remoteproc. The timers are enabled and disabled in line with the enabling/disabling of the remoteproc. The timer data is not mandatory if the advanced device management features are not required. The core timer functionality is provided by the OMAP DMTimer clocksource driver, which does not export any API. The logic is implemented through the timer device's platform data ops. The OMAP remoteproc driver mainly requires ops to request/free a dmtimer, and to start/stop a timer. The split ops helps in controlling the timer state without having to request and release a timer everytime it needs to use the timer. NOTE: If the gptimer is already in use by the time IPU and/or DSP are loaded, the processors will fail to boot. Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-11-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 291 ++++++++++++++++++++++++++- 1 file changed, 290 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index 5bccb5840a02..aac6158bc286 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -26,6 +26,9 @@ #include #include #include +#include + +#include #include "omap_remoteproc.h" #include "remoteproc_internal.h" @@ -57,6 +60,16 @@ struct omap_rproc_mem { size_t size; }; +/** + * struct omap_rproc_timer - data structure for a timer used by a omap rproc + * @odt: timer pointer + * @timer_ops: OMAP dmtimer ops for @odt timer + */ +struct omap_rproc_timer { + struct omap_dm_timer *odt; + const struct omap_dm_timer_ops *timer_ops; +}; + /** * struct omap_rproc - omap remote processor state * @mbox: mailbox channel handle @@ -64,6 +77,8 @@ struct omap_rproc_mem { * @boot_data: boot data structure for setting processor boot address * @mem: internal memory regions data * @num_mems: number of internal memory regions + * @num_timers: number of rproc timer(s) + * @timers: timer(s) info used by rproc * @rproc: rproc handle * @reset: reset handle */ @@ -73,6 +88,8 @@ struct omap_rproc { struct omap_rproc_boot_data *boot_data; struct omap_rproc_mem *mem; int num_mems; + int num_timers; + struct omap_rproc_timer *timers; struct rproc *rproc; struct reset_control *reset; }; @@ -97,6 +114,231 @@ struct omap_rproc_dev_data { const struct omap_rproc_mem_data *mems; }; +/** + * omap_rproc_request_timer() - request a timer for a remoteproc + * @dev: device requesting the timer + * @np: device node pointer to the desired timer + * @timer: handle to a struct omap_rproc_timer to return the timer handle + * + * This helper function is used primarily to request a timer associated with + * a remoteproc. The returned handle is stored in the .odt field of the + * @timer structure passed in, and is used to invoke other timer specific + * ops (like starting a timer either during device initialization or during + * a resume operation, or for stopping/freeing a timer). + * + * Return: 0 on success, otherwise an appropriate failure + */ +static int omap_rproc_request_timer(struct device *dev, struct device_node *np, + struct omap_rproc_timer *timer) +{ + int ret; + + timer->odt = timer->timer_ops->request_by_node(np); + if (!timer->odt) { + dev_err(dev, "request for timer node %p failed\n", np); + return -EBUSY; + } + + ret = timer->timer_ops->set_source(timer->odt, OMAP_TIMER_SRC_SYS_CLK); + if (ret) { + dev_err(dev, "error setting OMAP_TIMER_SRC_SYS_CLK as source for timer node %p\n", + np); + timer->timer_ops->free(timer->odt); + return ret; + } + + /* clean counter, remoteproc code will set the value */ + timer->timer_ops->set_load(timer->odt, 0, 0); + + return 0; +} + +/** + * omap_rproc_start_timer() - start a timer for a remoteproc + * @timer: handle to a OMAP rproc timer + * + * This helper function is used to start a timer associated with a remoteproc, + * obtained using the request_timer ops. The helper function needs to be + * invoked by the driver to start the timer (during device initialization) + * or to just resume the timer. + * + * Return: 0 on success, otherwise a failure as returned by DMTimer ops + */ +static inline int omap_rproc_start_timer(struct omap_rproc_timer *timer) +{ + return timer->timer_ops->start(timer->odt); +} + +/** + * omap_rproc_stop_timer() - stop a timer for a remoteproc + * @timer: handle to a OMAP rproc timer + * + * This helper function is used to disable a timer associated with a + * remoteproc, and needs to be called either during a device shutdown + * or suspend operation. The separate helper function allows the driver + * to just stop a timer without having to release the timer during a + * suspend operation. + * + * Return: 0 on success, otherwise a failure as returned by DMTimer ops + */ +static inline int omap_rproc_stop_timer(struct omap_rproc_timer *timer) +{ + return timer->timer_ops->stop(timer->odt); +} + +/** + * omap_rproc_release_timer() - release a timer for a remoteproc + * @timer: handle to a OMAP rproc timer + * + * This helper function is used primarily to release a timer associated + * with a remoteproc. The dmtimer will be available for other clients to + * use once released. + * + * Return: 0 on success, otherwise a failure as returned by DMTimer ops + */ +static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer) +{ + return timer->timer_ops->free(timer->odt); +} + +/** + * omap_rproc_enable_timers() - enable the timers for a remoteproc + * @rproc: handle of a remote processor + * @configure: boolean flag used to acquire and configure the timer handle + * + * This function is used primarily to enable the timers associated with + * a remoteproc. The configure flag is provided to allow the driver to + * to either acquire and start a timer (during device initialization) or + * to just start a timer (during a resume operation). + * + * Return: 0 on success, otherwise an appropriate failure + */ +static int omap_rproc_enable_timers(struct rproc *rproc, bool configure) +{ + int i; + int ret = 0; + struct platform_device *tpdev; + struct dmtimer_platform_data *tpdata; + const struct omap_dm_timer_ops *timer_ops; + struct omap_rproc *oproc = rproc->priv; + struct omap_rproc_timer *timers = oproc->timers; + struct device *dev = rproc->dev.parent; + struct device_node *np = NULL; + + if (!oproc->num_timers) + return 0; + + if (!configure) + goto start_timers; + + for (i = 0; i < oproc->num_timers; i++) { + np = of_parse_phandle(dev->of_node, "ti,timers", i); + if (!np) { + ret = -ENXIO; + dev_err(dev, "device node lookup for timer at index %d failed: %d\n", + i, ret); + goto free_timers; + } + + tpdev = of_find_device_by_node(np); + if (!tpdev) { + ret = -ENODEV; + dev_err(dev, "could not get timer platform device\n"); + goto put_node; + } + + tpdata = dev_get_platdata(&tpdev->dev); + put_device(&tpdev->dev); + if (!tpdata) { + ret = -EINVAL; + dev_err(dev, "dmtimer pdata structure NULL\n"); + goto put_node; + } + + timer_ops = tpdata->timer_ops; + if (!timer_ops || !timer_ops->request_by_node || + !timer_ops->set_source || !timer_ops->set_load || + !timer_ops->free || !timer_ops->start || + !timer_ops->stop) { + ret = -EINVAL; + dev_err(dev, "device does not have required timer ops\n"); + goto put_node; + } + + timers[i].timer_ops = timer_ops; + ret = omap_rproc_request_timer(dev, np, &timers[i]); + if (ret) { + dev_err(dev, "request for timer %p failed: %d\n", np, + ret); + goto put_node; + } + of_node_put(np); + } + +start_timers: + for (i = 0; i < oproc->num_timers; i++) { + ret = omap_rproc_start_timer(&timers[i]); + if (ret) { + dev_err(dev, "start timer %p failed failed: %d\n", np, + ret); + break; + } + } + if (ret) { + while (i >= 0) { + omap_rproc_stop_timer(&timers[i]); + i--; + } + goto put_node; + } + return 0; + +put_node: + if (configure) + of_node_put(np); +free_timers: + while (i--) { + omap_rproc_release_timer(&timers[i]); + timers[i].odt = NULL; + timers[i].timer_ops = NULL; + } + + return ret; +} + +/** + * omap_rproc_disable_timers() - disable the timers for a remoteproc + * @rproc: handle of a remote processor + * @configure: boolean flag used to release the timer handle + * + * This function is used primarily to disable the timers associated with + * a remoteproc. The configure flag is provided to allow the driver to + * to either stop and release a timer (during device shutdown) or to just + * stop a timer (during a suspend operation). + * + * Return: 0 on success or no timers + */ +static int omap_rproc_disable_timers(struct rproc *rproc, bool configure) +{ + int i; + struct omap_rproc *oproc = rproc->priv; + struct omap_rproc_timer *timers = oproc->timers; + + if (!oproc->num_timers) + return 0; + + for (i = 0; i < oproc->num_timers; i++) { + omap_rproc_stop_timer(&timers[i]); + if (configure) { + omap_rproc_release_timer(&timers[i]); + timers[i].odt = NULL; + timers[i].timer_ops = NULL; + } + } + + return 0; +} + /** * omap_rproc_mbox_callback() - inbound mailbox message handler * @client: mailbox client pointer used for requesting the mailbox channel @@ -232,14 +474,22 @@ static int omap_rproc_start(struct rproc *rproc) goto put_mbox; } + ret = omap_rproc_enable_timers(rproc, true); + if (ret) { + dev_err(dev, "omap_rproc_enable_timers failed: %d\n", ret); + goto put_mbox; + } + ret = reset_control_deassert(oproc->reset); if (ret) { dev_err(dev, "reset control deassert failed: %d\n", ret); - goto put_mbox; + goto disable_timers; } return 0; +disable_timers: + omap_rproc_disable_timers(rproc, true); put_mbox: mbox_free_channel(oproc->mbox); return ret; @@ -255,6 +505,10 @@ static int omap_rproc_stop(struct rproc *rproc) if (ret) return ret; + ret = omap_rproc_disable_timers(rproc, true); + if (ret) + return ret; + mbox_free_channel(oproc->mbox); return 0; @@ -482,6 +736,37 @@ static int omap_rproc_of_get_internal_memories(struct platform_device *pdev, return 0; } +static int omap_rproc_of_get_timers(struct platform_device *pdev, + struct rproc *rproc) +{ + struct device_node *np = pdev->dev.of_node; + struct omap_rproc *oproc = rproc->priv; + struct device *dev = &pdev->dev; + + /* + * Timer nodes are directly used in client nodes as phandles, so + * retrieve the count using appropriate size + */ + oproc->num_timers = of_count_phandle_with_args(np, "ti,timers", NULL); + if (oproc->num_timers <= 0) { + dev_dbg(dev, "device does not have timers, status = %d\n", + oproc->num_timers); + oproc->num_timers = 0; + } + + if (oproc->num_timers) { + oproc->timers = devm_kcalloc(dev, oproc->num_timers, + sizeof(*oproc->timers), + GFP_KERNEL); + if (!oproc->timers) + return -ENOMEM; + + dev_dbg(dev, "device has %d tick timers\n", oproc->num_timers); + } + + return 0; +} + static int omap_rproc_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -529,6 +814,10 @@ static int omap_rproc_probe(struct platform_device *pdev) if (ret) goto free_rproc; + ret = omap_rproc_of_get_timers(pdev, rproc); + if (ret) + goto free_rproc; + ret = of_reserved_mem_device_init(&pdev->dev); if (ret) { dev_warn(&pdev->dev, "device does not have specific CMA pool.\n"); From 9077ac1ab14be8b91ee9b18bbb654a4c27b7b10c Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:31 +0200 Subject: [PATCH 33/37] remoteproc/omap: Add support for system suspend/resume This patch adds the support for system suspend/resume to the OMAP remoteproc driver so that the OMAP remoteproc devices can be suspended/resumed during a system suspend/resume. The support is added through the driver PM .suspend/.resume callbacks, and requires appropriate support from the OS running on the remote processors. The IPU & DSP remote processors typically have their own private modules like registers, internal memories, caches etc. The context of these modules need to be saved and restored properly for a suspend/resume to work. These are in general not accessible from the MPU, so the remote processors themselves have to implement the logic for the context save & restore of these modules. The OMAP remoteproc driver initiates a suspend by sending a mailbox message requesting the remote processor to save its context and enter into an idle/standby state. The remote processor should usually stop whatever processing it is doing to switch to a context save mode. The OMAP remoteproc driver detects the completion of the context save by checking the module standby status for the remoteproc device. It also stops any resources used by the remote processors like the timers. The timers need to be running only when the processor is active and executing, and need to be stopped otherwise to allow the timer driver to reach low-power states. The IOMMUs are automatically suspended by the PM core during the late suspend stage, after the remoteproc suspend process is completed by putting the remote processor cores into reset. Thereafter, the Linux kernel can put the domain into further lower power states as possible. The resume sequence undoes the operations performed in the PM suspend callback, by starting the timers and finally releasing the processors from reset. This requires that the remote processor side OS be able to distinguish a power-resume boot from a power-on/cold boot, restore the context of its private modules saved during the suspend phase, and resume executing code from where it was suspended. The IOMMUs would have been resumed by the PM core during early resume, so they are already enabled by the time remoteproc resume callback gets invoked. The remote processors should save their context into System RAM (DDR), as any internal memories are not guaranteed to retain context as it depends on the lowest power domain that the remote processor device is put into. The management of the DDR contents will be managed by the Linux kernel. Signed-off-by: Suman Anna [t-kristo@ti.com: converted to use ti-sysc instead of hwmod] Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-12-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 191 +++++++++++++++++++++++++++ drivers/remoteproc/omap_remoteproc.h | 18 ++- 2 files changed, 207 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index aac6158bc286..fc83dd851c39 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -15,13 +15,17 @@ #include #include +#include +#include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -81,6 +85,9 @@ struct omap_rproc_timer { * @timers: timer(s) info used by rproc * @rproc: rproc handle * @reset: reset handle + * @pm_comp: completion primitive to sync for suspend response + * @fck: functional clock for the remoteproc + * @suspend_acked: state machine flag to store the suspend request ack */ struct omap_rproc { struct mbox_chan *mbox; @@ -92,6 +99,9 @@ struct omap_rproc { struct omap_rproc_timer *timers; struct rproc *rproc; struct reset_control *reset; + struct completion pm_comp; + struct clk *fck; + bool suspend_acked; }; /** @@ -371,6 +381,12 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data) case RP_MBOX_ECHO_REPLY: dev_info(dev, "received echo reply from %s\n", name); break; + case RP_MBOX_SUSPEND_ACK: + /* Fall through */ + case RP_MBOX_SUSPEND_CANCEL: + oproc->suspend_acked = msg == RP_MBOX_SUSPEND_ACK; + complete(&oproc->pm_comp); + break; default: if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) return; @@ -560,6 +576,168 @@ static const struct rproc_ops omap_rproc_ops = { .da_to_va = omap_rproc_da_to_va, }; +#ifdef CONFIG_PM +static bool _is_rproc_in_standby(struct omap_rproc *oproc) +{ + return ti_clk_is_in_standby(oproc->fck); +} + +/* 1 sec is long enough time to let the remoteproc side suspend the device */ +#define DEF_SUSPEND_TIMEOUT 1000 +static int _omap_rproc_suspend(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct omap_rproc *oproc = rproc->priv; + unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT); + unsigned long ta = jiffies + to; + int ret; + + reinit_completion(&oproc->pm_comp); + oproc->suspend_acked = false; + ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_SUSPEND_SYSTEM); + if (ret < 0) { + dev_err(dev, "PM mbox_send_message failed: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&oproc->pm_comp, to); + if (!oproc->suspend_acked) + return -EBUSY; + + /* + * The remoteproc side is returning the ACK message before saving the + * context, because the context saving is performed within a SYS/BIOS + * function, and it cannot have any inter-dependencies against the IPC + * layer. Also, as the SYS/BIOS needs to preserve properly the processor + * register set, sending this ACK or signalling the completion of the + * context save through a shared memory variable can never be the + * absolute last thing to be executed on the remoteproc side, and the + * MPU cannot use the ACK message as a sync point to put the remoteproc + * into reset. The only way to ensure that the remote processor has + * completed saving the context is to check that the module has reached + * STANDBY state (after saving the context, the SYS/BIOS executes the + * appropriate target-specific WFI instruction causing the module to + * enter STANDBY). + */ + while (!_is_rproc_in_standby(oproc)) { + if (time_after(jiffies, ta)) + return -ETIME; + schedule(); + } + + ret = reset_control_assert(oproc->reset); + if (ret) { + dev_err(dev, "reset assert during suspend failed %d\n", ret); + return ret; + } + + ret = omap_rproc_disable_timers(rproc, false); + if (ret) { + dev_err(dev, "disabling timers during suspend failed %d\n", + ret); + goto enable_device; + } + + return 0; + +enable_device: + reset_control_deassert(oproc->reset); + return ret; +} + +static int _omap_rproc_resume(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct omap_rproc *oproc = rproc->priv; + int ret; + + /* boot address could be lost after suspend, so restore it */ + if (oproc->boot_data) { + ret = omap_rproc_write_dsp_boot_addr(rproc); + if (ret) { + dev_err(dev, "boot address restore failed %d\n", ret); + goto out; + } + } + + ret = omap_rproc_enable_timers(rproc, false); + if (ret) { + dev_err(dev, "enabling timers during resume failed %d\n", ret); + goto out; + } + + ret = reset_control_deassert(oproc->reset); + if (ret) { + dev_err(dev, "reset deassert during resume failed %d\n", ret); + goto disable_timers; + } + + return 0; + +disable_timers: + omap_rproc_disable_timers(rproc, false); +out: + return ret; +} + +static int __maybe_unused omap_rproc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rproc *rproc = platform_get_drvdata(pdev); + int ret = 0; + + mutex_lock(&rproc->lock); + if (rproc->state == RPROC_OFFLINE) + goto out; + + if (rproc->state == RPROC_SUSPENDED) + goto out; + + if (rproc->state != RPROC_RUNNING) { + ret = -EBUSY; + goto out; + } + + ret = _omap_rproc_suspend(rproc); + if (ret) { + dev_err(dev, "suspend failed %d\n", ret); + goto out; + } + + rproc->state = RPROC_SUSPENDED; +out: + mutex_unlock(&rproc->lock); + return ret; +} + +static int __maybe_unused omap_rproc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct rproc *rproc = platform_get_drvdata(pdev); + int ret = 0; + + mutex_lock(&rproc->lock); + if (rproc->state == RPROC_OFFLINE) + goto out; + + if (rproc->state != RPROC_SUSPENDED) { + ret = -EBUSY; + goto out; + } + + ret = _omap_rproc_resume(rproc); + if (ret) { + dev_err(dev, "resume failed %d\n", ret); + goto out; + } + + rproc->state = RPROC_RUNNING; +out: + mutex_unlock(&rproc->lock); + return ret; +} +#endif /* CONFIG_PM */ + static const struct omap_rproc_mem_data ipu_mems[] = { { .name = "l2ram", .dev_addr = 0x20000000 }, { }, @@ -818,6 +996,14 @@ static int omap_rproc_probe(struct platform_device *pdev) if (ret) goto free_rproc; + init_completion(&oproc->pm_comp); + + oproc->fck = devm_clk_get(&pdev->dev, 0); + if (IS_ERR(oproc->fck)) { + ret = PTR_ERR(oproc->fck); + goto free_rproc; + } + ret = of_reserved_mem_device_init(&pdev->dev); if (ret) { dev_warn(&pdev->dev, "device does not have specific CMA pool.\n"); @@ -851,11 +1037,16 @@ static int omap_rproc_remove(struct platform_device *pdev) return 0; } +static const struct dev_pm_ops omap_rproc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume) +}; + static struct platform_driver omap_rproc_driver = { .probe = omap_rproc_probe, .remove = omap_rproc_remove, .driver = { .name = "omap-rproc", + .pm = &omap_rproc_pm_ops, .of_match_table = omap_rproc_of_match, }, }; diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h index 72f656c93caa..13f17d9135c0 100644 --- a/drivers/remoteproc/omap_remoteproc.h +++ b/drivers/remoteproc/omap_remoteproc.h @@ -1,7 +1,7 @@ /* * Remote processor messaging * - * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011-2020 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. * @@ -57,6 +57,16 @@ * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the * recovery mechanism (to some extent). * + * @RP_MBOX_SUSPEND_AUTO: auto suspend request for the remote processor + * + * @RP_MBOX_SUSPEND_SYSTEM: system suspend request for the remote processor + * + * @RP_MBOX_SUSPEND_ACK: successful response from remote processor for a + * suspend request + * + * @RP_MBOX_SUSPEND_CANCEL: a cancel suspend response from a remote processor + * on a suspend request + * * Introduce new message definitions if any here. * * @RP_MBOX_END_MSG: Indicates end of known/defined messages from remote core @@ -70,7 +80,11 @@ enum omap_rp_mbox_messages { RP_MBOX_ECHO_REQUEST = 0xFFFFFF03, RP_MBOX_ECHO_REPLY = 0xFFFFFF04, RP_MBOX_ABORT_REQUEST = 0xFFFFFF05, - RP_MBOX_END_MSG = 0xFFFFFF06, + RP_MBOX_SUSPEND_AUTO = 0xFFFFFF10, + RP_MBOX_SUSPEND_SYSTEM = 0xFFFFFF11, + RP_MBOX_SUSPEND_ACK = 0xFFFFFF12, + RP_MBOX_SUSPEND_CANCEL = 0xFFFFFF13, + RP_MBOX_END_MSG = 0xFFFFFF14, }; #endif /* _OMAP_RPMSG_H */ From 5f31b232c67434199558fd236e7644b432636b76 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:32 +0200 Subject: [PATCH 34/37] remoteproc/omap: Add support for runtime auto-suspend/resume This patch enhances the PM support in the OMAP remoteproc driver to support the runtime auto-suspend. A remoteproc may not be required to be running all the time, and typically will need to be active only during certain usecases. As such, to save power, it should be turned off during potential long periods of inactivity between usecases. This suspend and resume of the device is a relatively heavy process in terms of latencies, so a remoteproc should be suspended only after a certain period of prolonged inactivity. The OMAP remoteproc driver leverages the runtime pm framework's auto_suspend feature to accomplish this functionality. This feature is automatically enabled when a remote processor has successfully booted. The 'autosuspend_delay_ms' for each device dictates the inactivity period/time to wait for before suspending the device. The runtime auto-suspend design relies on marking the last busy time on every communication (virtqueue kick) to and from the remote processor. When there has been no activity for 'autosuspend_delay_ms' time, the runtime PM framework invokes the driver's runtime pm suspend callback to suspend the device. The remote processor will be woken up on the initiation of the next communication message through the runtime pm resume callback. The current auto-suspend design also allows a remote processor to deny a auto-suspend attempt, if it wishes to, by sending a NACK response to the initial suspend request message sent to the remote processor as part of the suspend process. The auto-suspend request is also only attempted if the remote processor is idled and in standby at the time of inactivity timer expiry. This choice is made to avoid unnecessary messaging, and the auto-suspend is simply rescheduled to be attempted again after a further lapse of autosuspend_delay_ms. The runtime pm callbacks functionality in this patch reuses most of the core logic from the suspend/resume support code, and make use of an additional auto_suspend flag to differentiate the logic in common code from system suspend. The system suspend/resume sequences are also updated to reflect the proper pm_runtime statuses, and also to really perform a suspend/resume only if the remoteproc has not been auto-suspended at the time of request. The remote processor is left in suspended state on a system resume if it has been auto-suspended before, and will be woken up only when a usecase needs to run. The OMAP remoteproc driver currently uses a default value of 10 seconds for all OMAP remoteprocs, and a different value can be chosen either by choosing a positive value for the 'ti,autosuspend-delay-ms' under DT or by updating the 'autosuspend_delay_ms' field at runtime through the sysfs interface. A negative value is equivalent to disabling the runtime suspend. Eg: To use 25 seconds for IPU2 on DRA7xx, echo 25000 > /sys/bus/platform/devices/55020000.ipu/power/autosuspend_delay_ms The runtime suspend feature can also be similarly enabled or disabled by writing 'auto' or 'on' to the device's 'control' power field. The default is enabled. Eg: To disable auto-suspend for IPU2 on DRA7xx SoC, echo on > /sys/bus/platform/devices/55020000.ipu/power/control Signed-off-by: Suman Anna [t-kristo@ti.com: converted to use ti-sysc instead of hwmod] Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-13-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 212 +++++++++++++++++++++++++-- 1 file changed, 203 insertions(+), 9 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index fc83dd851c39..b784f48e63d1 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,9 @@ #include "omap_remoteproc.h" #include "remoteproc_internal.h" +/* default auto-suspend delay (ms) */ +#define DEFAULT_AUTOSUSPEND_DELAY 10000 + /** * struct omap_rproc_boot_data - boot data structure for the DSP omap rprocs * @syscon: regmap handle for the system control configuration module @@ -83,6 +87,8 @@ struct omap_rproc_timer { * @num_mems: number of internal memory regions * @num_timers: number of rproc timer(s) * @timers: timer(s) info used by rproc + * @autosuspend_delay: auto-suspend delay value to be used for runtime pm + * @need_resume: if true a resume is needed in the system resume callback * @rproc: rproc handle * @reset: reset handle * @pm_comp: completion primitive to sync for suspend response @@ -97,6 +103,8 @@ struct omap_rproc { int num_mems; int num_timers; struct omap_rproc_timer *timers; + int autosuspend_delay; + bool need_resume; struct rproc *rproc; struct reset_control *reset; struct completion pm_comp; @@ -407,11 +415,23 @@ static void omap_rproc_kick(struct rproc *rproc, int vqid) struct device *dev = rproc->dev.parent; int ret; + /* wake up the rproc before kicking it */ + ret = pm_runtime_get_sync(dev); + if (WARN_ON(ret < 0)) { + dev_err(dev, "pm_runtime_get_sync() failed during kick, ret = %d\n", + ret); + pm_runtime_put_noidle(dev); + return; + } + /* send the index of the triggered virtqueue in the mailbox payload */ ret = mbox_send_message(oproc->mbox, (void *)vqid); if (ret < 0) dev_err(dev, "failed to send mailbox message, status = %d\n", ret); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } /** @@ -502,6 +522,18 @@ static int omap_rproc_start(struct rproc *rproc) goto disable_timers; } + /* + * remote processor is up, so update the runtime pm status and + * enable the auto-suspend. The device usage count is incremented + * manually for balancing it for auto-suspend + */ + pm_runtime_set_active(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_get_noresume(dev); + pm_runtime_enable(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return 0; disable_timers: @@ -514,20 +546,52 @@ put_mbox: /* power off the remote processor */ static int omap_rproc_stop(struct rproc *rproc) { + struct device *dev = rproc->dev.parent; struct omap_rproc *oproc = rproc->priv; int ret; + /* + * cancel any possible scheduled runtime suspend by incrementing + * the device usage count, and resuming the device. The remoteproc + * also needs to be woken up if suspended, to avoid the remoteproc + * OS to continue to remember any context that it has saved, and + * avoid potential issues in misindentifying a subsequent device + * reboot as a power restore boot + */ + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + ret = reset_control_assert(oproc->reset); if (ret) - return ret; + goto out; ret = omap_rproc_disable_timers(rproc, true); if (ret) - return ret; + goto enable_device; mbox_free_channel(oproc->mbox); + /* + * update the runtime pm states and status now that the remoteproc + * has stopped + */ + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put_noidle(dev); + pm_runtime_set_suspended(dev); + return 0; + +enable_device: + reset_control_deassert(oproc->reset); +out: + /* schedule the next auto-suspend */ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; } /** @@ -584,17 +648,19 @@ static bool _is_rproc_in_standby(struct omap_rproc *oproc) /* 1 sec is long enough time to let the remoteproc side suspend the device */ #define DEF_SUSPEND_TIMEOUT 1000 -static int _omap_rproc_suspend(struct rproc *rproc) +static int _omap_rproc_suspend(struct rproc *rproc, bool auto_suspend) { struct device *dev = rproc->dev.parent; struct omap_rproc *oproc = rproc->priv; unsigned long to = msecs_to_jiffies(DEF_SUSPEND_TIMEOUT); unsigned long ta = jiffies + to; + u32 suspend_msg = auto_suspend ? + RP_MBOX_SUSPEND_AUTO : RP_MBOX_SUSPEND_SYSTEM; int ret; reinit_completion(&oproc->pm_comp); oproc->suspend_acked = false; - ret = mbox_send_message(oproc->mbox, (void *)RP_MBOX_SUSPEND_SYSTEM); + ret = mbox_send_message(oproc->mbox, (void *)suspend_msg); if (ret < 0) { dev_err(dev, "PM mbox_send_message failed: %d\n", ret); return ret; @@ -638,32 +704,62 @@ static int _omap_rproc_suspend(struct rproc *rproc) goto enable_device; } + /* + * IOMMUs would have to be disabled specifically for runtime suspend. + * They are handled automatically through System PM callbacks for + * regular system suspend + */ + if (auto_suspend) { + ret = omap_iommu_domain_deactivate(rproc->domain); + if (ret) { + dev_err(dev, "iommu domain deactivate failed %d\n", + ret); + goto enable_timers; + } + } + return 0; +enable_timers: + /* ignore errors on re-enabling code */ + omap_rproc_enable_timers(rproc, false); enable_device: reset_control_deassert(oproc->reset); return ret; } -static int _omap_rproc_resume(struct rproc *rproc) +static int _omap_rproc_resume(struct rproc *rproc, bool auto_suspend) { struct device *dev = rproc->dev.parent; struct omap_rproc *oproc = rproc->priv; int ret; + /* + * IOMMUs would have to be enabled specifically for runtime resume. + * They would have been already enabled automatically through System + * PM callbacks for regular system resume + */ + if (auto_suspend) { + ret = omap_iommu_domain_activate(rproc->domain); + if (ret) { + dev_err(dev, "omap_iommu activate failed %d\n", ret); + goto out; + } + } + /* boot address could be lost after suspend, so restore it */ if (oproc->boot_data) { ret = omap_rproc_write_dsp_boot_addr(rproc); if (ret) { dev_err(dev, "boot address restore failed %d\n", ret); - goto out; + goto suspend_iommu; } } ret = omap_rproc_enable_timers(rproc, false); if (ret) { dev_err(dev, "enabling timers during resume failed %d\n", ret); - goto out; + goto suspend_iommu; } ret = reset_control_deassert(oproc->reset); @@ -676,6 +772,9 @@ static int _omap_rproc_resume(struct rproc *rproc) disable_timers: omap_rproc_disable_timers(rproc, false); +suspend_iommu: + if (auto_suspend) + omap_iommu_domain_deactivate(rproc->domain); out: return ret; } @@ -684,6 +783,7 @@ static int __maybe_unused omap_rproc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct rproc *rproc = platform_get_drvdata(pdev); + struct omap_rproc *oproc = rproc->priv; int ret = 0; mutex_lock(&rproc->lock); @@ -698,13 +798,19 @@ static int __maybe_unused omap_rproc_suspend(struct device *dev) goto out; } - ret = _omap_rproc_suspend(rproc); + ret = _omap_rproc_suspend(rproc, false); if (ret) { dev_err(dev, "suspend failed %d\n", ret); goto out; } + /* + * remoteproc is running at the time of system suspend, so remember + * it so as to wake it up during system resume + */ + oproc->need_resume = true; rproc->state = RPROC_SUSPENDED; + out: mutex_unlock(&rproc->lock); return ret; @@ -714,6 +820,7 @@ static int __maybe_unused omap_rproc_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct rproc *rproc = platform_get_drvdata(pdev); + struct omap_rproc *oproc = rproc->priv; int ret = 0; mutex_lock(&rproc->lock); @@ -725,12 +832,91 @@ static int __maybe_unused omap_rproc_resume(struct device *dev) goto out; } - ret = _omap_rproc_resume(rproc); + /* + * remoteproc was auto-suspended at the time of system suspend, + * so no need to wake-up the processor (leave it in suspended + * state, will be woken up during a subsequent runtime_resume) + */ + if (!oproc->need_resume) + goto out; + + ret = _omap_rproc_resume(rproc, false); if (ret) { dev_err(dev, "resume failed %d\n", ret); goto out; } + oproc->need_resume = false; + rproc->state = RPROC_RUNNING; + + pm_runtime_mark_last_busy(dev); +out: + mutex_unlock(&rproc->lock); + return ret; +} + +static int omap_rproc_runtime_suspend(struct device *dev) +{ + struct rproc *rproc = dev_get_drvdata(dev); + struct omap_rproc *oproc = rproc->priv; + int ret; + + mutex_lock(&rproc->lock); + if (rproc->state == RPROC_CRASHED) { + dev_dbg(dev, "rproc cannot be runtime suspended when crashed!\n"); + ret = -EBUSY; + goto out; + } + + if (WARN_ON(rproc->state != RPROC_RUNNING)) { + dev_err(dev, "rproc cannot be runtime suspended when not running!\n"); + ret = -EBUSY; + goto out; + } + + /* + * do not even attempt suspend if the remote processor is not + * idled for runtime auto-suspend + */ + if (!_is_rproc_in_standby(oproc)) { + ret = -EBUSY; + goto abort; + } + + ret = _omap_rproc_suspend(rproc, true); + if (ret) + goto abort; + + rproc->state = RPROC_SUSPENDED; + mutex_unlock(&rproc->lock); + return 0; + +abort: + pm_runtime_mark_last_busy(dev); +out: + mutex_unlock(&rproc->lock); + return ret; +} + +static int omap_rproc_runtime_resume(struct device *dev) +{ + struct rproc *rproc = dev_get_drvdata(dev); + int ret; + + mutex_lock(&rproc->lock); + if (WARN_ON(rproc->state != RPROC_SUSPENDED)) { + dev_err(dev, "rproc cannot be runtime resumed if not suspended! state=%d\n", + rproc->state); + ret = -EBUSY; + goto out; + } + + ret = _omap_rproc_resume(rproc, true); + if (ret) { + dev_err(dev, "runtime resume failed %d\n", ret); + goto out; + } + rproc->state = RPROC_RUNNING; out: mutex_unlock(&rproc->lock); @@ -997,6 +1183,12 @@ static int omap_rproc_probe(struct platform_device *pdev) goto free_rproc; init_completion(&oproc->pm_comp); + oproc->autosuspend_delay = DEFAULT_AUTOSUSPEND_DELAY; + + of_property_read_u32(pdev->dev.of_node, "ti,autosuspend-delay-ms", + &oproc->autosuspend_delay); + + pm_runtime_set_autosuspend_delay(&pdev->dev, oproc->autosuspend_delay); oproc->fck = devm_clk_get(&pdev->dev, 0); if (IS_ERR(oproc->fck)) { @@ -1039,6 +1231,8 @@ static int omap_rproc_remove(struct platform_device *pdev) static const struct dev_pm_ops omap_rproc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(omap_rproc_suspend, omap_rproc_resume) + SET_RUNTIME_PM_OPS(omap_rproc_runtime_suspend, + omap_rproc_runtime_resume, NULL) }; static struct platform_driver omap_rproc_driver = { From 232ba6ca007c9585ea666ad083f510b9666259dd Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:33 +0200 Subject: [PATCH 35/37] remoteproc/omap: Report device exceptions and trigger recovery The OMAP remote processors send a special mailbox message (RP_MBOX_CRASH) when they crash and detect an internal device exception. Add support to the mailbox handling function upon detection of this special message to report this crash to the remoteproc core. The remoteproc core can trigger a recovery using the prevailing recovery mechanism, already in use for MMU Fault recovery. Co-developed-by: Subramaniam Chanderashekarapuram Signed-off-by: Subramaniam Chanderashekarapuram Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Bjorn Andersson Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-14-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index b784f48e63d1..cee6c33869b3 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -383,8 +383,12 @@ static void omap_rproc_mbox_callback(struct mbox_client *client, void *data) switch (msg) { case RP_MBOX_CRASH: - /* just log this for now. later, we'll also do recovery */ + /* + * remoteproc detected an exception, notify the rproc core. + * The remoteproc core will handle the recovery. + */ dev_err(dev, "omap rproc %s crashed\n", name); + rproc_report_crash(oproc->rproc, RPROC_FATAL_ERROR); break; case RP_MBOX_ECHO_REPLY: dev_info(dev, "received echo reply from %s\n", name); From 69591e4a0e173e9a19c54c172cdb2febd151dda6 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:34 +0200 Subject: [PATCH 36/37] remoteproc/omap: Add watchdog functionality for remote processors Remote processors can be stuck in a loop, and may not be recoverable if they do not have a built-in watchdog. The watchdog implementation for OMAP remote processors uses external gptimers that can be used to interrupt both the Linux host as well as the remote processor. Each remote processor is responsible for refreshing the timer during normal behavior - during OS task scheduling or entering the idle loop properly. During a watchdog condition (executing a tight loop causing no scheduling), the host processor gets interrupts and schedules a recovery for the corresponding remote processor. The remote processor may also get interrupted to be able to print a back trace. A menuconfig option has also been added to enable/disable the Watchdog functionality, with the default as disabled. Acked-by: Mathieu Poirier Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Link: https://lore.kernel.org/r/20200324110035.29907-15-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/Kconfig | 12 ++ drivers/remoteproc/omap_remoteproc.c | 166 +++++++++++++++++++++++++-- 2 files changed, 167 insertions(+), 11 deletions(-) diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index b52abc2268cc..5f33358eb2f1 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -52,6 +52,18 @@ config OMAP_REMOTEPROC It's safe to say N here if you're not interested in multimedia offloading or just want a bare minimum kernel. +config OMAP_REMOTEPROC_WATCHDOG + bool "OMAP remoteproc watchdog timer" + depends on OMAP_REMOTEPROC + default n + help + Say Y here to enable watchdog timer for remote processors. + + This option controls the watchdog functionality for the remote + processors in OMAP. Dedicated OMAP DMTimers are used by the remote + processors and triggers the timer interrupt upon a watchdog + detection. + config WKUP_M3_RPROC tristate "AMx3xx Wakeup M3 remoteproc support" depends on SOC_AM33XX || SOC_AM43XX diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index cee6c33869b3..cdb546f7232e 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -72,10 +73,12 @@ struct omap_rproc_mem { * struct omap_rproc_timer - data structure for a timer used by a omap rproc * @odt: timer pointer * @timer_ops: OMAP dmtimer ops for @odt timer + * @irq: timer irq */ struct omap_rproc_timer { struct omap_dm_timer *odt; const struct omap_dm_timer_ops *timer_ops; + int irq; }; /** @@ -86,6 +89,7 @@ struct omap_rproc_timer { * @mem: internal memory regions data * @num_mems: number of internal memory regions * @num_timers: number of rproc timer(s) + * @num_wd_timers: number of rproc watchdog timers * @timers: timer(s) info used by rproc * @autosuspend_delay: auto-suspend delay value to be used for runtime pm * @need_resume: if true a resume is needed in the system resume callback @@ -102,6 +106,7 @@ struct omap_rproc { struct omap_rproc_mem *mem; int num_mems; int num_timers; + int num_wd_timers; struct omap_rproc_timer *timers; int autosuspend_delay; bool need_resume; @@ -219,6 +224,79 @@ static inline int omap_rproc_release_timer(struct omap_rproc_timer *timer) return timer->timer_ops->free(timer->odt); } +/** + * omap_rproc_get_timer_irq() - get the irq for a timer + * @timer: handle to a OMAP rproc timer + * + * This function is used to get the irq associated with a watchdog timer. The + * function is called by the OMAP remoteproc driver to register a interrupt + * handler to handle watchdog events on the remote processor. + * + * Return: irq id on success, otherwise a failure as returned by DMTimer ops + */ +static inline int omap_rproc_get_timer_irq(struct omap_rproc_timer *timer) +{ + return timer->timer_ops->get_irq(timer->odt); +} + +/** + * omap_rproc_ack_timer_irq() - acknowledge a timer irq + * @timer: handle to a OMAP rproc timer + * + * This function is used to clear the irq associated with a watchdog timer. The + * The function is called by the OMAP remoteproc upon a watchdog event on the + * remote processor to clear the interrupt status of the watchdog timer. + */ +static inline void omap_rproc_ack_timer_irq(struct omap_rproc_timer *timer) +{ + timer->timer_ops->write_status(timer->odt, OMAP_TIMER_INT_OVERFLOW); +} + +/** + * omap_rproc_watchdog_isr() - Watchdog ISR handler for remoteproc device + * @irq: IRQ number associated with a watchdog timer + * @data: IRQ handler data + * + * This ISR routine executes the required necessary low-level code to + * acknowledge a watchdog timer interrupt. There can be multiple watchdog + * timers associated with a rproc (like IPUs which have 2 watchdog timers, + * one per Cortex M3/M4 core), so a lookup has to be performed to identify + * the timer to acknowledge its interrupt. + * + * The function also invokes rproc_report_crash to report the watchdog event + * to the remoteproc driver core, to trigger a recovery. + * + * Return: IRQ_HANDLED on success, otherwise IRQ_NONE + */ +static irqreturn_t omap_rproc_watchdog_isr(int irq, void *data) +{ + struct rproc *rproc = data; + struct omap_rproc *oproc = rproc->priv; + struct device *dev = rproc->dev.parent; + struct omap_rproc_timer *timers = oproc->timers; + struct omap_rproc_timer *wd_timer = NULL; + int num_timers = oproc->num_timers + oproc->num_wd_timers; + int i; + + for (i = oproc->num_timers; i < num_timers; i++) { + if (timers[i].irq > 0 && irq == timers[i].irq) { + wd_timer = &timers[i]; + break; + } + } + + if (!wd_timer) { + dev_err(dev, "invalid timer\n"); + return IRQ_NONE; + } + + omap_rproc_ack_timer_irq(wd_timer); + + rproc_report_crash(rproc, RPROC_WATCHDOG); + + return IRQ_HANDLED; +} + /** * omap_rproc_enable_timers() - enable the timers for a remoteproc * @rproc: handle of a remote processor @@ -242,19 +320,26 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure) struct omap_rproc_timer *timers = oproc->timers; struct device *dev = rproc->dev.parent; struct device_node *np = NULL; + int num_timers = oproc->num_timers + oproc->num_wd_timers; - if (!oproc->num_timers) + if (!num_timers) return 0; if (!configure) goto start_timers; - for (i = 0; i < oproc->num_timers; i++) { - np = of_parse_phandle(dev->of_node, "ti,timers", i); + for (i = 0; i < num_timers; i++) { + if (i < oproc->num_timers) + np = of_parse_phandle(dev->of_node, "ti,timers", i); + else + np = of_parse_phandle(dev->of_node, + "ti,watchdog-timers", + (i - oproc->num_timers)); if (!np) { ret = -ENXIO; dev_err(dev, "device node lookup for timer at index %d failed: %d\n", - i, ret); + i < oproc->num_timers ? i : + i - oproc->num_timers, ret); goto free_timers; } @@ -277,12 +362,14 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure) if (!timer_ops || !timer_ops->request_by_node || !timer_ops->set_source || !timer_ops->set_load || !timer_ops->free || !timer_ops->start || - !timer_ops->stop) { + !timer_ops->stop || !timer_ops->get_irq || + !timer_ops->write_status) { ret = -EINVAL; dev_err(dev, "device does not have required timer ops\n"); goto put_node; } + timers[i].irq = -1; timers[i].timer_ops = timer_ops; ret = omap_rproc_request_timer(dev, np, &timers[i]); if (ret) { @@ -291,10 +378,33 @@ static int omap_rproc_enable_timers(struct rproc *rproc, bool configure) goto put_node; } of_node_put(np); + + if (i >= oproc->num_timers) { + timers[i].irq = omap_rproc_get_timer_irq(&timers[i]); + if (timers[i].irq < 0) { + dev_err(dev, "get_irq for timer %p failed: %d\n", + np, timers[i].irq); + ret = -EBUSY; + goto free_timers; + } + + ret = request_irq(timers[i].irq, + omap_rproc_watchdog_isr, IRQF_SHARED, + "rproc-wdt", rproc); + if (ret) { + dev_err(dev, "error requesting irq for timer %p\n", + np); + omap_rproc_release_timer(&timers[i]); + timers[i].odt = NULL; + timers[i].timer_ops = NULL; + timers[i].irq = -1; + goto free_timers; + } + } } start_timers: - for (i = 0; i < oproc->num_timers; i++) { + for (i = 0; i < num_timers; i++) { ret = omap_rproc_start_timer(&timers[i]); if (ret) { dev_err(dev, "start timer %p failed failed: %d\n", np, @@ -316,9 +426,12 @@ put_node: of_node_put(np); free_timers: while (i--) { + if (i >= oproc->num_timers) + free_irq(timers[i].irq, rproc); omap_rproc_release_timer(&timers[i]); timers[i].odt = NULL; timers[i].timer_ops = NULL; + timers[i].irq = -1; } return ret; @@ -341,16 +454,20 @@ static int omap_rproc_disable_timers(struct rproc *rproc, bool configure) int i; struct omap_rproc *oproc = rproc->priv; struct omap_rproc_timer *timers = oproc->timers; + int num_timers = oproc->num_timers + oproc->num_wd_timers; - if (!oproc->num_timers) + if (!num_timers) return 0; - for (i = 0; i < oproc->num_timers; i++) { + for (i = 0; i < num_timers; i++) { omap_rproc_stop_timer(&timers[i]); if (configure) { + if (i >= oproc->num_timers) + free_irq(timers[i].irq, rproc); omap_rproc_release_timer(&timers[i]); timers[i].odt = NULL; timers[i].timer_ops = NULL; + timers[i].irq = -1; } } @@ -1104,12 +1221,35 @@ static int omap_rproc_of_get_internal_memories(struct platform_device *pdev, return 0; } +#ifdef CONFIG_OMAP_REMOTEPROC_WATCHDOG +static int omap_rproc_count_wdog_timers(struct device *dev) +{ + struct device_node *np = dev->of_node; + int ret; + + ret = of_count_phandle_with_args(np, "ti,watchdog-timers", NULL); + if (ret <= 0) { + dev_dbg(dev, "device does not have watchdog timers, status = %d\n", + ret); + ret = 0; + } + + return ret; +} +#else +static int omap_rproc_count_wdog_timers(struct device *dev) +{ + return 0; +} +#endif + static int omap_rproc_of_get_timers(struct platform_device *pdev, struct rproc *rproc) { struct device_node *np = pdev->dev.of_node; struct omap_rproc *oproc = rproc->priv; struct device *dev = &pdev->dev; + int num_timers; /* * Timer nodes are directly used in client nodes as phandles, so @@ -1122,14 +1262,18 @@ static int omap_rproc_of_get_timers(struct platform_device *pdev, oproc->num_timers = 0; } - if (oproc->num_timers) { - oproc->timers = devm_kcalloc(dev, oproc->num_timers, + oproc->num_wd_timers = omap_rproc_count_wdog_timers(dev); + + num_timers = oproc->num_timers + oproc->num_wd_timers; + if (num_timers) { + oproc->timers = devm_kcalloc(dev, num_timers, sizeof(*oproc->timers), GFP_KERNEL); if (!oproc->timers) return -ENOMEM; - dev_dbg(dev, "device has %d tick timers\n", oproc->num_timers); + dev_dbg(dev, "device has %d tick timers and %d watchdog timers\n", + oproc->num_timers, oproc->num_wd_timers); } return 0; From a7084c3d47c4aaedcca217ce87b7b5b5fe3cfa35 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Tue, 24 Mar 2020 13:00:35 +0200 Subject: [PATCH 37/37] remoteproc/omap: Switch to SPDX license identifiers Use the appropriate SPDX license identifiers in various OMAP remoteproc source files and drop the previous boilerplate license text. Signed-off-by: Suman Anna Signed-off-by: Tero Kristo Reviewed-by: Andrew F. Davis Acked-by: Mathieu Poirier Link: https://lore.kernel.org/r/20200324110035.29907-16-t-kristo@ti.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/omap_remoteproc.h | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h index 13f17d9135c0..828e13256c02 100644 --- a/drivers/remoteproc/omap_remoteproc.h +++ b/drivers/remoteproc/omap_remoteproc.h @@ -1,35 +1,10 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Remote processor messaging * * Copyright (C) 2011-2020 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Texas Instruments nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _OMAP_RPMSG_H