diff options
24 files changed, 1029 insertions, 486 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt index b85885a298d8..75ad7b8df0b1 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt | |||
@@ -9,6 +9,7 @@ on the Qualcomm ADSP Hexagon core. | |||
9 | Definition: must be one of: | 9 | Definition: must be one of: |
10 | "qcom,msm8974-adsp-pil" | 10 | "qcom,msm8974-adsp-pil" |
11 | "qcom,msm8996-adsp-pil" | 11 | "qcom,msm8996-adsp-pil" |
12 | "qcom,msm8996-slpi-pil" | ||
12 | 13 | ||
13 | - interrupts-extended: | 14 | - interrupts-extended: |
14 | Usage: required | 15 | Usage: required |
@@ -24,13 +25,13 @@ on the Qualcomm ADSP Hexagon core. | |||
24 | - clocks: | 25 | - clocks: |
25 | Usage: required | 26 | Usage: required |
26 | Value type: <prop-encoded-array> | 27 | Value type: <prop-encoded-array> |
27 | Definition: reference to the xo clock to be held on behalf of the | 28 | Definition: reference to the xo clock and optionally aggre2 clock to be |
28 | booting Hexagon core | 29 | held on behalf of the booting Hexagon core |
29 | 30 | ||
30 | - clock-names: | 31 | - clock-names: |
31 | Usage: required | 32 | Usage: required |
32 | Value type: <stringlist> | 33 | Value type: <stringlist> |
33 | Definition: must be "xo" | 34 | Definition: must be "xo" and optionally include "aggre2" |
34 | 35 | ||
35 | - cx-supply: | 36 | - cx-supply: |
36 | Usage: required | 37 | Usage: required |
@@ -38,6 +39,12 @@ on the Qualcomm ADSP Hexagon core. | |||
38 | Definition: reference to the regulator to be held on behalf of the | 39 | Definition: reference to the regulator to be held on behalf of the |
39 | booting Hexagon core | 40 | booting Hexagon core |
40 | 41 | ||
42 | - px-supply: | ||
43 | Usage: required | ||
44 | Value type: <phandle> | ||
45 | Definition: reference to the px regulator to be held on behalf of the | ||
46 | booting Hexagon core | ||
47 | |||
41 | - memory-region: | 48 | - memory-region: |
42 | Usage: required | 49 | Usage: required |
43 | Value type: <phandle> | 50 | Value type: <phandle> |
@@ -96,3 +103,31 @@ ADSP, as it is found on MSM8974 boards. | |||
96 | qcom,smd-edge = <1>; | 103 | qcom,smd-edge = <1>; |
97 | }; | 104 | }; |
98 | }; | 105 | }; |
106 | |||
107 | The following example describes the resources needed to boot control the | ||
108 | SLPI, as it is found on MSM8996 boards. | ||
109 | |||
110 | slpi { | ||
111 | compatible = "qcom,msm8996-slpi-pil"; | ||
112 | interrupts-extended = <&intc 0 390 IRQ_TYPE_EDGE_RISING>, | ||
113 | <&slpi_smp2p_in 0 IRQ_TYPE_EDGE_RISING>, | ||
114 | <&slpi_smp2p_in 1 IRQ_TYPE_EDGE_RISING>, | ||
115 | <&slpi_smp2p_in 2 IRQ_TYPE_EDGE_RISING>, | ||
116 | <&slpi_smp2p_in 3 IRQ_TYPE_EDGE_RISING>; | ||
117 | interrupt-names = "wdog", | ||
118 | "fatal", | ||
119 | "ready", | ||
120 | "handover", | ||
121 | "stop-ack"; | ||
122 | |||
123 | clocks = <&rpmcc MSM8996_RPM_SMD_XO_CLK_SRC>, | ||
124 | <&rpmcc MSM8996_RPM_SMD_AGGR2_NOC_CLK>; | ||
125 | clock-names = "xo", "aggre2"; | ||
126 | |||
127 | cx-supply = <&pm8994_l26>; | ||
128 | px-supply = <&pm8994_lvs2>; | ||
129 | |||
130 | memory-region = <&slpi_region>; | ||
131 | qcom,smem-states = <&slpi_smp2p_out 0>; | ||
132 | qcom,smem-state-names = "stop"; | ||
133 | }; | ||
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index 57cb49ec55ca..92347fe6890e 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | |||
@@ -7,7 +7,9 @@ on the Qualcomm Hexagon core. | |||
7 | Usage: required | 7 | Usage: required |
8 | Value type: <string> | 8 | Value type: <string> |
9 | Definition: must be one of: | 9 | Definition: must be one of: |
10 | "qcom,q6v5-pil" | 10 | "qcom,q6v5-pil", |
11 | "qcom,msm8916-mss-pil", | ||
12 | "qcom,msm8974-mss-pil" | ||
11 | 13 | ||
12 | - reg: | 14 | - reg: |
13 | Usage: required | 15 | Usage: required |
diff --git a/MAINTAINERS b/MAINTAINERS index d461a94d2eb0..5a5fa41ac961 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -10548,6 +10548,7 @@ S: Maintained | |||
10548 | F: drivers/rpmsg/ | 10548 | F: drivers/rpmsg/ |
10549 | F: Documentation/rpmsg.txt | 10549 | F: Documentation/rpmsg.txt |
10550 | F: include/linux/rpmsg.h | 10550 | F: include/linux/rpmsg.h |
10551 | F: include/linux/rpmsg/ | ||
10551 | 10552 | ||
10552 | RENESAS CLOCK DRIVERS | 10553 | RENESAS CLOCK DRIVERS |
10553 | M: Geert Uytterhoeven <geert+renesas@glider.be> | 10554 | M: Geert Uytterhoeven <geert+renesas@glider.be> |
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 8f9cf0bc571c..65f86bc24c07 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig | |||
@@ -7,6 +7,9 @@ config REMOTEPROC | |||
7 | select FW_LOADER | 7 | select FW_LOADER |
8 | select VIRTIO | 8 | select VIRTIO |
9 | select VIRTUALIZATION | 9 | select VIRTUALIZATION |
10 | help | ||
11 | Support for remote processors (such as DSP coprocessors). These | ||
12 | are mainly used on embedded systems. | ||
10 | 13 | ||
11 | if REMOTEPROC | 14 | if REMOTEPROC |
12 | 15 | ||
@@ -25,11 +28,11 @@ config OMAP_REMOTEPROC | |||
25 | 28 | ||
26 | Currently only supported on OMAP4. | 29 | Currently only supported on OMAP4. |
27 | 30 | ||
28 | Usually you want to say y here, in order to enable multimedia | 31 | Usually you want to say Y here, in order to enable multimedia |
29 | use-cases to run on your platform (multimedia codecs are | 32 | use-cases to run on your platform (multimedia codecs are |
30 | offloaded to remote DSP processors using this framework). | 33 | offloaded to remote DSP processors using this framework). |
31 | 34 | ||
32 | It's safe to say n here if you're not interested in multimedia | 35 | It's safe to say N here if you're not interested in multimedia |
33 | offloading or just want a bare minimum kernel. | 36 | offloading or just want a bare minimum kernel. |
34 | 37 | ||
35 | config WKUP_M3_RPROC | 38 | config WKUP_M3_RPROC |
@@ -73,14 +76,16 @@ config QCOM_ADSP_PIL | |||
73 | depends on OF && ARCH_QCOM | 76 | depends on OF && ARCH_QCOM |
74 | depends on REMOTEPROC | 77 | depends on REMOTEPROC |
75 | depends on QCOM_SMEM | 78 | depends on QCOM_SMEM |
79 | depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) | ||
76 | select MFD_SYSCON | 80 | select MFD_SYSCON |
77 | select QCOM_MDT_LOADER | 81 | select QCOM_MDT_LOADER |
82 | select QCOM_RPROC_COMMON | ||
78 | select QCOM_SCM | 83 | select QCOM_SCM |
79 | help | 84 | help |
80 | Say y here to support the TrustZone based Peripherial Image Loader | 85 | Say y here to support the TrustZone based Peripherial Image Loader |
81 | for the Qualcomm ADSP remote processors. | 86 | for the Qualcomm ADSP remote processors. |
82 | 87 | ||
83 | config QCOM_MDT_LOADER | 88 | config QCOM_RPROC_COMMON |
84 | tristate | 89 | tristate |
85 | 90 | ||
86 | config QCOM_Q6V5_PIL | 91 | config QCOM_Q6V5_PIL |
@@ -88,8 +93,9 @@ config QCOM_Q6V5_PIL | |||
88 | depends on OF && ARCH_QCOM | 93 | depends on OF && ARCH_QCOM |
89 | depends on QCOM_SMEM | 94 | depends on QCOM_SMEM |
90 | depends on REMOTEPROC | 95 | depends on REMOTEPROC |
96 | depends on QCOM_SMD || (COMPILE_TEST && QCOM_SMD=n) | ||
91 | select MFD_SYSCON | 97 | select MFD_SYSCON |
92 | select QCOM_MDT_LOADER | 98 | select QCOM_RPROC_COMMON |
93 | select QCOM_SCM | 99 | select QCOM_SCM |
94 | help | 100 | help |
95 | Say y here to support the Qualcomm Peripherial Image Loader for the | 101 | Say y here to support the Qualcomm Peripherial Image Loader for the |
@@ -102,6 +108,7 @@ config QCOM_WCNSS_PIL | |||
102 | depends on QCOM_SMEM | 108 | depends on QCOM_SMEM |
103 | depends on REMOTEPROC | 109 | depends on REMOTEPROC |
104 | select QCOM_MDT_LOADER | 110 | select QCOM_MDT_LOADER |
111 | select QCOM_RPROC_COMMON | ||
105 | select QCOM_SCM | 112 | select QCOM_SCM |
106 | help | 113 | help |
107 | Say y here to support the Peripheral Image Loader for the Qualcomm | 114 | Say y here to support the Peripheral Image Loader for the Qualcomm |
@@ -111,6 +118,9 @@ config ST_REMOTEPROC | |||
111 | tristate "ST remoteproc support" | 118 | tristate "ST remoteproc support" |
112 | depends on ARCH_STI | 119 | depends on ARCH_STI |
113 | depends on REMOTEPROC | 120 | depends on REMOTEPROC |
121 | select MAILBOX | ||
122 | select STI_MBOX | ||
123 | select RPMSG_VIRTIO | ||
114 | help | 124 | help |
115 | Say y here to support ST's adjunct processors via the remote | 125 | Say y here to support ST's adjunct processors via the remote |
116 | processor framework. | 126 | processor framework. |
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 0938ea3c41ba..ffc5e430df27 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile | |||
@@ -12,7 +12,7 @@ obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o | |||
12 | obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o | 12 | obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o |
13 | obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o | 13 | obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o |
14 | obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o | 14 | obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o |
15 | obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o | 15 | obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o |
16 | obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o | 16 | obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o |
17 | obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o | 17 | obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o |
18 | qcom_wcnss_pil-y += qcom_wcnss.o | 18 | qcom_wcnss_pil-y += qcom_wcnss.o |
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 1afac8f31be0..3814de28599c 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c | |||
@@ -151,7 +151,7 @@ static void da8xx_rproc_kick(struct rproc *rproc, int vqid) | |||
151 | writel(SYSCFG_CHIPSIG2, drproc->chipsig); | 151 | writel(SYSCFG_CHIPSIG2, drproc->chipsig); |
152 | } | 152 | } |
153 | 153 | ||
154 | static struct rproc_ops da8xx_rproc_ops = { | 154 | static const struct rproc_ops da8xx_rproc_ops = { |
155 | .start = da8xx_rproc_start, | 155 | .start = da8xx_rproc_start, |
156 | .stop = da8xx_rproc_stop, | 156 | .stop = da8xx_rproc_stop, |
157 | .kick = da8xx_rproc_kick, | 157 | .kick = da8xx_rproc_kick, |
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index fa63bf2eb885..a96ce9083f7f 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c | |||
@@ -177,7 +177,7 @@ static int omap_rproc_stop(struct rproc *rproc) | |||
177 | return 0; | 177 | return 0; |
178 | } | 178 | } |
179 | 179 | ||
180 | static struct rproc_ops omap_rproc_ops = { | 180 | static const struct rproc_ops omap_rproc_ops = { |
181 | .start = omap_rproc_start, | 181 | .start = omap_rproc_start, |
182 | .stop = omap_rproc_stop, | 182 | .stop = omap_rproc_stop, |
183 | .kick = omap_rproc_kick, | 183 | .kick = omap_rproc_kick, |
diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 43a4ed2f346c..49fe2f807e1d 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Qualcomm ADSP Peripheral Image Loader for MSM8974 and MSM8996 | 2 | * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996 |
3 | * | 3 | * |
4 | * Copyright (C) 2016 Linaro Ltd | 4 | * Copyright (C) 2016 Linaro Ltd |
5 | * Copyright (C) 2014 Sony Mobile Communications AB | 5 | * Copyright (C) 2014 Sony Mobile Communications AB |
@@ -26,15 +26,19 @@ | |||
26 | #include <linux/qcom_scm.h> | 26 | #include <linux/qcom_scm.h> |
27 | #include <linux/regulator/consumer.h> | 27 | #include <linux/regulator/consumer.h> |
28 | #include <linux/remoteproc.h> | 28 | #include <linux/remoteproc.h> |
29 | #include <linux/soc/qcom/mdt_loader.h> | ||
29 | #include <linux/soc/qcom/smem.h> | 30 | #include <linux/soc/qcom/smem.h> |
30 | #include <linux/soc/qcom/smem_state.h> | 31 | #include <linux/soc/qcom/smem_state.h> |
31 | 32 | ||
32 | #include "qcom_mdt_loader.h" | 33 | #include "qcom_common.h" |
33 | #include "remoteproc_internal.h" | 34 | #include "remoteproc_internal.h" |
34 | 35 | ||
35 | #define ADSP_CRASH_REASON_SMEM 423 | 36 | struct adsp_data { |
36 | #define ADSP_FIRMWARE_NAME "adsp.mdt" | 37 | int crash_reason_smem; |
37 | #define ADSP_PAS_ID 1 | 38 | const char *firmware_name; |
39 | int pas_id; | ||
40 | bool has_aggre2_clk; | ||
41 | }; | ||
38 | 42 | ||
39 | struct qcom_adsp { | 43 | struct qcom_adsp { |
40 | struct device *dev; | 44 | struct device *dev; |
@@ -50,8 +54,14 @@ struct qcom_adsp { | |||
50 | unsigned stop_bit; | 54 | unsigned stop_bit; |
51 | 55 | ||
52 | struct clk *xo; | 56 | struct clk *xo; |
57 | struct clk *aggre2_clk; | ||
53 | 58 | ||
54 | struct regulator *cx_supply; | 59 | struct regulator *cx_supply; |
60 | struct regulator *px_supply; | ||
61 | |||
62 | int pas_id; | ||
63 | int crash_reason_smem; | ||
64 | bool has_aggre2_clk; | ||
55 | 65 | ||
56 | struct completion start_done; | 66 | struct completion start_done; |
57 | struct completion stop_done; | 67 | struct completion stop_done; |
@@ -60,39 +70,16 @@ struct qcom_adsp { | |||
60 | phys_addr_t mem_reloc; | 70 | phys_addr_t mem_reloc; |
61 | void *mem_region; | 71 | void *mem_region; |
62 | size_t mem_size; | 72 | size_t mem_size; |
73 | |||
74 | struct qcom_rproc_subdev smd_subdev; | ||
63 | }; | 75 | }; |
64 | 76 | ||
65 | static int adsp_load(struct rproc *rproc, const struct firmware *fw) | 77 | static int adsp_load(struct rproc *rproc, const struct firmware *fw) |
66 | { | 78 | { |
67 | struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; | 79 | struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; |
68 | phys_addr_t fw_addr; | ||
69 | size_t fw_size; | ||
70 | bool relocate; | ||
71 | int ret; | ||
72 | |||
73 | ret = qcom_scm_pas_init_image(ADSP_PAS_ID, fw->data, fw->size); | ||
74 | if (ret) { | ||
75 | dev_err(&rproc->dev, "invalid firmware metadata\n"); | ||
76 | return ret; | ||
77 | } | ||
78 | |||
79 | ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); | ||
80 | if (ret) { | ||
81 | dev_err(&rproc->dev, "failed to parse mdt header\n"); | ||
82 | return ret; | ||
83 | } | ||
84 | 80 | ||
85 | if (relocate) { | 81 | return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, |
86 | adsp->mem_reloc = fw_addr; | 82 | adsp->mem_region, adsp->mem_phys, adsp->mem_size); |
87 | |||
88 | ret = qcom_scm_pas_mem_setup(ADSP_PAS_ID, adsp->mem_phys, fw_size); | ||
89 | if (ret) { | ||
90 | dev_err(&rproc->dev, "unable to setup memory for image\n"); | ||
91 | return ret; | ||
92 | } | ||
93 | } | ||
94 | |||
95 | return qcom_mdt_load(rproc, fw, rproc->firmware); | ||
96 | } | 83 | } |
97 | 84 | ||
98 | static const struct rproc_fw_ops adsp_fw_ops = { | 85 | static const struct rproc_fw_ops adsp_fw_ops = { |
@@ -109,31 +96,43 @@ static int adsp_start(struct rproc *rproc) | |||
109 | if (ret) | 96 | if (ret) |
110 | return ret; | 97 | return ret; |
111 | 98 | ||
99 | ret = clk_prepare_enable(adsp->aggre2_clk); | ||
100 | if (ret) | ||
101 | goto disable_xo_clk; | ||
102 | |||
112 | ret = regulator_enable(adsp->cx_supply); | 103 | ret = regulator_enable(adsp->cx_supply); |
113 | if (ret) | 104 | if (ret) |
114 | goto disable_clocks; | 105 | goto disable_aggre2_clk; |
106 | |||
107 | ret = regulator_enable(adsp->px_supply); | ||
108 | if (ret) | ||
109 | goto disable_cx_supply; | ||
115 | 110 | ||
116 | ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID); | 111 | ret = qcom_scm_pas_auth_and_reset(adsp->pas_id); |
117 | if (ret) { | 112 | if (ret) { |
118 | dev_err(adsp->dev, | 113 | dev_err(adsp->dev, |
119 | "failed to authenticate image and release reset\n"); | 114 | "failed to authenticate image and release reset\n"); |
120 | goto disable_regulators; | 115 | goto disable_px_supply; |
121 | } | 116 | } |
122 | 117 | ||
123 | ret = wait_for_completion_timeout(&adsp->start_done, | 118 | ret = wait_for_completion_timeout(&adsp->start_done, |
124 | msecs_to_jiffies(5000)); | 119 | msecs_to_jiffies(5000)); |
125 | if (!ret) { | 120 | if (!ret) { |
126 | dev_err(adsp->dev, "start timed out\n"); | 121 | dev_err(adsp->dev, "start timed out\n"); |
127 | qcom_scm_pas_shutdown(ADSP_PAS_ID); | 122 | qcom_scm_pas_shutdown(adsp->pas_id); |
128 | ret = -ETIMEDOUT; | 123 | ret = -ETIMEDOUT; |
129 | goto disable_regulators; | 124 | goto disable_px_supply; |
130 | } | 125 | } |
131 | 126 | ||
132 | ret = 0; | 127 | ret = 0; |
133 | 128 | ||
134 | disable_regulators: | 129 | disable_px_supply: |
130 | regulator_disable(adsp->px_supply); | ||
131 | disable_cx_supply: | ||
135 | regulator_disable(adsp->cx_supply); | 132 | regulator_disable(adsp->cx_supply); |
136 | disable_clocks: | 133 | disable_aggre2_clk: |
134 | clk_disable_unprepare(adsp->aggre2_clk); | ||
135 | disable_xo_clk: | ||
137 | clk_disable_unprepare(adsp->xo); | 136 | clk_disable_unprepare(adsp->xo); |
138 | 137 | ||
139 | return ret; | 138 | return ret; |
@@ -157,7 +156,7 @@ static int adsp_stop(struct rproc *rproc) | |||
157 | BIT(adsp->stop_bit), | 156 | BIT(adsp->stop_bit), |
158 | 0); | 157 | 0); |
159 | 158 | ||
160 | ret = qcom_scm_pas_shutdown(ADSP_PAS_ID); | 159 | ret = qcom_scm_pas_shutdown(adsp->pas_id); |
161 | if (ret) | 160 | if (ret) |
162 | dev_err(adsp->dev, "failed to shutdown: %d\n", ret); | 161 | dev_err(adsp->dev, "failed to shutdown: %d\n", ret); |
163 | 162 | ||
@@ -197,7 +196,7 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev) | |||
197 | size_t len; | 196 | size_t len; |
198 | char *msg; | 197 | char *msg; |
199 | 198 | ||
200 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len); | 199 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, adsp->crash_reason_smem, &len); |
201 | if (!IS_ERR(msg) && len > 0 && msg[0]) | 200 | if (!IS_ERR(msg) && len > 0 && msg[0]) |
202 | dev_err(adsp->dev, "fatal error received: %s\n", msg); | 201 | dev_err(adsp->dev, "fatal error received: %s\n", msg); |
203 | 202 | ||
@@ -244,6 +243,17 @@ static int adsp_init_clock(struct qcom_adsp *adsp) | |||
244 | return ret; | 243 | return ret; |
245 | } | 244 | } |
246 | 245 | ||
246 | if (adsp->has_aggre2_clk) { | ||
247 | adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2"); | ||
248 | if (IS_ERR(adsp->aggre2_clk)) { | ||
249 | ret = PTR_ERR(adsp->aggre2_clk); | ||
250 | if (ret != -EPROBE_DEFER) | ||
251 | dev_err(adsp->dev, | ||
252 | "failed to get aggre2 clock"); | ||
253 | return ret; | ||
254 | } | ||
255 | } | ||
256 | |||
247 | return 0; | 257 | return 0; |
248 | } | 258 | } |
249 | 259 | ||
@@ -255,6 +265,10 @@ static int adsp_init_regulator(struct qcom_adsp *adsp) | |||
255 | 265 | ||
256 | regulator_set_load(adsp->cx_supply, 100000); | 266 | regulator_set_load(adsp->cx_supply, 100000); |
257 | 267 | ||
268 | adsp->px_supply = devm_regulator_get(adsp->dev, "px"); | ||
269 | if (IS_ERR(adsp->px_supply)) | ||
270 | return PTR_ERR(adsp->px_supply); | ||
271 | |||
258 | return 0; | 272 | return 0; |
259 | } | 273 | } |
260 | 274 | ||
@@ -311,20 +325,20 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) | |||
311 | 325 | ||
312 | static int adsp_probe(struct platform_device *pdev) | 326 | static int adsp_probe(struct platform_device *pdev) |
313 | { | 327 | { |
328 | const struct adsp_data *desc; | ||
314 | struct qcom_adsp *adsp; | 329 | struct qcom_adsp *adsp; |
315 | struct rproc *rproc; | 330 | struct rproc *rproc; |
316 | int ret; | 331 | int ret; |
317 | 332 | ||
333 | desc = of_device_get_match_data(&pdev->dev); | ||
334 | if (!desc) | ||
335 | return -EINVAL; | ||
336 | |||
318 | if (!qcom_scm_is_available()) | 337 | if (!qcom_scm_is_available()) |
319 | return -EPROBE_DEFER; | 338 | return -EPROBE_DEFER; |
320 | 339 | ||
321 | if (!qcom_scm_pas_supported(ADSP_PAS_ID)) { | ||
322 | dev_err(&pdev->dev, "PAS is not available for ADSP\n"); | ||
323 | return -ENXIO; | ||
324 | } | ||
325 | |||
326 | rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, | 340 | rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, |
327 | ADSP_FIRMWARE_NAME, sizeof(*adsp)); | 341 | desc->firmware_name, sizeof(*adsp)); |
328 | if (!rproc) { | 342 | if (!rproc) { |
329 | dev_err(&pdev->dev, "unable to allocate remoteproc\n"); | 343 | dev_err(&pdev->dev, "unable to allocate remoteproc\n"); |
330 | return -ENOMEM; | 344 | return -ENOMEM; |
@@ -335,6 +349,9 @@ static int adsp_probe(struct platform_device *pdev) | |||
335 | adsp = (struct qcom_adsp *)rproc->priv; | 349 | adsp = (struct qcom_adsp *)rproc->priv; |
336 | adsp->dev = &pdev->dev; | 350 | adsp->dev = &pdev->dev; |
337 | adsp->rproc = rproc; | 351 | adsp->rproc = rproc; |
352 | adsp->pas_id = desc->pas_id; | ||
353 | adsp->crash_reason_smem = desc->crash_reason_smem; | ||
354 | adsp->has_aggre2_clk = desc->has_aggre2_clk; | ||
338 | platform_set_drvdata(pdev, adsp); | 355 | platform_set_drvdata(pdev, adsp); |
339 | 356 | ||
340 | init_completion(&adsp->start_done); | 357 | init_completion(&adsp->start_done); |
@@ -384,6 +401,8 @@ static int adsp_probe(struct platform_device *pdev) | |||
384 | goto free_rproc; | 401 | goto free_rproc; |
385 | } | 402 | } |
386 | 403 | ||
404 | qcom_add_smd_subdev(rproc, &adsp->smd_subdev); | ||
405 | |||
387 | ret = rproc_add(rproc); | 406 | ret = rproc_add(rproc); |
388 | if (ret) | 407 | if (ret) |
389 | goto free_rproc; | 408 | goto free_rproc; |
@@ -402,14 +421,31 @@ static int adsp_remove(struct platform_device *pdev) | |||
402 | 421 | ||
403 | qcom_smem_state_put(adsp->state); | 422 | qcom_smem_state_put(adsp->state); |
404 | rproc_del(adsp->rproc); | 423 | rproc_del(adsp->rproc); |
424 | |||
425 | qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); | ||
405 | rproc_free(adsp->rproc); | 426 | rproc_free(adsp->rproc); |
406 | 427 | ||
407 | return 0; | 428 | return 0; |
408 | } | 429 | } |
409 | 430 | ||
431 | static const struct adsp_data adsp_resource_init = { | ||
432 | .crash_reason_smem = 423, | ||
433 | .firmware_name = "adsp.mdt", | ||
434 | .pas_id = 1, | ||
435 | .has_aggre2_clk = false, | ||
436 | }; | ||
437 | |||
438 | static const struct adsp_data slpi_resource_init = { | ||
439 | .crash_reason_smem = 424, | ||
440 | .firmware_name = "slpi.mdt", | ||
441 | .pas_id = 12, | ||
442 | .has_aggre2_clk = true, | ||
443 | }; | ||
444 | |||
410 | static const struct of_device_id adsp_of_match[] = { | 445 | static const struct of_device_id adsp_of_match[] = { |
411 | { .compatible = "qcom,msm8974-adsp-pil" }, | 446 | { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init}, |
412 | { .compatible = "qcom,msm8996-adsp-pil" }, | 447 | { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init}, |
448 | { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init}, | ||
413 | { }, | 449 | { }, |
414 | }; | 450 | }; |
415 | MODULE_DEVICE_TABLE(of, adsp_of_match); | 451 | MODULE_DEVICE_TABLE(of, adsp_of_match); |
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c new file mode 100644 index 000000000000..bb90481215c6 --- /dev/null +++ b/drivers/remoteproc/qcom_common.c | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Qualcomm Peripheral Image Loader helpers | ||
3 | * | ||
4 | * Copyright (C) 2016 Linaro Ltd | ||
5 | * Copyright (C) 2015 Sony Mobile Communications Inc | ||
6 | * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/firmware.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/remoteproc.h> | ||
22 | #include <linux/rpmsg/qcom_smd.h> | ||
23 | |||
24 | #include "remoteproc_internal.h" | ||
25 | #include "qcom_common.h" | ||
26 | |||
27 | #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) | ||
28 | |||
29 | /** | ||
30 | * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc | ||
31 | * @rproc: remoteproc handle | ||
32 | * @fw: firmware header | ||
33 | * @tablesz: outgoing size of the table | ||
34 | * | ||
35 | * Returns a dummy table. | ||
36 | */ | ||
37 | struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, | ||
38 | const struct firmware *fw, | ||
39 | int *tablesz) | ||
40 | { | ||
41 | static struct resource_table table = { .ver = 1, }; | ||
42 | |||
43 | *tablesz = sizeof(table); | ||
44 | return &table; | ||
45 | } | ||
46 | EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); | ||
47 | |||
48 | static int smd_subdev_probe(struct rproc_subdev *subdev) | ||
49 | { | ||
50 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); | ||
51 | |||
52 | smd->edge = qcom_smd_register_edge(smd->dev, smd->node); | ||
53 | |||
54 | return IS_ERR(smd->edge) ? PTR_ERR(smd->edge) : 0; | ||
55 | } | ||
56 | |||
57 | static void smd_subdev_remove(struct rproc_subdev *subdev) | ||
58 | { | ||
59 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); | ||
60 | |||
61 | qcom_smd_unregister_edge(smd->edge); | ||
62 | smd->edge = NULL; | ||
63 | } | ||
64 | |||
65 | /** | ||
66 | * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc | ||
67 | * @rproc: rproc handle to parent the subdevice | ||
68 | * @smd: reference to a Qualcomm subdev context | ||
69 | */ | ||
70 | void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) | ||
71 | { | ||
72 | struct device *dev = &rproc->dev; | ||
73 | |||
74 | smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge"); | ||
75 | if (!smd->node) | ||
76 | return; | ||
77 | |||
78 | smd->dev = dev; | ||
79 | rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove); | ||
80 | } | ||
81 | EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); | ||
82 | |||
83 | /** | ||
84 | * qcom_remove_smd_subdev() - remove the smd subdevice from rproc | ||
85 | * @rproc: rproc handle | ||
86 | * @smd: the SMD subdevice to remove | ||
87 | */ | ||
88 | void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) | ||
89 | { | ||
90 | rproc_remove_subdev(rproc, &smd->subdev); | ||
91 | of_node_put(smd->node); | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev); | ||
94 | |||
95 | MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); | ||
96 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h new file mode 100644 index 000000000000..db5c826d5cd4 --- /dev/null +++ b/drivers/remoteproc/qcom_common.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef __RPROC_QCOM_COMMON_H__ | ||
2 | #define __RPROC_QCOM_COMMON_H__ | ||
3 | |||
4 | #include <linux/remoteproc.h> | ||
5 | #include "remoteproc_internal.h" | ||
6 | |||
7 | struct qcom_rproc_subdev { | ||
8 | struct rproc_subdev subdev; | ||
9 | |||
10 | struct device *dev; | ||
11 | struct device_node *node; | ||
12 | struct qcom_smd_edge *edge; | ||
13 | }; | ||
14 | |||
15 | struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, | ||
16 | const struct firmware *fw, | ||
17 | int *tablesz); | ||
18 | |||
19 | void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); | ||
20 | void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); | ||
21 | |||
22 | #endif | ||
diff --git a/drivers/remoteproc/qcom_mdt_loader.c b/drivers/remoteproc/qcom_mdt_loader.c deleted file mode 100644 index 2ff18cd6c096..000000000000 --- a/drivers/remoteproc/qcom_mdt_loader.c +++ /dev/null | |||
@@ -1,180 +0,0 @@ | |||
1 | /* | ||
2 | * Qualcomm Peripheral Image Loader | ||
3 | * | ||
4 | * Copyright (C) 2016 Linaro Ltd | ||
5 | * Copyright (C) 2015 Sony Mobile Communications Inc | ||
6 | * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/elf.h> | ||
19 | #include <linux/firmware.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/remoteproc.h> | ||
23 | #include <linux/sizes.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | #include "remoteproc_internal.h" | ||
27 | #include "qcom_mdt_loader.h" | ||
28 | |||
29 | /** | ||
30 | * qcom_mdt_find_rsc_table() - provide dummy resource table for remoteproc | ||
31 | * @rproc: remoteproc handle | ||
32 | * @fw: firmware header | ||
33 | * @tablesz: outgoing size of the table | ||
34 | * | ||
35 | * Returns a dummy table. | ||
36 | */ | ||
37 | struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc, | ||
38 | const struct firmware *fw, | ||
39 | int *tablesz) | ||
40 | { | ||
41 | static struct resource_table table = { .ver = 1, }; | ||
42 | |||
43 | *tablesz = sizeof(table); | ||
44 | return &table; | ||
45 | } | ||
46 | EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table); | ||
47 | |||
48 | /** | ||
49 | * qcom_mdt_parse() - extract useful parameters from the mdt header | ||
50 | * @fw: firmware handle | ||
51 | * @fw_addr: optional reference for base of the firmware's memory region | ||
52 | * @fw_size: optional reference for size of the firmware's memory region | ||
53 | * @fw_relocate: optional reference for flagging if the firmware is relocatable | ||
54 | * | ||
55 | * Returns 0 on success, negative errno otherwise. | ||
56 | */ | ||
57 | int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, | ||
58 | size_t *fw_size, bool *fw_relocate) | ||
59 | { | ||
60 | const struct elf32_phdr *phdrs; | ||
61 | const struct elf32_phdr *phdr; | ||
62 | const struct elf32_hdr *ehdr; | ||
63 | phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; | ||
64 | phys_addr_t max_addr = 0; | ||
65 | bool relocate = false; | ||
66 | int i; | ||
67 | |||
68 | ehdr = (struct elf32_hdr *)fw->data; | ||
69 | phdrs = (struct elf32_phdr *)(ehdr + 1); | ||
70 | |||
71 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
72 | phdr = &phdrs[i]; | ||
73 | |||
74 | if (phdr->p_type != PT_LOAD) | ||
75 | continue; | ||
76 | |||
77 | if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) | ||
78 | continue; | ||
79 | |||
80 | if (!phdr->p_memsz) | ||
81 | continue; | ||
82 | |||
83 | if (phdr->p_flags & QCOM_MDT_RELOCATABLE) | ||
84 | relocate = true; | ||
85 | |||
86 | if (phdr->p_paddr < min_addr) | ||
87 | min_addr = phdr->p_paddr; | ||
88 | |||
89 | if (phdr->p_paddr + phdr->p_memsz > max_addr) | ||
90 | max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); | ||
91 | } | ||
92 | |||
93 | if (fw_addr) | ||
94 | *fw_addr = min_addr; | ||
95 | if (fw_size) | ||
96 | *fw_size = max_addr - min_addr; | ||
97 | if (fw_relocate) | ||
98 | *fw_relocate = relocate; | ||
99 | |||
100 | return 0; | ||
101 | } | ||
102 | EXPORT_SYMBOL_GPL(qcom_mdt_parse); | ||
103 | |||
104 | /** | ||
105 | * qcom_mdt_load() - load the firmware which header is defined in fw | ||
106 | * @rproc: rproc handle | ||
107 | * @fw: frimware object for the header | ||
108 | * @firmware: filename of the firmware, for building .bXX names | ||
109 | * | ||
110 | * Returns 0 on success, negative errno otherwise. | ||
111 | */ | ||
112 | int qcom_mdt_load(struct rproc *rproc, | ||
113 | const struct firmware *fw, | ||
114 | const char *firmware) | ||
115 | { | ||
116 | const struct elf32_phdr *phdrs; | ||
117 | const struct elf32_phdr *phdr; | ||
118 | const struct elf32_hdr *ehdr; | ||
119 | size_t fw_name_len; | ||
120 | char *fw_name; | ||
121 | void *ptr; | ||
122 | int ret; | ||
123 | int i; | ||
124 | |||
125 | ehdr = (struct elf32_hdr *)fw->data; | ||
126 | phdrs = (struct elf32_phdr *)(ehdr + 1); | ||
127 | |||
128 | fw_name_len = strlen(firmware); | ||
129 | if (fw_name_len <= 4) | ||
130 | return -EINVAL; | ||
131 | |||
132 | fw_name = kstrdup(firmware, GFP_KERNEL); | ||
133 | if (!fw_name) | ||
134 | return -ENOMEM; | ||
135 | |||
136 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
137 | phdr = &phdrs[i]; | ||
138 | |||
139 | if (phdr->p_type != PT_LOAD) | ||
140 | continue; | ||
141 | |||
142 | if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) | ||
143 | continue; | ||
144 | |||
145 | if (!phdr->p_memsz) | ||
146 | continue; | ||
147 | |||
148 | ptr = rproc_da_to_va(rproc, phdr->p_paddr, phdr->p_memsz); | ||
149 | if (!ptr) { | ||
150 | dev_err(&rproc->dev, "segment outside memory range\n"); | ||
151 | ret = -EINVAL; | ||
152 | break; | ||
153 | } | ||
154 | |||
155 | if (phdr->p_filesz) { | ||
156 | sprintf(fw_name + fw_name_len - 3, "b%02d", i); | ||
157 | ret = request_firmware(&fw, fw_name, &rproc->dev); | ||
158 | if (ret) { | ||
159 | dev_err(&rproc->dev, "failed to load %s\n", | ||
160 | fw_name); | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | memcpy(ptr, fw->data, fw->size); | ||
165 | |||
166 | release_firmware(fw); | ||
167 | } | ||
168 | |||
169 | if (phdr->p_memsz > phdr->p_filesz) | ||
170 | memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); | ||
171 | } | ||
172 | |||
173 | kfree(fw_name); | ||
174 | |||
175 | return ret; | ||
176 | } | ||
177 | EXPORT_SYMBOL_GPL(qcom_mdt_load); | ||
178 | |||
179 | MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); | ||
180 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/remoteproc/qcom_mdt_loader.h b/drivers/remoteproc/qcom_mdt_loader.h deleted file mode 100644 index c5d7122755b6..000000000000 --- a/drivers/remoteproc/qcom_mdt_loader.h +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | #ifndef __QCOM_MDT_LOADER_H__ | ||
2 | #define __QCOM_MDT_LOADER_H__ | ||
3 | |||
4 | #define QCOM_MDT_TYPE_MASK (7 << 24) | ||
5 | #define QCOM_MDT_TYPE_HASH (2 << 24) | ||
6 | #define QCOM_MDT_RELOCATABLE BIT(27) | ||
7 | |||
8 | struct resource_table * qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz); | ||
9 | int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name); | ||
10 | |||
11 | int qcom_mdt_parse(const struct firmware *fw, phys_addr_t *fw_addr, size_t *fw_size, bool *fw_relocate); | ||
12 | |||
13 | #endif | ||
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index b08989b48df7..8fd697a3cf8f 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c | |||
@@ -23,22 +23,21 @@ | |||
23 | #include <linux/mfd/syscon.h> | 23 | #include <linux/mfd/syscon.h> |
24 | #include <linux/module.h> | 24 | #include <linux/module.h> |
25 | #include <linux/of_address.h> | 25 | #include <linux/of_address.h> |
26 | #include <linux/of_device.h> | ||
26 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
27 | #include <linux/regmap.h> | 28 | #include <linux/regmap.h> |
28 | #include <linux/regulator/consumer.h> | 29 | #include <linux/regulator/consumer.h> |
29 | #include <linux/remoteproc.h> | 30 | #include <linux/remoteproc.h> |
30 | #include <linux/reset.h> | 31 | #include <linux/reset.h> |
32 | #include <linux/soc/qcom/mdt_loader.h> | ||
31 | #include <linux/soc/qcom/smem.h> | 33 | #include <linux/soc/qcom/smem.h> |
32 | #include <linux/soc/qcom/smem_state.h> | 34 | #include <linux/soc/qcom/smem_state.h> |
33 | 35 | ||
34 | #include "remoteproc_internal.h" | 36 | #include "remoteproc_internal.h" |
35 | #include "qcom_mdt_loader.h" | 37 | #include "qcom_common.h" |
36 | 38 | ||
37 | #include <linux/qcom_scm.h> | 39 | #include <linux/qcom_scm.h> |
38 | 40 | ||
39 | #define MBA_FIRMWARE_NAME "mba.b00" | ||
40 | #define MPSS_FIRMWARE_NAME "modem.mdt" | ||
41 | |||
42 | #define MPSS_CRASH_REASON_SMEM 421 | 41 | #define MPSS_CRASH_REASON_SMEM 421 |
43 | 42 | ||
44 | /* RMB Status Register Values */ | 43 | /* RMB Status Register Values */ |
@@ -93,6 +92,26 @@ | |||
93 | #define QDSS_BHS_ON BIT(21) | 92 | #define QDSS_BHS_ON BIT(21) |
94 | #define QDSS_LDO_BYP BIT(22) | 93 | #define QDSS_LDO_BYP BIT(22) |
95 | 94 | ||
95 | struct reg_info { | ||
96 | struct regulator *reg; | ||
97 | int uV; | ||
98 | int uA; | ||
99 | }; | ||
100 | |||
101 | struct qcom_mss_reg_res { | ||
102 | const char *supply; | ||
103 | int uV; | ||
104 | int uA; | ||
105 | }; | ||
106 | |||
107 | struct rproc_hexagon_res { | ||
108 | const char *hexagon_mba_image; | ||
109 | struct qcom_mss_reg_res *proxy_supply; | ||
110 | struct qcom_mss_reg_res *active_supply; | ||
111 | char **proxy_clk_names; | ||
112 | char **active_clk_names; | ||
113 | }; | ||
114 | |||
96 | struct q6v5 { | 115 | struct q6v5 { |
97 | struct device *dev; | 116 | struct device *dev; |
98 | struct rproc *rproc; | 117 | struct rproc *rproc; |
@@ -110,11 +129,15 @@ struct q6v5 { | |||
110 | struct qcom_smem_state *state; | 129 | struct qcom_smem_state *state; |
111 | unsigned stop_bit; | 130 | unsigned stop_bit; |
112 | 131 | ||
113 | struct regulator_bulk_data supply[4]; | 132 | struct clk *active_clks[8]; |
133 | struct clk *proxy_clks[4]; | ||
134 | int active_clk_count; | ||
135 | int proxy_clk_count; | ||
114 | 136 | ||
115 | struct clk *ahb_clk; | 137 | struct reg_info active_regs[1]; |
116 | struct clk *axi_clk; | 138 | struct reg_info proxy_regs[3]; |
117 | struct clk *rom_clk; | 139 | int active_reg_count; |
140 | int proxy_reg_count; | ||
118 | 141 | ||
119 | struct completion start_done; | 142 | struct completion start_done; |
120 | struct completion stop_done; | 143 | struct completion stop_done; |
@@ -128,65 +151,141 @@ struct q6v5 { | |||
128 | phys_addr_t mpss_reloc; | 151 | phys_addr_t mpss_reloc; |
129 | void *mpss_region; | 152 | void *mpss_region; |
130 | size_t mpss_size; | 153 | size_t mpss_size; |
131 | }; | ||
132 | 154 | ||
133 | enum { | 155 | struct qcom_rproc_subdev smd_subdev; |
134 | Q6V5_SUPPLY_CX, | ||
135 | Q6V5_SUPPLY_MX, | ||
136 | Q6V5_SUPPLY_MSS, | ||
137 | Q6V5_SUPPLY_PLL, | ||
138 | }; | 156 | }; |
139 | 157 | ||
140 | static int q6v5_regulator_init(struct q6v5 *qproc) | 158 | static int q6v5_regulator_init(struct device *dev, struct reg_info *regs, |
159 | const struct qcom_mss_reg_res *reg_res) | ||
141 | { | 160 | { |
142 | int ret; | 161 | int rc; |
162 | int i; | ||
143 | 163 | ||
144 | qproc->supply[Q6V5_SUPPLY_CX].supply = "cx"; | 164 | if (!reg_res) |
145 | qproc->supply[Q6V5_SUPPLY_MX].supply = "mx"; | 165 | return 0; |
146 | qproc->supply[Q6V5_SUPPLY_MSS].supply = "mss"; | 166 | |
147 | qproc->supply[Q6V5_SUPPLY_PLL].supply = "pll"; | 167 | for (i = 0; reg_res[i].supply; i++) { |
168 | regs[i].reg = devm_regulator_get(dev, reg_res[i].supply); | ||
169 | if (IS_ERR(regs[i].reg)) { | ||
170 | rc = PTR_ERR(regs[i].reg); | ||
171 | if (rc != -EPROBE_DEFER) | ||
172 | dev_err(dev, "Failed to get %s\n regulator", | ||
173 | reg_res[i].supply); | ||
174 | return rc; | ||
175 | } | ||
148 | 176 | ||
149 | ret = devm_regulator_bulk_get(qproc->dev, | 177 | regs[i].uV = reg_res[i].uV; |
150 | ARRAY_SIZE(qproc->supply), qproc->supply); | 178 | regs[i].uA = reg_res[i].uA; |
151 | if (ret < 0) { | ||
152 | dev_err(qproc->dev, "failed to get supplies\n"); | ||
153 | return ret; | ||
154 | } | 179 | } |
155 | 180 | ||
156 | regulator_set_load(qproc->supply[Q6V5_SUPPLY_CX].consumer, 100000); | 181 | return i; |
157 | regulator_set_load(qproc->supply[Q6V5_SUPPLY_MSS].consumer, 100000); | 182 | } |
158 | regulator_set_load(qproc->supply[Q6V5_SUPPLY_PLL].consumer, 10000); | 183 | |
184 | static int q6v5_regulator_enable(struct q6v5 *qproc, | ||
185 | struct reg_info *regs, int count) | ||
186 | { | ||
187 | int ret; | ||
188 | int i; | ||
189 | |||
190 | for (i = 0; i < count; i++) { | ||
191 | if (regs[i].uV > 0) { | ||
192 | ret = regulator_set_voltage(regs[i].reg, | ||
193 | regs[i].uV, INT_MAX); | ||
194 | if (ret) { | ||
195 | dev_err(qproc->dev, | ||
196 | "Failed to request voltage for %d.\n", | ||
197 | i); | ||
198 | goto err; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | if (regs[i].uA > 0) { | ||
203 | ret = regulator_set_load(regs[i].reg, | ||
204 | regs[i].uA); | ||
205 | if (ret < 0) { | ||
206 | dev_err(qproc->dev, | ||
207 | "Failed to set regulator mode\n"); | ||
208 | goto err; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | ret = regulator_enable(regs[i].reg); | ||
213 | if (ret) { | ||
214 | dev_err(qproc->dev, "Regulator enable failed\n"); | ||
215 | goto err; | ||
216 | } | ||
217 | } | ||
159 | 218 | ||
160 | return 0; | 219 | return 0; |
220 | err: | ||
221 | for (; i >= 0; i--) { | ||
222 | if (regs[i].uV > 0) | ||
223 | regulator_set_voltage(regs[i].reg, 0, INT_MAX); | ||
224 | |||
225 | if (regs[i].uA > 0) | ||
226 | regulator_set_load(regs[i].reg, 0); | ||
227 | |||
228 | regulator_disable(regs[i].reg); | ||
229 | } | ||
230 | |||
231 | return ret; | ||
161 | } | 232 | } |
162 | 233 | ||
163 | static int q6v5_regulator_enable(struct q6v5 *qproc) | 234 | static void q6v5_regulator_disable(struct q6v5 *qproc, |
235 | struct reg_info *regs, int count) | ||
164 | { | 236 | { |
165 | struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer; | 237 | int i; |
166 | struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer; | ||
167 | int ret; | ||
168 | 238 | ||
169 | /* TODO: Q6V5_SUPPLY_CX is supposed to be set to super-turbo here */ | 239 | for (i = 0; i < count; i++) { |
240 | if (regs[i].uV > 0) | ||
241 | regulator_set_voltage(regs[i].reg, 0, INT_MAX); | ||
170 | 242 | ||
171 | ret = regulator_set_voltage(mx, 1050000, INT_MAX); | 243 | if (regs[i].uA > 0) |
172 | if (ret) | 244 | regulator_set_load(regs[i].reg, 0); |
173 | return ret; | 245 | |
246 | regulator_disable(regs[i].reg); | ||
247 | } | ||
248 | } | ||
174 | 249 | ||
175 | regulator_set_voltage(mss, 1000000, 1150000); | 250 | static int q6v5_clk_enable(struct device *dev, |
251 | struct clk **clks, int count) | ||
252 | { | ||
253 | int rc; | ||
254 | int i; | ||
176 | 255 | ||
177 | return regulator_bulk_enable(ARRAY_SIZE(qproc->supply), qproc->supply); | 256 | for (i = 0; i < count; i++) { |
257 | rc = clk_prepare_enable(clks[i]); | ||
258 | if (rc) { | ||
259 | dev_err(dev, "Clock enable failed\n"); | ||
260 | goto err; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | return 0; | ||
265 | err: | ||
266 | for (i--; i >= 0; i--) | ||
267 | clk_disable_unprepare(clks[i]); | ||
268 | |||
269 | return rc; | ||
178 | } | 270 | } |
179 | 271 | ||
180 | static void q6v5_regulator_disable(struct q6v5 *qproc) | 272 | static void q6v5_clk_disable(struct device *dev, |
273 | struct clk **clks, int count) | ||
181 | { | 274 | { |
182 | struct regulator *mss = qproc->supply[Q6V5_SUPPLY_MSS].consumer; | 275 | int i; |
183 | struct regulator *mx = qproc->supply[Q6V5_SUPPLY_MX].consumer; | 276 | |
277 | for (i = 0; i < count; i++) | ||
278 | clk_disable_unprepare(clks[i]); | ||
279 | } | ||
184 | 280 | ||
185 | /* TODO: Q6V5_SUPPLY_CX corner votes should be released */ | 281 | static struct resource_table *q6v5_find_rsc_table(struct rproc *rproc, |
282 | const struct firmware *fw, | ||
283 | int *tablesz) | ||
284 | { | ||
285 | static struct resource_table table = { .ver = 1, }; | ||
186 | 286 | ||
187 | regulator_bulk_disable(ARRAY_SIZE(qproc->supply), qproc->supply); | 287 | *tablesz = sizeof(table); |
188 | regulator_set_voltage(mx, 0, INT_MAX); | 288 | return &table; |
189 | regulator_set_voltage(mss, 0, 1150000); | ||
190 | } | 289 | } |
191 | 290 | ||
192 | static int q6v5_load(struct rproc *rproc, const struct firmware *fw) | 291 | static int q6v5_load(struct rproc *rproc, const struct firmware *fw) |
@@ -199,7 +298,7 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw) | |||
199 | } | 298 | } |
200 | 299 | ||
201 | static const struct rproc_fw_ops q6v5_fw_ops = { | 300 | static const struct rproc_fw_ops q6v5_fw_ops = { |
202 | .find_rsc_table = qcom_mdt_find_rsc_table, | 301 | .find_rsc_table = q6v5_find_rsc_table, |
203 | .load = q6v5_load, | 302 | .load = q6v5_load, |
204 | }; | 303 | }; |
205 | 304 | ||
@@ -376,45 +475,109 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) | |||
376 | return ret < 0 ? ret : 0; | 475 | return ret < 0 ? ret : 0; |
377 | } | 476 | } |
378 | 477 | ||
379 | static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw) | 478 | static bool q6v5_phdr_valid(const struct elf32_phdr *phdr) |
479 | { | ||
480 | if (phdr->p_type != PT_LOAD) | ||
481 | return false; | ||
482 | |||
483 | if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) | ||
484 | return false; | ||
485 | |||
486 | if (!phdr->p_memsz) | ||
487 | return false; | ||
488 | |||
489 | return true; | ||
490 | } | ||
491 | |||
492 | static int q6v5_mpss_load(struct q6v5 *qproc) | ||
380 | { | 493 | { |
381 | const struct elf32_phdr *phdrs; | 494 | const struct elf32_phdr *phdrs; |
382 | const struct elf32_phdr *phdr; | 495 | const struct elf32_phdr *phdr; |
496 | const struct firmware *seg_fw; | ||
497 | const struct firmware *fw; | ||
383 | struct elf32_hdr *ehdr; | 498 | struct elf32_hdr *ehdr; |
499 | phys_addr_t mpss_reloc; | ||
384 | phys_addr_t boot_addr; | 500 | phys_addr_t boot_addr; |
385 | phys_addr_t fw_addr; | 501 | phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; |
386 | bool relocate; | 502 | phys_addr_t max_addr = 0; |
503 | bool relocate = false; | ||
504 | char seg_name[10]; | ||
505 | ssize_t offset; | ||
387 | size_t size; | 506 | size_t size; |
507 | void *ptr; | ||
388 | int ret; | 508 | int ret; |
389 | int i; | 509 | int i; |
390 | 510 | ||
391 | ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate); | 511 | ret = request_firmware(&fw, "modem.mdt", qproc->dev); |
392 | if (ret) { | 512 | if (ret < 0) { |
393 | dev_err(qproc->dev, "failed to parse mdt header\n"); | 513 | dev_err(qproc->dev, "unable to load modem.mdt\n"); |
394 | return ret; | 514 | return ret; |
395 | } | 515 | } |
396 | 516 | ||
397 | if (relocate) | 517 | /* Initialize the RMB validator */ |
398 | boot_addr = qproc->mpss_phys; | 518 | writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); |
399 | else | 519 | |
400 | boot_addr = fw_addr; | 520 | ret = q6v5_mpss_init_image(qproc, fw); |
521 | if (ret) | ||
522 | goto release_firmware; | ||
401 | 523 | ||
402 | ehdr = (struct elf32_hdr *)fw->data; | 524 | ehdr = (struct elf32_hdr *)fw->data; |
403 | phdrs = (struct elf32_phdr *)(ehdr + 1); | 525 | phdrs = (struct elf32_phdr *)(ehdr + 1); |
404 | for (i = 0; i < ehdr->e_phnum; i++, phdr++) { | 526 | |
527 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
405 | phdr = &phdrs[i]; | 528 | phdr = &phdrs[i]; |
406 | 529 | ||
407 | if (phdr->p_type != PT_LOAD) | 530 | if (!q6v5_phdr_valid(phdr)) |
408 | continue; | 531 | continue; |
409 | 532 | ||
410 | if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) | 533 | if (phdr->p_flags & QCOM_MDT_RELOCATABLE) |
411 | continue; | 534 | relocate = true; |
535 | |||
536 | if (phdr->p_paddr < min_addr) | ||
537 | min_addr = phdr->p_paddr; | ||
538 | |||
539 | if (phdr->p_paddr + phdr->p_memsz > max_addr) | ||
540 | max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); | ||
541 | } | ||
542 | |||
543 | mpss_reloc = relocate ? min_addr : qproc->mpss_phys; | ||
412 | 544 | ||
413 | if (!phdr->p_memsz) | 545 | for (i = 0; i < ehdr->e_phnum; i++) { |
546 | phdr = &phdrs[i]; | ||
547 | |||
548 | if (!q6v5_phdr_valid(phdr)) | ||
414 | continue; | 549 | continue; |
415 | 550 | ||
551 | offset = phdr->p_paddr - mpss_reloc; | ||
552 | if (offset < 0 || offset + phdr->p_memsz > qproc->mpss_size) { | ||
553 | dev_err(qproc->dev, "segment outside memory range\n"); | ||
554 | ret = -EINVAL; | ||
555 | goto release_firmware; | ||
556 | } | ||
557 | |||
558 | ptr = qproc->mpss_region + offset; | ||
559 | |||
560 | if (phdr->p_filesz) { | ||
561 | snprintf(seg_name, sizeof(seg_name), "modem.b%02d", i); | ||
562 | ret = request_firmware(&seg_fw, seg_name, qproc->dev); | ||
563 | if (ret) { | ||
564 | dev_err(qproc->dev, "failed to load %s\n", seg_name); | ||
565 | goto release_firmware; | ||
566 | } | ||
567 | |||
568 | memcpy(ptr, seg_fw->data, seg_fw->size); | ||
569 | |||
570 | release_firmware(seg_fw); | ||
571 | } | ||
572 | |||
573 | if (phdr->p_memsz > phdr->p_filesz) { | ||
574 | memset(ptr + phdr->p_filesz, 0, | ||
575 | phdr->p_memsz - phdr->p_filesz); | ||
576 | } | ||
577 | |||
416 | size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); | 578 | size = readl(qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); |
417 | if (!size) { | 579 | if (!size) { |
580 | boot_addr = relocate ? qproc->mpss_phys : min_addr; | ||
418 | writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); | 581 | writel(boot_addr, qproc->rmb_base + RMB_PMI_CODE_START_REG); |
419 | writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); | 582 | writel(RMB_CMD_LOAD_READY, qproc->rmb_base + RMB_MBA_COMMAND_REG); |
420 | } | 583 | } |
@@ -429,44 +592,6 @@ static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw) | |||
429 | else if (ret < 0) | 592 | else if (ret < 0) |
430 | dev_err(qproc->dev, "MPSS authentication failed: %d\n", ret); | 593 | dev_err(qproc->dev, "MPSS authentication failed: %d\n", ret); |
431 | 594 | ||
432 | return ret < 0 ? ret : 0; | ||
433 | } | ||
434 | |||
435 | static int q6v5_mpss_load(struct q6v5 *qproc) | ||
436 | { | ||
437 | const struct firmware *fw; | ||
438 | phys_addr_t fw_addr; | ||
439 | bool relocate; | ||
440 | int ret; | ||
441 | |||
442 | ret = request_firmware(&fw, MPSS_FIRMWARE_NAME, qproc->dev); | ||
443 | if (ret < 0) { | ||
444 | dev_err(qproc->dev, "unable to load " MPSS_FIRMWARE_NAME "\n"); | ||
445 | return ret; | ||
446 | } | ||
447 | |||
448 | ret = qcom_mdt_parse(fw, &fw_addr, NULL, &relocate); | ||
449 | if (ret) { | ||
450 | dev_err(qproc->dev, "failed to parse mdt header\n"); | ||
451 | goto release_firmware; | ||
452 | } | ||
453 | |||
454 | if (relocate) | ||
455 | qproc->mpss_reloc = fw_addr; | ||
456 | |||
457 | /* Initialize the RMB validator */ | ||
458 | writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); | ||
459 | |||
460 | ret = q6v5_mpss_init_image(qproc, fw); | ||
461 | if (ret) | ||
462 | goto release_firmware; | ||
463 | |||
464 | ret = qcom_mdt_load(qproc->rproc, fw, MPSS_FIRMWARE_NAME); | ||
465 | if (ret) | ||
466 | goto release_firmware; | ||
467 | |||
468 | ret = q6v5_mpss_validate(qproc, fw); | ||
469 | |||
470 | release_firmware: | 595 | release_firmware: |
471 | release_firmware(fw); | 596 | release_firmware(fw); |
472 | 597 | ||
@@ -478,29 +603,38 @@ static int q6v5_start(struct rproc *rproc) | |||
478 | struct q6v5 *qproc = (struct q6v5 *)rproc->priv; | 603 | struct q6v5 *qproc = (struct q6v5 *)rproc->priv; |
479 | int ret; | 604 | int ret; |
480 | 605 | ||
481 | ret = q6v5_regulator_enable(qproc); | 606 | ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, |
607 | qproc->proxy_reg_count); | ||
482 | if (ret) { | 608 | if (ret) { |
483 | dev_err(qproc->dev, "failed to enable supplies\n"); | 609 | dev_err(qproc->dev, "failed to enable proxy supplies\n"); |
484 | return ret; | 610 | return ret; |
485 | } | 611 | } |
486 | 612 | ||
613 | ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, | ||
614 | qproc->proxy_clk_count); | ||
615 | if (ret) { | ||
616 | dev_err(qproc->dev, "failed to enable proxy clocks\n"); | ||
617 | goto disable_proxy_reg; | ||
618 | } | ||
619 | |||
620 | ret = q6v5_regulator_enable(qproc, qproc->active_regs, | ||
621 | qproc->active_reg_count); | ||
622 | if (ret) { | ||
623 | dev_err(qproc->dev, "failed to enable supplies\n"); | ||
624 | goto disable_proxy_clk; | ||
625 | } | ||
487 | ret = reset_control_deassert(qproc->mss_restart); | 626 | ret = reset_control_deassert(qproc->mss_restart); |
488 | if (ret) { | 627 | if (ret) { |
489 | dev_err(qproc->dev, "failed to deassert mss restart\n"); | 628 | dev_err(qproc->dev, "failed to deassert mss restart\n"); |
490 | goto disable_vdd; | 629 | goto disable_vdd; |
491 | } | 630 | } |
492 | 631 | ||
493 | ret = clk_prepare_enable(qproc->ahb_clk); | 632 | ret = q6v5_clk_enable(qproc->dev, qproc->active_clks, |
494 | if (ret) | 633 | qproc->active_clk_count); |
634 | if (ret) { | ||
635 | dev_err(qproc->dev, "failed to enable clocks\n"); | ||
495 | goto assert_reset; | 636 | goto assert_reset; |
496 | 637 | } | |
497 | ret = clk_prepare_enable(qproc->axi_clk); | ||
498 | if (ret) | ||
499 | goto disable_ahb_clk; | ||
500 | |||
501 | ret = clk_prepare_enable(qproc->rom_clk); | ||
502 | if (ret) | ||
503 | goto disable_axi_clk; | ||
504 | 638 | ||
505 | writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); | 639 | writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); |
506 | 640 | ||
@@ -535,7 +669,10 @@ static int q6v5_start(struct rproc *rproc) | |||
535 | 669 | ||
536 | qproc->running = true; | 670 | qproc->running = true; |
537 | 671 | ||
538 | /* TODO: All done, release the handover resources */ | 672 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, |
673 | qproc->proxy_clk_count); | ||
674 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | ||
675 | qproc->proxy_reg_count); | ||
539 | 676 | ||
540 | return 0; | 677 | return 0; |
541 | 678 | ||
@@ -543,16 +680,19 @@ halt_axi_ports: | |||
543 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); | 680 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); |
544 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); | 681 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); |
545 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); | 682 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); |
546 | 683 | q6v5_clk_disable(qproc->dev, qproc->active_clks, | |
547 | clk_disable_unprepare(qproc->rom_clk); | 684 | qproc->active_clk_count); |
548 | disable_axi_clk: | ||
549 | clk_disable_unprepare(qproc->axi_clk); | ||
550 | disable_ahb_clk: | ||
551 | clk_disable_unprepare(qproc->ahb_clk); | ||
552 | assert_reset: | 685 | assert_reset: |
553 | reset_control_assert(qproc->mss_restart); | 686 | reset_control_assert(qproc->mss_restart); |
554 | disable_vdd: | 687 | disable_vdd: |
555 | q6v5_regulator_disable(qproc); | 688 | q6v5_regulator_disable(qproc, qproc->active_regs, |
689 | qproc->active_reg_count); | ||
690 | disable_proxy_clk: | ||
691 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, | ||
692 | qproc->proxy_clk_count); | ||
693 | disable_proxy_reg: | ||
694 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | ||
695 | qproc->proxy_reg_count); | ||
556 | 696 | ||
557 | return ret; | 697 | return ret; |
558 | } | 698 | } |
@@ -579,10 +719,10 @@ static int q6v5_stop(struct rproc *rproc) | |||
579 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); | 719 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); |
580 | 720 | ||
581 | reset_control_assert(qproc->mss_restart); | 721 | reset_control_assert(qproc->mss_restart); |
582 | clk_disable_unprepare(qproc->rom_clk); | 722 | q6v5_clk_disable(qproc->dev, qproc->active_clks, |
583 | clk_disable_unprepare(qproc->axi_clk); | 723 | qproc->active_clk_count); |
584 | clk_disable_unprepare(qproc->ahb_clk); | 724 | q6v5_regulator_disable(qproc, qproc->active_regs, |
585 | q6v5_regulator_disable(qproc); | 725 | qproc->active_reg_count); |
586 | 726 | ||
587 | return 0; | 727 | return 0; |
588 | } | 728 | } |
@@ -702,27 +842,27 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) | |||
702 | return 0; | 842 | return 0; |
703 | } | 843 | } |
704 | 844 | ||
705 | static int q6v5_init_clocks(struct q6v5 *qproc) | 845 | static int q6v5_init_clocks(struct device *dev, struct clk **clks, |
846 | char **clk_names) | ||
706 | { | 847 | { |
707 | qproc->ahb_clk = devm_clk_get(qproc->dev, "iface"); | 848 | int i; |
708 | if (IS_ERR(qproc->ahb_clk)) { | ||
709 | dev_err(qproc->dev, "failed to get iface clock\n"); | ||
710 | return PTR_ERR(qproc->ahb_clk); | ||
711 | } | ||
712 | 849 | ||
713 | qproc->axi_clk = devm_clk_get(qproc->dev, "bus"); | 850 | if (!clk_names) |
714 | if (IS_ERR(qproc->axi_clk)) { | 851 | return 0; |
715 | dev_err(qproc->dev, "failed to get bus clock\n"); | ||
716 | return PTR_ERR(qproc->axi_clk); | ||
717 | } | ||
718 | 852 | ||
719 | qproc->rom_clk = devm_clk_get(qproc->dev, "mem"); | 853 | for (i = 0; clk_names[i]; i++) { |
720 | if (IS_ERR(qproc->rom_clk)) { | 854 | clks[i] = devm_clk_get(dev, clk_names[i]); |
721 | dev_err(qproc->dev, "failed to get mem clock\n"); | 855 | if (IS_ERR(clks[i])) { |
722 | return PTR_ERR(qproc->rom_clk); | 856 | int rc = PTR_ERR(clks[i]); |
857 | |||
858 | if (rc != -EPROBE_DEFER) | ||
859 | dev_err(dev, "Failed to get %s clock\n", | ||
860 | clk_names[i]); | ||
861 | return rc; | ||
862 | } | ||
723 | } | 863 | } |
724 | 864 | ||
725 | return 0; | 865 | return i; |
726 | } | 866 | } |
727 | 867 | ||
728 | static int q6v5_init_reset(struct q6v5 *qproc) | 868 | static int q6v5_init_reset(struct q6v5 *qproc) |
@@ -805,12 +945,17 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) | |||
805 | 945 | ||
806 | static int q6v5_probe(struct platform_device *pdev) | 946 | static int q6v5_probe(struct platform_device *pdev) |
807 | { | 947 | { |
948 | const struct rproc_hexagon_res *desc; | ||
808 | struct q6v5 *qproc; | 949 | struct q6v5 *qproc; |
809 | struct rproc *rproc; | 950 | struct rproc *rproc; |
810 | int ret; | 951 | int ret; |
811 | 952 | ||
953 | desc = of_device_get_match_data(&pdev->dev); | ||
954 | if (!desc) | ||
955 | return -EINVAL; | ||
956 | |||
812 | rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, | 957 | rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, |
813 | MBA_FIRMWARE_NAME, sizeof(*qproc)); | 958 | desc->hexagon_mba_image, sizeof(*qproc)); |
814 | if (!rproc) { | 959 | if (!rproc) { |
815 | dev_err(&pdev->dev, "failed to allocate rproc\n"); | 960 | dev_err(&pdev->dev, "failed to allocate rproc\n"); |
816 | return -ENOMEM; | 961 | return -ENOMEM; |
@@ -834,13 +979,37 @@ static int q6v5_probe(struct platform_device *pdev) | |||
834 | if (ret) | 979 | if (ret) |
835 | goto free_rproc; | 980 | goto free_rproc; |
836 | 981 | ||
837 | ret = q6v5_init_clocks(qproc); | 982 | ret = q6v5_init_clocks(&pdev->dev, qproc->proxy_clks, |
838 | if (ret) | 983 | desc->proxy_clk_names); |
984 | if (ret < 0) { | ||
985 | dev_err(&pdev->dev, "Failed to get proxy clocks.\n"); | ||
839 | goto free_rproc; | 986 | goto free_rproc; |
987 | } | ||
988 | qproc->proxy_clk_count = ret; | ||
840 | 989 | ||
841 | ret = q6v5_regulator_init(qproc); | 990 | ret = q6v5_init_clocks(&pdev->dev, qproc->active_clks, |
842 | if (ret) | 991 | desc->active_clk_names); |
992 | if (ret < 0) { | ||
993 | dev_err(&pdev->dev, "Failed to get active clocks.\n"); | ||
994 | goto free_rproc; | ||
995 | } | ||
996 | qproc->active_clk_count = ret; | ||
997 | |||
998 | ret = q6v5_regulator_init(&pdev->dev, qproc->proxy_regs, | ||
999 | desc->proxy_supply); | ||
1000 | if (ret < 0) { | ||
1001 | dev_err(&pdev->dev, "Failed to get proxy regulators.\n"); | ||
1002 | goto free_rproc; | ||
1003 | } | ||
1004 | qproc->proxy_reg_count = ret; | ||
1005 | |||
1006 | ret = q6v5_regulator_init(&pdev->dev, qproc->active_regs, | ||
1007 | desc->active_supply); | ||
1008 | if (ret < 0) { | ||
1009 | dev_err(&pdev->dev, "Failed to get active regulators.\n"); | ||
843 | goto free_rproc; | 1010 | goto free_rproc; |
1011 | } | ||
1012 | qproc->active_reg_count = ret; | ||
844 | 1013 | ||
845 | ret = q6v5_init_reset(qproc); | 1014 | ret = q6v5_init_reset(qproc); |
846 | if (ret) | 1015 | if (ret) |
@@ -868,6 +1037,8 @@ static int q6v5_probe(struct platform_device *pdev) | |||
868 | goto free_rproc; | 1037 | goto free_rproc; |
869 | } | 1038 | } |
870 | 1039 | ||
1040 | qcom_add_smd_subdev(rproc, &qproc->smd_subdev); | ||
1041 | |||
871 | ret = rproc_add(rproc); | 1042 | ret = rproc_add(rproc); |
872 | if (ret) | 1043 | if (ret) |
873 | goto free_rproc; | 1044 | goto free_rproc; |
@@ -885,13 +1056,83 @@ static int q6v5_remove(struct platform_device *pdev) | |||
885 | struct q6v5 *qproc = platform_get_drvdata(pdev); | 1056 | struct q6v5 *qproc = platform_get_drvdata(pdev); |
886 | 1057 | ||
887 | rproc_del(qproc->rproc); | 1058 | rproc_del(qproc->rproc); |
1059 | |||
1060 | qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); | ||
888 | rproc_free(qproc->rproc); | 1061 | rproc_free(qproc->rproc); |
889 | 1062 | ||
890 | return 0; | 1063 | return 0; |
891 | } | 1064 | } |
892 | 1065 | ||
1066 | static const struct rproc_hexagon_res msm8916_mss = { | ||
1067 | .hexagon_mba_image = "mba.mbn", | ||
1068 | .proxy_supply = (struct qcom_mss_reg_res[]) { | ||
1069 | { | ||
1070 | .supply = "mx", | ||
1071 | .uV = 1050000, | ||
1072 | }, | ||
1073 | { | ||
1074 | .supply = "cx", | ||
1075 | .uA = 100000, | ||
1076 | }, | ||
1077 | { | ||
1078 | .supply = "pll", | ||
1079 | .uA = 100000, | ||
1080 | }, | ||
1081 | {} | ||
1082 | }, | ||
1083 | .proxy_clk_names = (char*[]){ | ||
1084 | "xo", | ||
1085 | NULL | ||
1086 | }, | ||
1087 | .active_clk_names = (char*[]){ | ||
1088 | "iface", | ||
1089 | "bus", | ||
1090 | "mem", | ||
1091 | NULL | ||
1092 | }, | ||
1093 | }; | ||
1094 | |||
1095 | static const struct rproc_hexagon_res msm8974_mss = { | ||
1096 | .hexagon_mba_image = "mba.b00", | ||
1097 | .proxy_supply = (struct qcom_mss_reg_res[]) { | ||
1098 | { | ||
1099 | .supply = "mx", | ||
1100 | .uV = 1050000, | ||
1101 | }, | ||
1102 | { | ||
1103 | .supply = "cx", | ||
1104 | .uA = 100000, | ||
1105 | }, | ||
1106 | { | ||
1107 | .supply = "pll", | ||
1108 | .uA = 100000, | ||
1109 | }, | ||
1110 | {} | ||
1111 | }, | ||
1112 | .active_supply = (struct qcom_mss_reg_res[]) { | ||
1113 | { | ||
1114 | .supply = "mss", | ||
1115 | .uV = 1050000, | ||
1116 | .uA = 100000, | ||
1117 | }, | ||
1118 | {} | ||
1119 | }, | ||
1120 | .proxy_clk_names = (char*[]){ | ||
1121 | "xo", | ||
1122 | NULL | ||
1123 | }, | ||
1124 | .active_clk_names = (char*[]){ | ||
1125 | "iface", | ||
1126 | "bus", | ||
1127 | "mem", | ||
1128 | NULL | ||
1129 | }, | ||
1130 | }; | ||
1131 | |||
893 | static const struct of_device_id q6v5_of_match[] = { | 1132 | static const struct of_device_id q6v5_of_match[] = { |
894 | { .compatible = "qcom,q6v5-pil", }, | 1133 | { .compatible = "qcom,q6v5-pil", .data = &msm8916_mss}, |
1134 | { .compatible = "qcom,msm8916-mss-pil", .data = &msm8916_mss}, | ||
1135 | { .compatible = "qcom,msm8974-mss-pil", .data = &msm8974_mss}, | ||
895 | { }, | 1136 | { }, |
896 | }; | 1137 | }; |
897 | MODULE_DEVICE_TABLE(of, q6v5_of_match); | 1138 | MODULE_DEVICE_TABLE(of, q6v5_of_match); |
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index ebd61f5d18bb..c7686393d505 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c | |||
@@ -28,11 +28,12 @@ | |||
28 | #include <linux/qcom_scm.h> | 28 | #include <linux/qcom_scm.h> |
29 | #include <linux/regulator/consumer.h> | 29 | #include <linux/regulator/consumer.h> |
30 | #include <linux/remoteproc.h> | 30 | #include <linux/remoteproc.h> |
31 | #include <linux/soc/qcom/mdt_loader.h> | ||
31 | #include <linux/soc/qcom/smem.h> | 32 | #include <linux/soc/qcom/smem.h> |
32 | #include <linux/soc/qcom/smem_state.h> | 33 | #include <linux/soc/qcom/smem_state.h> |
33 | #include <linux/rpmsg/qcom_smd.h> | 34 | #include <linux/rpmsg/qcom_smd.h> |
34 | 35 | ||
35 | #include "qcom_mdt_loader.h" | 36 | #include "qcom_common.h" |
36 | #include "remoteproc_internal.h" | 37 | #include "remoteproc_internal.h" |
37 | #include "qcom_wcnss.h" | 38 | #include "qcom_wcnss.h" |
38 | 39 | ||
@@ -96,9 +97,7 @@ struct qcom_wcnss { | |||
96 | void *mem_region; | 97 | void *mem_region; |
97 | size_t mem_size; | 98 | size_t mem_size; |
98 | 99 | ||
99 | struct device_node *smd_node; | 100 | struct qcom_rproc_subdev smd_subdev; |
100 | struct qcom_smd_edge *smd_edge; | ||
101 | struct rproc_subdev smd_subdev; | ||
102 | }; | 101 | }; |
103 | 102 | ||
104 | static const struct wcnss_data riva_data = { | 103 | static const struct wcnss_data riva_data = { |
@@ -152,34 +151,9 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, | |||
152 | static int wcnss_load(struct rproc *rproc, const struct firmware *fw) | 151 | static int wcnss_load(struct rproc *rproc, const struct firmware *fw) |
153 | { | 152 | { |
154 | struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; | 153 | struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; |
155 | phys_addr_t fw_addr; | ||
156 | size_t fw_size; | ||
157 | bool relocate; | ||
158 | int ret; | ||
159 | |||
160 | ret = qcom_scm_pas_init_image(WCNSS_PAS_ID, fw->data, fw->size); | ||
161 | if (ret) { | ||
162 | dev_err(&rproc->dev, "invalid firmware metadata\n"); | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate); | ||
167 | if (ret) { | ||
168 | dev_err(&rproc->dev, "failed to parse mdt header\n"); | ||
169 | return ret; | ||
170 | } | ||
171 | 154 | ||
172 | if (relocate) { | 155 | return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, |
173 | wcnss->mem_reloc = fw_addr; | 156 | wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size); |
174 | |||
175 | ret = qcom_scm_pas_mem_setup(WCNSS_PAS_ID, wcnss->mem_phys, fw_size); | ||
176 | if (ret) { | ||
177 | dev_err(&rproc->dev, "unable to setup memory for image\n"); | ||
178 | return ret; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | return qcom_mdt_load(rproc, fw, rproc->firmware); | ||
183 | } | 157 | } |
184 | 158 | ||
185 | static const struct rproc_fw_ops wcnss_fw_ops = { | 159 | static const struct rproc_fw_ops wcnss_fw_ops = { |
@@ -400,23 +374,6 @@ static irqreturn_t wcnss_stop_ack_interrupt(int irq, void *dev) | |||
400 | return IRQ_HANDLED; | 374 | return IRQ_HANDLED; |
401 | } | 375 | } |
402 | 376 | ||
403 | static int wcnss_smd_probe(struct rproc_subdev *subdev) | ||
404 | { | ||
405 | struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev); | ||
406 | |||
407 | wcnss->smd_edge = qcom_smd_register_edge(wcnss->dev, wcnss->smd_node); | ||
408 | |||
409 | return IS_ERR(wcnss->smd_edge) ? PTR_ERR(wcnss->smd_edge) : 0; | ||
410 | } | ||
411 | |||
412 | static void wcnss_smd_remove(struct rproc_subdev *subdev) | ||
413 | { | ||
414 | struct qcom_wcnss *wcnss = container_of(subdev, struct qcom_wcnss, smd_subdev); | ||
415 | |||
416 | qcom_smd_unregister_edge(wcnss->smd_edge); | ||
417 | wcnss->smd_edge = NULL; | ||
418 | } | ||
419 | |||
420 | static int wcnss_init_regulators(struct qcom_wcnss *wcnss, | 377 | static int wcnss_init_regulators(struct qcom_wcnss *wcnss, |
421 | const struct wcnss_vreg_info *info, | 378 | const struct wcnss_vreg_info *info, |
422 | int num_vregs) | 379 | int num_vregs) |
@@ -599,9 +556,7 @@ static int wcnss_probe(struct platform_device *pdev) | |||
599 | } | 556 | } |
600 | } | 557 | } |
601 | 558 | ||
602 | wcnss->smd_node = of_get_child_by_name(pdev->dev.of_node, "smd-edge"); | 559 | qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); |
603 | if (wcnss->smd_node) | ||
604 | rproc_add_subdev(rproc, &wcnss->smd_subdev, wcnss_smd_probe, wcnss_smd_remove); | ||
605 | 560 | ||
606 | ret = rproc_add(rproc); | 561 | ret = rproc_add(rproc); |
607 | if (ret) | 562 | if (ret) |
@@ -621,9 +576,10 @@ static int wcnss_remove(struct platform_device *pdev) | |||
621 | 576 | ||
622 | of_platform_depopulate(&pdev->dev); | 577 | of_platform_depopulate(&pdev->dev); |
623 | 578 | ||
624 | of_node_put(wcnss->smd_node); | ||
625 | qcom_smem_state_put(wcnss->state); | 579 | qcom_smem_state_put(wcnss->state); |
626 | rproc_del(wcnss->rproc); | 580 | rproc_del(wcnss->rproc); |
581 | |||
582 | qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev); | ||
627 | rproc_free(wcnss->rproc); | 583 | rproc_free(wcnss->rproc); |
628 | 584 | ||
629 | return 0; | 585 | return 0; |
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 90b05c72186c..3dabb20b8d5d 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c | |||
@@ -961,48 +961,35 @@ clean_up: | |||
961 | } | 961 | } |
962 | 962 | ||
963 | /* | 963 | /* |
964 | * take a firmware and look for virtio devices to register. | 964 | * take a firmware and boot it up. |
965 | * | 965 | * |
966 | * Note: this function is called asynchronously upon registration of the | 966 | * Note: this function is called asynchronously upon registration of the |
967 | * remote processor (so we must wait until it completes before we try | 967 | * remote processor (so we must wait until it completes before we try |
968 | * to unregister the device. one other option is just to use kref here, | 968 | * to unregister the device. one other option is just to use kref here, |
969 | * that might be cleaner). | 969 | * that might be cleaner). |
970 | */ | 970 | */ |
971 | static void rproc_fw_config_virtio(const struct firmware *fw, void *context) | 971 | static void rproc_auto_boot_callback(const struct firmware *fw, void *context) |
972 | { | 972 | { |
973 | struct rproc *rproc = context; | 973 | struct rproc *rproc = context; |
974 | 974 | ||
975 | /* if rproc is marked always-on, request it to boot */ | 975 | rproc_boot(rproc); |
976 | if (rproc->auto_boot) | ||
977 | rproc_boot(rproc); | ||
978 | 976 | ||
979 | release_firmware(fw); | 977 | release_firmware(fw); |
980 | /* allow rproc_del() contexts, if any, to proceed */ | ||
981 | complete_all(&rproc->firmware_loading_complete); | ||
982 | } | 978 | } |
983 | 979 | ||
984 | static int rproc_add_virtio_devices(struct rproc *rproc) | 980 | static int rproc_trigger_auto_boot(struct rproc *rproc) |
985 | { | 981 | { |
986 | int ret; | 982 | int ret; |
987 | 983 | ||
988 | /* rproc_del() calls must wait until async loader completes */ | ||
989 | init_completion(&rproc->firmware_loading_complete); | ||
990 | |||
991 | /* | 984 | /* |
992 | * We must retrieve early virtio configuration info from | ||
993 | * the firmware (e.g. whether to register a virtio device, | ||
994 | * what virtio features does it support, ...). | ||
995 | * | ||
996 | * We're initiating an asynchronous firmware loading, so we can | 985 | * We're initiating an asynchronous firmware loading, so we can |
997 | * be built-in kernel code, without hanging the boot process. | 986 | * be built-in kernel code, without hanging the boot process. |
998 | */ | 987 | */ |
999 | ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, | 988 | ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, |
1000 | rproc->firmware, &rproc->dev, GFP_KERNEL, | 989 | rproc->firmware, &rproc->dev, GFP_KERNEL, |
1001 | rproc, rproc_fw_config_virtio); | 990 | rproc, rproc_auto_boot_callback); |
1002 | if (ret < 0) { | 991 | if (ret < 0) |
1003 | dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret); | 992 | dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret); |
1004 | complete_all(&rproc->firmware_loading_complete); | ||
1005 | } | ||
1006 | 993 | ||
1007 | return ret; | 994 | return ret; |
1008 | } | 995 | } |
@@ -1099,6 +1086,12 @@ static int __rproc_boot(struct rproc *rproc) | |||
1099 | return ret; | 1086 | return ret; |
1100 | } | 1087 | } |
1101 | 1088 | ||
1089 | if (rproc->state == RPROC_DELETED) { | ||
1090 | ret = -ENODEV; | ||
1091 | dev_err(dev, "can't boot deleted rproc %s\n", rproc->name); | ||
1092 | goto unlock_mutex; | ||
1093 | } | ||
1094 | |||
1102 | /* skip the boot process if rproc is already powered up */ | 1095 | /* skip the boot process if rproc is already powered up */ |
1103 | if (atomic_inc_return(&rproc->power) > 1) { | 1096 | if (atomic_inc_return(&rproc->power) > 1) { |
1104 | ret = 0; | 1097 | ret = 0; |
@@ -1287,9 +1280,13 @@ int rproc_add(struct rproc *rproc) | |||
1287 | 1280 | ||
1288 | /* create debugfs entries */ | 1281 | /* create debugfs entries */ |
1289 | rproc_create_debug_dir(rproc); | 1282 | rproc_create_debug_dir(rproc); |
1290 | ret = rproc_add_virtio_devices(rproc); | 1283 | |
1291 | if (ret < 0) | 1284 | /* if rproc is marked always-on, request it to boot */ |
1292 | return ret; | 1285 | if (rproc->auto_boot) { |
1286 | ret = rproc_trigger_auto_boot(rproc); | ||
1287 | if (ret < 0) | ||
1288 | return ret; | ||
1289 | } | ||
1293 | 1290 | ||
1294 | /* expose to rproc_get_by_phandle users */ | 1291 | /* expose to rproc_get_by_phandle users */ |
1295 | mutex_lock(&rproc_list_mutex); | 1292 | mutex_lock(&rproc_list_mutex); |
@@ -1315,8 +1312,6 @@ static void rproc_type_release(struct device *dev) | |||
1315 | 1312 | ||
1316 | dev_info(&rproc->dev, "releasing %s\n", rproc->name); | 1313 | dev_info(&rproc->dev, "releasing %s\n", rproc->name); |
1317 | 1314 | ||
1318 | rproc_delete_debug_dir(rproc); | ||
1319 | |||
1320 | idr_destroy(&rproc->notifyids); | 1315 | idr_destroy(&rproc->notifyids); |
1321 | 1316 | ||
1322 | if (rproc->index >= 0) | 1317 | if (rproc->index >= 0) |
@@ -1483,14 +1478,17 @@ int rproc_del(struct rproc *rproc) | |||
1483 | if (!rproc) | 1478 | if (!rproc) |
1484 | return -EINVAL; | 1479 | return -EINVAL; |
1485 | 1480 | ||
1486 | /* if rproc is just being registered, wait */ | ||
1487 | wait_for_completion(&rproc->firmware_loading_complete); | ||
1488 | |||
1489 | /* if rproc is marked always-on, rproc_add() booted it */ | 1481 | /* if rproc is marked always-on, rproc_add() booted it */ |
1490 | /* TODO: make sure this works with rproc->power > 1 */ | 1482 | /* TODO: make sure this works with rproc->power > 1 */ |
1491 | if (rproc->auto_boot) | 1483 | if (rproc->auto_boot) |
1492 | rproc_shutdown(rproc); | 1484 | rproc_shutdown(rproc); |
1493 | 1485 | ||
1486 | mutex_lock(&rproc->lock); | ||
1487 | rproc->state = RPROC_DELETED; | ||
1488 | mutex_unlock(&rproc->lock); | ||
1489 | |||
1490 | rproc_delete_debug_dir(rproc); | ||
1491 | |||
1494 | /* the rproc is downref'ed as soon as it's removed from the klist */ | 1492 | /* the rproc is downref'ed as soon as it's removed from the klist */ |
1495 | mutex_lock(&rproc_list_mutex); | 1493 | mutex_lock(&rproc_list_mutex); |
1496 | list_del(&rproc->node); | 1494 | list_del(&rproc->node); |
diff --git a/drivers/remoteproc/remoteproc_sysfs.c b/drivers/remoteproc/remoteproc_sysfs.c index bc5b0e00efb1..47be411400e5 100644 --- a/drivers/remoteproc/remoteproc_sysfs.c +++ b/drivers/remoteproc/remoteproc_sysfs.c | |||
@@ -73,6 +73,7 @@ static const char * const rproc_state_string[] = { | |||
73 | [RPROC_SUSPENDED] = "suspended", | 73 | [RPROC_SUSPENDED] = "suspended", |
74 | [RPROC_RUNNING] = "running", | 74 | [RPROC_RUNNING] = "running", |
75 | [RPROC_CRASHED] = "crashed", | 75 | [RPROC_CRASHED] = "crashed", |
76 | [RPROC_DELETED] = "deleted", | ||
76 | [RPROC_LAST] = "invalid", | 77 | [RPROC_LAST] = "invalid", |
77 | }; | 78 | }; |
78 | 79 | ||
diff --git a/drivers/remoteproc/st_remoteproc.c b/drivers/remoteproc/st_remoteproc.c index da4e152e9733..d534bf23dc56 100644 --- a/drivers/remoteproc/st_remoteproc.c +++ b/drivers/remoteproc/st_remoteproc.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/kernel.h> | 17 | #include <linux/kernel.h> |
18 | #include <linux/mailbox_client.h> | ||
18 | #include <linux/mfd/syscon.h> | 19 | #include <linux/mfd/syscon.h> |
19 | #include <linux/module.h> | 20 | #include <linux/module.h> |
20 | #include <linux/of.h> | 21 | #include <linux/of.h> |
@@ -25,6 +26,16 @@ | |||
25 | #include <linux/remoteproc.h> | 26 | #include <linux/remoteproc.h> |
26 | #include <linux/reset.h> | 27 | #include <linux/reset.h> |
27 | 28 | ||
29 | #include "remoteproc_internal.h" | ||
30 | |||
31 | #define ST_RPROC_VQ0 0 | ||
32 | #define ST_RPROC_VQ1 1 | ||
33 | #define ST_RPROC_MAX_VRING 2 | ||
34 | |||
35 | #define MBOX_RX 0 | ||
36 | #define MBOX_TX 1 | ||
37 | #define MBOX_MAX 2 | ||
38 | |||
28 | struct st_rproc_config { | 39 | struct st_rproc_config { |
29 | bool sw_reset; | 40 | bool sw_reset; |
30 | bool pwr_reset; | 41 | bool pwr_reset; |
@@ -39,8 +50,47 @@ struct st_rproc { | |||
39 | u32 clk_rate; | 50 | u32 clk_rate; |
40 | struct regmap *boot_base; | 51 | struct regmap *boot_base; |
41 | u32 boot_offset; | 52 | u32 boot_offset; |
53 | struct mbox_chan *mbox_chan[ST_RPROC_MAX_VRING * MBOX_MAX]; | ||
54 | struct mbox_client mbox_client_vq0; | ||
55 | struct mbox_client mbox_client_vq1; | ||
42 | }; | 56 | }; |
43 | 57 | ||
58 | static void st_rproc_mbox_callback(struct device *dev, u32 msg) | ||
59 | { | ||
60 | struct rproc *rproc = dev_get_drvdata(dev); | ||
61 | |||
62 | if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE) | ||
63 | dev_dbg(dev, "no message was found in vqid %d\n", msg); | ||
64 | } | ||
65 | |||
66 | static | ||
67 | void st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data) | ||
68 | { | ||
69 | st_rproc_mbox_callback(mbox_client->dev, 0); | ||
70 | } | ||
71 | |||
72 | static | ||
73 | void st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data) | ||
74 | { | ||
75 | st_rproc_mbox_callback(mbox_client->dev, 1); | ||
76 | } | ||
77 | |||
78 | static void st_rproc_kick(struct rproc *rproc, int vqid) | ||
79 | { | ||
80 | struct st_rproc *ddata = rproc->priv; | ||
81 | struct device *dev = rproc->dev.parent; | ||
82 | int ret; | ||
83 | |||
84 | /* send the index of the triggered virtqueue in the mailbox payload */ | ||
85 | if (WARN_ON(vqid >= ST_RPROC_MAX_VRING)) | ||
86 | return; | ||
87 | |||
88 | ret = mbox_send_message(ddata->mbox_chan[vqid * MBOX_MAX + MBOX_TX], | ||
89 | (void *)&vqid); | ||
90 | if (ret < 0) | ||
91 | dev_err(dev, "failed to send message via mbox: %d\n", ret); | ||
92 | } | ||
93 | |||
44 | static int st_rproc_start(struct rproc *rproc) | 94 | static int st_rproc_start(struct rproc *rproc) |
45 | { | 95 | { |
46 | struct st_rproc *ddata = rproc->priv; | 96 | struct st_rproc *ddata = rproc->priv; |
@@ -107,7 +157,8 @@ static int st_rproc_stop(struct rproc *rproc) | |||
107 | return sw_err ?: pwr_err; | 157 | return sw_err ?: pwr_err; |
108 | } | 158 | } |
109 | 159 | ||
110 | static struct rproc_ops st_rproc_ops = { | 160 | static const struct rproc_ops st_rproc_ops = { |
161 | .kick = st_rproc_kick, | ||
111 | .start = st_rproc_start, | 162 | .start = st_rproc_start, |
112 | .stop = st_rproc_stop, | 163 | .stop = st_rproc_stop, |
113 | }; | 164 | }; |
@@ -221,8 +272,9 @@ static int st_rproc_probe(struct platform_device *pdev) | |||
221 | struct st_rproc *ddata; | 272 | struct st_rproc *ddata; |
222 | struct device_node *np = dev->of_node; | 273 | struct device_node *np = dev->of_node; |
223 | struct rproc *rproc; | 274 | struct rproc *rproc; |
275 | struct mbox_chan *chan; | ||
224 | int enabled; | 276 | int enabled; |
225 | int ret; | 277 | int ret, i; |
226 | 278 | ||
227 | match = of_match_device(st_rproc_match, dev); | 279 | match = of_match_device(st_rproc_match, dev); |
228 | if (!match || !match->data) { | 280 | if (!match || !match->data) { |
@@ -247,7 +299,7 @@ static int st_rproc_probe(struct platform_device *pdev) | |||
247 | enabled = st_rproc_state(pdev); | 299 | enabled = st_rproc_state(pdev); |
248 | if (enabled < 0) { | 300 | if (enabled < 0) { |
249 | ret = enabled; | 301 | ret = enabled; |
250 | goto free_rproc; | 302 | goto free_clk; |
251 | } | 303 | } |
252 | 304 | ||
253 | if (enabled) { | 305 | if (enabled) { |
@@ -257,12 +309,67 @@ static int st_rproc_probe(struct platform_device *pdev) | |||
257 | clk_set_rate(ddata->clk, ddata->clk_rate); | 309 | clk_set_rate(ddata->clk, ddata->clk_rate); |
258 | } | 310 | } |
259 | 311 | ||
312 | if (of_get_property(np, "mbox-names", NULL)) { | ||
313 | ddata->mbox_client_vq0.dev = dev; | ||
314 | ddata->mbox_client_vq0.tx_done = NULL; | ||
315 | ddata->mbox_client_vq0.tx_block = false; | ||
316 | ddata->mbox_client_vq0.knows_txdone = false; | ||
317 | ddata->mbox_client_vq0.rx_callback = st_rproc_mbox_callback_vq0; | ||
318 | |||
319 | ddata->mbox_client_vq1.dev = dev; | ||
320 | ddata->mbox_client_vq1.tx_done = NULL; | ||
321 | ddata->mbox_client_vq1.tx_block = false; | ||
322 | ddata->mbox_client_vq1.knows_txdone = false; | ||
323 | ddata->mbox_client_vq1.rx_callback = st_rproc_mbox_callback_vq1; | ||
324 | |||
325 | /* | ||
326 | * To control a co-processor without IPC mechanism. | ||
327 | * This driver can be used without mbox and rpmsg. | ||
328 | */ | ||
329 | chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_rx"); | ||
330 | if (IS_ERR(chan)) { | ||
331 | dev_err(&rproc->dev, "failed to request mbox chan 0\n"); | ||
332 | ret = PTR_ERR(chan); | ||
333 | goto free_clk; | ||
334 | } | ||
335 | ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_RX] = chan; | ||
336 | |||
337 | chan = mbox_request_channel_byname(&ddata->mbox_client_vq0, "vq0_tx"); | ||
338 | if (IS_ERR(chan)) { | ||
339 | dev_err(&rproc->dev, "failed to request mbox chan 0\n"); | ||
340 | ret = PTR_ERR(chan); | ||
341 | goto free_mbox; | ||
342 | } | ||
343 | ddata->mbox_chan[ST_RPROC_VQ0 * MBOX_MAX + MBOX_TX] = chan; | ||
344 | |||
345 | chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_rx"); | ||
346 | if (IS_ERR(chan)) { | ||
347 | dev_err(&rproc->dev, "failed to request mbox chan 1\n"); | ||
348 | ret = PTR_ERR(chan); | ||
349 | goto free_mbox; | ||
350 | } | ||
351 | ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_RX] = chan; | ||
352 | |||
353 | chan = mbox_request_channel_byname(&ddata->mbox_client_vq1, "vq1_tx"); | ||
354 | if (IS_ERR(chan)) { | ||
355 | dev_err(&rproc->dev, "failed to request mbox chan 1\n"); | ||
356 | ret = PTR_ERR(chan); | ||
357 | goto free_mbox; | ||
358 | } | ||
359 | ddata->mbox_chan[ST_RPROC_VQ1 * MBOX_MAX + MBOX_TX] = chan; | ||
360 | } | ||
361 | |||
260 | ret = rproc_add(rproc); | 362 | ret = rproc_add(rproc); |
261 | if (ret) | 363 | if (ret) |
262 | goto free_rproc; | 364 | goto free_mbox; |
263 | 365 | ||
264 | return 0; | 366 | return 0; |
265 | 367 | ||
368 | free_mbox: | ||
369 | for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) | ||
370 | mbox_free_channel(ddata->mbox_chan[i]); | ||
371 | free_clk: | ||
372 | clk_unprepare(ddata->clk); | ||
266 | free_rproc: | 373 | free_rproc: |
267 | rproc_free(rproc); | 374 | rproc_free(rproc); |
268 | return ret; | 375 | return ret; |
@@ -272,6 +379,7 @@ static int st_rproc_remove(struct platform_device *pdev) | |||
272 | { | 379 | { |
273 | struct rproc *rproc = platform_get_drvdata(pdev); | 380 | struct rproc *rproc = platform_get_drvdata(pdev); |
274 | struct st_rproc *ddata = rproc->priv; | 381 | struct st_rproc *ddata = rproc->priv; |
382 | int i; | ||
275 | 383 | ||
276 | rproc_del(rproc); | 384 | rproc_del(rproc); |
277 | 385 | ||
@@ -279,6 +387,9 @@ static int st_rproc_remove(struct platform_device *pdev) | |||
279 | 387 | ||
280 | of_reserved_mem_device_release(&pdev->dev); | 388 | of_reserved_mem_device_release(&pdev->dev); |
281 | 389 | ||
390 | for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++) | ||
391 | mbox_free_channel(ddata->mbox_chan[i]); | ||
392 | |||
282 | rproc_free(rproc); | 393 | rproc_free(rproc); |
283 | 394 | ||
284 | return 0; | 395 | return 0; |
diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 507716c8721f..6cfd862f945b 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c | |||
@@ -200,7 +200,7 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) | |||
200 | return va; | 200 | return va; |
201 | } | 201 | } |
202 | 202 | ||
203 | static struct rproc_ops slim_rproc_ops = { | 203 | static const struct rproc_ops slim_rproc_ops = { |
204 | .start = slim_rproc_start, | 204 | .start = slim_rproc_start, |
205 | .stop = slim_rproc_stop, | 205 | .stop = slim_rproc_stop, |
206 | .da_to_va = slim_rproc_da_to_va, | 206 | .da_to_va = slim_rproc_da_to_va, |
diff --git a/drivers/remoteproc/wkup_m3_rproc.c b/drivers/remoteproc/wkup_m3_rproc.c index 18175d0331fd..1ada0e51fef6 100644 --- a/drivers/remoteproc/wkup_m3_rproc.c +++ b/drivers/remoteproc/wkup_m3_rproc.c | |||
@@ -111,7 +111,7 @@ static void *wkup_m3_rproc_da_to_va(struct rproc *rproc, u64 da, int len) | |||
111 | return va; | 111 | return va; |
112 | } | 112 | } |
113 | 113 | ||
114 | static struct rproc_ops wkup_m3_rproc_ops = { | 114 | static const struct rproc_ops wkup_m3_rproc_ops = { |
115 | .start = wkup_m3_rproc_start, | 115 | .start = wkup_m3_rproc_start, |
116 | .stop = wkup_m3_rproc_stop, | 116 | .stop = wkup_m3_rproc_stop, |
117 | .da_to_va = wkup_m3_rproc_da_to_va, | 117 | .da_to_va = wkup_m3_rproc_da_to_va, |
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 461b387d03cc..78b1bb7bcf20 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig | |||
@@ -10,6 +10,10 @@ config QCOM_GSBI | |||
10 | functions for connecting the underlying serial UART, SPI, and I2C | 10 | functions for connecting the underlying serial UART, SPI, and I2C |
11 | devices to the output pins. | 11 | devices to the output pins. |
12 | 12 | ||
13 | config QCOM_MDT_LOADER | ||
14 | tristate | ||
15 | select QCOM_SCM | ||
16 | |||
13 | config QCOM_PM | 17 | config QCOM_PM |
14 | bool "Qualcomm Power Management" | 18 | bool "Qualcomm Power Management" |
15 | depends on ARCH_QCOM && !ARM64 | 19 | depends on ARCH_QCOM && !ARM64 |
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index fdd664edf0bd..1f30260b06b8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o | 1 | obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o |
2 | obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o | ||
2 | obj-$(CONFIG_QCOM_PM) += spm.o | 3 | obj-$(CONFIG_QCOM_PM) += spm.o |
3 | obj-$(CONFIG_QCOM_SMD) += smd.o | 4 | obj-$(CONFIG_QCOM_SMD) += smd.o |
4 | obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o | 5 | obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o |
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c new file mode 100644 index 000000000000..bd63df0d14e0 --- /dev/null +++ b/drivers/soc/qcom/mdt_loader.c | |||
@@ -0,0 +1,204 @@ | |||
1 | /* | ||
2 | * Qualcomm Peripheral Image Loader | ||
3 | * | ||
4 | * Copyright (C) 2016 Linaro Ltd | ||
5 | * Copyright (C) 2015 Sony Mobile Communications Inc | ||
6 | * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/device.h> | ||
19 | #include <linux/elf.h> | ||
20 | #include <linux/firmware.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/qcom_scm.h> | ||
24 | #include <linux/sizes.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/soc/qcom/mdt_loader.h> | ||
27 | |||
28 | static bool mdt_phdr_valid(const struct elf32_phdr *phdr) | ||
29 | { | ||
30 | if (phdr->p_type != PT_LOAD) | ||
31 | return false; | ||
32 | |||
33 | if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) | ||
34 | return false; | ||
35 | |||
36 | if (!phdr->p_memsz) | ||
37 | return false; | ||
38 | |||
39 | return true; | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * qcom_mdt_get_size() - acquire size of the memory region needed to load mdt | ||
44 | * @fw: firmware object for the mdt file | ||
45 | * | ||
46 | * Returns size of the loaded firmware blob, or -EINVAL on failure. | ||
47 | */ | ||
48 | ssize_t qcom_mdt_get_size(const struct firmware *fw) | ||
49 | { | ||
50 | const struct elf32_phdr *phdrs; | ||
51 | const struct elf32_phdr *phdr; | ||
52 | const struct elf32_hdr *ehdr; | ||
53 | phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; | ||
54 | phys_addr_t max_addr = 0; | ||
55 | int i; | ||
56 | |||
57 | ehdr = (struct elf32_hdr *)fw->data; | ||
58 | phdrs = (struct elf32_phdr *)(ehdr + 1); | ||
59 | |||
60 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
61 | phdr = &phdrs[i]; | ||
62 | |||
63 | if (!mdt_phdr_valid(phdr)) | ||
64 | continue; | ||
65 | |||
66 | if (phdr->p_paddr < min_addr) | ||
67 | min_addr = phdr->p_paddr; | ||
68 | |||
69 | if (phdr->p_paddr + phdr->p_memsz > max_addr) | ||
70 | max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); | ||
71 | } | ||
72 | |||
73 | return min_addr < max_addr ? max_addr - min_addr : -EINVAL; | ||
74 | } | ||
75 | EXPORT_SYMBOL_GPL(qcom_mdt_get_size); | ||
76 | |||
77 | /** | ||
78 | * qcom_mdt_load() - load the firmware which header is loaded as fw | ||
79 | * @dev: device handle to associate resources with | ||
80 | * @fw: firmware object for the mdt file | ||
81 | * @firmware: name of the firmware, for construction of segment file names | ||
82 | * @pas_id: PAS identifier | ||
83 | * @mem_region: allocated memory region to load firmware into | ||
84 | * @mem_phys: physical address of allocated memory region | ||
85 | * @mem_size: size of the allocated memory region | ||
86 | * | ||
87 | * Returns 0 on success, negative errno otherwise. | ||
88 | */ | ||
89 | int qcom_mdt_load(struct device *dev, const struct firmware *fw, | ||
90 | const char *firmware, int pas_id, void *mem_region, | ||
91 | phys_addr_t mem_phys, size_t mem_size) | ||
92 | { | ||
93 | const struct elf32_phdr *phdrs; | ||
94 | const struct elf32_phdr *phdr; | ||
95 | const struct elf32_hdr *ehdr; | ||
96 | const struct firmware *seg_fw; | ||
97 | phys_addr_t mem_reloc; | ||
98 | phys_addr_t min_addr = (phys_addr_t)ULLONG_MAX; | ||
99 | phys_addr_t max_addr = 0; | ||
100 | size_t fw_name_len; | ||
101 | ssize_t offset; | ||
102 | char *fw_name; | ||
103 | bool relocate = false; | ||
104 | void *ptr; | ||
105 | int ret; | ||
106 | int i; | ||
107 | |||
108 | if (!fw || !mem_region || !mem_phys || !mem_size) | ||
109 | return -EINVAL; | ||
110 | |||
111 | ehdr = (struct elf32_hdr *)fw->data; | ||
112 | phdrs = (struct elf32_phdr *)(ehdr + 1); | ||
113 | |||
114 | fw_name_len = strlen(firmware); | ||
115 | if (fw_name_len <= 4) | ||
116 | return -EINVAL; | ||
117 | |||
118 | fw_name = kstrdup(firmware, GFP_KERNEL); | ||
119 | if (!fw_name) | ||
120 | return -ENOMEM; | ||
121 | |||
122 | ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); | ||
123 | if (ret) { | ||
124 | dev_err(dev, "invalid firmware metadata\n"); | ||
125 | goto out; | ||
126 | } | ||
127 | |||
128 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
129 | phdr = &phdrs[i]; | ||
130 | |||
131 | if (!mdt_phdr_valid(phdr)) | ||
132 | continue; | ||
133 | |||
134 | if (phdr->p_flags & QCOM_MDT_RELOCATABLE) | ||
135 | relocate = true; | ||
136 | |||
137 | if (phdr->p_paddr < min_addr) | ||
138 | min_addr = phdr->p_paddr; | ||
139 | |||
140 | if (phdr->p_paddr + phdr->p_memsz > max_addr) | ||
141 | max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); | ||
142 | } | ||
143 | |||
144 | if (relocate) { | ||
145 | ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); | ||
146 | if (ret) { | ||
147 | dev_err(dev, "unable to setup relocation\n"); | ||
148 | goto out; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * The image is relocatable, so offset each segment based on | ||
153 | * the lowest segment address. | ||
154 | */ | ||
155 | mem_reloc = min_addr; | ||
156 | } else { | ||
157 | /* | ||
158 | * Image is not relocatable, so offset each segment based on | ||
159 | * the allocated physical chunk of memory. | ||
160 | */ | ||
161 | mem_reloc = mem_phys; | ||
162 | } | ||
163 | |||
164 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
165 | phdr = &phdrs[i]; | ||
166 | |||
167 | if (!mdt_phdr_valid(phdr)) | ||
168 | continue; | ||
169 | |||
170 | offset = phdr->p_paddr - mem_reloc; | ||
171 | if (offset < 0 || offset + phdr->p_memsz > mem_size) { | ||
172 | dev_err(dev, "segment outside memory range\n"); | ||
173 | ret = -EINVAL; | ||
174 | break; | ||
175 | } | ||
176 | |||
177 | ptr = mem_region + offset; | ||
178 | |||
179 | if (phdr->p_filesz) { | ||
180 | sprintf(fw_name + fw_name_len - 3, "b%02d", i); | ||
181 | ret = request_firmware(&seg_fw, fw_name, dev); | ||
182 | if (ret) { | ||
183 | dev_err(dev, "failed to load %s\n", fw_name); | ||
184 | break; | ||
185 | } | ||
186 | |||
187 | memcpy(ptr, seg_fw->data, seg_fw->size); | ||
188 | |||
189 | release_firmware(seg_fw); | ||
190 | } | ||
191 | |||
192 | if (phdr->p_memsz > phdr->p_filesz) | ||
193 | memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); | ||
194 | } | ||
195 | |||
196 | out: | ||
197 | kfree(fw_name); | ||
198 | |||
199 | return ret; | ||
200 | } | ||
201 | EXPORT_SYMBOL_GPL(qcom_mdt_load); | ||
202 | |||
203 | MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); | ||
204 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index 8265d351c9f0..81da49564ff4 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h | |||
@@ -346,6 +346,7 @@ struct rproc_ops { | |||
346 | * a message. | 346 | * a message. |
347 | * @RPROC_RUNNING: device is up and running | 347 | * @RPROC_RUNNING: device is up and running |
348 | * @RPROC_CRASHED: device has crashed; need to start recovery | 348 | * @RPROC_CRASHED: device has crashed; need to start recovery |
349 | * @RPROC_DELETED: device is deleted | ||
349 | * @RPROC_LAST: just keep this one at the end | 350 | * @RPROC_LAST: just keep this one at the end |
350 | * | 351 | * |
351 | * Please note that the values of these states are used as indices | 352 | * Please note that the values of these states are used as indices |
@@ -359,7 +360,8 @@ enum rproc_state { | |||
359 | RPROC_SUSPENDED = 1, | 360 | RPROC_SUSPENDED = 1, |
360 | RPROC_RUNNING = 2, | 361 | RPROC_RUNNING = 2, |
361 | RPROC_CRASHED = 3, | 362 | RPROC_CRASHED = 3, |
362 | RPROC_LAST = 4, | 363 | RPROC_DELETED = 4, |
364 | RPROC_LAST = 5, | ||
363 | }; | 365 | }; |
364 | 366 | ||
365 | /** | 367 | /** |
@@ -397,7 +399,6 @@ enum rproc_crash_type { | |||
397 | * @num_traces: number of trace buffers | 399 | * @num_traces: number of trace buffers |
398 | * @carveouts: list of physically contiguous memory allocations | 400 | * @carveouts: list of physically contiguous memory allocations |
399 | * @mappings: list of iommu mappings we initiated, needed on shutdown | 401 | * @mappings: list of iommu mappings we initiated, needed on shutdown |
400 | * @firmware_loading_complete: marks e/o asynchronous firmware loading | ||
401 | * @bootaddr: address of first instruction to boot rproc with (optional) | 402 | * @bootaddr: address of first instruction to boot rproc with (optional) |
402 | * @rvdevs: list of remote virtio devices | 403 | * @rvdevs: list of remote virtio devices |
403 | * @subdevs: list of subdevices, to following the running state | 404 | * @subdevs: list of subdevices, to following the running state |
@@ -429,7 +430,6 @@ struct rproc { | |||
429 | int num_traces; | 430 | int num_traces; |
430 | struct list_head carveouts; | 431 | struct list_head carveouts; |
431 | struct list_head mappings; | 432 | struct list_head mappings; |
432 | struct completion firmware_loading_complete; | ||
433 | u32 bootaddr; | 433 | u32 bootaddr; |
434 | struct list_head rvdevs; | 434 | struct list_head rvdevs; |
435 | struct list_head subdevs; | 435 | struct list_head subdevs; |
diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h new file mode 100644 index 000000000000..f423001db3a9 --- /dev/null +++ b/include/linux/soc/qcom/mdt_loader.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef __QCOM_MDT_LOADER_H__ | ||
2 | #define __QCOM_MDT_LOADER_H__ | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | #define QCOM_MDT_TYPE_MASK (7 << 24) | ||
7 | #define QCOM_MDT_TYPE_HASH (2 << 24) | ||
8 | #define QCOM_MDT_RELOCATABLE BIT(27) | ||
9 | |||
10 | struct device; | ||
11 | struct firmware; | ||
12 | |||
13 | ssize_t qcom_mdt_get_size(const struct firmware *fw); | ||
14 | int qcom_mdt_load(struct device *dev, const struct firmware *fw, | ||
15 | const char *fw_name, int pas_id, void *mem_region, | ||
16 | phys_addr_t mem_phys, size_t mem_size); | ||
17 | |||
18 | #endif | ||