summaryrefslogtreecommitdiff
path: root/drivers/remoteproc
diff options
context:
space:
mode:
authorBjorn Andersson <andersson@kernel.org>2026-01-13 21:44:18 -0600
committerBjorn Andersson <andersson@kernel.org>2026-01-13 21:44:18 -0600
commit1aab44c02ad26f0c59bf015bc01cc63c4f9e2d68 (patch)
treed32b65d67da90492c726bef14e7f8e81cd7cf1bc /drivers/remoteproc
parenta3bf6ee15a59d25724746f284de167af6dc76baf (diff)
parent5c720260e840b508053dd5338577e0175ef31739 (diff)
downloadlwn-1aab44c02ad26f0c59bf015bc01cc63c4f9e2d68.tar.gz
lwn-1aab44c02ad26f0c59bf015bc01cc63c4f9e2d68.zip
Merge branch '20260105-kvmrprocv10-v10-0-022e96815380@oss.qualcomm.com' of https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux into rproc-next
Merge the support for loading and managing TustZone-based remote processors found in the Qualcomm Glymur platform from a shared topic branch, as it's a mix of qcom-soc and remoteproc patches.
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r--drivers/remoteproc/qcom_q6v5_pas.c165
1 files changed, 130 insertions, 35 deletions
diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c
index 52680ac99589..46204da046fa 100644
--- a/drivers/remoteproc/qcom_q6v5_pas.c
+++ b/drivers/remoteproc/qcom_q6v5_pas.c
@@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -117,8 +118,8 @@ struct qcom_pas {
struct qcom_rproc_ssr ssr_subdev;
struct qcom_sysmon *sysmon;
- struct qcom_scm_pas_metadata pas_metadata;
- struct qcom_scm_pas_metadata dtb_pas_metadata;
+ struct qcom_scm_pas_context *pas_ctx;
+ struct qcom_scm_pas_context *dtb_pas_ctx;
};
static void qcom_pas_segment_dump(struct rproc *rproc,
@@ -211,9 +212,9 @@ static int qcom_pas_unprepare(struct rproc *rproc)
* auth_and_reset() was successful, but in other cases clean it up
* here.
*/
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
return 0;
}
@@ -239,15 +240,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return ret;
}
- ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
- pas->dtb_pas_id, pas->dtb_mem_phys,
- &pas->dtb_pas_metadata);
- if (ret)
- goto release_dtb_firmware;
-
- ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name,
- pas->dtb_mem_region, pas->dtb_mem_phys,
- pas->dtb_mem_size, &pas->dtb_mem_reloc);
+ ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware,
+ pas->dtb_firmware_name, pas->dtb_mem_region,
+ &pas->dtb_mem_reloc);
if (ret)
goto release_dtb_metadata;
}
@@ -255,14 +250,28 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw)
return 0;
release_dtb_metadata:
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
-
-release_dtb_firmware:
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
release_firmware(pas->dtb_firmware);
return ret;
}
+static void qcom_pas_unmap_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
+{
+ if (rproc->has_iommu)
+ iommu_unmap(rproc->domain, mem_phys, size);
+}
+
+static int qcom_pas_map_carveout(struct rproc *rproc, phys_addr_t mem_phys, size_t size)
+{
+ int ret = 0;
+
+ if (rproc->has_iommu)
+ ret = iommu_map(rproc->domain, mem_phys, mem_phys, size,
+ IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
+ return ret;
+}
+
static int qcom_pas_start(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -297,54 +306,62 @@ static int qcom_pas_start(struct rproc *rproc)
}
if (pas->dtb_pas_id) {
- ret = qcom_scm_pas_auth_and_reset(pas->dtb_pas_id);
+ ret = qcom_pas_map_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
+ if (ret)
+ goto disable_px_supply;
+
+ ret = qcom_scm_pas_prepare_and_auth_reset(pas->dtb_pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate dtb image and release reset\n");
- goto disable_px_supply;
+ goto unmap_dtb_carveout;
}
}
- ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id,
- pas->mem_phys, &pas->pas_metadata);
- if (ret)
- goto disable_px_supply;
-
- ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware,
- pas->mem_region, pas->mem_phys, pas->mem_size,
- &pas->mem_reloc);
+ ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware,
+ pas->mem_region, &pas->mem_reloc);
if (ret)
goto release_pas_metadata;
qcom_pil_info_store(pas->info_name, pas->mem_phys, pas->mem_size);
- ret = qcom_scm_pas_auth_and_reset(pas->pas_id);
+ ret = qcom_pas_map_carveout(rproc, pas->mem_phys, pas->mem_size);
+ if (ret)
+ goto release_pas_metadata;
+
+ ret = qcom_scm_pas_prepare_and_auth_reset(pas->pas_ctx);
if (ret) {
dev_err(pas->dev,
"failed to authenticate image and release reset\n");
- goto release_pas_metadata;
+ goto unmap_carveout;
}
ret = qcom_q6v5_wait_for_start(&pas->q6v5, msecs_to_jiffies(5000));
if (ret == -ETIMEDOUT) {
dev_err(pas->dev, "start timed out\n");
qcom_scm_pas_shutdown(pas->pas_id);
- goto release_pas_metadata;
+ goto unmap_carveout;
}
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
/* firmware is used to pass reference from qcom_pas_start(), drop it now */
pas->firmware = NULL;
return 0;
+unmap_carveout:
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
release_pas_metadata:
- qcom_scm_pas_metadata_release(&pas->pas_metadata);
+ qcom_scm_pas_metadata_release(pas->pas_ctx);
+ if (pas->dtb_pas_id)
+ qcom_scm_pas_metadata_release(pas->dtb_pas_ctx);
+
+unmap_dtb_carveout:
if (pas->dtb_pas_id)
- qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata);
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
disable_px_supply:
if (pas->px_supply)
regulator_disable(pas->px_supply);
@@ -400,8 +417,12 @@ static int qcom_pas_stop(struct rproc *rproc)
ret = qcom_scm_pas_shutdown(pas->dtb_pas_id);
if (ret)
dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret);
+
+ qcom_pas_unmap_carveout(rproc, pas->dtb_mem_phys, pas->dtb_mem_size);
}
+ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size);
+
handover = qcom_q6v5_unprepare(&pas->q6v5);
if (handover)
qcom_pas_handover(&pas->q6v5);
@@ -427,6 +448,61 @@ static void *qcom_pas_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is
return pas->mem_region + offset;
}
+static int qcom_pas_parse_firmware(struct rproc *rproc, const struct firmware *fw)
+{
+ struct qcom_pas *pas = rproc->priv;
+ struct resource_table *table = NULL;
+ size_t output_rt_size;
+ void *output_rt;
+ 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 (!rproc->has_iommu)
+ return 0;
+
+ ret = rproc_elf_load_rsc_table(rproc, fw);
+ if (ret)
+ dev_dbg(&rproc->dev, "Failed to load resource table from firmware\n");
+
+ table = rproc->table_ptr;
+ table_sz = rproc->table_sz;
+
+ /*
+ * The resources consumed by Qualcomm remote processors fall into two categories:
+ * static (such as the memory carveouts for the rproc firmware) and dynamic (like
+ * shared memory pools). Both are managed by a Qualcomm hypervisor (such as QHEE
+ * or Gunyah), if one is present. Otherwise, a resource table must be retrieved
+ * via an SCM call. That table will list all dynamic resources (if any) and possibly
+ * the static ones. The static resources may also come from a resource table embedded
+ * in the rproc firmware instead.
+ *
+ * Here, we call rproc_elf_load_rsc_table() to check firmware binary has resources
+ * or not and if it is not having then we pass NULL and zero as input resource
+ * table pointer and size respectively to the argument of qcom_scm_pas_get_rsc_table()
+ * and this is even true for Qualcomm remote processor who does follow remoteproc
+ * framework.
+ */
+ output_rt = qcom_scm_pas_get_rsc_table(pas->pas_ctx, table, table_sz, &output_rt_size);
+ ret = IS_ERR(output_rt) ? PTR_ERR(output_rt) : 0;
+ if (ret) {
+ dev_err(pas->dev, "Error in getting resource table: %d\n", ret);
+ return ret;
+ }
+
+ kfree(rproc->cached_table);
+ rproc->cached_table = output_rt;
+ rproc->table_ptr = rproc->cached_table;
+ rproc->table_sz = output_rt_size;
+
+ return ret;
+}
+
static unsigned long qcom_pas_panic(struct rproc *rproc)
{
struct qcom_pas *pas = rproc->priv;
@@ -439,7 +515,7 @@ static const struct rproc_ops qcom_pas_ops = {
.start = qcom_pas_start,
.stop = qcom_pas_stop,
.da_to_va = qcom_pas_da_to_va,
- .parse_fw = qcom_register_dump_segments,
+ .parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
};
@@ -449,7 +525,7 @@ static const struct rproc_ops qcom_pas_minidump_ops = {
.start = qcom_pas_start,
.stop = qcom_pas_stop,
.da_to_va = qcom_pas_da_to_va,
- .parse_fw = qcom_register_dump_segments,
+ .parse_fw = qcom_pas_parse_firmware,
.load = qcom_pas_load,
.panic = qcom_pas_panic,
.coredump = qcom_pas_minidump,
@@ -697,6 +773,7 @@ static int qcom_pas_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus");
rproc->auto_boot = desc->auto_boot;
rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);
@@ -760,6 +837,24 @@ static int qcom_pas_probe(struct platform_device *pdev)
}
qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name);
+
+ pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id,
+ pas->mem_phys, pas->mem_size);
+ if (IS_ERR(pas->pas_ctx)) {
+ ret = PTR_ERR(pas->pas_ctx);
+ goto remove_ssr_sysmon;
+ }
+
+ pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id,
+ pas->dtb_mem_phys,
+ pas->dtb_mem_size);
+ if (IS_ERR(pas->dtb_pas_ctx)) {
+ ret = PTR_ERR(pas->dtb_pas_ctx);
+ goto remove_ssr_sysmon;
+ }
+
+ pas->pas_ctx->use_tzmem = rproc->has_iommu;
+ pas->dtb_pas_ctx->use_tzmem = rproc->has_iommu;
ret = rproc_add(rproc);
if (ret)
goto remove_ssr_sysmon;