From d82bd359972a7fe71a778396cf287bc9f9f3b981 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 24 Oct 2017 21:22:24 +0530 Subject: firmware: scm: Add new SCM call API for switching memory ownership Two different processors on a SOC need to switch memory ownership during load/unload. To enable this, second level memory map table need to be updated, which is done by secure layer. This patch adds the interface for making secure monitor call for memory ownership switching request. Acked-by: Andy Gross Signed-off-by: Avaneesh Kumar Dwivedi [bjorn: Minor style and kerneldoc updates] Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm-32.c | 7 ++++ drivers/firmware/qcom_scm-64.c | 27 ++++++++++++ drivers/firmware/qcom_scm.c | 95 ++++++++++++++++++++++++++++++++++++++++++ drivers/firmware/qcom_scm.h | 5 +++ include/linux/qcom_scm.h | 16 +++++++ 5 files changed, 150 insertions(+) diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 93e3b96b6dfa..60d96bcf6694 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -579,6 +579,13 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) return ret ? : le32_to_cpu(scm_ret); } +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + return -ENODEV; +} + int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) { diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 6e6d561708e2..ea5b1e680f67 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -382,6 +382,33 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) return ret ? : res.a1; } +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = mem_region; + desc.args[1] = mem_sz; + desc.args[2] = src; + desc.args[3] = src_sz; + desc.args[4] = dest; + desc.args[5] = dest_sz; + desc.args[6] = 0; + + desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, + QCOM_SCM_VAL, QCOM_SCM_VAL); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, + QCOM_MEM_PROT_ASSIGN_ID, + &desc, &res); + + return ret ? : res.a1; +} + int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) { struct qcom_scm_desc desc = {0}; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index bb16510d75ba..334eaa341828 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -40,6 +40,19 @@ struct qcom_scm { struct reset_controller_dev reset; }; +struct qcom_scm_current_perm_info { + __le32 vmid; + __le32 perm; + __le64 ctx; + __le32 ctx_size; + __le32 unused; +}; + +struct qcom_scm_mem_map_info { + __le64 mem_addr; + __le64 mem_size; +}; + static struct qcom_scm *__scm; static int qcom_scm_clk_enable(void) @@ -348,6 +361,88 @@ int qcom_scm_set_remote_state(u32 state, u32 id) } EXPORT_SYMBOL(qcom_scm_set_remote_state); +/** + * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership + * @mem_addr: mem region whose ownership need to be reassigned + * @mem_sz: size of the region. + * @srcvm: vmid for current set of owners, each set bit in + * flag indicate a unique owner + * @newvm: array having new owners and corrsponding permission + * flags + * @dest_cnt: number of owners in next set. + * + * Return negative errno on failure, 0 on success, with @srcvm updated. + */ +int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + unsigned int *srcvm, + struct qcom_scm_vmperm *newvm, int dest_cnt) +{ + struct qcom_scm_current_perm_info *destvm; + struct qcom_scm_mem_map_info *mem_to_map; + phys_addr_t mem_to_map_phys; + phys_addr_t dest_phys; + phys_addr_t ptr_phys; + size_t mem_to_map_sz; + size_t dest_sz; + size_t src_sz; + size_t ptr_sz; + int next_vm; + __le32 *src; + void *ptr; + int ret; + int len; + int i; + + src_sz = hweight_long(*srcvm) * sizeof(*src); + mem_to_map_sz = sizeof(*mem_to_map); + dest_sz = dest_cnt * sizeof(*destvm); + ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + + ALIGN(dest_sz, SZ_64); + + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + /* Fill source vmid detail */ + src = ptr; + len = hweight_long(*srcvm); + for (i = 0; i < len; i++) { + src[i] = cpu_to_le32(ffs(*srcvm) - 1); + *srcvm ^= 1 << (ffs(*srcvm) - 1); + } + + /* Fill details of mem buff to map */ + mem_to_map = ptr + ALIGN(src_sz, SZ_64); + mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); + mem_to_map[0].mem_addr = cpu_to_le64(mem_addr); + mem_to_map[0].mem_size = cpu_to_le64(mem_sz); + + next_vm = 0; + /* Fill details of next vmid detail */ + destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + for (i = 0; i < dest_cnt; i++) { + destvm[i].vmid = cpu_to_le32(newvm[i].vmid); + destvm[i].perm = cpu_to_le32(newvm[i].perm); + destvm[i].ctx = 0; + destvm[i].ctx_size = 0; + next_vm |= BIT(newvm[i].vmid); + } + + ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, + ptr_phys, src_sz, dest_phys, dest_sz); + dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys); + if (ret) { + dev_err(__scm->dev, + "Assign memory protection call failed %d.\n", ret); + return -EINVAL; + } + + *srcvm = next_vm; + return 0; +} +EXPORT_SYMBOL(qcom_scm_assign_mem); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 9bea691f30fb..fe54b7ba4db4 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -95,5 +95,10 @@ extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, size_t *size); extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, u32 spare); +#define QCOM_MEM_PROT_ASSIGN_ID 0x16 +extern int __qcom_scm_assign_mem(struct device *dev, + phys_addr_t mem_region, size_t mem_sz, + phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz); #endif diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index e5380471c2cd..6f8da9e182f9 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -23,6 +23,19 @@ struct qcom_scm_hdcp_req { u32 val; }; +struct qcom_scm_vmperm { + int vmid; + int perm; +}; + +#define QCOM_SCM_VMID_HLOS 0x3 +#define QCOM_SCM_VMID_MSS_MSA 0xF +#define QCOM_SCM_PERM_READ 0x4 +#define QCOM_SCM_PERM_WRITE 0x2 +#define QCOM_SCM_PERM_EXEC 0x1 +#define QCOM_SCM_PERM_RW (QCOM_SCM_PERM_READ | QCOM_SCM_PERM_WRITE) +#define QCOM_SCM_PERM_RWX (QCOM_SCM_PERM_RW | QCOM_SCM_PERM_EXEC) + #if IS_ENABLED(CONFIG_QCOM_SCM) extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); @@ -37,6 +50,9 @@ extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); extern int qcom_scm_pas_auth_and_reset(u32 peripheral); extern int qcom_scm_pas_shutdown(u32 peripheral); +extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + unsigned int *src, struct qcom_scm_vmperm *newvm, + int dest_cnt); extern void qcom_scm_cpu_power_down(u32 flags); extern u32 qcom_scm_get_version(void); extern int qcom_scm_set_remote_state(u32 state, u32 id); -- cgit v1.2.3-59-g8ed1b From 94c907859a4c678de8e74eeece5c0487b5629361 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 24 Oct 2017 21:22:25 +0530 Subject: remoteproc: qcom: refactor mss fw image loading sequence This patch refactor code to first load all firmware blobs and then update modem proc to authenticate and boot fw. Tested-and-acked-by: Bjorn Andersson Signed-off-by: Avaneesh Kumar Dwivedi Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pil.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 2d3d5ac92c06..39b9ee4a63b7 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -504,7 +504,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) bool relocate = false; char seg_name[10]; ssize_t offset; - size_t size; + size_t size = 0; void *ptr; int ret; int i; @@ -542,7 +542,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) } mpss_reloc = relocate ? min_addr : qproc->mpss_phys; - + /* Load firmware segments */ for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; @@ -575,18 +575,15 @@ static int q6v5_mpss_load(struct q6v5 *qproc) memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); } - - size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); - if (!size) { - 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); - } - size += phdr->p_memsz; - writel(size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); } + /* Transfer ownership of modem ddr region with q6*/ + 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"); -- cgit v1.2.3-59-g8ed1b From 6c5a9dc2481b4819c00bbcef8005b59c04d64963 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 24 Oct 2017 21:22:26 +0530 Subject: remoteproc: qcom: Make secure world call for mem ownership switch MSS proc on msm8996 can not access fw loaded region without stage second translation of memory pages where mpss image are loaded. This patch in order to enable mss boot on msm8996 invoke scm call to switch or share ownership between apps and modem. Signed-off-by: Avaneesh Kumar Dwivedi [bjorn: Corrected error path in q6v5_start()] Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pil.c | 106 +++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 5 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 39b9ee4a63b7..728179ba7db2 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -110,6 +110,7 @@ struct rproc_hexagon_res { struct qcom_mss_reg_res *active_supply; char **proxy_clk_names; char **active_clk_names; + bool need_mem_protection; }; struct q6v5 { @@ -154,6 +155,10 @@ struct q6v5 { struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; + bool need_mem_protection; + int mpss_perm; + int mba_perm; + }; static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, @@ -289,6 +294,35 @@ static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc, return &table; } +static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, + bool remote_owner, phys_addr_t addr, + size_t size) +{ + struct qcom_scm_vmperm next; + int ret; + + 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)) + 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; + + ret = qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), + current_perm, &next, 1); + if (ret < 0) { + pr_err("Failed to assign memory access in range %p to %p to %s ret = %d\n", + (void *)addr, (void *)(addr + size), + remote_owner ? "mss" : "hlos", ret); + return ret; + } + + return 0; +} + static int q6v5_load(struct rproc *rproc, const struct firmware *fw) { struct q6v5 *qproc = rproc->priv; @@ -451,6 +485,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) { unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; dma_addr_t phys; + int mdata_perm; + int xferop_ret; void *ptr; int ret; @@ -462,6 +498,13 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) memcpy(ptr, fw->data, fw->size); + /* Hypervisor mapping to access metadata by modem */ + mdata_perm = BIT(QCOM_SCM_VMID_HLOS); + ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, + true, phys, fw->size); + if (ret) + return -EAGAIN; + writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG); writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); @@ -471,6 +514,13 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) else if (ret < 0) 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, fw->size); + if (xferop_ret) + dev_warn(qproc->dev, + "mdt buffer not reclaimed system may become unstable\n"); + dma_free_attrs(qproc->dev, fw->size, ptr, phys, dma_attrs); return ret < 0 ? ret : 0; @@ -578,7 +628,12 @@ static int q6v5_mpss_load(struct q6v5 *qproc) size += phdr->p_memsz; } - /* Transfer ownership of modem ddr region with q6*/ + /* Transfer ownership of modem ddr region to q6 */ + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, + qproc->mpss_phys, qproc->mpss_size); + if (ret) + return -EAGAIN; + 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); @@ -599,6 +654,7 @@ release_firmware: static int q6v5_start(struct rproc *rproc) { struct q6v5 *qproc = (struct q6v5 *)rproc->priv; + int xfermemop_ret; int ret; ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, @@ -634,11 +690,18 @@ static int q6v5_start(struct rproc *rproc) goto assert_reset; } + /* Assign MBA image access in DDR to q6 */ + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, + qproc->mba_phys, + qproc->mba_size); + if (xfermemop_ret) + goto disable_active_clks; + writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); ret = q6v5proc_reset(qproc); if (ret) - goto halt_axi_ports; + goto reclaim_mba; ret = q6v5_rmb_mba_wait(qproc, 0, 5000); if (ret == -ETIMEDOUT) { @@ -655,16 +718,22 @@ static int q6v5_start(struct rproc *rproc) ret = q6v5_mpss_load(qproc); if (ret) - goto halt_axi_ports; + goto reclaim_mpss; ret = wait_for_completion_timeout(&qproc->start_done, msecs_to_jiffies(5000)); if (ret == 0) { dev_err(qproc->dev, "start timed out\n"); ret = -ETIMEDOUT; - goto halt_axi_ports; + goto reclaim_mpss; } + xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, false, + qproc->mba_phys, + qproc->mba_size); + if (xfermemop_ret) + dev_err(qproc->dev, + "Failed to reclaim mba buffer system may become unstable\n"); qproc->running = true; q6v5_clk_disable(qproc->dev, qproc->proxy_clks, @@ -674,12 +743,30 @@ 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); + halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); 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, + qproc->mba_size); + if (xfermemop_ret) { + dev_err(qproc->dev, + "Failed to reclaim mba buffer, system may become unstable\n"); + } + +disable_active_clks: q6v5_clk_disable(qproc->dev, qproc->active_clks, qproc->active_clk_count); + assert_reset: reset_control_assert(qproc->mss_restart); disable_vdd: @@ -699,6 +786,7 @@ static int q6v5_stop(struct rproc *rproc) { struct q6v5 *qproc = (struct q6v5 *)rproc->priv; int ret; + u32 val; qproc->running = false; @@ -716,6 +804,10 @@ static int q6v5_stop(struct rproc *rproc) q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); + ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, false, + qproc->mpss_phys, qproc->mpss_size); + WARN_ON(ret); + reset_control_assert(qproc->mss_restart); q6v5_clk_disable(qproc->dev, qproc->active_clks, qproc->active_clk_count); @@ -1014,6 +1106,7 @@ static int q6v5_probe(struct platform_device *pdev) if (ret) goto free_rproc; + qproc->need_mem_protection = desc->need_mem_protection; ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); if (ret < 0) goto free_rproc; @@ -1035,7 +1128,8 @@ static int q6v5_probe(struct platform_device *pdev) ret = PTR_ERR(qproc->state); goto free_rproc; } - + qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); + qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); @@ -1091,6 +1185,7 @@ static const struct rproc_hexagon_res msm8916_mss = { "mem", NULL }, + .need_mem_protection = false, }; static const struct rproc_hexagon_res msm8974_mss = { @@ -1128,6 +1223,7 @@ static const struct rproc_hexagon_res msm8974_mss = { "mem", NULL }, + .need_mem_protection = false, }; static const struct of_device_id q6v5_of_match[] = { -- cgit v1.2.3-59-g8ed1b From 9f058fa2efb10cd75884c4ee6c357bab07715f02 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 24 Oct 2017 21:22:27 +0530 Subject: remoteproc: qcom: Add support for mss remoteproc on msm8996 This patch add support for mss boot on msm8996. Major changes include initializing mss rproc for msm8996, making appropriate change for executing mss reset sequence etc. Tested-and-acked-by: Bjorn Andersson Signed-off-by: Avaneesh Kumar Dwivedi Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/remoteproc/qcom,q6v5.txt | 1 + drivers/remoteproc/qcom_q6v5_pil.c | 164 ++++++++++++++++++--- 2 files changed, 141 insertions(+), 24 deletions(-) diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 7ff3f7903f26..00d3d58a102f 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -10,6 +10,7 @@ on the Qualcomm Hexagon core. "qcom,q6v5-pil", "qcom,msm8916-mss-pil", "qcom,msm8974-mss-pil" + "qcom,msm8996-mss-pil" - reg: Usage: required diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 728179ba7db2..5460f61ee21c 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "remoteproc_internal.h" #include "qcom_common.h" @@ -64,6 +65,8 @@ #define QDSP6SS_RESET_REG 0x014 #define QDSP6SS_GFMUX_CTL_REG 0x020 #define QDSP6SS_PWR_CTL_REG 0x030 +#define QDSP6SS_MEM_PWR_CTL 0x0B0 +#define QDSP6SS_STRAP_ACC 0x110 /* AXI Halt Register Offsets */ #define AXI_HALTREQ_REG 0x0 @@ -92,6 +95,15 @@ #define QDSS_BHS_ON BIT(21) #define QDSS_LDO_BYP BIT(22) +/* QDSP6v56 parameters */ +#define QDSP6v56_LDO_BYP BIT(25) +#define QDSP6v56_BHS_ON BIT(24) +#define QDSP6v56_CLAMP_WL BIT(21) +#define QDSP6v56_CLAMP_QMC_MEM BIT(22) +#define HALT_CHECK_MAX_LOOPS 200 +#define QDSP6SS_XO_CBCR 0x0038 +#define QDSP6SS_ACC_OVERRIDE_VAL 0x20 + struct reg_info { struct regulator *reg; int uV; @@ -110,6 +122,7 @@ struct rproc_hexagon_res { struct qcom_mss_reg_res *active_supply; char **proxy_clk_names; char **active_clk_names; + int version; bool need_mem_protection; }; @@ -158,7 +171,13 @@ struct q6v5 { bool need_mem_protection; int mpss_perm; int mba_perm; + int version; +}; +enum { + MSS_MSM8916, + MSS_MSM8974, + MSS_MSM8996, }; static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, @@ -387,33 +406,98 @@ static int q6v5proc_reset(struct q6v5 *qproc) { u32 val; int ret; + int i; - /* Assert resets, stop core */ - val = readl(qproc->reg_base + QDSP6SS_RESET_REG); - val |= (Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE); - writel(val, qproc->reg_base + QDSP6SS_RESET_REG); - /* Enable power block headswitch, and wait for it to stabilize */ - val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= QDSS_BHS_ON | QDSS_LDO_BYP; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - udelay(1); - - /* - * Turn on memories. L2 banks should be done individually - * to minimize inrush current. - */ - val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N | - Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_L2DATA_SLP_NRET_N_2; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_L2DATA_SLP_NRET_N_1; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); - val |= Q6SS_L2DATA_SLP_NRET_N_0; - writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + if (qproc->version == MSS_MSM8996) { + /* Override the ACC value if required */ + writel(QDSP6SS_ACC_OVERRIDE_VAL, + qproc->reg_base + QDSP6SS_STRAP_ACC); + /* Assert resets, stop core */ + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); + val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); + + /* BHS require xo cbcr to be enabled */ + val = readl(qproc->reg_base + QDSP6SS_XO_CBCR); + val |= 0x1; + writel(val, qproc->reg_base + QDSP6SS_XO_CBCR); + + /* Read CLKOFF bit to go low indicating CLK is enabled */ + ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_XO_CBCR, + val, !(val & BIT(31)), 1, + HALT_CHECK_MAX_LOOPS); + if (ret) { + dev_err(qproc->dev, + "xo cbcr enabling timed out (rc:%d)\n", ret); + return ret; + } + /* Enable power block headswitch and wait for it to stabilize */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= QDSP6v56_BHS_ON; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + udelay(1); + + /* Put LDO in bypass mode */ + val |= QDSP6v56_LDO_BYP; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Deassert QDSP6 compiler memory clamp */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val &= ~QDSP6v56_CLAMP_QMC_MEM; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Deassert memory peripheral sleep and L2 memory standby */ + val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + + /* Turn on L1, L2, ETB and JU memories 1 at a time */ + val = readl(qproc->reg_base + QDSP6SS_MEM_PWR_CTL); + for (i = 19; i >= 0; i--) { + val |= BIT(i); + writel(val, qproc->reg_base + + QDSP6SS_MEM_PWR_CTL); + /* + * Read back value to ensure the write is done then + * wait for 1us for both memory peripheral and data + * array to turn on. + */ + val |= readl(qproc->reg_base + QDSP6SS_MEM_PWR_CTL); + udelay(1); + } + /* Remove word line clamp */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val &= ~QDSP6v56_CLAMP_WL; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + } else { + /* Assert resets, stop core */ + val = readl(qproc->reg_base + QDSP6SS_RESET_REG); + val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; + writel(val, qproc->reg_base + QDSP6SS_RESET_REG); + + /* Enable power block headswitch and wait for it to stabilize */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= QDSS_BHS_ON | QDSS_LDO_BYP; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + udelay(1); + /* + * Turn on memories. L2 banks should be done individually + * to minimize inrush current. + */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_SLP_RET_N | Q6SS_L2TAG_SLP_NRET_N | + Q6SS_ETB_SLP_NRET_N | Q6SS_L2DATA_STBY_N; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_L2DATA_SLP_NRET_N_2; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_L2DATA_SLP_NRET_N_1; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_L2DATA_SLP_NRET_N_0; + writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); + } /* Remove IO clamp */ val &= ~Q6SS_CLAMP_IO; writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); @@ -803,6 +887,16 @@ static int q6v5_stop(struct rproc *rproc) q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); + if (qproc->version == MSS_MSM8996) { + /* + * To avoid high MX current during LPASS/MSS restart. + */ + val = readl(qproc->reg_base + QDSP6SS_PWR_CTL_REG); + val |= Q6SS_CLAMP_IO | QDSP6v56_CLAMP_WL | + QDSP6v56_CLAMP_QMC_MEM; + 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); @@ -1106,6 +1200,7 @@ static int q6v5_probe(struct platform_device *pdev) if (ret) goto free_rproc; + qproc->version = desc->version; qproc->need_mem_protection = desc->need_mem_protection; ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); if (ret < 0) @@ -1158,6 +1253,24 @@ static int q6v5_remove(struct platform_device *pdev) return 0; } +static const struct rproc_hexagon_res msm8996_mss = { + .hexagon_mba_image = "mba.mbn", + .proxy_clk_names = (char*[]){ + "xo", + "pnoc", + NULL + }, + .active_clk_names = (char*[]){ + "iface", + "bus", + "mem", + "gpll0_mss_clk", + NULL + }, + .need_mem_protection = true, + .version = MSS_MSM8996, +}; + static const struct rproc_hexagon_res msm8916_mss = { .hexagon_mba_image = "mba.mbn", .proxy_supply = (struct qcom_mss_reg_res[]) { @@ -1186,6 +1299,7 @@ static const struct rproc_hexagon_res msm8916_mss = { NULL }, .need_mem_protection = false, + .version = MSS_MSM8916, }; static const struct rproc_hexagon_res msm8974_mss = { @@ -1224,12 +1338,14 @@ static const struct rproc_hexagon_res msm8974_mss = { NULL }, .need_mem_protection = false, + .version = MSS_MSM8974, }; static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss}, { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss}, { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss}, + { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss}, { }, }; MODULE_DEVICE_TABLE(of, q6v5_of_match); -- cgit v1.2.3-59-g8ed1b From bdd8edb9b0cd552f09a81c32d699af041155a390 Mon Sep 17 00:00:00 2001 From: Loic Pallardy Date: Mon, 6 Nov 2017 18:09:55 +0100 Subject: remoteproc: debug: add resource table dump feature This patch adds the capability to display the content of the resource table associated to a remote processor firmware. Signed-off-by: Loic Pallardy Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_debugfs.c | 99 +++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 1c122e230cec..dc5e25943df7 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -155,6 +155,103 @@ static const struct file_operations rproc_recovery_ops = { .llseek = generic_file_llseek, }; +/* Expose resource table content via debugfs */ +static int rproc_rsc_table_show(struct seq_file *seq, void *p) +{ + static const char * const types[] = {"carveout", "devmem", "trace", "vdev"}; + struct rproc *rproc = seq->private; + struct resource_table *table = rproc->table_ptr; + struct fw_rsc_carveout *c; + struct fw_rsc_devmem *d; + struct fw_rsc_trace *t; + struct fw_rsc_vdev *v; + int i, j; + + if (!table) { + seq_puts(seq, "No resource table found\n"); + return 0; + } + + for (i = 0; i < table->num; i++) { + int offset = table->offset[i]; + struct fw_rsc_hdr *hdr = (void *)table + offset; + void *rsc = (void *)hdr + sizeof(*hdr); + + switch (hdr->type) { + case RSC_CARVEOUT: + c = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + seq_printf(seq, " Device Address 0x%x\n", c->da); + seq_printf(seq, " Physical Address 0x%x\n", c->pa); + seq_printf(seq, " Length 0x%x Bytes\n", c->len); + seq_printf(seq, " Flags 0x%x\n", c->flags); + seq_printf(seq, " Reserved (should be zero) [%d]\n", c->reserved); + seq_printf(seq, " Name %s\n\n", c->name); + break; + case RSC_DEVMEM: + d = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + seq_printf(seq, " Device Address 0x%x\n", d->da); + seq_printf(seq, " Physical Address 0x%x\n", d->pa); + seq_printf(seq, " Length 0x%x Bytes\n", d->len); + seq_printf(seq, " Flags 0x%x\n", d->flags); + seq_printf(seq, " Reserved (should be zero) [%d]\n", d->reserved); + seq_printf(seq, " Name %s\n\n", d->name); + break; + case RSC_TRACE: + t = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + seq_printf(seq, " Device Address 0x%x\n", t->da); + seq_printf(seq, " Length 0x%x Bytes\n", t->len); + seq_printf(seq, " Reserved (should be zero) [%d]\n", t->reserved); + seq_printf(seq, " Name %s\n\n", t->name); + break; + case RSC_VDEV: + v = rsc; + seq_printf(seq, "Entry %d is of type %s\n", i, types[hdr->type]); + + seq_printf(seq, " ID %d\n", v->id); + seq_printf(seq, " Notify ID %d\n", v->notifyid); + seq_printf(seq, " Device features 0x%x\n", v->dfeatures); + seq_printf(seq, " Guest features 0x%x\n", v->gfeatures); + seq_printf(seq, " Config length 0x%x\n", v->config_len); + seq_printf(seq, " Status 0x%x\n", v->status); + seq_printf(seq, " Number of vrings %d\n", v->num_of_vrings); + seq_printf(seq, " Reserved (should be zero) [%d][%d]\n\n", + v->reserved[0], v->reserved[1]); + + for (j = 0; j < v->num_of_vrings; j++) { + seq_printf(seq, " Vring %d\n", j); + seq_printf(seq, " Device Address 0x%x\n", v->vring[j].da); + seq_printf(seq, " Alignment %d\n", v->vring[j].align); + seq_printf(seq, " Number of buffers %d\n", v->vring[j].num); + seq_printf(seq, " Notify ID %d\n", v->vring[j].notifyid); + seq_printf(seq, " Physical Address 0x%x\n\n", + v->vring[j].pa); + } + break; + default: + seq_printf(seq, "Unknown resource type found: %d [hdr: %p]\n", + hdr->type, hdr); + break; + } + } + + return 0; +} + +static int rproc_rsc_table_open(struct inode *inode, struct file *file) +{ + return single_open(file, rproc_rsc_table_show, inode->i_private); +} + +static const struct file_operations rproc_rsc_table_ops = { + .open = rproc_rsc_table_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + void rproc_remove_trace_file(struct dentry *tfile) { debugfs_remove(tfile); @@ -198,6 +295,8 @@ void rproc_create_debug_dir(struct rproc *rproc) rproc, &rproc_name_ops); debugfs_create_file("recovery", 0400, rproc->dbg_dir, rproc, &rproc_recovery_ops); + debugfs_create_file("resource_table", 0400, rproc->dbg_dir, + rproc, &rproc_rsc_table_ops); } void __init rproc_init_debugfs(void) -- cgit v1.2.3-59-g8ed1b From b89188394164a5df4bd649380f75ec74e6b8a4d3 Mon Sep 17 00:00:00 2001 From: Loic Pallardy Date: Mon, 6 Nov 2017 18:09:56 +0100 Subject: remoteproc: debug: add carveouts list dump feature This patch offers the capability to dump memory carveouts associated to one remoteprocessor. Signed-off-by: Loic Pallardy Signed-off-by: Bjorn Andersson --- drivers/remoteproc/remoteproc_debugfs.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index dc5e25943df7..a20488336aa0 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -252,6 +252,35 @@ static const struct file_operations rproc_rsc_table_ops = { .release = single_release, }; +/* Expose carveout content via debugfs */ +static int rproc_carveouts_show(struct seq_file *seq, void *p) +{ + struct rproc *rproc = seq->private; + struct rproc_mem_entry *carveout; + + list_for_each_entry(carveout, &rproc->carveouts, node) { + seq_puts(seq, "Carveout memory entry:\n"); + seq_printf(seq, "\tVirtual address: %p\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); + } + + return 0; +} + +static int rproc_carveouts_open(struct inode *inode, struct file *file) +{ + return single_open(file, rproc_carveouts_show, inode->i_private); +} + +static const struct file_operations rproc_carveouts_ops = { + .open = rproc_carveouts_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + void rproc_remove_trace_file(struct dentry *tfile) { debugfs_remove(tfile); @@ -297,6 +326,8 @@ void rproc_create_debug_dir(struct rproc *rproc) rproc, &rproc_recovery_ops); debugfs_create_file("resource_table", 0400, rproc->dbg_dir, rproc, &rproc_rsc_table_ops); + debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, + rproc, &rproc_carveouts_ops); } void __init rproc_init_debugfs(void) -- cgit v1.2.3-59-g8ed1b From 9f2a4342a8bf24c644204311f0cf154f78489b53 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 6 Nov 2017 22:26:41 -0800 Subject: remoteproc: qcom: Drop pr_err in q6v5_xfer_mem_ownership() The pr_err() in q6v5_xfer_mem_ownership() prints, upon failure, the memory range that failed to be transitioned. But on 32-bit architectures with CONFIG_PHYS_ADDR_T_64BIT set we cannot cast the phys_addr_t variable to a pointer, per below build error. Instead these should be formatted with %pap. In file included from include/linux/kernel.h:14:0, from include/linux/clk.h:16, from drivers/remoteproc/qcom_q6v5_pil.c:18: drivers/remoteproc/qcom_q6v5_pil.c: In function 'q6v5_xfer_mem_ownership': drivers/remoteproc/qcom_q6v5_pil.c:337:10: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] (void *)addr, (void *)(addr + size), ^ Most callers will upon failure print a specific error message describing which memory region that we failed to pass ownership of, so rather than fixing the format string this patch fixes up the last callers and drop the print from this function, saving us from spamming the log in most of these error cases. Fixes: 6c5a9dc2481b ("remoteproc: qcom: Make secure world call for mem ownership switch") Reported-by: Arnd Bergmann Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pil.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 5460f61ee21c..a019796c363a 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -318,7 +318,6 @@ static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, size_t size) { struct qcom_scm_vmperm next; - int ret; if (!qproc->need_mem_protection) return 0; @@ -330,16 +329,8 @@ static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, 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; - ret = qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), - current_perm, &next, 1); - if (ret < 0) { - pr_err("Failed to assign memory access in range %p to %p to %s ret = %d\n", - (void *)addr, (void *)(addr + size), - remote_owner ? "mss" : "hlos", ret); - return ret; - } - - return 0; + return qcom_scm_assign_mem(addr, ALIGN(size, SZ_4K), + current_perm, &next, 1); } static int q6v5_load(struct rproc *rproc, const struct firmware *fw) @@ -586,8 +577,11 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) mdata_perm = BIT(QCOM_SCM_VMID_HLOS); ret = q6v5_xfer_mem_ownership(qproc, &mdata_perm, true, phys, fw->size); - if (ret) + if (ret) { + dev_err(qproc->dev, + "assigning Q6 access to metadata failed: %d\n", ret); return -EAGAIN; + } writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG); writel(RMB_CMD_META_DATA_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); @@ -715,8 +709,11 @@ static int q6v5_mpss_load(struct q6v5 *qproc) /* Transfer ownership of modem ddr region to q6 */ ret = q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, true, qproc->mpss_phys, qproc->mpss_size); - if (ret) + if (ret) { + dev_err(qproc->dev, + "assigning Q6 access to mpss memory failed: %d\n", ret); return -EAGAIN; + } boot_addr = relocate ? qproc->mpss_phys : min_addr; writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); @@ -778,8 +775,12 @@ static int q6v5_start(struct rproc *rproc) xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, qproc->mba_phys, qproc->mba_size); - if (xfermemop_ret) + if (xfermemop_ret) { + dev_err(qproc->dev, + "assigning Q6 access to mba memory failed: %d\n", + xfermemop_ret); goto disable_active_clks; + } writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); -- cgit v1.2.3-59-g8ed1b From 1a5d5c592e902191bfa091ec9169aa43299a7d0f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 15 Nov 2017 07:58:35 +0100 Subject: remoteproc: qcom: Fix error handling paths in order to avoid memory leaks In case of error returned by 'q6v5_xfer_mem_ownership', we must free some resources before returning. In 'q6v5_mpss_init_image()', add a new label to undo a previous 'dma_alloc_attrs()'. In 'q6v5_mpss_load()', re-use the already existing error handling code to undo a previous 'request_firmware()', as already done in the other error handling paths of the function. Signed-off-by: Christophe JAILLET Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pil.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index a019796c363a..8a3fa2bcc9f6 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c @@ -580,7 +580,8 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) if (ret) { dev_err(qproc->dev, "assigning Q6 access to metadata failed: %d\n", ret); - return -EAGAIN; + ret = -EAGAIN; + goto free_dma_attrs; } writel(phys, qproc->rmb_base + RMB_PMI_META_DATA_REG); @@ -599,6 +600,7 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) dev_warn(qproc->dev, "mdt buffer not reclaimed system may become unstable\n"); +free_dma_attrs: dma_free_attrs(qproc->dev, fw->size, ptr, phys, dma_attrs); return ret < 0 ? ret : 0; @@ -712,7 +714,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc) if (ret) { dev_err(qproc->dev, "assigning Q6 access to mpss memory failed: %d\n", ret); - return -EAGAIN; + ret = -EAGAIN; + goto release_firmware; } boot_addr = relocate ? qproc->mpss_phys : min_addr; -- cgit v1.2.3-59-g8ed1b