A remoteproc might be already running during boot, e.g. because it was already started by the boot firmware. This is the case for example on X1E, where the boot firmware starts a "lite" ADSP firmware that supports charging and USB-CC detection, but is missing audio functionality. This firmware uses the same interfaces as the full firmware and can be reused in case the device-specific firmware is missing (e.g. in generic distro installers). The running remoteproc is currently not modelled at all - it is just killed through qcom_pas_shutdown() without even using the SMP2P stop signal beforehand. If the firmware is present the "lite" firmware is now stopped more gracefully with the SMP2P stop signal. diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 672af7336..3cf24f202 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -236,10 +237,13 @@ /* Store firmware handle to be used in qcom_pas_start() */ pas->firmware = fw; - if (pas->lite_pas_id) - qcom_scm_pas_shutdown(pas->lite_pas_id); - if (pas->lite_dtb_pas_id) - qcom_scm_pas_shutdown(pas->lite_dtb_pas_id); + /* + * We don't support loading the "lite" firmware, so we don't need to + * keep trying to shut it down. If it was running, it should have + * already been stopped by adsp_stop(). + */ + pas->lite_pas_id = 0; + pas->lite_dtb_pas_id = 0; if (pas->dtb_pas_id) { ret = request_firmware(&pas->dtb_firmware, pas->dtb_firmware_name, pas->dev); @@ -401,6 +405,28 @@ qcom_pas_pds_disable(pas, pas->proxy_pds, pas->proxy_pd_count); } +static int qcom_q6v5_pas_shutdown(int pas_id, int lite_pas_id) +{ + int ret, lite_ret = -ENODEV; + + /* + * We don't know if the boot firmware started the "full" or "lite" + * firmware, so we don't know if we need to shutdown the lite_pas_id or + * the normal pas_id. Unfortunately, the return codes of the SCM calls + * are also not helpful to figure that out. Since shutting down a + * stopped remoteproc is a no-op, we just shutdown both and if one of + * the calls succeeds, we assume it's okay. + */ + if (lite_pas_id) + lite_ret = qcom_scm_pas_shutdown(lite_pas_id); + + ret = qcom_scm_pas_shutdown(pas_id); + if (ret && lite_ret) + return ret; + + return 0; +} + static int qcom_pas_stop(struct rproc *rproc) { struct qcom_pas *pas = rproc->priv; @@ -411,7 +437,7 @@ if (ret == -ETIMEDOUT) dev_err(pas->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(pas->pas_id); + ret = qcom_q6v5_pas_shutdown(pas->pas_id, pas->lite_pas_id); if (ret && pas->decrypt_shutdown) ret = qcom_pas_shutdown_poll_decrypt(pas); @@ -419,7 +445,7 @@ dev_err(pas->dev, "failed to shutdown: %d\n", ret); if (pas->dtb_pas_id) { - ret = qcom_scm_pas_shutdown(pas->dtb_pas_id); + ret = qcom_q6v5_pas_shutdown(pas->dtb_pas_id, pas->lite_dtb_pas_id); if (ret) dev_err(pas->dev, "failed to shutdown dtb: %d\n", ret); @@ -428,9 +454,11 @@ qcom_pas_unmap_carveout(rproc, pas->mem_phys, pas->mem_size); - handover = qcom_q6v5_unprepare(&pas->q6v5); - if (handover) - qcom_pas_handover(&pas->q6v5); + if (rproc->state != RPROC_DETACHED) { + handover = qcom_q6v5_unprepare(&pas->q6v5); + if (handover) + qcom_pas_handover(&pas->q6v5); + } if (pas->smem_host_id) ret = qcom_smem_bust_hwspin_lock_by_host(pas->smem_host_id); @@ -859,7 +890,7 @@ rproc->has_iommu = of_property_present(pdev->dev.of_node, "iommus"); if (desc->auto_boot) - rproc->auto_boot = RPROC_AUTO_BOOT_ATTACH_OR_START; + rproc->auto_boot = RPROC_AUTO_BOOT_RESTART_IF_FW_AVAILABLE; else rproc->auto_boot = RPROC_AUTO_BOOT_DISABLED; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE);