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. | |||
| 35 | Value type: <stringlist> | 35 | Value type: <stringlist> |
| 36 | Definition: List of clock input name strings sorted in the same | 36 | Definition: List of clock input name strings sorted in the same |
| 37 | order as the clocks property. Definition must have | 37 | order as the clocks property. Definition must have |
| 38 | "xo", "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", | 38 | "xo", "sway_cbcr", "lpass_ahbs_aon_cbcr", |
| 39 | "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep" | 39 | "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep" |
| 40 | and "qdsp6ss_core". | 40 | and "qdsp6ss_core". |
| 41 | 41 | ||
| @@ -100,13 +100,12 @@ ADSP, as it is found on SDM845 boards. | |||
| 100 | 100 | ||
| 101 | clocks = <&rpmhcc RPMH_CXO_CLK>, | 101 | clocks = <&rpmhcc RPMH_CXO_CLK>, |
| 102 | <&gcc GCC_LPASS_SWAY_CLK>, | 102 | <&gcc GCC_LPASS_SWAY_CLK>, |
| 103 | <&lpasscc LPASS_AUDIO_WRAPPER_AON_CLK>, | ||
| 104 | <&lpasscc LPASS_Q6SS_AHBS_AON_CLK>, | 103 | <&lpasscc LPASS_Q6SS_AHBS_AON_CLK>, |
| 105 | <&lpasscc LPASS_Q6SS_AHBM_AON_CLK>, | 104 | <&lpasscc LPASS_Q6SS_AHBM_AON_CLK>, |
| 106 | <&lpasscc LPASS_QDSP6SS_XO_CLK>, | 105 | <&lpasscc LPASS_QDSP6SS_XO_CLK>, |
| 107 | <&lpasscc LPASS_QDSP6SS_SLEEP_CLK>, | 106 | <&lpasscc LPASS_QDSP6SS_SLEEP_CLK>, |
| 108 | <&lpasscc LPASS_QDSP6SS_CORE_CLK>; | 107 | <&lpasscc LPASS_QDSP6SS_CORE_CLK>; |
| 109 | clock-names = "xo", "sway_cbcr", "lpass_aon", | 108 | clock-names = "xo", "sway_cbcr", |
| 110 | "lpass_ahbs_aon_cbcr", | 109 | "lpass_ahbs_aon_cbcr", |
| 111 | "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", | 110 | "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", |
| 112 | "qdsp6ss_sleep", "qdsp6ss_core"; | 111 | "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. | |||
| 19 | - interrupts-extended: | 19 | - interrupts-extended: |
| 20 | Usage: required | 20 | Usage: required |
| 21 | Value type: <prop-encoded-array> | 21 | Value type: <prop-encoded-array> |
| 22 | Definition: must list the watchdog, fatal IRQs ready, handover and | 22 | Definition: reference to the interrupts that match interrupt-names |
| 23 | stop-ack IRQs | ||
| 24 | 23 | ||
| 25 | - interrupt-names: | 24 | - interrupt-names: |
| 26 | Usage: required | 25 | Usage: required |
| 27 | Value type: <stringlist> | 26 | Value type: <stringlist> |
| 28 | Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" | 27 | Definition: The interrupts needed depends on the compatible |
| 28 | string: | ||
| 29 | qcom,msm8974-adsp-pil: | ||
| 30 | qcom,msm8996-adsp-pil: | ||
| 31 | qcom,msm8996-slpi-pil: | ||
| 32 | qcom,qcs404-adsp-pas: | ||
| 33 | qcom,qcs404-cdsp-pas: | ||
| 34 | qcom,sdm845-adsp-pas: | ||
| 35 | qcom,sdm845-cdsp-pas: | ||
| 36 | must be "wdog", "fatal", "ready", "handover", "stop-ack" | ||
| 37 | qcom,qcs404-wcss-pas: | ||
| 38 | must be "wdog", "fatal", "ready", "handover", "stop-ack", | ||
| 39 | "shutdown-ack" | ||
| 40 | |||
| 41 | - firmware-name: | ||
| 42 | Usage: optional | ||
| 43 | Value type: <string> | ||
| 44 | Definition: must list the relative firmware image path for the | ||
| 45 | Hexagon Core. | ||
| 29 | 46 | ||
| 30 | - clocks: | 47 | - clocks: |
| 31 | Usage: required | 48 | 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. | |||
| 28 | - interrupts-extended: | 28 | - interrupts-extended: |
| 29 | Usage: required | 29 | Usage: required |
| 30 | Value type: <prop-encoded-array> | 30 | Value type: <prop-encoded-array> |
| 31 | Definition: must list the watchdog, fatal IRQs ready, handover and | 31 | Definition: reference to the interrupts that match interrupt-names |
| 32 | stop-ack IRQs | ||
| 33 | 32 | ||
| 34 | - interrupt-names: | 33 | - interrupt-names: |
| 35 | Usage: required | 34 | Usage: required |
| 36 | Value type: <stringlist> | 35 | Value type: <stringlist> |
| 37 | Definition: must be "wdog", "fatal", "ready", "handover", "stop-ack" | 36 | Definition: The interrupts needed depends on the the compatible |
| 37 | string: | ||
| 38 | qcom,q6v5-pil: | ||
| 39 | qcom,ipq8074-wcss-pil: | ||
| 40 | qcom,msm8916-mss-pil: | ||
| 41 | qcom,msm8974-mss-pil: | ||
| 42 | must be "wdog", "fatal", "ready", "handover", "stop-ack" | ||
| 43 | qcom,msm8996-mss-pil: | ||
| 44 | qcom,sdm845-mss-pil: | ||
| 45 | must be "wdog", "fatal", "ready", "handover", "stop-ack", | ||
| 46 | "shutdown-ack" | ||
| 47 | |||
| 48 | - firmware-name: | ||
| 49 | Usage: optional | ||
| 50 | Value type: <stringlist> | ||
| 51 | Definition: must list the relative firmware image paths for mba and | ||
| 52 | modem. They are used for booting and authenticating the | ||
| 53 | Hexagon core. | ||
| 38 | 54 | ||
| 39 | - clocks: | 55 | - clocks: |
| 40 | Usage: required | 56 | Usage: required |
| 41 | Value type: <phandle> | 57 | Value type: <phandle> |
| 42 | Definition: reference to the iface, bus and mem clocks to be held on | 58 | Definition: reference to the clocks that match clock-names |
| 43 | behalf of the booting of the Hexagon core | ||
| 44 | 59 | ||
| 45 | - clock-names: | 60 | - clock-names: |
| 46 | Usage: required | 61 | Usage: required |
| 47 | Value type: <stringlist> | 62 | Value type: <stringlist> |
| 48 | Definition: must be "iface", "bus", "mem" | 63 | Definition: The clocks needed depend on the compatible string: |
| 64 | qcom,ipq8074-wcss-pil: | ||
| 65 | no clock names required | ||
| 66 | qcom,q6v5-pil: | ||
| 67 | qcom,msm8916-mss-pil: | ||
| 68 | qcom,msm8974-mss-pil: | ||
| 69 | must be "iface", "bus", "mem", "xo" | ||
| 70 | qcom,msm8996-mss-pil: | ||
| 71 | must be "iface", "bus", "mem", "xo", "gpll0_mss", | ||
| 72 | "snoc_axi", "mnoc_axi", "pnoc", "qdss" | ||
| 73 | qcom,sdm845-mss-pil: | ||
| 74 | must be "iface", "bus", "mem", "xo", "gpll0_mss", | ||
| 75 | "snoc_axi", "mnoc_axi", "prng" | ||
| 49 | 76 | ||
| 50 | - resets: | 77 | - resets: |
| 51 | Usage: required | 78 | Usage: required |
| @@ -65,6 +92,19 @@ on the Qualcomm Hexagon core. | |||
| 65 | must be "mss_restart", "pdc_reset" for the modem | 92 | must be "mss_restart", "pdc_reset" for the modem |
| 66 | sub-system on SDM845 SoCs | 93 | sub-system on SDM845 SoCs |
| 67 | 94 | ||
| 95 | For the compatible strings below the following supplies are required: | ||
| 96 | "qcom,q6v5-pil" | ||
| 97 | "qcom,msm8916-mss-pil", | ||
| 98 | - cx-supply: | ||
| 99 | - mx-supply: | ||
| 100 | - pll-supply: | ||
| 101 | Usage: required | ||
| 102 | Value type: <phandle> | ||
| 103 | Definition: reference to the regulators to be held on behalf of the | ||
| 104 | booting of the Hexagon core | ||
| 105 | |||
| 106 | For the compatible string below the following supplies are required: | ||
| 107 | "qcom,msm8974-mss-pil" | ||
| 68 | - cx-supply: | 108 | - cx-supply: |
| 69 | - mss-supply: | 109 | - mss-supply: |
| 70 | - mx-supply: | 110 | - mx-supply: |
| @@ -74,6 +114,33 @@ on the Qualcomm Hexagon core. | |||
| 74 | Definition: reference to the regulators to be held on behalf of the | 114 | Definition: reference to the regulators to be held on behalf of the |
| 75 | booting of the Hexagon core | 115 | booting of the Hexagon core |
| 76 | 116 | ||
| 117 | For the compatible string below the following supplies are required: | ||
| 118 | "qcom,msm8996-mss-pil" | ||
| 119 | - pll-supply: | ||
| 120 | Usage: required | ||
| 121 | Value type: <phandle> | ||
| 122 | Definition: reference to the regulators to be held on behalf of the | ||
| 123 | booting of the Hexagon core | ||
| 124 | |||
| 125 | - power-domains: | ||
| 126 | Usage: required | ||
| 127 | Value type: <phandle> | ||
| 128 | Definition: reference to power-domains that match power-domain-names | ||
| 129 | |||
| 130 | - power-domain-names: | ||
| 131 | Usage: required | ||
| 132 | Value type: <stringlist> | ||
| 133 | Definition: The power-domains needed depend on the compatible string: | ||
| 134 | qcom,q6v5-pil: | ||
| 135 | qcom,ipq8074-wcss-pil: | ||
| 136 | qcom,msm8916-mss-pil: | ||
| 137 | qcom,msm8974-mss-pil: | ||
| 138 | no power-domain names required | ||
| 139 | qcom,msm8996-mss-pil: | ||
| 140 | must be "cx", "mx" | ||
| 141 | qcom,sdm845-mss-pil: | ||
| 142 | must be "cx", "mx", "mss", "load_state" | ||
| 143 | |||
| 77 | - qcom,smem-states: | 144 | - qcom,smem-states: |
| 78 | Usage: required | 145 | Usage: required |
| 79 | Value type: <phandle> | 146 | 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 @@ | |||
| 48 | 48 | ||
| 49 | /* list of clocks required by ADSP PIL */ | 49 | /* list of clocks required by ADSP PIL */ |
| 50 | static const char * const adsp_clk_id[] = { | 50 | static const char * const adsp_clk_id[] = { |
| 51 | "sway_cbcr", "lpass_aon", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", | 51 | "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", |
| 52 | "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", | 52 | "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| @@ -439,6 +439,10 @@ static int adsp_probe(struct platform_device *pdev) | |||
| 439 | adsp->sysmon = qcom_add_sysmon_subdev(rproc, | 439 | adsp->sysmon = qcom_add_sysmon_subdev(rproc, |
| 440 | desc->sysmon_name, | 440 | desc->sysmon_name, |
| 441 | desc->ssctl_id); | 441 | desc->ssctl_id); |
| 442 | if (IS_ERR(adsp->sysmon)) { | ||
| 443 | ret = PTR_ERR(adsp->sysmon); | ||
| 444 | goto disable_pm; | ||
| 445 | } | ||
| 442 | 446 | ||
| 443 | ret = rproc_add(rproc); | 447 | ret = rproc_add(rproc); |
| 444 | if (ret) | 448 | 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 @@ | |||
| 25 | #include <linux/of_address.h> | 25 | #include <linux/of_address.h> |
| 26 | #include <linux/of_device.h> | 26 | #include <linux/of_device.h> |
| 27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
| 28 | #include <linux/pm_domain.h> | ||
| 29 | #include <linux/pm_runtime.h> | ||
| 28 | #include <linux/regmap.h> | 30 | #include <linux/regmap.h> |
| 29 | #include <linux/regulator/consumer.h> | 31 | #include <linux/regulator/consumer.h> |
| 30 | #include <linux/remoteproc.h> | 32 | #include <linux/remoteproc.h> |
| @@ -131,6 +133,8 @@ struct rproc_hexagon_res { | |||
| 131 | char **proxy_clk_names; | 133 | char **proxy_clk_names; |
| 132 | char **reset_clk_names; | 134 | char **reset_clk_names; |
| 133 | char **active_clk_names; | 135 | char **active_clk_names; |
| 136 | char **active_pd_names; | ||
| 137 | char **proxy_pd_names; | ||
| 134 | int version; | 138 | int version; |
| 135 | bool need_mem_protection; | 139 | bool need_mem_protection; |
| 136 | bool has_alt_reset; | 140 | bool has_alt_reset; |
| @@ -156,9 +160,13 @@ struct q6v5 { | |||
| 156 | struct clk *active_clks[8]; | 160 | struct clk *active_clks[8]; |
| 157 | struct clk *reset_clks[4]; | 161 | struct clk *reset_clks[4]; |
| 158 | struct clk *proxy_clks[4]; | 162 | struct clk *proxy_clks[4]; |
| 163 | struct device *active_pds[1]; | ||
| 164 | struct device *proxy_pds[3]; | ||
| 159 | int active_clk_count; | 165 | int active_clk_count; |
| 160 | int reset_clk_count; | 166 | int reset_clk_count; |
| 161 | int proxy_clk_count; | 167 | int proxy_clk_count; |
| 168 | int active_pd_count; | ||
| 169 | int proxy_pd_count; | ||
| 162 | 170 | ||
| 163 | struct reg_info active_regs[1]; | 171 | struct reg_info active_regs[1]; |
| 164 | struct reg_info proxy_regs[3]; | 172 | struct reg_info proxy_regs[3]; |
| @@ -188,6 +196,7 @@ struct q6v5 { | |||
| 188 | bool has_alt_reset; | 196 | bool has_alt_reset; |
| 189 | int mpss_perm; | 197 | int mpss_perm; |
| 190 | int mba_perm; | 198 | int mba_perm; |
| 199 | const char *hexagon_mdt_image; | ||
| 191 | int version; | 200 | int version; |
| 192 | }; | 201 | }; |
| 193 | 202 | ||
| @@ -321,6 +330,41 @@ static void q6v5_clk_disable(struct device *dev, | |||
| 321 | clk_disable_unprepare(clks[i]); | 330 | clk_disable_unprepare(clks[i]); |
| 322 | } | 331 | } |
| 323 | 332 | ||
| 333 | static int q6v5_pds_enable(struct q6v5 *qproc, struct device **pds, | ||
| 334 | size_t pd_count) | ||
| 335 | { | ||
| 336 | int ret; | ||
| 337 | int i; | ||
| 338 | |||
| 339 | for (i = 0; i < pd_count; i++) { | ||
| 340 | dev_pm_genpd_set_performance_state(pds[i], INT_MAX); | ||
| 341 | ret = pm_runtime_get_sync(pds[i]); | ||
| 342 | if (ret < 0) | ||
| 343 | goto unroll_pd_votes; | ||
| 344 | } | ||
| 345 | |||
| 346 | return 0; | ||
| 347 | |||
| 348 | unroll_pd_votes: | ||
| 349 | for (i--; i >= 0; i--) { | ||
| 350 | dev_pm_genpd_set_performance_state(pds[i], 0); | ||
| 351 | pm_runtime_put(pds[i]); | ||
| 352 | } | ||
| 353 | |||
| 354 | return ret; | ||
| 355 | }; | ||
| 356 | |||
| 357 | static void q6v5_pds_disable(struct q6v5 *qproc, struct device **pds, | ||
| 358 | size_t pd_count) | ||
| 359 | { | ||
| 360 | int i; | ||
| 361 | |||
| 362 | for (i = 0; i < pd_count; i++) { | ||
| 363 | dev_pm_genpd_set_performance_state(pds[i], 0); | ||
| 364 | pm_runtime_put(pds[i]); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 324 | static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, | 368 | static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, |
| 325 | bool remote_owner, phys_addr_t addr, | 369 | bool remote_owner, phys_addr_t addr, |
| 326 | size_t size) | 370 | size_t size) |
| @@ -690,11 +734,23 @@ static int q6v5_mba_load(struct q6v5 *qproc) | |||
| 690 | 734 | ||
| 691 | qcom_q6v5_prepare(&qproc->q6v5); | 735 | qcom_q6v5_prepare(&qproc->q6v5); |
| 692 | 736 | ||
| 737 | ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count); | ||
| 738 | if (ret < 0) { | ||
| 739 | dev_err(qproc->dev, "failed to enable active power domains\n"); | ||
| 740 | goto disable_irqs; | ||
| 741 | } | ||
| 742 | |||
| 743 | ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); | ||
| 744 | if (ret < 0) { | ||
| 745 | dev_err(qproc->dev, "failed to enable proxy power domains\n"); | ||
| 746 | goto disable_active_pds; | ||
| 747 | } | ||
| 748 | |||
| 693 | ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, | 749 | ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, |
| 694 | qproc->proxy_reg_count); | 750 | qproc->proxy_reg_count); |
| 695 | if (ret) { | 751 | if (ret) { |
| 696 | dev_err(qproc->dev, "failed to enable proxy supplies\n"); | 752 | dev_err(qproc->dev, "failed to enable proxy supplies\n"); |
| 697 | goto disable_irqs; | 753 | goto disable_proxy_pds; |
| 698 | } | 754 | } |
| 699 | 755 | ||
| 700 | ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, | 756 | ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, |
| @@ -791,6 +847,10 @@ disable_proxy_clk: | |||
| 791 | disable_proxy_reg: | 847 | disable_proxy_reg: |
| 792 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | 848 | q6v5_regulator_disable(qproc, qproc->proxy_regs, |
| 793 | qproc->proxy_reg_count); | 849 | qproc->proxy_reg_count); |
| 850 | disable_proxy_pds: | ||
| 851 | q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); | ||
| 852 | disable_active_pds: | ||
| 853 | q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); | ||
| 794 | disable_irqs: | 854 | disable_irqs: |
| 795 | qcom_q6v5_unprepare(&qproc->q6v5); | 855 | qcom_q6v5_unprepare(&qproc->q6v5); |
| 796 | 856 | ||
| @@ -830,6 +890,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) | |||
| 830 | qproc->active_clk_count); | 890 | qproc->active_clk_count); |
| 831 | q6v5_regulator_disable(qproc, qproc->active_regs, | 891 | q6v5_regulator_disable(qproc, qproc->active_regs, |
| 832 | qproc->active_reg_count); | 892 | qproc->active_reg_count); |
| 893 | q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); | ||
| 833 | 894 | ||
| 834 | /* In case of failure or coredump scenario where reclaiming MBA memory | 895 | /* In case of failure or coredump scenario where reclaiming MBA memory |
| 835 | * could not happen reclaim it here. | 896 | * could not happen reclaim it here. |
| @@ -841,6 +902,8 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) | |||
| 841 | 902 | ||
| 842 | ret = qcom_q6v5_unprepare(&qproc->q6v5); | 903 | ret = qcom_q6v5_unprepare(&qproc->q6v5); |
| 843 | if (ret) { | 904 | if (ret) { |
| 905 | q6v5_pds_disable(qproc, qproc->proxy_pds, | ||
| 906 | qproc->proxy_pd_count); | ||
| 844 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, | 907 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, |
| 845 | qproc->proxy_clk_count); | 908 | qproc->proxy_clk_count); |
| 846 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | 909 | q6v5_regulator_disable(qproc, qproc->proxy_regs, |
| @@ -860,17 +923,26 @@ static int q6v5_mpss_load(struct q6v5 *qproc) | |||
| 860 | phys_addr_t min_addr = PHYS_ADDR_MAX; | 923 | phys_addr_t min_addr = PHYS_ADDR_MAX; |
| 861 | phys_addr_t max_addr = 0; | 924 | phys_addr_t max_addr = 0; |
| 862 | bool relocate = false; | 925 | bool relocate = false; |
| 863 | char seg_name[10]; | 926 | char *fw_name; |
| 927 | size_t fw_name_len; | ||
| 864 | ssize_t offset; | 928 | ssize_t offset; |
| 865 | size_t size = 0; | 929 | size_t size = 0; |
| 866 | void *ptr; | 930 | void *ptr; |
| 867 | int ret; | 931 | int ret; |
| 868 | int i; | 932 | int i; |
| 869 | 933 | ||
| 870 | ret = request_firmware(&fw, "modem.mdt", qproc->dev); | 934 | fw_name_len = strlen(qproc->hexagon_mdt_image); |
| 935 | if (fw_name_len <= 4) | ||
| 936 | return -EINVAL; | ||
| 937 | |||
| 938 | fw_name = kstrdup(qproc->hexagon_mdt_image, GFP_KERNEL); | ||
| 939 | if (!fw_name) | ||
| 940 | return -ENOMEM; | ||
| 941 | |||
| 942 | ret = request_firmware(&fw, fw_name, qproc->dev); | ||
| 871 | if (ret < 0) { | 943 | if (ret < 0) { |
| 872 | dev_err(qproc->dev, "unable to load modem.mdt\n"); | 944 | dev_err(qproc->dev, "unable to load %s\n", fw_name); |
| 873 | return ret; | 945 | goto out; |
| 874 | } | 946 | } |
| 875 | 947 | ||
| 876 | /* Initialize the RMB validator */ | 948 | /* Initialize the RMB validator */ |
| @@ -918,10 +990,11 @@ static int q6v5_mpss_load(struct q6v5 *qproc) | |||
| 918 | ptr = qproc->mpss_region + offset; | 990 | ptr = qproc->mpss_region + offset; |
| 919 | 991 | ||
| 920 | if (phdr->p_filesz) { | 992 | if (phdr->p_filesz) { |
| 921 | snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i); | 993 | /* Replace "xxx.xxx" with "xxx.bxx" */ |
| 922 | ret = request_firmware(&seg_fw, seg_name, qproc->dev); | 994 | sprintf(fw_name + fw_name_len - 3, "b%02d", i); |
| 995 | ret = request_firmware(&seg_fw, fw_name, qproc->dev); | ||
| 923 | if (ret) { | 996 | if (ret) { |
| 924 | dev_err(qproc->dev, "failed to load %s\n", seg_name); | 997 | dev_err(qproc->dev, "failed to load %s\n", fw_name); |
| 925 | goto release_firmware; | 998 | goto release_firmware; |
| 926 | } | 999 | } |
| 927 | 1000 | ||
| @@ -960,6 +1033,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc) | |||
| 960 | 1033 | ||
| 961 | release_firmware: | 1034 | release_firmware: |
| 962 | release_firmware(fw); | 1035 | release_firmware(fw); |
| 1036 | out: | ||
| 1037 | kfree(fw_name); | ||
| 963 | 1038 | ||
| 964 | return ret < 0 ? ret : 0; | 1039 | return ret < 0 ? ret : 0; |
| 965 | } | 1040 | } |
| @@ -1075,9 +1150,10 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, | |||
| 1075 | unsigned long i; | 1150 | unsigned long i; |
| 1076 | int ret; | 1151 | int ret; |
| 1077 | 1152 | ||
| 1078 | ret = request_firmware(&fw, "modem.mdt", qproc->dev); | 1153 | ret = request_firmware(&fw, qproc->hexagon_mdt_image, qproc->dev); |
| 1079 | if (ret < 0) { | 1154 | if (ret < 0) { |
| 1080 | dev_err(qproc->dev, "unable to load modem.mdt\n"); | 1155 | dev_err(qproc->dev, "unable to load %s\n", |
| 1156 | qproc->hexagon_mdt_image); | ||
| 1081 | return ret; | 1157 | return ret; |
| 1082 | } | 1158 | } |
| 1083 | 1159 | ||
| @@ -1121,6 +1197,7 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5) | |||
| 1121 | qproc->proxy_clk_count); | 1197 | qproc->proxy_clk_count); |
| 1122 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | 1198 | q6v5_regulator_disable(qproc, qproc->proxy_regs, |
| 1123 | qproc->proxy_reg_count); | 1199 | qproc->proxy_reg_count); |
| 1200 | q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); | ||
| 1124 | } | 1201 | } |
| 1125 | 1202 | ||
| 1126 | static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) | 1203 | 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, | |||
| 1181 | return i; | 1258 | return i; |
| 1182 | } | 1259 | } |
| 1183 | 1260 | ||
| 1261 | static int q6v5_pds_attach(struct device *dev, struct device **devs, | ||
| 1262 | char **pd_names) | ||
| 1263 | { | ||
| 1264 | size_t num_pds = 0; | ||
| 1265 | int ret; | ||
| 1266 | int i; | ||
| 1267 | |||
| 1268 | if (!pd_names) | ||
| 1269 | return 0; | ||
| 1270 | |||
| 1271 | while (pd_names[num_pds]) | ||
| 1272 | num_pds++; | ||
| 1273 | |||
| 1274 | for (i = 0; i < num_pds; i++) { | ||
| 1275 | devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]); | ||
| 1276 | if (IS_ERR(devs[i])) { | ||
| 1277 | ret = PTR_ERR(devs[i]); | ||
| 1278 | goto unroll_attach; | ||
| 1279 | } | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | return num_pds; | ||
| 1283 | |||
| 1284 | unroll_attach: | ||
| 1285 | for (i--; i >= 0; i--) | ||
| 1286 | dev_pm_domain_detach(devs[i], false); | ||
| 1287 | |||
| 1288 | return ret; | ||
| 1289 | }; | ||
| 1290 | |||
| 1291 | static void q6v5_pds_detach(struct q6v5 *qproc, struct device **pds, | ||
| 1292 | size_t pd_count) | ||
| 1293 | { | ||
| 1294 | int i; | ||
| 1295 | |||
| 1296 | for (i = 0; i < pd_count; i++) | ||
| 1297 | dev_pm_domain_detach(pds[i], false); | ||
| 1298 | } | ||
| 1299 | |||
| 1184 | static int q6v5_init_reset(struct q6v5 *qproc) | 1300 | static int q6v5_init_reset(struct q6v5 *qproc) |
| 1185 | { | 1301 | { |
| 1186 | qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev, | 1302 | qproc->mss_restart = devm_reset_control_get_exclusive(qproc->dev, |
| @@ -1253,6 +1369,7 @@ static int q6v5_probe(struct platform_device *pdev) | |||
| 1253 | const struct rproc_hexagon_res *desc; | 1369 | const struct rproc_hexagon_res *desc; |
| 1254 | struct q6v5 *qproc; | 1370 | struct q6v5 *qproc; |
| 1255 | struct rproc *rproc; | 1371 | struct rproc *rproc; |
| 1372 | const char *mba_image; | ||
| 1256 | int ret; | 1373 | int ret; |
| 1257 | 1374 | ||
| 1258 | desc = of_device_get_match_data(&pdev->dev); | 1375 | desc = of_device_get_match_data(&pdev->dev); |
| @@ -1262,16 +1379,30 @@ static int q6v5_probe(struct platform_device *pdev) | |||
| 1262 | if (desc->need_mem_protection && !qcom_scm_is_available()) | 1379 | if (desc->need_mem_protection && !qcom_scm_is_available()) |
| 1263 | return -EPROBE_DEFER; | 1380 | return -EPROBE_DEFER; |
| 1264 | 1381 | ||
| 1382 | mba_image = desc->hexagon_mba_image; | ||
| 1383 | ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", | ||
| 1384 | 0, &mba_image); | ||
| 1385 | if (ret < 0 && ret != -EINVAL) | ||
| 1386 | return ret; | ||
| 1387 | |||
| 1265 | rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, | 1388 | rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, |
| 1266 | desc->hexagon_mba_image, sizeof(*qproc)); | 1389 | mba_image, sizeof(*qproc)); |
| 1267 | if (!rproc) { | 1390 | if (!rproc) { |
| 1268 | dev_err(&pdev->dev, "failed to allocate rproc\n"); | 1391 | dev_err(&pdev->dev, "failed to allocate rproc\n"); |
| 1269 | return -ENOMEM; | 1392 | return -ENOMEM; |
| 1270 | } | 1393 | } |
| 1271 | 1394 | ||
| 1395 | rproc->auto_boot = false; | ||
| 1396 | |||
| 1272 | qproc = (struct q6v5 *)rproc->priv; | 1397 | qproc = (struct q6v5 *)rproc->priv; |
| 1273 | qproc->dev = &pdev->dev; | 1398 | qproc->dev = &pdev->dev; |
| 1274 | qproc->rproc = rproc; | 1399 | qproc->rproc = rproc; |
| 1400 | qproc->hexagon_mdt_image = "modem.mdt"; | ||
| 1401 | ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", | ||
| 1402 | 1, &qproc->hexagon_mdt_image); | ||
| 1403 | if (ret < 0 && ret != -EINVAL) | ||
| 1404 | return ret; | ||
| 1405 | |||
| 1275 | platform_set_drvdata(pdev, qproc); | 1406 | platform_set_drvdata(pdev, qproc); |
| 1276 | 1407 | ||
| 1277 | ret = q6v5_init_mem(qproc, pdev); | 1408 | ret = q6v5_init_mem(qproc, pdev); |
| @@ -1322,10 +1453,26 @@ static int q6v5_probe(struct platform_device *pdev) | |||
| 1322 | } | 1453 | } |
| 1323 | qproc->active_reg_count = ret; | 1454 | qproc->active_reg_count = ret; |
| 1324 | 1455 | ||
| 1456 | ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds, | ||
| 1457 | desc->active_pd_names); | ||
| 1458 | if (ret < 0) { | ||
| 1459 | dev_err(&pdev->dev, "Failed to attach active power domains\n"); | ||
| 1460 | goto free_rproc; | ||
| 1461 | } | ||
| 1462 | qproc->active_pd_count = ret; | ||
| 1463 | |||
| 1464 | ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, | ||
| 1465 | desc->proxy_pd_names); | ||
| 1466 | if (ret < 0) { | ||
| 1467 | dev_err(&pdev->dev, "Failed to init power domains\n"); | ||
| 1468 | goto detach_active_pds; | ||
| 1469 | } | ||
| 1470 | qproc->proxy_pd_count = ret; | ||
| 1471 | |||
| 1325 | qproc->has_alt_reset = desc->has_alt_reset; | 1472 | qproc->has_alt_reset = desc->has_alt_reset; |
| 1326 | ret = q6v5_init_reset(qproc); | 1473 | ret = q6v5_init_reset(qproc); |
| 1327 | if (ret) | 1474 | if (ret) |
| 1328 | goto free_rproc; | 1475 | goto detach_proxy_pds; |
| 1329 | 1476 | ||
| 1330 | qproc->version = desc->version; | 1477 | qproc->version = desc->version; |
| 1331 | qproc->need_mem_protection = desc->need_mem_protection; | 1478 | qproc->need_mem_protection = desc->need_mem_protection; |
| @@ -1333,7 +1480,7 @@ static int q6v5_probe(struct platform_device *pdev) | |||
| 1333 | ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, | 1480 | ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, |
| 1334 | qcom_msa_handover); | 1481 | qcom_msa_handover); |
| 1335 | if (ret) | 1482 | if (ret) |
| 1336 | goto free_rproc; | 1483 | goto detach_proxy_pds; |
| 1337 | 1484 | ||
| 1338 | qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); | 1485 | qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); |
| 1339 | qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); | 1486 | qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); |
| @@ -1341,13 +1488,21 @@ static int q6v5_probe(struct platform_device *pdev) | |||
| 1341 | qcom_add_smd_subdev(rproc, &qproc->smd_subdev); | 1488 | qcom_add_smd_subdev(rproc, &qproc->smd_subdev); |
| 1342 | qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); | 1489 | qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); |
| 1343 | qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); | 1490 | qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); |
| 1491 | if (IS_ERR(qproc->sysmon)) { | ||
| 1492 | ret = PTR_ERR(qproc->sysmon); | ||
| 1493 | goto detach_proxy_pds; | ||
| 1494 | } | ||
| 1344 | 1495 | ||
| 1345 | ret = rproc_add(rproc); | 1496 | ret = rproc_add(rproc); |
| 1346 | if (ret) | 1497 | if (ret) |
| 1347 | goto free_rproc; | 1498 | goto detach_proxy_pds; |
| 1348 | 1499 | ||
| 1349 | return 0; | 1500 | return 0; |
| 1350 | 1501 | ||
| 1502 | detach_proxy_pds: | ||
| 1503 | q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); | ||
| 1504 | detach_active_pds: | ||
| 1505 | q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); | ||
| 1351 | free_rproc: | 1506 | free_rproc: |
| 1352 | rproc_free(rproc); | 1507 | rproc_free(rproc); |
| 1353 | 1508 | ||
| @@ -1364,6 +1519,10 @@ static int q6v5_remove(struct platform_device *pdev) | |||
| 1364 | qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev); | 1519 | qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev); |
| 1365 | qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); | 1520 | qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); |
| 1366 | qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); | 1521 | qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); |
| 1522 | |||
| 1523 | q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); | ||
| 1524 | q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); | ||
| 1525 | |||
| 1367 | rproc_free(qproc->rproc); | 1526 | rproc_free(qproc->rproc); |
| 1368 | 1527 | ||
| 1369 | return 0; | 1528 | return 0; |
| @@ -1388,6 +1547,16 @@ static const struct rproc_hexagon_res sdm845_mss = { | |||
| 1388 | "mnoc_axi", | 1547 | "mnoc_axi", |
| 1389 | NULL | 1548 | NULL |
| 1390 | }, | 1549 | }, |
| 1550 | .active_pd_names = (char*[]){ | ||
| 1551 | "load_state", | ||
| 1552 | NULL | ||
| 1553 | }, | ||
| 1554 | .proxy_pd_names = (char*[]){ | ||
| 1555 | "cx", | ||
| 1556 | "mx", | ||
| 1557 | "mss", | ||
| 1558 | NULL | ||
| 1559 | }, | ||
| 1391 | .need_mem_protection = true, | 1560 | .need_mem_protection = true, |
| 1392 | .has_alt_reset = true, | 1561 | .has_alt_reset = true, |
| 1393 | .version = MSS_SDM845, | 1562 | .version = MSS_SDM845, |
| @@ -1395,16 +1564,26 @@ static const struct rproc_hexagon_res sdm845_mss = { | |||
| 1395 | 1564 | ||
| 1396 | static const struct rproc_hexagon_res msm8996_mss = { | 1565 | static const struct rproc_hexagon_res msm8996_mss = { |
| 1397 | .hexagon_mba_image = "mba.mbn", | 1566 | .hexagon_mba_image = "mba.mbn", |
| 1567 | .proxy_supply = (struct qcom_mss_reg_res[]) { | ||
| 1568 | { | ||
| 1569 | .supply = "pll", | ||
| 1570 | .uA = 100000, | ||
| 1571 | }, | ||
| 1572 | {} | ||
| 1573 | }, | ||
| 1398 | .proxy_clk_names = (char*[]){ | 1574 | .proxy_clk_names = (char*[]){ |
| 1399 | "xo", | 1575 | "xo", |
| 1400 | "pnoc", | 1576 | "pnoc", |
| 1577 | "qdss", | ||
| 1401 | NULL | 1578 | NULL |
| 1402 | }, | 1579 | }, |
| 1403 | .active_clk_names = (char*[]){ | 1580 | .active_clk_names = (char*[]){ |
| 1404 | "iface", | 1581 | "iface", |
| 1405 | "bus", | 1582 | "bus", |
| 1406 | "mem", | 1583 | "mem", |
| 1407 | "gpll0_mss_clk", | 1584 | "gpll0_mss", |
| 1585 | "snoc_axi", | ||
| 1586 | "mnoc_axi", | ||
| 1408 | NULL | 1587 | NULL |
| 1409 | }, | 1588 | }, |
| 1410 | .need_mem_protection = true, | 1589 | .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) | |||
| 258 | const struct adsp_data *desc; | 258 | const struct adsp_data *desc; |
| 259 | struct qcom_adsp *adsp; | 259 | struct qcom_adsp *adsp; |
| 260 | struct rproc *rproc; | 260 | struct rproc *rproc; |
| 261 | const char *fw_name; | ||
| 261 | int ret; | 262 | int ret; |
| 262 | 263 | ||
| 263 | desc = of_device_get_match_data(&pdev->dev); | 264 | desc = of_device_get_match_data(&pdev->dev); |
| @@ -267,8 +268,14 @@ static int adsp_probe(struct platform_device *pdev) | |||
| 267 | if (!qcom_scm_is_available()) | 268 | if (!qcom_scm_is_available()) |
| 268 | return -EPROBE_DEFER; | 269 | return -EPROBE_DEFER; |
| 269 | 270 | ||
| 271 | fw_name = desc->firmware_name; | ||
| 272 | ret = of_property_read_string(pdev->dev.of_node, "firmware-name", | ||
| 273 | &fw_name); | ||
| 274 | if (ret < 0 && ret != -EINVAL) | ||
| 275 | return ret; | ||
| 276 | |||
| 270 | rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, | 277 | rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, |
| 271 | desc->firmware_name, sizeof(*adsp)); | 278 | fw_name, sizeof(*adsp)); |
| 272 | if (!rproc) { | 279 | if (!rproc) { |
| 273 | dev_err(&pdev->dev, "unable to allocate remoteproc\n"); | 280 | dev_err(&pdev->dev, "unable to allocate remoteproc\n"); |
| 274 | return -ENOMEM; | 281 | return -ENOMEM; |
| @@ -304,6 +311,10 @@ static int adsp_probe(struct platform_device *pdev) | |||
| 304 | adsp->sysmon = qcom_add_sysmon_subdev(rproc, | 311 | adsp->sysmon = qcom_add_sysmon_subdev(rproc, |
| 305 | desc->sysmon_name, | 312 | desc->sysmon_name, |
| 306 | desc->ssctl_id); | 313 | desc->ssctl_id); |
| 314 | if (IS_ERR(adsp->sysmon)) { | ||
| 315 | ret = PTR_ERR(adsp->sysmon); | ||
| 316 | goto free_rproc; | ||
| 317 | } | ||
| 307 | 318 | ||
| 308 | ret = rproc_add(rproc); | 319 | ret = rproc_add(rproc); |
| 309 | if (ret) | 320 | 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 @@ | |||
| 6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
| 7 | #include <linux/notifier.h> | 7 | #include <linux/notifier.h> |
| 8 | #include <linux/slab.h> | 8 | #include <linux/slab.h> |
| 9 | #include <linux/interrupt.h> | ||
| 9 | #include <linux/io.h> | 10 | #include <linux/io.h> |
| 10 | #include <linux/notifier.h> | 11 | #include <linux/of_irq.h> |
| 11 | #include <linux/of_platform.h> | 12 | #include <linux/of_platform.h> |
| 12 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
| 13 | #include <linux/remoteproc/qcom_rproc.h> | 14 | #include <linux/remoteproc/qcom_rproc.h> |
| @@ -25,6 +26,7 @@ struct qcom_sysmon { | |||
| 25 | 26 | ||
| 26 | const char *name; | 27 | const char *name; |
| 27 | 28 | ||
| 29 | int shutdown_irq; | ||
| 28 | int ssctl_version; | 30 | int ssctl_version; |
| 29 | int ssctl_instance; | 31 | int ssctl_instance; |
| 30 | 32 | ||
| @@ -34,6 +36,8 @@ struct qcom_sysmon { | |||
| 34 | 36 | ||
| 35 | struct rpmsg_endpoint *ept; | 37 | struct rpmsg_endpoint *ept; |
| 36 | struct completion comp; | 38 | struct completion comp; |
| 39 | struct completion ind_comp; | ||
| 40 | struct completion shutdown_comp; | ||
| 37 | struct mutex lock; | 41 | struct mutex lock; |
| 38 | 42 | ||
| 39 | bool ssr_ack; | 43 | bool ssr_ack; |
| @@ -137,6 +141,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, | |||
| 137 | } | 141 | } |
| 138 | 142 | ||
| 139 | #define SSCTL_SHUTDOWN_REQ 0x21 | 143 | #define SSCTL_SHUTDOWN_REQ 0x21 |
| 144 | #define SSCTL_SHUTDOWN_READY_IND 0x21 | ||
| 140 | #define SSCTL_SUBSYS_EVENT_REQ 0x23 | 145 | #define SSCTL_SUBSYS_EVENT_REQ 0x23 |
| 141 | 146 | ||
| 142 | #define SSCTL_MAX_MSG_LEN 7 | 147 | #define SSCTL_MAX_MSG_LEN 7 |
| @@ -252,6 +257,29 @@ static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { | |||
| 252 | {} | 257 | {} |
| 253 | }; | 258 | }; |
| 254 | 259 | ||
| 260 | static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { | ||
| 261 | {} | ||
| 262 | }; | ||
| 263 | |||
| 264 | static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, | ||
| 265 | struct qmi_txn *txn, const void *data) | ||
| 266 | { | ||
| 267 | struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi); | ||
| 268 | |||
| 269 | complete(&sysmon->ind_comp); | ||
| 270 | } | ||
| 271 | |||
| 272 | static struct qmi_msg_handler qmi_indication_handler[] = { | ||
| 273 | { | ||
| 274 | .type = QMI_INDICATION, | ||
| 275 | .msg_id = SSCTL_SHUTDOWN_READY_IND, | ||
| 276 | .ei = ssctl_shutdown_ind_ei, | ||
| 277 | .decoded_size = 0, | ||
| 278 | .fn = sysmon_ind_cb | ||
| 279 | }, | ||
| 280 | {} | ||
| 281 | }; | ||
| 282 | |||
| 255 | /** | 283 | /** |
| 256 | * ssctl_request_shutdown() - request shutdown via SSCTL QMI service | 284 | * ssctl_request_shutdown() - request shutdown via SSCTL QMI service |
| 257 | * @sysmon: sysmon context | 285 | * @sysmon: sysmon context |
| @@ -262,6 +290,8 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) | |||
| 262 | struct qmi_txn txn; | 290 | struct qmi_txn txn; |
| 263 | int ret; | 291 | int ret; |
| 264 | 292 | ||
| 293 | reinit_completion(&sysmon->ind_comp); | ||
| 294 | reinit_completion(&sysmon->shutdown_comp); | ||
| 265 | ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); | 295 | ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); |
| 266 | if (ret < 0) { | 296 | if (ret < 0) { |
| 267 | dev_err(sysmon->dev, "failed to allocate QMI txn\n"); | 297 | dev_err(sysmon->dev, "failed to allocate QMI txn\n"); |
| @@ -283,6 +313,17 @@ static void ssctl_request_shutdown(struct qcom_sysmon *sysmon) | |||
| 283 | dev_err(sysmon->dev, "shutdown request failed\n"); | 313 | dev_err(sysmon->dev, "shutdown request failed\n"); |
| 284 | else | 314 | else |
| 285 | dev_dbg(sysmon->dev, "shutdown request completed\n"); | 315 | dev_dbg(sysmon->dev, "shutdown request completed\n"); |
| 316 | |||
| 317 | if (sysmon->shutdown_irq > 0) { | ||
| 318 | ret = wait_for_completion_timeout(&sysmon->shutdown_comp, | ||
| 319 | 10 * HZ); | ||
| 320 | if (!ret) { | ||
| 321 | ret = try_wait_for_completion(&sysmon->ind_comp); | ||
| 322 | if (!ret) | ||
| 323 | dev_err(sysmon->dev, | ||
| 324 | "timeout waiting for shutdown ack\n"); | ||
| 325 | } | ||
| 326 | } | ||
| 286 | } | 327 | } |
| 287 | 328 | ||
| 288 | /** | 329 | /** |
| @@ -432,6 +473,15 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, | |||
| 432 | return NOTIFY_DONE; | 473 | return NOTIFY_DONE; |
| 433 | } | 474 | } |
| 434 | 475 | ||
| 476 | static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) | ||
| 477 | { | ||
| 478 | struct qcom_sysmon *sysmon = data; | ||
| 479 | |||
| 480 | complete(&sysmon->shutdown_comp); | ||
| 481 | |||
| 482 | return IRQ_HANDLED; | ||
| 483 | } | ||
| 484 | |||
| 435 | /** | 485 | /** |
| 436 | * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc | 486 | * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc |
| 437 | * @rproc: rproc context to associate the subdev with | 487 | * @rproc: rproc context to associate the subdev with |
| @@ -449,7 +499,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, | |||
| 449 | 499 | ||
| 450 | sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); | 500 | sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL); |
| 451 | if (!sysmon) | 501 | if (!sysmon) |
| 452 | return NULL; | 502 | return ERR_PTR(-ENOMEM); |
| 453 | 503 | ||
| 454 | sysmon->dev = rproc->dev.parent; | 504 | sysmon->dev = rproc->dev.parent; |
| 455 | sysmon->rproc = rproc; | 505 | sysmon->rproc = rproc; |
| @@ -458,13 +508,37 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, | |||
| 458 | sysmon->ssctl_instance = ssctl_instance; | 508 | sysmon->ssctl_instance = ssctl_instance; |
| 459 | 509 | ||
| 460 | init_completion(&sysmon->comp); | 510 | init_completion(&sysmon->comp); |
| 511 | init_completion(&sysmon->ind_comp); | ||
| 512 | init_completion(&sysmon->shutdown_comp); | ||
| 461 | mutex_init(&sysmon->lock); | 513 | mutex_init(&sysmon->lock); |
| 462 | 514 | ||
| 463 | ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL); | 515 | sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, |
| 516 | "shutdown-ack"); | ||
| 517 | if (sysmon->shutdown_irq < 0) { | ||
| 518 | if (sysmon->shutdown_irq != -ENODATA) { | ||
| 519 | dev_err(sysmon->dev, | ||
| 520 | "failed to retrieve shutdown-ack IRQ\n"); | ||
| 521 | return ERR_PTR(sysmon->shutdown_irq); | ||
| 522 | } | ||
| 523 | } else { | ||
| 524 | ret = devm_request_threaded_irq(sysmon->dev, | ||
| 525 | sysmon->shutdown_irq, | ||
| 526 | NULL, sysmon_shutdown_interrupt, | ||
| 527 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
| 528 | "q6v5 shutdown-ack", sysmon); | ||
| 529 | if (ret) { | ||
| 530 | dev_err(sysmon->dev, | ||
| 531 | "failed to acquire shutdown-ack IRQ\n"); | ||
| 532 | return ERR_PTR(ret); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, | ||
| 537 | qmi_indication_handler); | ||
| 464 | if (ret < 0) { | 538 | if (ret < 0) { |
| 465 | dev_err(sysmon->dev, "failed to initialize qmi handle\n"); | 539 | dev_err(sysmon->dev, "failed to initialize qmi handle\n"); |
| 466 | kfree(sysmon); | 540 | kfree(sysmon); |
| 467 | return NULL; | 541 | return ERR_PTR(ret); |
| 468 | } | 542 | } |
| 469 | 543 | ||
| 470 | qmi_add_lookup(&sysmon->qmi, 43, 0, 0); | 544 | 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) | |||
| 553 | 553 | ||
| 554 | qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); | 554 | qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); |
| 555 | wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); | 555 | wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID); |
| 556 | if (IS_ERR(wcnss->sysmon)) { | ||
| 557 | ret = PTR_ERR(wcnss->sysmon); | ||
| 558 | goto free_rproc; | ||
| 559 | } | ||
| 556 | 560 | ||
| 557 | ret = rproc_add(rproc); | 561 | ret = rproc_add(rproc); |
| 558 | if (ret) | 562 | if (ret) |
| @@ -622,5 +626,5 @@ static void __exit wcnss_exit(void) | |||
| 622 | } | 626 | } |
| 623 | module_exit(wcnss_exit); | 627 | module_exit(wcnss_exit); |
| 624 | 628 | ||
| 625 | MODULE_DESCRIPTION("Qualcomm Peripherial Image Loader for Wireless Subsystem"); | 629 | MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Wireless Subsystem"); |
| 626 | MODULE_LICENSE("GPL v2"); | 630 | 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 @@ | |||
| 39 | #include <linux/idr.h> | 39 | #include <linux/idr.h> |
| 40 | #include <linux/elf.h> | 40 | #include <linux/elf.h> |
| 41 | #include <linux/crc32.h> | 41 | #include <linux/crc32.h> |
| 42 | #include <linux/of_reserved_mem.h> | ||
| 42 | #include <linux/virtio_ids.h> | 43 | #include <linux/virtio_ids.h> |
| 43 | #include <linux/virtio_ring.h> | 44 | #include <linux/virtio_ring.h> |
| 44 | #include <asm/byteorder.h> | 45 | #include <asm/byteorder.h> |
| 46 | #include <linux/platform_device.h> | ||
| 45 | 47 | ||
| 46 | #include "remoteproc_internal.h" | 48 | #include "remoteproc_internal.h" |
| 47 | 49 | ||
| 50 | #define HIGH_BITS_MASK 0xFFFFFFFF00000000ULL | ||
| 51 | |||
| 48 | static DEFINE_MUTEX(rproc_list_mutex); | 52 | static DEFINE_MUTEX(rproc_list_mutex); |
| 49 | static LIST_HEAD(rproc_list); | 53 | static LIST_HEAD(rproc_list); |
| 50 | 54 | ||
| @@ -145,7 +149,7 @@ static void rproc_disable_iommu(struct rproc *rproc) | |||
| 145 | iommu_domain_free(domain); | 149 | iommu_domain_free(domain); |
| 146 | } | 150 | } |
| 147 | 151 | ||
| 148 | static phys_addr_t rproc_va_to_pa(void *cpu_addr) | 152 | phys_addr_t rproc_va_to_pa(void *cpu_addr) |
| 149 | { | 153 | { |
| 150 | /* | 154 | /* |
| 151 | * Return physical address according to virtual address location | 155 | * Return physical address according to virtual address location |
| @@ -160,6 +164,7 @@ static phys_addr_t rproc_va_to_pa(void *cpu_addr) | |||
| 160 | WARN_ON(!virt_addr_valid(cpu_addr)); | 164 | WARN_ON(!virt_addr_valid(cpu_addr)); |
| 161 | return virt_to_phys(cpu_addr); | 165 | return virt_to_phys(cpu_addr); |
| 162 | } | 166 | } |
| 167 | EXPORT_SYMBOL(rproc_va_to_pa); | ||
| 163 | 168 | ||
| 164 | /** | 169 | /** |
| 165 | * rproc_da_to_va() - lookup the kernel virtual address for a remoteproc address | 170 | * 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) | |||
| 204 | list_for_each_entry(carveout, &rproc->carveouts, node) { | 209 | list_for_each_entry(carveout, &rproc->carveouts, node) { |
| 205 | int offset = da - carveout->da; | 210 | int offset = da - carveout->da; |
| 206 | 211 | ||
| 212 | /* Verify that carveout is allocated */ | ||
| 213 | if (!carveout->va) | ||
| 214 | continue; | ||
| 215 | |||
| 207 | /* try next carveout if da is too small */ | 216 | /* try next carveout if da is too small */ |
| 208 | if (offset < 0) | 217 | if (offset < 0) |
| 209 | continue; | 218 | continue; |
| @@ -272,25 +281,27 @@ rproc_find_carveout_by_name(struct rproc *rproc, const char *name, ...) | |||
| 272 | * @len: associated area size | 281 | * @len: associated area size |
| 273 | * | 282 | * |
| 274 | * This function is a helper function to verify requested device area (couple | 283 | * This function is a helper function to verify requested device area (couple |
| 275 | * da, len) is part of specified carevout. | 284 | * da, len) is part of specified carveout. |
| 285 | * If da is not set (defined as FW_RSC_ADDR_ANY), only requested length is | ||
| 286 | * checked. | ||
| 276 | * | 287 | * |
| 277 | * Return: 0 if carveout match request else -ENOMEM | 288 | * Return: 0 if carveout matches request else error |
| 278 | */ | 289 | */ |
| 279 | int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem, | 290 | static int rproc_check_carveout_da(struct rproc *rproc, |
| 280 | u32 da, u32 len) | 291 | struct rproc_mem_entry *mem, u32 da, u32 len) |
| 281 | { | 292 | { |
| 282 | struct device *dev = &rproc->dev; | 293 | struct device *dev = &rproc->dev; |
| 283 | int delta = 0; | 294 | int delta; |
| 284 | 295 | ||
| 285 | /* Check requested resource length */ | 296 | /* Check requested resource length */ |
| 286 | if (len > mem->len) { | 297 | if (len > mem->len) { |
| 287 | dev_err(dev, "Registered carveout doesn't fit len request\n"); | 298 | dev_err(dev, "Registered carveout doesn't fit len request\n"); |
| 288 | return -ENOMEM; | 299 | return -EINVAL; |
| 289 | } | 300 | } |
| 290 | 301 | ||
| 291 | if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { | 302 | if (da != FW_RSC_ADDR_ANY && mem->da == FW_RSC_ADDR_ANY) { |
| 292 | /* Update existing carveout da */ | 303 | /* Address doesn't match registered carveout configuration */ |
| 293 | mem->da = da; | 304 | return -EINVAL; |
| 294 | } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { | 305 | } else if (da != FW_RSC_ADDR_ANY && mem->da != FW_RSC_ADDR_ANY) { |
| 295 | delta = da - mem->da; | 306 | delta = da - mem->da; |
| 296 | 307 | ||
| @@ -298,13 +309,13 @@ int rproc_check_carveout_da(struct rproc *rproc, struct rproc_mem_entry *mem, | |||
| 298 | if (delta < 0) { | 309 | if (delta < 0) { |
| 299 | dev_err(dev, | 310 | dev_err(dev, |
| 300 | "Registered carveout doesn't fit da request\n"); | 311 | "Registered carveout doesn't fit da request\n"); |
| 301 | return -ENOMEM; | 312 | return -EINVAL; |
| 302 | } | 313 | } |
| 303 | 314 | ||
| 304 | if (delta + len > mem->len) { | 315 | if (delta + len > mem->len) { |
| 305 | dev_err(dev, | 316 | dev_err(dev, |
| 306 | "Registered carveout doesn't fit len request\n"); | 317 | "Registered carveout doesn't fit len request\n"); |
| 307 | return -ENOMEM; | 318 | return -EINVAL; |
| 308 | } | 319 | } |
| 309 | } | 320 | } |
| 310 | 321 | ||
| @@ -418,8 +429,25 @@ static int rproc_vdev_do_start(struct rproc_subdev *subdev) | |||
| 418 | static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) | 429 | static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) |
| 419 | { | 430 | { |
| 420 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); | 431 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); |
| 432 | int ret; | ||
| 421 | 433 | ||
| 422 | rproc_remove_virtio_dev(rvdev); | 434 | ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev); |
| 435 | if (ret) | ||
| 436 | dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret); | ||
| 437 | } | ||
| 438 | |||
| 439 | /** | ||
| 440 | * rproc_rvdev_release() - release the existence of a rvdev | ||
| 441 | * | ||
| 442 | * @dev: the subdevice's dev | ||
| 443 | */ | ||
| 444 | static void rproc_rvdev_release(struct device *dev) | ||
| 445 | { | ||
| 446 | struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev); | ||
| 447 | |||
| 448 | of_reserved_mem_device_release(dev); | ||
| 449 | |||
| 450 | kfree(rvdev); | ||
| 423 | } | 451 | } |
| 424 | 452 | ||
| 425 | /** | 453 | /** |
| @@ -455,6 +483,7 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, | |||
| 455 | struct device *dev = &rproc->dev; | 483 | struct device *dev = &rproc->dev; |
| 456 | struct rproc_vdev *rvdev; | 484 | struct rproc_vdev *rvdev; |
| 457 | int i, ret; | 485 | int i, ret; |
| 486 | char name[16]; | ||
| 458 | 487 | ||
| 459 | /* make sure resource isn't truncated */ | 488 | /* make sure resource isn't truncated */ |
| 460 | if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) | 489 | if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring) |
| @@ -488,6 +517,29 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, | |||
| 488 | rvdev->rproc = rproc; | 517 | rvdev->rproc = rproc; |
| 489 | rvdev->index = rproc->nb_vdev++; | 518 | rvdev->index = rproc->nb_vdev++; |
| 490 | 519 | ||
| 520 | /* Initialise vdev subdevice */ | ||
| 521 | snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); | ||
| 522 | rvdev->dev.parent = rproc->dev.parent; | ||
| 523 | rvdev->dev.release = rproc_rvdev_release; | ||
| 524 | dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); | ||
| 525 | dev_set_drvdata(&rvdev->dev, rvdev); | ||
| 526 | |||
| 527 | ret = device_register(&rvdev->dev); | ||
| 528 | if (ret) { | ||
| 529 | put_device(&rvdev->dev); | ||
| 530 | return ret; | ||
| 531 | } | ||
| 532 | /* Make device dma capable by inheriting from parent's capabilities */ | ||
| 533 | set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent)); | ||
| 534 | |||
| 535 | ret = dma_coerce_mask_and_coherent(&rvdev->dev, | ||
| 536 | dma_get_mask(rproc->dev.parent)); | ||
| 537 | if (ret) { | ||
| 538 | dev_warn(dev, | ||
| 539 | "Failed to set DMA mask %llx. Trying to continue... %x\n", | ||
| 540 | dma_get_mask(rproc->dev.parent), ret); | ||
| 541 | } | ||
| 542 | |||
| 491 | /* parse the vrings */ | 543 | /* parse the vrings */ |
| 492 | for (i = 0; i < rsc->num_of_vrings; i++) { | 544 | for (i = 0; i < rsc->num_of_vrings; i++) { |
| 493 | ret = rproc_parse_vring(rvdev, rsc, i); | 545 | ret = rproc_parse_vring(rvdev, rsc, i); |
| @@ -518,7 +570,7 @@ unwind_vring_allocations: | |||
| 518 | for (i--; i >= 0; i--) | 570 | for (i--; i >= 0; i--) |
| 519 | rproc_free_vring(&rvdev->vring[i]); | 571 | rproc_free_vring(&rvdev->vring[i]); |
| 520 | free_rvdev: | 572 | free_rvdev: |
| 521 | kfree(rvdev); | 573 | device_unregister(&rvdev->dev); |
| 522 | return ret; | 574 | return ret; |
| 523 | } | 575 | } |
| 524 | 576 | ||
| @@ -536,7 +588,7 @@ void rproc_vdev_release(struct kref *ref) | |||
| 536 | 588 | ||
| 537 | rproc_remove_subdev(rproc, &rvdev->subdev); | 589 | rproc_remove_subdev(rproc, &rvdev->subdev); |
| 538 | list_del(&rvdev->node); | 590 | list_del(&rvdev->node); |
| 539 | kfree(rvdev); | 591 | device_unregister(&rvdev->dev); |
| 540 | } | 592 | } |
| 541 | 593 | ||
| 542 | /** | 594 | /** |
| @@ -558,9 +610,8 @@ void rproc_vdev_release(struct kref *ref) | |||
| 558 | static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, | 610 | static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, |
| 559 | int offset, int avail) | 611 | int offset, int avail) |
| 560 | { | 612 | { |
| 561 | struct rproc_mem_entry *trace; | 613 | struct rproc_debug_trace *trace; |
| 562 | struct device *dev = &rproc->dev; | 614 | struct device *dev = &rproc->dev; |
| 563 | void *ptr; | ||
| 564 | char name[15]; | 615 | char name[15]; |
| 565 | 616 | ||
| 566 | if (sizeof(*rsc) > avail) { | 617 | if (sizeof(*rsc) > avail) { |
| @@ -574,28 +625,23 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, | |||
| 574 | return -EINVAL; | 625 | return -EINVAL; |
| 575 | } | 626 | } |
| 576 | 627 | ||
| 577 | /* what's the kernel address of this resource ? */ | ||
| 578 | ptr = rproc_da_to_va(rproc, rsc->da, rsc->len); | ||
| 579 | if (!ptr) { | ||
| 580 | dev_err(dev, "erroneous trace resource entry\n"); | ||
| 581 | return -EINVAL; | ||
| 582 | } | ||
| 583 | |||
| 584 | trace = kzalloc(sizeof(*trace), GFP_KERNEL); | 628 | trace = kzalloc(sizeof(*trace), GFP_KERNEL); |
| 585 | if (!trace) | 629 | if (!trace) |
| 586 | return -ENOMEM; | 630 | return -ENOMEM; |
| 587 | 631 | ||
| 588 | /* set the trace buffer dma properties */ | 632 | /* set the trace buffer dma properties */ |
| 589 | trace->len = rsc->len; | 633 | trace->trace_mem.len = rsc->len; |
| 590 | trace->va = ptr; | 634 | trace->trace_mem.da = rsc->da; |
| 635 | |||
| 636 | /* set pointer on rproc device */ | ||
| 637 | trace->rproc = rproc; | ||
| 591 | 638 | ||
| 592 | /* make sure snprintf always null terminates, even if truncating */ | 639 | /* make sure snprintf always null terminates, even if truncating */ |
| 593 | snprintf(name, sizeof(name), "trace%d", rproc->num_traces); | 640 | snprintf(name, sizeof(name), "trace%d", rproc->num_traces); |
| 594 | 641 | ||
| 595 | /* create the debugfs entry */ | 642 | /* create the debugfs entry */ |
| 596 | trace->priv = rproc_create_trace_file(name, rproc, trace); | 643 | trace->tfile = rproc_create_trace_file(name, rproc, trace); |
| 597 | if (!trace->priv) { | 644 | if (!trace->tfile) { |
| 598 | trace->va = NULL; | ||
| 599 | kfree(trace); | 645 | kfree(trace); |
| 600 | return -EINVAL; | 646 | return -EINVAL; |
| 601 | } | 647 | } |
| @@ -604,8 +650,8 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, | |||
| 604 | 650 | ||
| 605 | rproc->num_traces++; | 651 | rproc->num_traces++; |
| 606 | 652 | ||
| 607 | dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n", | 653 | dev_dbg(dev, "%s added: da 0x%x, len 0x%x\n", |
| 608 | name, ptr, rsc->da, rsc->len); | 654 | name, rsc->da, rsc->len); |
| 609 | 655 | ||
| 610 | return 0; | 656 | return 0; |
| 611 | } | 657 | } |
| @@ -715,6 +761,18 @@ static int rproc_alloc_carveout(struct rproc *rproc, | |||
| 715 | dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", | 761 | dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", |
| 716 | va, &dma, mem->len); | 762 | va, &dma, mem->len); |
| 717 | 763 | ||
| 764 | if (mem->da != FW_RSC_ADDR_ANY && !rproc->domain) { | ||
| 765 | /* | ||
| 766 | * Check requested da is equal to dma address | ||
| 767 | * and print a warn message in case of missalignment. | ||
| 768 | * Don't stop rproc_start sequence as coprocessor may | ||
| 769 | * build pa to da translation on its side. | ||
| 770 | */ | ||
| 771 | if (mem->da != (u32)dma) | ||
| 772 | dev_warn(dev->parent, | ||
| 773 | "Allocated carveout doesn't fit device address request\n"); | ||
| 774 | } | ||
| 775 | |||
| 718 | /* | 776 | /* |
| 719 | * Ok, this is non-standard. | 777 | * Ok, this is non-standard. |
| 720 | * | 778 | * |
| @@ -732,15 +790,7 @@ static int rproc_alloc_carveout(struct rproc *rproc, | |||
| 732 | * to use the iommu-based DMA API: we expect 'dma' to contain the | 790 | * to use the iommu-based DMA API: we expect 'dma' to contain the |
| 733 | * physical address in this case. | 791 | * physical address in this case. |
| 734 | */ | 792 | */ |
| 735 | 793 | if (mem->da != FW_RSC_ADDR_ANY && rproc->domain) { | |
| 736 | if (mem->da != FW_RSC_ADDR_ANY) { | ||
| 737 | if (!rproc->domain) { | ||
| 738 | dev_err(dev->parent, | ||
| 739 | "Bad carveout rsc configuration\n"); | ||
| 740 | ret = -ENOMEM; | ||
| 741 | goto dma_free; | ||
| 742 | } | ||
| 743 | |||
| 744 | mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); | 794 | mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); |
| 745 | if (!mapping) { | 795 | if (!mapping) { |
| 746 | ret = -ENOMEM; | 796 | ret = -ENOMEM; |
| @@ -767,11 +817,17 @@ static int rproc_alloc_carveout(struct rproc *rproc, | |||
| 767 | 817 | ||
| 768 | dev_dbg(dev, "carveout mapped 0x%x to %pad\n", | 818 | dev_dbg(dev, "carveout mapped 0x%x to %pad\n", |
| 769 | mem->da, &dma); | 819 | mem->da, &dma); |
| 770 | } else { | 820 | } |
| 821 | |||
| 822 | if (mem->da == FW_RSC_ADDR_ANY) { | ||
| 823 | /* Update device address as undefined by requester */ | ||
| 824 | if ((u64)dma & HIGH_BITS_MASK) | ||
| 825 | dev_warn(dev, "DMA address cast in 32bit to fit resource table format\n"); | ||
| 826 | |||
| 771 | mem->da = (u32)dma; | 827 | mem->da = (u32)dma; |
| 772 | } | 828 | } |
| 773 | 829 | ||
| 774 | mem->dma = (u32)dma; | 830 | mem->dma = dma; |
| 775 | mem->va = va; | 831 | mem->va = va; |
| 776 | 832 | ||
| 777 | return 0; | 833 | return 0; |
| @@ -900,7 +956,8 @@ EXPORT_SYMBOL(rproc_add_carveout); | |||
| 900 | * @dma: dma address | 956 | * @dma: dma address |
| 901 | * @len: memory carveout length | 957 | * @len: memory carveout length |
| 902 | * @da: device address | 958 | * @da: device address |
| 903 | * @release: memory carveout function | 959 | * @alloc: memory carveout allocation function |
| 960 | * @release: memory carveout release function | ||
| 904 | * @name: carveout name | 961 | * @name: carveout name |
| 905 | * | 962 | * |
| 906 | * This function allocates a rproc_mem_entry struct and fill it with parameters | 963 | * This function allocates a rproc_mem_entry struct and fill it with parameters |
| @@ -1110,6 +1167,7 @@ static int rproc_alloc_registered_carveouts(struct rproc *rproc) | |||
| 1110 | struct rproc_mem_entry *entry, *tmp; | 1167 | struct rproc_mem_entry *entry, *tmp; |
| 1111 | struct fw_rsc_carveout *rsc; | 1168 | struct fw_rsc_carveout *rsc; |
| 1112 | struct device *dev = &rproc->dev; | 1169 | struct device *dev = &rproc->dev; |
| 1170 | u64 pa; | ||
| 1113 | int ret; | 1171 | int ret; |
| 1114 | 1172 | ||
| 1115 | list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { | 1173 | list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) { |
| @@ -1146,10 +1204,15 @@ static int rproc_alloc_registered_carveouts(struct rproc *rproc) | |||
| 1146 | 1204 | ||
| 1147 | /* Use va if defined else dma to generate pa */ | 1205 | /* Use va if defined else dma to generate pa */ |
| 1148 | if (entry->va) | 1206 | if (entry->va) |
| 1149 | rsc->pa = (u32)rproc_va_to_pa(entry->va); | 1207 | pa = (u64)rproc_va_to_pa(entry->va); |
| 1150 | else | 1208 | else |
| 1151 | rsc->pa = (u32)entry->dma; | 1209 | pa = (u64)entry->dma; |
| 1210 | |||
| 1211 | if (((u64)pa) & HIGH_BITS_MASK) | ||
| 1212 | dev_warn(dev, | ||
| 1213 | "Physical address cast in 32bit to fit resource table format\n"); | ||
| 1152 | 1214 | ||
| 1215 | rsc->pa = (u32)pa; | ||
| 1153 | rsc->da = entry->da; | 1216 | rsc->da = entry->da; |
| 1154 | rsc->len = entry->len; | 1217 | rsc->len = entry->len; |
| 1155 | } | 1218 | } |
| @@ -1182,15 +1245,16 @@ static void rproc_coredump_cleanup(struct rproc *rproc) | |||
| 1182 | static void rproc_resource_cleanup(struct rproc *rproc) | 1245 | static void rproc_resource_cleanup(struct rproc *rproc) |
| 1183 | { | 1246 | { |
| 1184 | struct rproc_mem_entry *entry, *tmp; | 1247 | struct rproc_mem_entry *entry, *tmp; |
| 1248 | struct rproc_debug_trace *trace, *ttmp; | ||
| 1185 | struct rproc_vdev *rvdev, *rvtmp; | 1249 | struct rproc_vdev *rvdev, *rvtmp; |
| 1186 | struct device *dev = &rproc->dev; | 1250 | struct device *dev = &rproc->dev; |
| 1187 | 1251 | ||
| 1188 | /* clean up debugfs trace entries */ | 1252 | /* clean up debugfs trace entries */ |
| 1189 | list_for_each_entry_safe(entry, tmp, &rproc->traces, node) { | 1253 | list_for_each_entry_safe(trace, ttmp, &rproc->traces, node) { |
| 1190 | rproc_remove_trace_file(entry->priv); | 1254 | rproc_remove_trace_file(trace->tfile); |
| 1191 | rproc->num_traces--; | 1255 | rproc->num_traces--; |
| 1192 | list_del(&entry->node); | 1256 | list_del(&trace->node); |
| 1193 | kfree(entry); | 1257 | kfree(trace); |
| 1194 | } | 1258 | } |
| 1195 | 1259 | ||
| 1196 | /* clean up iommu mapping entries */ | 1260 | /* clean up iommu mapping entries */ |
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index e90135c64af0..6da934b8dc4b 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c | |||
| @@ -47,10 +47,23 @@ static struct dentry *rproc_dbg; | |||
| 47 | static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, | 47 | static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf, |
| 48 | size_t count, loff_t *ppos) | 48 | size_t count, loff_t *ppos) |
| 49 | { | 49 | { |
| 50 | struct rproc_mem_entry *trace = filp->private_data; | 50 | struct rproc_debug_trace *data = filp->private_data; |
| 51 | int len = strnlen(trace->va, trace->len); | 51 | struct rproc_mem_entry *trace = &data->trace_mem; |
| 52 | void *va; | ||
| 53 | char buf[100]; | ||
| 54 | int len; | ||
| 55 | |||
| 56 | va = rproc_da_to_va(data->rproc, trace->da, trace->len); | ||
| 52 | 57 | ||
| 53 | return simple_read_from_buffer(userbuf, count, ppos, trace->va, len); | 58 | if (!va) { |
| 59 | len = scnprintf(buf, sizeof(buf), "Trace %s not available\n", | ||
| 60 | trace->name); | ||
| 61 | va = buf; | ||
| 62 | } else { | ||
| 63 | len = strnlen(va, trace->len); | ||
| 64 | } | ||
| 65 | |||
| 66 | return simple_read_from_buffer(userbuf, count, ppos, va, len); | ||
| 54 | } | 67 | } |
| 55 | 68 | ||
| 56 | static const struct file_operations trace_rproc_ops = { | 69 | static const struct file_operations trace_rproc_ops = { |
| @@ -155,6 +168,30 @@ static const struct file_operations rproc_recovery_ops = { | |||
| 155 | .llseek = generic_file_llseek, | 168 | .llseek = generic_file_llseek, |
| 156 | }; | 169 | }; |
| 157 | 170 | ||
| 171 | /* expose the crash trigger via debugfs */ | ||
| 172 | static ssize_t | ||
| 173 | rproc_crash_write(struct file *filp, const char __user *user_buf, | ||
| 174 | size_t count, loff_t *ppos) | ||
| 175 | { | ||
| 176 | struct rproc *rproc = filp->private_data; | ||
| 177 | unsigned int type; | ||
| 178 | int ret; | ||
| 179 | |||
| 180 | ret = kstrtouint_from_user(user_buf, count, 0, &type); | ||
| 181 | if (ret < 0) | ||
| 182 | return ret; | ||
| 183 | |||
| 184 | rproc_report_crash(rproc, type); | ||
| 185 | |||
| 186 | return count; | ||
| 187 | } | ||
| 188 | |||
| 189 | static const struct file_operations rproc_crash_ops = { | ||
| 190 | .write = rproc_crash_write, | ||
| 191 | .open = simple_open, | ||
| 192 | .llseek = generic_file_llseek, | ||
| 193 | }; | ||
| 194 | |||
| 158 | /* Expose resource table content via debugfs */ | 195 | /* Expose resource table content via debugfs */ |
| 159 | static int rproc_rsc_table_show(struct seq_file *seq, void *p) | 196 | static int rproc_rsc_table_show(struct seq_file *seq, void *p) |
| 160 | { | 197 | { |
| @@ -288,7 +325,7 @@ void rproc_remove_trace_file(struct dentry *tfile) | |||
| 288 | } | 325 | } |
| 289 | 326 | ||
| 290 | struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, | 327 | struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, |
| 291 | struct rproc_mem_entry *trace) | 328 | struct rproc_debug_trace *trace) |
| 292 | { | 329 | { |
| 293 | struct dentry *tfile; | 330 | struct dentry *tfile; |
| 294 | 331 | ||
| @@ -325,6 +362,8 @@ void rproc_create_debug_dir(struct rproc *rproc) | |||
| 325 | rproc, &rproc_name_ops); | 362 | rproc, &rproc_name_ops); |
| 326 | debugfs_create_file("recovery", 0400, rproc->dbg_dir, | 363 | debugfs_create_file("recovery", 0400, rproc->dbg_dir, |
| 327 | rproc, &rproc_recovery_ops); | 364 | rproc, &rproc_recovery_ops); |
| 365 | debugfs_create_file("crash", 0200, rproc->dbg_dir, | ||
| 366 | rproc, &rproc_crash_ops); | ||
| 328 | debugfs_create_file("resource_table", 0400, rproc->dbg_dir, | 367 | debugfs_create_file("resource_table", 0400, rproc->dbg_dir, |
| 329 | rproc, &rproc_rsc_table_ops); | 368 | rproc, &rproc_rsc_table_ops); |
| 330 | debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, | 369 | debugfs_create_file("carveout_memories", 0400, rproc->dbg_dir, |
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index f6cad243d7ca..45ff76a06c72 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h | |||
| @@ -25,6 +25,13 @@ | |||
| 25 | 25 | ||
| 26 | struct rproc; | 26 | struct rproc; |
| 27 | 27 | ||
| 28 | struct rproc_debug_trace { | ||
| 29 | struct rproc *rproc; | ||
| 30 | struct dentry *tfile; | ||
| 31 | struct list_head node; | ||
| 32 | struct rproc_mem_entry trace_mem; | ||
| 33 | }; | ||
| 34 | |||
| 28 | /* from remoteproc_core.c */ | 35 | /* from remoteproc_core.c */ |
| 29 | void rproc_release(struct kref *kref); | 36 | void rproc_release(struct kref *kref); |
| 30 | irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); | 37 | irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id); |
| @@ -32,12 +39,12 @@ void rproc_vdev_release(struct kref *ref); | |||
| 32 | 39 | ||
| 33 | /* from remoteproc_virtio.c */ | 40 | /* from remoteproc_virtio.c */ |
| 34 | int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); | 41 | int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id); |
| 35 | void rproc_remove_virtio_dev(struct rproc_vdev *rvdev); | 42 | int rproc_remove_virtio_dev(struct device *dev, void *data); |
| 36 | 43 | ||
| 37 | /* from remoteproc_debugfs.c */ | 44 | /* from remoteproc_debugfs.c */ |
| 38 | void rproc_remove_trace_file(struct dentry *tfile); | 45 | void rproc_remove_trace_file(struct dentry *tfile); |
| 39 | struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, | 46 | struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc, |
| 40 | struct rproc_mem_entry *trace); | 47 | struct rproc_debug_trace *trace); |
| 41 | void rproc_delete_debug_dir(struct rproc *rproc); | 48 | void rproc_delete_debug_dir(struct rproc *rproc); |
| 42 | void rproc_create_debug_dir(struct rproc *rproc); | 49 | void rproc_create_debug_dir(struct rproc *rproc); |
| 43 | void rproc_init_debugfs(void); | 50 | void rproc_init_debugfs(void); |
| @@ -52,6 +59,7 @@ void rproc_free_vring(struct rproc_vring *rvring); | |||
| 52 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); | 59 | int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); |
| 53 | 60 | ||
| 54 | void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); | 61 | void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); |
| 62 | phys_addr_t rproc_va_to_pa(void *cpu_addr); | ||
| 55 | int rproc_trigger_recovery(struct rproc *rproc); | 63 | int rproc_trigger_recovery(struct rproc *rproc); |
| 56 | 64 | ||
| 57 | int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); | 65 | int rproc_elf_sanity_check(struct rproc *rproc, const struct firmware *fw); |
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index 2d7cd344f3bf..44774de6f17b 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c | |||
| @@ -17,7 +17,9 @@ | |||
| 17 | * GNU General Public License for more details. | 17 | * GNU General Public License for more details. |
| 18 | */ | 18 | */ |
| 19 | 19 | ||
| 20 | #include <linux/dma-mapping.h> | ||
| 20 | #include <linux/export.h> | 21 | #include <linux/export.h> |
| 22 | #include <linux/of_reserved_mem.h> | ||
| 21 | #include <linux/remoteproc.h> | 23 | #include <linux/remoteproc.h> |
| 22 | #include <linux/virtio.h> | 24 | #include <linux/virtio.h> |
| 23 | #include <linux/virtio_config.h> | 25 | #include <linux/virtio_config.h> |
| @@ -316,6 +318,8 @@ static void rproc_virtio_dev_release(struct device *dev) | |||
| 316 | struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); | 318 | struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
| 317 | struct rproc *rproc = vdev_to_rproc(vdev); | 319 | struct rproc *rproc = vdev_to_rproc(vdev); |
| 318 | 320 | ||
| 321 | kfree(vdev); | ||
| 322 | |||
| 319 | kref_put(&rvdev->refcount, rproc_vdev_release); | 323 | kref_put(&rvdev->refcount, rproc_vdev_release); |
| 320 | 324 | ||
| 321 | put_device(&rproc->dev); | 325 | put_device(&rproc->dev); |
| @@ -333,10 +337,53 @@ static void rproc_virtio_dev_release(struct device *dev) | |||
| 333 | int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) | 337 | int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) |
| 334 | { | 338 | { |
| 335 | struct rproc *rproc = rvdev->rproc; | 339 | struct rproc *rproc = rvdev->rproc; |
| 336 | struct device *dev = &rproc->dev; | 340 | struct device *dev = &rvdev->dev; |
| 337 | struct virtio_device *vdev = &rvdev->vdev; | 341 | struct virtio_device *vdev; |
| 342 | struct rproc_mem_entry *mem; | ||
| 338 | int ret; | 343 | int ret; |
| 339 | 344 | ||
| 345 | /* Try to find dedicated vdev buffer carveout */ | ||
| 346 | mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); | ||
| 347 | if (mem) { | ||
| 348 | phys_addr_t pa; | ||
| 349 | |||
| 350 | if (mem->of_resm_idx != -1) { | ||
| 351 | struct device_node *np = rproc->dev.parent->of_node; | ||
| 352 | |||
| 353 | /* Associate reserved memory to vdev device */ | ||
| 354 | ret = of_reserved_mem_device_init_by_idx(dev, np, | ||
| 355 | mem->of_resm_idx); | ||
| 356 | if (ret) { | ||
| 357 | dev_err(dev, "Can't associate reserved memory\n"); | ||
| 358 | goto out; | ||
| 359 | } | ||
| 360 | } else { | ||
| 361 | if (mem->va) { | ||
| 362 | dev_warn(dev, "vdev %d buffer already mapped\n", | ||
| 363 | rvdev->index); | ||
| 364 | pa = rproc_va_to_pa(mem->va); | ||
| 365 | } else { | ||
| 366 | /* Use dma address as carveout no memmapped yet */ | ||
| 367 | pa = (phys_addr_t)mem->dma; | ||
| 368 | } | ||
| 369 | |||
| 370 | /* Associate vdev buffer memory pool to vdev subdev */ | ||
| 371 | ret = dma_declare_coherent_memory(dev, pa, | ||
| 372 | mem->da, | ||
| 373 | mem->len); | ||
| 374 | if (ret < 0) { | ||
| 375 | dev_err(dev, "Failed to associate buffer\n"); | ||
| 376 | goto out; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | } | ||
| 380 | |||
| 381 | /* Allocate virtio device */ | ||
| 382 | vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); | ||
| 383 | if (!vdev) { | ||
| 384 | ret = -ENOMEM; | ||
| 385 | goto out; | ||
| 386 | } | ||
| 340 | vdev->id.device = id, | 387 | vdev->id.device = id, |
| 341 | vdev->config = &rproc_virtio_config_ops, | 388 | vdev->config = &rproc_virtio_config_ops, |
| 342 | vdev->dev.parent = dev; | 389 | vdev->dev.parent = dev; |
| @@ -370,11 +417,15 @@ out: | |||
| 370 | 417 | ||
| 371 | /** | 418 | /** |
| 372 | * rproc_remove_virtio_dev() - remove an rproc-induced virtio device | 419 | * rproc_remove_virtio_dev() - remove an rproc-induced virtio device |
| 373 | * @rvdev: the remote vdev | 420 | * @dev: the virtio device |
| 421 | * @data: must be null | ||
| 374 | * | 422 | * |
| 375 | * This function unregisters an existing virtio device. | 423 | * This function unregisters an existing virtio device. |
| 376 | */ | 424 | */ |
| 377 | void rproc_remove_virtio_dev(struct rproc_vdev *rvdev) | 425 | int rproc_remove_virtio_dev(struct device *dev, void *data) |
| 378 | { | 426 | { |
| 379 | unregister_virtio_device(&rvdev->vdev); | 427 | struct virtio_device *vdev = dev_to_virtio(dev); |
| 428 | |||
| 429 | unregister_virtio_device(vdev); | ||
| 430 | return 0; | ||
| 380 | } | 431 | } |
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index aacef0ea3b90..51049d17b1e5 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/mfd/syscon.h> | 19 | #include <linux/mfd/syscon.h> |
| 20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
| 21 | #include <linux/of.h> | 21 | #include <linux/of.h> |
| 22 | #include <linux/of_address.h> | ||
| 22 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
| 23 | #include <linux/of_reserved_mem.h> | 24 | #include <linux/of_reserved_mem.h> |
| 24 | #include <linux/platform_device.h> | 25 | #include <linux/platform_device.h> |
| @@ -91,6 +92,77 @@ static void st_rproc_kick(struct rproc *rproc, int vqid) | |||
| 91 | dev_err(dev, "failed to send message via mbox: %d\n", ret); | 92 | dev_err(dev, "failed to send message via mbox: %d\n", ret); |
| 92 | } | 93 | } |
| 93 | 94 | ||
| 95 | static int st_rproc_mem_alloc(struct rproc *rproc, | ||
| 96 | struct rproc_mem_entry *mem) | ||
| 97 | { | ||
| 98 | struct device *dev = rproc->dev.parent; | ||
| 99 | void *va; | ||
| 100 | |||
| 101 | va = ioremap_wc(mem->dma, mem->len); | ||
| 102 | if (!va) { | ||
| 103 | dev_err(dev, "Unable to map memory region: %pa+%zx\n", | ||
| 104 | &mem->dma, mem->len); | ||
| 105 | return -ENOMEM; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Update memory entry va */ | ||
| 109 | mem->va = va; | ||
| 110 | |||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | |||
| 114 | static int st_rproc_mem_release(struct rproc *rproc, | ||
| 115 | struct rproc_mem_entry *mem) | ||
| 116 | { | ||
| 117 | iounmap(mem->va); | ||
| 118 | |||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | static int st_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) | ||
| 123 | { | ||
| 124 | struct device *dev = rproc->dev.parent; | ||
| 125 | struct device_node *np = dev->of_node; | ||
| 126 | struct rproc_mem_entry *mem; | ||
| 127 | struct reserved_mem *rmem; | ||
| 128 | struct of_phandle_iterator it; | ||
| 129 | int index = 0; | ||
| 130 | |||
| 131 | of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); | ||
| 132 | while (of_phandle_iterator_next(&it) == 0) { | ||
| 133 | rmem = of_reserved_mem_lookup(it.node); | ||
| 134 | if (!rmem) { | ||
| 135 | dev_err(dev, "unable to acquire memory-region\n"); | ||
| 136 | return -EINVAL; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* No need to map vdev buffer */ | ||
| 140 | if (strcmp(it.node->name, "vdev0buffer")) { | ||
| 141 | /* Register memory region */ | ||
| 142 | mem = rproc_mem_entry_init(dev, NULL, | ||
| 143 | (dma_addr_t)rmem->base, | ||
| 144 | rmem->size, rmem->base, | ||
| 145 | st_rproc_mem_alloc, | ||
| 146 | st_rproc_mem_release, | ||
| 147 | it.node->name); | ||
| 148 | } else { | ||
| 149 | /* Register reserved memory for vdev buffer allocation */ | ||
| 150 | mem = rproc_of_resm_mem_entry_init(dev, index, | ||
| 151 | rmem->size, | ||
| 152 | rmem->base, | ||
| 153 | it.node->name); | ||
| 154 | } | ||
| 155 | |||
| 156 | if (!mem) | ||
| 157 | return -ENOMEM; | ||
| 158 | |||
| 159 | rproc_add_carveout(rproc, mem); | ||
| 160 | index++; | ||
| 161 | } | ||
| 162 | |||
| 163 | return rproc_elf_load_rsc_table(rproc, fw); | ||
| 164 | } | ||
| 165 | |||
| 94 | static int st_rproc_start(struct rproc *rproc) | 166 | static int st_rproc_start(struct rproc *rproc) |
| 95 | { | 167 | { |
| 96 | struct st_rproc *ddata = rproc->priv; | 168 | struct st_rproc *ddata = rproc->priv; |
| @@ -158,9 +230,14 @@ static int st_rproc_stop(struct rproc *rproc) | |||
| 158 | } | 230 | } |
| 159 | 231 | ||
| 160 | static const struct rproc_ops st_rproc_ops = { | 232 | static const struct rproc_ops st_rproc_ops = { |
| 161 | .kick = st_rproc_kick, | 233 | .kick = st_rproc_kick, |
| 162 | .start = st_rproc_start, | 234 | .start = st_rproc_start, |
| 163 | .stop = st_rproc_stop, | 235 | .stop = st_rproc_stop, |
| 236 | .parse_fw = st_rproc_parse_fw, | ||
| 237 | .load = rproc_elf_load_segments, | ||
| 238 | .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, | ||
| 239 | .sanity_check = rproc_elf_sanity_check, | ||
| 240 | .get_boot_addr = rproc_elf_get_boot_addr, | ||
| 164 | }; | 241 | }; |
| 165 | 242 | ||
| 166 | /* | 243 | /* |
| @@ -254,12 +331,6 @@ static int st_rproc_parse_dt(struct platform_device *pdev) | |||
| 254 | return -EINVAL; | 331 | return -EINVAL; |
| 255 | } | 332 | } |
| 256 | 333 | ||
| 257 | err = of_reserved_mem_device_init(dev); | ||
| 258 | if (err) { | ||
| 259 | dev_err(dev, "Failed to obtain shared memory\n"); | ||
| 260 | return err; | ||
| 261 | } | ||
| 262 | |||
| 263 | err = clk_prepare(ddata->clk); | 334 | err = clk_prepare(ddata->clk); |
| 264 | if (err) | 335 | if (err) |
| 265 | dev_err(dev, "failed to get clock\n"); | 336 | dev_err(dev, "failed to get clock\n"); |
| @@ -387,8 +458,6 @@ static int st_rproc_remove(struct platform_device *pdev) | |||
| 387 | 458 | ||
| 388 | clk_disable_unprepare(ddata->clk); | 459 | clk_disable_unprepare(ddata->clk); |
| 389 | 460 | ||
| 390 | of_reserved_mem_device_release(&pdev->dev); | ||
| 391 | |||
| 392 | for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) | 461 | for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) |
| 393 | mbox_free_channel(ddata->mbox_chan[i]); | 462 | mbox_free_channel(ddata->mbox_chan[i]); |
| 394 | 463 | ||
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c index 664f957012cd..5d3685bd76a2 100644 --- a/drivers/rpmsg/virtio_rpmsg_bus.c +++ b/drivers/rpmsg/virtio_rpmsg_bus.c | |||
| @@ -11,21 +11,21 @@ | |||
| 11 | 11 | ||
| 12 | #define pr_fmt(fmt) "%s: " fmt, __func__ | 12 | #define pr_fmt(fmt) "%s: " fmt, __func__ |
| 13 | 13 | ||
| 14 | #include <linux/dma-mapping.h> | ||
| 15 | #include <linux/idr.h> | ||
| 16 | #include <linux/jiffies.h> | ||
| 14 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
| 15 | #include <linux/module.h> | 18 | #include <linux/module.h> |
| 16 | #include <linux/virtio.h> | 19 | #include <linux/mutex.h> |
| 17 | #include <linux/virtio_ids.h> | 20 | #include <linux/of_device.h> |
| 18 | #include <linux/virtio_config.h> | 21 | #include <linux/rpmsg.h> |
| 19 | #include <linux/scatterlist.h> | 22 | #include <linux/scatterlist.h> |
| 20 | #include <linux/dma-mapping.h> | ||
| 21 | #include <linux/slab.h> | 23 | #include <linux/slab.h> |
| 22 | #include <linux/idr.h> | ||
| 23 | #include <linux/jiffies.h> | ||
| 24 | #include <linux/sched.h> | 24 | #include <linux/sched.h> |
| 25 | #include <linux/virtio.h> | ||
| 26 | #include <linux/virtio_ids.h> | ||
| 27 | #include <linux/virtio_config.h> | ||
| 25 | #include <linux/wait.h> | 28 | #include <linux/wait.h> |
| 26 | #include <linux/rpmsg.h> | ||
| 27 | #include <linux/mutex.h> | ||
| 28 | #include <linux/of_device.h> | ||
| 29 | 29 | ||
| 30 | #include "rpmsg_internal.h" | 30 | #include "rpmsg_internal.h" |
| 31 | 31 | ||
| @@ -912,7 +912,7 @@ static int rpmsg_probe(struct virtio_device *vdev) | |||
| 912 | total_buf_space = vrp->num_bufs * vrp->buf_size; | 912 | total_buf_space = vrp->num_bufs * vrp->buf_size; |
| 913 | 913 | ||
| 914 | /* allocate coherent memory for the buffers */ | 914 | /* allocate coherent memory for the buffers */ |
| 915 | bufs_va = dma_alloc_coherent(vdev->dev.parent->parent, | 915 | bufs_va = dma_alloc_coherent(vdev->dev.parent, |
| 916 | total_buf_space, &vrp->bufs_dma, | 916 | total_buf_space, &vrp->bufs_dma, |
| 917 | GFP_KERNEL); | 917 | GFP_KERNEL); |
| 918 | if (!bufs_va) { | 918 | if (!bufs_va) { |
| @@ -980,7 +980,7 @@ static int rpmsg_probe(struct virtio_device *vdev) | |||
| 980 | return 0; | 980 | return 0; |
| 981 | 981 | ||
| 982 | free_coherent: | 982 | free_coherent: |
| 983 | dma_free_coherent(vdev->dev.parent->parent, total_buf_space, | 983 | dma_free_coherent(vdev->dev.parent, total_buf_space, |
| 984 | bufs_va, vrp->bufs_dma); | 984 | bufs_va, vrp->bufs_dma); |
| 985 | vqs_del: | 985 | vqs_del: |
| 986 | vdev->config->del_vqs(vrp->vdev); | 986 | vdev->config->del_vqs(vrp->vdev); |
| @@ -1015,7 +1015,7 @@ static void rpmsg_remove(struct virtio_device *vdev) | |||
| 1015 | 1015 | ||
| 1016 | vdev->config->del_vqs(vrp->vdev); | 1016 | vdev->config->del_vqs(vrp->vdev); |
| 1017 | 1017 | ||
| 1018 | dma_free_coherent(vdev->dev.parent->parent, total_buf_space, | 1018 | dma_free_coherent(vdev->dev.parent, total_buf_space, |
| 1019 | vrp->rbufs, vrp->bufs_dma); | 1019 | vrp->rbufs, vrp->bufs_dma); |
| 1020 | 1020 | ||
| 1021 | kfree(vrp); | 1021 | kfree(vrp); |
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 507a2b524208..04d04709f2bd 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h | |||
| @@ -345,9 +345,9 @@ struct firmware; | |||
| 345 | * @stop: power off the device | 345 | * @stop: power off the device |
| 346 | * @kick: kick a virtqueue (virtqueue id given as a parameter) | 346 | * @kick: kick a virtqueue (virtqueue id given as a parameter) |
| 347 | * @da_to_va: optional platform hook to perform address translations | 347 | * @da_to_va: optional platform hook to perform address translations |
| 348 | * @load_rsc_table: load resource table from firmware image | 348 | * @parse_fw: parse firmware to extract information (e.g. resource table) |
| 349 | * @find_loaded_rsc_table: find the loaded resouce table | 349 | * @find_loaded_rsc_table: find the loaded resouce table |
| 350 | * @load: load firmeware to memory, where the remote processor | 350 | * @load: load firmware to memory, where the remote processor |
| 351 | * expects to find it | 351 | * expects to find it |
| 352 | * @sanity_check: sanity check the fw image | 352 | * @sanity_check: sanity check the fw image |
| 353 | * @get_boot_addr: get boot address to entry point specified in firmware | 353 | * @get_boot_addr: get boot address to entry point specified in firmware |
| @@ -554,11 +554,11 @@ struct rproc_vdev { | |||
| 554 | struct kref refcount; | 554 | struct kref refcount; |
| 555 | 555 | ||
| 556 | struct rproc_subdev subdev; | 556 | struct rproc_subdev subdev; |
| 557 | struct device dev; | ||
| 557 | 558 | ||
| 558 | unsigned int id; | 559 | unsigned int id; |
| 559 | struct list_head node; | 560 | struct list_head node; |
| 560 | struct rproc *rproc; | 561 | struct rproc *rproc; |
| 561 | struct virtio_device vdev; | ||
| 562 | struct rproc_vring vring[RVDEV_NUM_VRINGS]; | 562 | struct rproc_vring vring[RVDEV_NUM_VRINGS]; |
| 563 | u32 rsc_offset; | 563 | u32 rsc_offset; |
| 564 | u32 index; | 564 | u32 index; |
| @@ -601,7 +601,7 @@ int rproc_coredump_add_custom_segment(struct rproc *rproc, | |||
| 601 | 601 | ||
| 602 | static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) | 602 | static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) |
| 603 | { | 603 | { |
| 604 | return container_of(vdev, struct rproc_vdev, vdev); | 604 | return container_of(vdev->dev.parent, struct rproc_vdev, dev); |
| 605 | } | 605 | } |
| 606 | 606 | ||
| 607 | static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) | 607 | static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) |
