remoteproc: qcom_q6v5_pas: add support for dtb co-firmware loading
authorNeil Armstrong <neil.armstrong@linaro.org>
Wed, 18 Jan 2023 16:22:41 +0000 (17:22 +0100)
committerBjorn Andersson <andersson@kernel.org>
Wed, 18 Jan 2023 17:41:05 +0000 (11:41 -0600)
Starting from the SM8550 SoC, starting the aDSP, cDSP and MPSS will
require loading a separate "Devicetree" firmware.

In order to satisfy the load & authentication order required by the SM8550
SoC, the following is implemented:
- "Devicetree" firmware request & load in dedicated memory
- Q6V5 prepare
- Power Domain & Clocks enable
- "Devicetree" firmware authentication
- Main firmware load in dedicated memory
- Main firmware authentication
- Q6V5 startup
- "Devicetree" firmware metadata release
- Main metadata release

When booting older platforms, the "Devicetree" steps would be
bypassed and the load & authentication order would still be valid.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
Link: https://lore.kernel.org/r/20221114-narmstrong-sm8550-upstream-remoteproc-v4-3-54154c08c0b7@linaro.org
drivers/remoteproc/qcom_q6v5_pas.c

index f95e022..cdd8a1a 100644 (file)
@@ -35,7 +35,9 @@
 struct adsp_data {
        int crash_reason_smem;
        const char *firmware_name;
+       const char *dtb_firmware_name;
        int pas_id;
+       int dtb_pas_id;
        unsigned int minidump_id;
        bool auto_boot;
        bool decrypt_shutdown;
@@ -64,19 +66,28 @@ struct qcom_adsp {
 
        int proxy_pd_count;
 
+       const char *dtb_firmware_name;
        int pas_id;
+       int dtb_pas_id;
        unsigned int minidump_id;
        int crash_reason_smem;
        bool decrypt_shutdown;
        const char *info_name;
 
+       const struct firmware *firmware;
+       const struct firmware *dtb_firmware;
+
        struct completion start_done;
        struct completion stop_done;
 
        phys_addr_t mem_phys;
+       phys_addr_t dtb_mem_phys;
        phys_addr_t mem_reloc;
+       phys_addr_t dtb_mem_reloc;
        void *mem_region;
+       void *dtb_mem_region;
        size_t mem_size;
+       size_t dtb_mem_size;
 
        struct qcom_rproc_glink glink_subdev;
        struct qcom_rproc_subdev smd_subdev;
@@ -84,6 +95,7 @@ struct qcom_adsp {
        struct qcom_sysmon *sysmon;
 
        struct qcom_scm_pas_metadata pas_metadata;
+       struct qcom_scm_pas_metadata dtb_pas_metadata;
 };
 
 static void adsp_minidump(struct rproc *rproc)
@@ -158,6 +170,8 @@ static int adsp_unprepare(struct rproc *rproc)
         * here.
         */
        qcom_scm_pas_metadata_release(&adsp->pas_metadata);
+       if (adsp->dtb_pas_id)
+               qcom_scm_pas_metadata_release(&adsp->dtb_pas_metadata);
 
        return 0;
 }
@@ -167,20 +181,40 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw)
        struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
        int ret;
 
-       ret = qcom_mdt_pas_init(adsp->dev, fw, rproc->firmware, adsp->pas_id,
-                               adsp->mem_phys, &adsp->pas_metadata);
-       if (ret)
-               return ret;
+       /* Store firmware handle to be used in adsp_start() */
+       adsp->firmware = fw;
 
-       ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, adsp->pas_id,
-                                   adsp->mem_region, adsp->mem_phys, adsp->mem_size,
-                                   &adsp->mem_reloc);
-       if (ret)
-               return ret;
+       if (adsp->dtb_pas_id) {
+               ret = request_firmware(&adsp->dtb_firmware, adsp->dtb_firmware_name, adsp->dev);
+               if (ret) {
+                       dev_err(adsp->dev, "request_firmware failed for %s: %d\n",
+                               adsp->dtb_firmware_name, ret);
+                       return ret;
+               }
 
-       qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size);
+               ret = qcom_mdt_pas_init(adsp->dev, adsp->dtb_firmware, adsp->dtb_firmware_name,
+                                       adsp->dtb_pas_id, adsp->dtb_mem_phys,
+                                       &adsp->dtb_pas_metadata);
+               if (ret)
+                       goto release_dtb_firmware;
+
+               ret = qcom_mdt_load_no_init(adsp->dev, adsp->dtb_firmware, adsp->dtb_firmware_name,
+                                           adsp->dtb_pas_id, adsp->dtb_mem_region,
+                                           adsp->dtb_mem_phys, adsp->dtb_mem_size,
+                                           &adsp->dtb_mem_reloc);
+               if (ret)
+                       goto release_dtb_metadata;
+       }
 
        return 0;
+
+release_dtb_metadata:
+       qcom_scm_pas_metadata_release(&adsp->dtb_pas_metadata);
+
+release_dtb_firmware:
+       release_firmware(adsp->dtb_firmware);
+
+       return ret;
 }
 
 static int adsp_start(struct rproc *rproc)
@@ -216,24 +250,55 @@ static int adsp_start(struct rproc *rproc)
                        goto disable_cx_supply;
        }
 
+       if (adsp->dtb_pas_id) {
+               ret = qcom_scm_pas_auth_and_reset(adsp->dtb_pas_id);
+               if (ret) {
+                       dev_err(adsp->dev,
+                               "failed to authenticate dtb image and release reset\n");
+                       goto disable_px_supply;
+               }
+       }
+
+       ret = qcom_mdt_pas_init(adsp->dev, adsp->firmware, rproc->firmware, adsp->pas_id,
+                               adsp->mem_phys, &adsp->pas_metadata);
+       if (ret)
+               goto disable_px_supply;
+
+       ret = qcom_mdt_load_no_init(adsp->dev, adsp->firmware, rproc->firmware, adsp->pas_id,
+                                   adsp->mem_region, adsp->mem_phys, adsp->mem_size,
+                                   &adsp->mem_reloc);
+       if (ret)
+               goto release_pas_metadata;
+
+       qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size);
+
        ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
        if (ret) {
                dev_err(adsp->dev,
                        "failed to authenticate image and release reset\n");
-               goto disable_px_supply;
+               goto release_pas_metadata;
        }
 
        ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000));
        if (ret == -ETIMEDOUT) {
                dev_err(adsp->dev, "start timed out\n");
                qcom_scm_pas_shutdown(adsp->pas_id);
-               goto disable_px_supply;
+               goto release_pas_metadata;
        }
 
        qcom_scm_pas_metadata_release(&adsp->pas_metadata);
+       if (adsp->dtb_pas_id)
+               qcom_scm_pas_metadata_release(&adsp->dtb_pas_metadata);
+
+       /* Remove pointer to the loaded firmware, only valid in adsp_load() & adsp_start() */
+       adsp->firmware = NULL;
 
        return 0;
 
+release_pas_metadata:
+       qcom_scm_pas_metadata_release(&adsp->pas_metadata);
+       if (adsp->dtb_pas_id)
+               qcom_scm_pas_metadata_release(&adsp->dtb_pas_metadata);
 disable_px_supply:
        if (adsp->px_supply)
                regulator_disable(adsp->px_supply);
@@ -249,6 +314,9 @@ disable_proxy_pds:
 disable_irqs:
        qcom_q6v5_unprepare(&adsp->q6v5);
 
+       /* Remove pointer to the loaded firmware, only valid in adsp_load() & adsp_start() */
+       adsp->firmware = NULL;
+
        return ret;
 }
 
@@ -282,6 +350,12 @@ static int adsp_stop(struct rproc *rproc)
        if (ret)
                dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
 
+       if (adsp->dtb_pas_id) {
+               ret = qcom_scm_pas_shutdown(adsp->dtb_pas_id);
+               if (ret)
+                       dev_err(adsp->dev, "failed to shutdown dtb: %d\n", ret);
+       }
+
        handover = qcom_q6v5_unprepare(&adsp->q6v5);
        if (handover)
                qcom_pas_handover(&adsp->q6v5);
@@ -458,6 +532,28 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
                return -EBUSY;
        }
 
+       if (!adsp->dtb_pas_id)
+               return 0;
+
+       node = of_parse_phandle(adsp->dev->of_node, "memory-region", 1);
+       if (!node) {
+               dev_err(adsp->dev, "no dtb memory-region specified\n");
+               return -EINVAL;
+       }
+
+       ret = of_address_to_resource(node, 0, &r);
+       if (ret)
+               return ret;
+
+       adsp->dtb_mem_phys = adsp->dtb_mem_reloc = r.start;
+       adsp->dtb_mem_size = resource_size(&r);
+       adsp->dtb_mem_region = devm_ioremap_wc(adsp->dev, adsp->dtb_mem_phys, adsp->dtb_mem_size);
+       if (!adsp->dtb_mem_region) {
+               dev_err(adsp->dev, "unable to map dtb memory region: %pa+%zx\n",
+                       &r.start, adsp->dtb_mem_size);
+               return -EBUSY;
+       }
+
        return 0;
 }
 
@@ -466,7 +562,7 @@ static int adsp_probe(struct platform_device *pdev)
        const struct adsp_data *desc;
        struct qcom_adsp *adsp;
        struct rproc *rproc;
-       const char *fw_name;
+       const char *fw_name, *dtb_fw_name = NULL;
        const struct rproc_ops *ops = &adsp_ops;
        int ret;
 
@@ -483,6 +579,14 @@ static int adsp_probe(struct platform_device *pdev)
        if (ret < 0 && ret != -EINVAL)
                return ret;
 
+       if (desc->dtb_firmware_name) {
+               dtb_fw_name = desc->dtb_firmware_name;
+               ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", 1,
+                                                   &dtb_fw_name);
+               if (ret < 0 && ret != -EINVAL)
+                       return ret;
+       }
+
        if (desc->minidump_id)
                ops = &adsp_minidump_ops;
 
@@ -503,6 +607,10 @@ static int adsp_probe(struct platform_device *pdev)
        adsp->pas_id = desc->pas_id;
        adsp->info_name = desc->sysmon_name;
        adsp->decrypt_shutdown = desc->decrypt_shutdown;
+       if (dtb_fw_name) {
+               adsp->dtb_firmware_name = dtb_fw_name;
+               adsp->dtb_pas_id = desc->dtb_pas_id;
+       }
        platform_set_drvdata(pdev, adsp);
 
        ret = device_init_wakeup(adsp->dev, true);