diff options
| -rw-r--r-- | Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt | 5 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt | 23 | ||||
| -rw-r--r-- | Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | 79 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_adsp.c | 6 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_mss.c | 209 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_q6v5_pas.c | 13 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_sysmon.c | 82 | ||||
| -rw-r--r-- | drivers/remoteproc/qcom_wcnss.c | 6 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 160 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_debugfs.c | 47 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_internal.h | 12 | ||||
| -rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 61 | ||||
| -rw-r--r-- | drivers/remoteproc/st_remoteproc.c | 91 | ||||
| -rw-r--r-- | drivers/rpmsg/virtio_rpmsg_bus.c | 24 | ||||
| -rw-r--r-- | include/linux/remoteproc.h | 8 |
15 files changed, 706 insertions, 120 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt index a842a782b557..66af2c30944f 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp-pil.txt @@ -35,7 +35,7 @@ on the Qualcomm Technology Inc. ADSP Hexagon core. Value type: <stringlist> Definition: List of clock input name strings sorted in the same order as the clocks property. Definition must have - "xo", "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", + "xo", "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep" and "qdsp6ss_core". @@ -100,13 +100,12 @@ ADSP, as it is found on SDM845 boards. clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GCC_LPASS_SWAY_CLK>, - <&lpasscc LPASS_AUDIO_WRAPPER_AON_CLK>, <&lpasscc LPASS_Q6SS_AHBS_AON_CLK>, <&lpasscc LPASS_Q6SS_AHBM_AON_CLK>, <&lpasscc LPASS_QDSP6SS_XO_CLK>, <&lpasscc LPASS_QDSP6SS_SLEEP_CLK>, <&lpasscc LPASS_QDSP6SS_CORE_CLK>; - clock-names = "xo", "sway_cbcr", "lpass_aon", + clock-names = "xo", "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core"; diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt index 9c0cff3a5ed8..292dfda9770d 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt @@ -19,13 +19,30 @@ on the Qualcomm ADSP Hexagon core. - interrupts-extended: Usage: required Value type: <prop-encoded-array> - Definition: must list the watchdog, fatal IRQs ready, handover and - stop-ack IRQs + Definition: reference to the interrupts that match interrupt-names - interrupt-names: Usage: required Value type: <stringlist> - Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" + Definition: The interrupts needed depends on the compatible + string: + qcom,msm8974-adsp-pil: + qcom,msm8996-adsp-pil: + qcom,msm8996-slpi-pil: + qcom,qcs404-adsp-pas: + qcom,qcs404-cdsp-pas: + qcom,sdm845-adsp-pas: + qcom,sdm845-cdsp-pas: + must be "wdog", "fatal", "ready", "handover", "stop-ack" + qcom,qcs404-wcss-pas: + must be "wdog", "fatal", "ready", "handover", "stop-ack", + "shutdown-ack" + +- firmware-name: + Usage: optional + Value type: <string> + Definition: must list the relative firmware image path for the + Hexagon Core. - clocks: Usage: required diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 9ff5b0309417..41ca5df5be5a 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -28,24 +28,51 @@ on the Qualcomm Hexagon core. - interrupts-extended: Usage: required Value type: <prop-encoded-array> - Definition: must list the watchdog, fatal IRQs ready, handover and - stop-ack IRQs + Definition: reference to the interrupts that match interrupt-names - interrupt-names: Usage: required Value type: <stringlist> - Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" + Definition: The interrupts needed depends on the the compatible + string: + qcom,q6v5-pil: + qcom,ipq8074-wcss-pil: + qcom,msm8916-mss-pil: + qcom,msm8974-mss-pil: + must be "wdog", "fatal", "ready", "handover", "stop-ack" + qcom,msm8996-mss-pil: + qcom,sdm845-mss-pil: + must be "wdog", "fatal", "ready", "handover", "stop-ack", + "shutdown-ack" + +- firmware-name: + Usage: optional + Value type: <stringlist> + Definition: must list the relative firmware image paths for mba and + modem. They are used for booting and authenticating the + Hexagon core. - clocks: Usage: required Value type: <phandle> - Definition: reference to the iface, bus and mem clocks to be held on - behalf of the booting of the Hexagon core + Definition: reference to the clocks that match clock-names - clock-names: Usage: required Value type: <stringlist> - Definition: must be "iface", "bus", "mem" + Definition: The clocks needed depend on the compatible string: + qcom,ipq8074-wcss-pil: + no clock names required + qcom,q6v5-pil: + qcom,msm8916-mss-pil: + qcom,msm8974-mss-pil: + must be "iface", "bus", "mem", "xo" + qcom,msm8996-mss-pil: + must be "iface", "bus", "mem", "xo", "gpll0_mss", + "snoc_axi", "mnoc_axi", "pnoc", "qdss" + qcom,sdm845-mss-pil: + must be "iface", "bus", "mem", "xo", "gpll0_mss", + "snoc_axi", "mnoc_axi", "prng" - resets: Usage: required @@ -65,6 +92,19 @@ on the Qualcomm Hexagon core. must be "mss_restart", "pdc_reset" for the modem sub-system on SDM845 SoCs +For the compatible strings below the following supplies are required: + "qcom,q6v5-pil" + "qcom,msm8916-mss-pil", +- cx-supply: +- mx-supply: +- pll-supply: + Usage: required + Value type: <phandle> + Definition: reference to the regulators to be held on behalf of the + booting of the Hexagon core + +For the compatible string below the following supplies are required: + "qcom,msm8974-mss-pil" - cx-supply: - mss-supply: - mx-supply: @@ -74,6 +114,33 @@ on the Qualcomm Hexagon core. Definition: reference to the regulators to be held on behalf of the booting of the Hexagon core +For the compatible string below the following supplies are required: + "qcom,msm8996-mss-pil" +- pll-supply: + Usage: required + Value type: <phandle> + Definition: reference to the regulators to be held on behalf of the + booting of the Hexagon core + +- power-domains: + Usage: required + Value type: <phandle> + Definition: reference to power-domains that match power-domain-names + +- power-domain-names: + Usage: required + Value type: <stringlist> + Definition: The power-domains needed depend on the compatible string: + qcom,q6v5-pil: + qcom,ipq8074-wcss-pil: + qcom,msm8916-mss-pil: + qcom,msm8974-mss-pil: + no power-domain names required + qcom,msm8996-mss-pil: + must be "cx", "mx" + qcom,sdm845-mss-pil: + must be "cx", "mx", "mss", "load_state" + - qcom,smem-states: Usage: required Value type: <phandle> diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 79374d1de311..1f3ef9ee493c 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -48,7 +48,7 @@ /* list of clocks required by ADSP PIL */ static const char * const adsp_clk_id[] = { - "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", + "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", }; @@ -439,6 +439,10 @@ static int adsp_probe(struct platform_device *pdev) adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(adsp->sysmon)) { + ret = PTR_ERR(adsp->sysmon); + goto disable_pm; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 01be7314e176..eacdf10fcfaf 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -25,6 +25,8 @@ #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> @@ -131,6 +133,8 @@ struct rproc_hexagon_res { char **proxy_clk_names; char **reset_clk_names; char **active_clk_names; + char **active_pd_names; + char **proxy_pd_names; int version; bool need_mem_protection; bool has_alt_reset; @@ -156,9 +160,13 @@ struct q6v5 { struct clk *active_clks[8]; struct clk *reset_clks[4]; struct clk *proxy_clks[4]; + struct device *active_pds[1]; + struct device *proxy_pds[3]; int active_clk_count; int reset_clk_count; int proxy_clk_count; + int active_pd_count; + int proxy_pd_count; struct reg_info active_regs[1]; struct reg_info proxy_regs[3]; @@ -188,6 +196,7 @@ struct q6v5 { bool has_alt_reset; int mpss_perm; int mba_perm; + const char *hexagon_mdt_image; int version; }; @@ -321,6 +330,41 @@ static void q6v5_clk_disable(struct device *dev, clk_disable_unprepare(clks[i]); } +static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int ret; + int i; + + for (i = 0; i < pd_count; i++) { + dev_pm_genpd_set_performance_state(pds[i], INT_MAX); + ret = pm_runtime_get_sync(pds[i]); + if (ret < 0) + goto unroll_pd_votes; + } + + return 0; + +unroll_pd_votes: + for (i--; i >= 0; i--) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } + + return ret; +}; + +static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int i; + + for (i = 0; i < pd_count; i++) { + dev_pm_genpd_set_performance_state(pds[i], 0); + pm_runtime_put(pds[i]); + } +} + static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, bool remote_owner, phys_addr_t addr, size_t size) @@ -690,11 +734,23 @@ static int q6v5_mba_load(struct q6v5 *qproc) qcom_q6v5_prepare(&qproc->q6v5); + ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable active power domains\n"); + goto disable_irqs; + } + + ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable proxy power domains\n"); + goto disable_active_pds; + } + ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); if (ret) { dev_err(qproc->dev, "failed to enable proxy supplies\n"); - goto disable_irqs; + goto disable_proxy_pds; } ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, @@ -791,6 +847,10 @@ disable_proxy_clk: disable_proxy_reg: q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); +disable_proxy_pds: + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +disable_active_pds: + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); disable_irqs: qcom_q6v5_unprepare(&qproc->q6v5); @@ -830,6 +890,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) qproc->active_clk_count); q6v5_regulator_disable(qproc, qproc->active_regs, qproc->active_reg_count); + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); /* In case of failure or coredump scenario where reclaiming MBA memory * could not happen reclaim it here. @@ -841,6 +902,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) ret = qcom_q6v5_unprepare(&qproc->q6v5); if (ret) { + q6v5_pds_disable(qproc, qproc->proxy_pds, + qproc->proxy_pd_count); q6v5_clk_disable(qproc->dev, qproc->proxy_clks, qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, @@ -860,17 +923,26 @@ static int q6v5_mpss_load(struct q6v5 *qproc) phys_addr_t min_addr = PHYS_ADDR_MAX; phys_addr_t max_addr = 0; bool relocate = false; - char seg_name[10]; + char *fw_name; + size_t fw_name_len; ssize_t offset; size_t size = 0; void *ptr; int ret; int i; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); + fw_name_len = strlen(qproc->hexagon_mdt_image); + if (fw_name_len <= 4) + return -EINVAL; + + fw_name = kstrdup(qproc->hexagon_mdt_image, GFP_KERNEL); + if (!fw_name) + return -ENOMEM; + + ret = request_firmware(&fw, fw_name, qproc->dev); if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); - return ret; + dev_err(qproc->dev, "unable to load %s\n", fw_name); + goto out; } /* Initialize the RMB validator */ @@ -918,10 +990,11 @@ static int q6v5_mpss_load(struct q6v5 *qproc) ptr = qproc->mpss_region + offset; if (phdr->p_filesz) { - snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i); - ret = request_firmware(&seg_fw, seg_name, qproc->dev); + /* Replace "xxx.xxx" with "xxx.bxx" */ + sprintf(fw_name + fw_name_len - 3, "b%02d", i); + ret = request_firmware(&seg_fw, fw_name, qproc->dev); if (ret) { - dev_err(qproc->dev, "failed to load %s\n", seg_name); + dev_err(qproc->dev, "failed to load %s\n", fw_name); goto release_firmware; } @@ -960,6 +1033,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc) release_firmware: release_firmware(fw); +out: + kfree(fw_name); return ret < 0 ? ret : 0; } @@ -1075,9 +1150,10 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, unsigned long i; int ret; - ret = request_firmware(&fw, "modem.mdt", qproc->dev); + ret = request_firmware(&fw, qproc->hexagon_mdt_image, qproc->dev); if (ret < 0) { - dev_err(qproc->dev, "unable to load modem.mdt\n"); + dev_err(qproc->dev, "unable to load %s\n", + qproc->hexagon_mdt_image); return ret; } @@ -1121,6 +1197,7 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5) qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); + q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); } static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) @@ -1181,6 +1258,45 @@ static int q6v5_init_clocks(struct device *dev, struct clk **clks, return i; } +static int q6v5_pds_attach(struct device *dev, struct device **devs, + char **pd_names) +{ + size_t num_pds = 0; + int ret; + int i; + + if (!pd_names) + return 0; + + while (pd_names[num_pds]) + num_pds++; + + for (i = 0; i < num_pds; i++) { + devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]); + if (IS_ERR(devs[i])) { + ret = PTR_ERR(devs[i]); + goto unroll_attach; + } + } + + return num_pds; + +unroll_attach: + for (i--; i >= 0; i--) + dev_pm_domain_detach(devs[i], false); + + return ret; +}; + +static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds, + size_t pd_count) +{ + int i; + + for (i = 0; i < pd_count; i++) + dev_pm_domain_detach(pds[i], false); +} + static int q6v5_init_reset(struct q6v5 *qproc) { qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev, @@ -1253,6 +1369,7 @@ static int q6v5_probe(struct platform_device *pdev) const struct rproc_hexagon_res *desc; struct q6v5 *qproc; struct rproc *rproc; + const char *mba_image; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -1262,16 +1379,30 @@ static int q6v5_probe(struct platform_device *pdev) if (desc->need_mem_protection && !qcom_scm_is_available()) return -EPROBE_DEFER; + mba_image = desc->hexagon_mba_image; + ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", + 0, &mba_image); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, - desc->hexagon_mba_image, sizeof(*qproc)); + mba_image, sizeof(*qproc)); if (!rproc) { dev_err(&pdev->dev, "failed to allocate rproc\n"); return -ENOMEM; } + rproc->auto_boot = false; + qproc = (struct q6v5 *)rproc->priv; qproc->dev = &pdev->dev; qproc->rproc = rproc; + qproc->hexagon_mdt_image = "modem.mdt"; + ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", + 1, &qproc->hexagon_mdt_image); + if (ret < 0 && ret != -EINVAL) + return ret; + platform_set_drvdata(pdev, qproc); ret = q6v5_init_mem(qproc, pdev); @@ -1322,10 +1453,26 @@ static int q6v5_probe(struct platform_device *pdev) } qproc->active_reg_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds, + desc->active_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to attach active power domains\n"); + goto free_rproc; + } + qproc->active_pd_count = ret; + + ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, + desc->proxy_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init power domains\n"); + goto detach_active_pds; + } + qproc->proxy_pd_count = ret; + qproc->has_alt_reset = desc->has_alt_reset; ret = q6v5_init_reset(qproc); if (ret) - goto free_rproc; + goto detach_proxy_pds; qproc->version = desc->version; qproc->need_mem_protection = desc->need_mem_protection; @@ -1333,7 +1480,7 @@ static int q6v5_probe(struct platform_device *pdev) ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, qcom_msa_handover); if (ret) - goto free_rproc; + goto detach_proxy_pds; qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); @@ -1341,13 +1488,21 @@ static int q6v5_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); + if (IS_ERR(qproc->sysmon)) { + ret = PTR_ERR(qproc->sysmon); + goto detach_proxy_pds; + } ret = rproc_add(rproc); if (ret) - goto free_rproc; + goto detach_proxy_pds; return 0; +detach_proxy_pds: + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +detach_active_pds: + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); free_rproc: rproc_free(rproc); @@ -1364,6 +1519,10 @@ static int q6v5_remove(struct platform_device *pdev) qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); + + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); + q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + rproc_free(qproc->rproc); return 0; @@ -1388,6 +1547,16 @@ static const struct rproc_hexagon_res sdm845_mss = { "mnoc_axi", NULL }, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + "mx", + "mss", + NULL + }, .need_mem_protection = true, .has_alt_reset = true, .version = MSS_SDM845, @@ -1395,16 +1564,26 @@ static const struct rproc_hexagon_res sdm845_mss = { static const struct rproc_hexagon_res msm8996_mss = { .hexagon_mba_image = "mba.mbn", + .proxy_supply = (struct qcom_mss_reg_res[]) { + { + .supply = "pll", + .uA = 100000, + }, + {} + }, .proxy_clk_names = (char*[]){ "xo", "pnoc", + "qdss", NULL }, .active_clk_names = (char*[]){ "iface", "bus", "mem", - "gpll0_mss_clk", + "gpll0_mss", + "snoc_axi", + "mnoc_axi", NULL }, .need_mem_protection = true, diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index b1e63fcd5fdf..f280f196d007 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -258,6 +258,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; int ret; desc = of_device_get_match_data(&pdev->dev); @@ -267,8 +268,14 @@ static int adsp_probe(struct platform_device *pdev) if (!qcom_scm_is_available()) return -EPROBE_DEFER; + fw_name = desc->firmware_name; + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &fw_name); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, - desc->firmware_name, sizeof(*adsp)); + fw_name, sizeof(*adsp)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; @@ -304,6 +311,10 @@ static int adsp_probe(struct platform_device *pdev) adsp->sysmon = qcom_add_sysmon_subdev(rproc, desc->sysmon_name, desc->ssctl_id); + if (IS_ERR(adsp->sysmon)) { + ret = PTR_ERR(adsp->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index e976a602b015..c231314eab66 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -6,8 +6,9 @@ #include <linux/module.h> #include <linux/notifier.h> #include <linux/slab.h> +#include <linux/interrupt.h> #include <linux/io.h> -#include <linux/notifier.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/remoteproc/qcom_rproc.h> @@ -25,6 +26,7 @@ struct qcom_sysmon { const char *name; + int shutdown_irq; int ssctl_version; int ssctl_instance; @@ -34,6 +36,8 @@ struct qcom_sysmon { struct rpmsg_endpoint *ept; struct completion comp; + struct completion ind_comp; + struct completion shutdown_comp; struct mutex lock; bool ssr_ack; @@ -137,6 +141,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, } #define SSCTL_SHUTDOWN_REQ 0x21 +#define SSCTL_SHUTDOWN_READY_IND 0x21 #define SSCTL_SUBSYS_EVENT_REQ 0x23 #define SSCTL_MAX_MSG_LEN 7 @@ -252,6 +257,29 @@ static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { {} }; +static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { + {} +}; + +static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *data) +{ + struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); + + complete(&sysmon->ind_comp); +} + +static struct qmi_msg_handler qmi_indication_handler[] = { + { + .type = QMI_INDICATION, + .msg_id = SSCTL_SHUTDOWN_READY_IND, + .ei = ssctl_shutdown_ind_ei, + .decoded_size = 0, + .fn = sysmon_ind_cb + }, + {} +}; + /** * ssctl_request_shutdown() - request shutdown via SSCTL QMI service * @sysmon: sysmon context @@ -262,6 +290,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) struct qmi_txn txn; int ret; + reinit_completion(&sysmon->ind_comp); + reinit_completion(&sysmon->shutdown_comp); ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); if (ret < 0) { dev_err(sysmon->dev, "failed to allocate QMI txn\n"); @@ -283,6 +313,17 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) dev_err(sysmon->dev, "shutdown request failed\n"); else dev_dbg(sysmon->dev, "shutdown request completed\n"); + + if (sysmon->shutdown_irq > 0) { + ret = wait_for_completion_timeout(&sysmon->shutdown_comp, + 10 * HZ); + if (!ret) { + ret = try_wait_for_completion(&sysmon->ind_comp); + if (!ret) + dev_err(sysmon->dev, + "timeout waiting for shutdown ack\n"); + } + } } /** @@ -432,6 +473,15 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; } +static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) +{ + struct qcom_sysmon *sysmon = data; + + complete(&sysmon->shutdown_comp); + + return IRQ_HANDLED; +} + /** * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc * @rproc: rproc context to associate the subdev with @@ -449,7 +499,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); if (!sysmon) - return NULL; + return ERR_PTR(-ENOMEM); sysmon->dev = rproc->dev.parent; sysmon->rproc = rproc; @@ -458,13 +508,37 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, sysmon->ssctl_instance = ssctl_instance; init_completion(&sysmon->comp); + init_completion(&sysmon->ind_comp); + init_completion(&sysmon->shutdown_comp); mutex_init(&sysmon->lock); - ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); + sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, + "shutdown-ack"); + if (sysmon->shutdown_irq < 0) { + if (sysmon->shutdown_irq != -ENODATA) { + dev_err(sysmon->dev, + "failed to retrieve shutdown-ack IRQ\n"); + return ERR_PTR(sysmon->shutdown_irq); + } + } else { + ret = devm_request_threaded_irq(sysmon->dev, + sysmon->shutdown_irq, + NULL, sysmon_shutdown_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "q6v5 shutdown-ack", sysmon); + if (ret) { + dev_err(sysmon->dev, + "failed to acquire shutdown-ack IRQ\n"); + return ERR_PTR(ret); + } + } + + ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, + qmi_indication_handler); if (ret < 0) { dev_err(sysmon->dev, "failed to initialize qmi handle\n"); kfree(sysmon); - return NULL; + return ERR_PTR(ret); } qmi_add_lookup(&sysmon->qmi, 43, 0, 0); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index b0e07e9f42d5..adcce523971e 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -553,6 +553,10 @@ static int wcnss_probe(struct platform_device *pdev) qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); + if (IS_ERR(wcnss->sysmon)) { + ret = PTR_ERR(wcnss->sysmon); + goto free_rproc; + } ret = rproc_add(rproc); if (ret) @@ -622,5 +626,5 @@ static void __exit wcnss_exit(void) } module_exit(wcnss_exit); -MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem"); +MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Wireless Subsystem"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 54ec38fc5dca..48feebd6d0a2 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -39,12 +39,16 @@ #include <linux/idr.h> #include <linux/elf.h> #include <linux/crc32.h> +#include <linux/of_reserved_mem.h> #include <linux/virtio_ids.h> #include <linux/virtio_ring.h> #include <asm/byteorder.h> +#include <linux/platform_device.h> #include "remoteproc_internal.h" +#define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL + static DEFINE_MUTEX(rproc_list_mutex); static LIST_HEAD(rproc_list); @@ -145,7 +149,7 @@ static void rproc_disable_iommu(struct rproc *rproc) iommu_domain_free(domain); } -static phys_addr_t rproc_va_to_pa(void *cpu_addr) +phys_addr_t rproc_va_to_pa(void *cpu_addr) { /* * Return physical address according to virtual address location @@ -160,6 +164,7 @@ static phys_addr_t rproc_va_to_pa(void *cpu_addr) WARN_ON(!virt_addr_valid(cpu_addr)); return virt_to_phys(cpu_addr); } +EXPORT_SYMBOL(rproc_va_to_pa); /** * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address @@ -204,6 +209,10 @@ void *rproc_da_to_va(struct rproc *rproc, u64 da, int len) list_for_each_entry(carveout, &rproc->carveouts, node) { int offset = da - carveout->da; + /* Verify that carveout is allocated */ + if (!carveout->va) + continue; + /* try next carveout if da is too small */ if (offset < 0) continue; @@ -272,25 +281,27 @@ rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...) * @len: associated area size * * This function is a helper function to verify requested device area (couple - * da, len) is part of specified carevout. + * da, len) is part of specified carveout. + * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is + * checked. * - * Return: 0 if carveout match request else -ENOMEM + * Return: 0 if carveout matches request else error */ -int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem, - u32 da, u32 len) +static int rproc_check_carveout_da(struct rproc *rproc, + struct rproc_mem_entry *mem, u32 da, u32 len) { struct device *dev = &rproc->dev; - int delta = 0; + int delta; /* Check requested resource length */ if (len > mem->len) { dev_err(dev, "Registered carveout doesn't fit len request\n"); - return -ENOMEM; + return -EINVAL; } if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { - /* Update existing carveout da */ - mem->da = da; + /* Address doesn't match registered carveout configuration */ + return -EINVAL; } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { delta = da - mem->da; @@ -298,13 +309,13 @@ int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem, if (delta < 0) { dev_err(dev, "Registered carveout doesn't fit da request\n"); - return -ENOMEM; + return -EINVAL; } if (delta + len > mem->len) { dev_err(dev, "Registered carveout doesn't fit len request\n"); - return -ENOMEM; + return -EINVAL; } } @@ -418,8 +429,25 @@ static int rproc_vdev_do_start(struct rproc_subdev *subdev) static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) { struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); + int ret; - rproc_remove_virtio_dev(rvdev); + ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev); + if (ret) + dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret); +} + +/** + * rproc_rvdev_release() - release the existence of a rvdev + * + * @dev: the subdevice's dev + */ +static void rproc_rvdev_release(struct device *dev) +{ + struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); + + of_reserved_m |
