Add coredump support for PAS remoteproc instances with a secondary processor by registering fixed dump segments for both primary and secondary carveouts at probe time and mapping them during collection. The attached-state path is for debug dump collection and does not provide operational device recovery.
Signed-off-by: Anvesh Jain P <[email protected]> --- drivers/remoteproc/qcom_q6v5_pas.c | 102 +++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 11 deletions(-) diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 0bce2f0d5b4a..374cafd04b62 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -117,6 +117,7 @@ struct qcom_pas { phys_addr_t region_assign_phys[MAX_ASSIGN_COUNT]; void __iomem *mem_region; + void __iomem *secondary_mem_region; size_t mem_size; size_t dtb_mem_size; @@ -162,6 +163,28 @@ static void qcom_pas_segment_dump(struct rproc *rproc, memcpy_fromio(dest, pas->mem_region + total_offset, size); } +static bool qcom_pas_has_secondary(const struct qcom_pas *pas) +{ + return pas->secondary_pas_id && pas->secondary_dtb_pas_id; +} + +static int qcom_pas_register_dump_segments(struct rproc *rproc, + struct qcom_pas *pas) +{ + int ret; + + ret = rproc_coredump_add_segment(rproc, pas->mem_phys, pas->mem_size); + if (ret) + return ret; + + ret = rproc_coredump_add_segment(rproc, pas->secondary_mem_phys, + pas->secondary_mem_size); + if (ret) + rproc_coredump_cleanup(rproc); + + return ret; +} + static void qcom_pas_minidump(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; @@ -577,16 +600,38 @@ static int qcom_pas_stop(struct rproc *rproc) static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) { struct qcom_pas *pas = rproc->priv; - int offset; + phys_addr_t base; + void __iomem *region; + + if (da >= pas->mem_reloc && len <= pas->mem_size && + da - pas->mem_reloc <= pas->mem_size - len) { + base = pas->mem_reloc; + region = pas->mem_region; + } else if (da >= pas->mem_phys && len <= pas->mem_size && + da - pas->mem_phys <= pas->mem_size - len) { + base = pas->mem_phys; + region = pas->mem_region; + } else if (da >= pas->secondary_mem_reloc && + len <= pas->secondary_mem_size && + da - pas->secondary_mem_reloc <= pas->secondary_mem_size - len) { + base = pas->secondary_mem_reloc; + region = pas->secondary_mem_region; + } else if (da >= pas->secondary_mem_phys && + len <= pas->secondary_mem_size && + da - pas->secondary_mem_phys <= pas->secondary_mem_size - len) { + base = pas->secondary_mem_phys; + region = pas->secondary_mem_region; + } else { + return NULL; + } - offset = da - pas->mem_reloc; - if (offset < 0 || offset + len > pas->mem_size) + if (!region) return NULL; if (is_iomem) *is_iomem = true; - return (__force void *)pas->mem_region + offset; + return (__force void *)region + (da - base); } static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *fw) @@ -598,10 +643,21 @@ static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *f size_t table_sz; int ret; - ret = qcom_register_dump_segments(rproc, fw); - if (ret) { - dev_err(pas->dev, "Error in registering dump segments\n"); - return ret; + if (qcom_pas_has_secondary(pas)) { + if (list_empty(&rproc->dump_segments)) { + ret = qcom_pas_register_dump_segments(rproc, pas); + if (ret) { + dev_err(pas->dev, + "failed to register dump segments: %d\n", ret); + return ret; + } + } + } else { + ret = qcom_register_dump_segments(rproc, fw); + if (ret) { + dev_err(pas->dev, "Error in registering dump segments\n"); + return ret; + } } if (!rproc->has_iommu) @@ -716,13 +772,21 @@ static void qcom_pas_coredump(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; - pas->mem_region = qcom_pas_ctx_map(pas->pas_ctx); + if (!pas->mem_region) + pas->mem_region = qcom_pas_ctx_map(pas->pas_ctx); if (!pas->mem_region) return; + if (qcom_pas_has_secondary(pas) && !pas->secondary_mem_region) { + pas->secondary_mem_region = + qcom_pas_ctx_map(pas->secondary_pas_ctx); + if (!pas->secondary_mem_region) { + dev_err(pas->dev, "unable to map secondary memory for coredump\n"); + return; + } + } + rproc_coredump(rproc); - iounmap(pas->mem_region); - pas->mem_region = NULL; } static const struct rproc_ops qcom_pas_ops = { @@ -1133,6 +1197,12 @@ static int qcom_pas_probe(struct platform_device *pdev) if (desc->early_boot) pas->rproc->state = RPROC_DETACHED; + if (qcom_pas_has_secondary(pas)) { + ret = qcom_pas_register_dump_segments(rproc, pas); + if (ret) + goto remove_ssr_sysmon; + } + ret = rproc_add(rproc); if (ret) goto remove_ssr_sysmon; @@ -1144,6 +1214,7 @@ static int qcom_pas_probe(struct platform_device *pdev) return 0; remove_ssr_sysmon: + rproc_coredump_cleanup(rproc); qcom_remove_ssr_subdev(rproc, &pas->ssr_subdev); qcom_remove_sysmon_subdev(pas->sysmon); deinit_remove_pdm_smd_glink: @@ -1169,6 +1240,15 @@ static void qcom_pas_remove(struct platform_device *pdev) of_platform_device_destroy(&pas->bam_dmux->dev, NULL); rproc_del(pas->rproc); + rproc_coredump_cleanup(pas->rproc); + if (pas->secondary_mem_region) { + iounmap(pas->secondary_mem_region); + pas->secondary_mem_region = NULL; + } + if (pas->mem_region) { + iounmap(pas->mem_region); + pas->mem_region = NULL; + } qcom_q6v5_deinit(&pas->q6v5); qcom_pas_unassign_memory_region(pas); -- 2.34.1

