aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/remoteproc/qcom,adsp.txt41
-rw-r--r--Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt4
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/remoteproc/Kconfig18
-rw-r--r--drivers/remoteproc/Makefile2
-rw-r--r--drivers/remoteproc/da8xx_remoteproc.c2
-rw-r--r--drivers/remoteproc/omap_remoteproc.c2
-rw-r--r--drivers/remoteproc/qcom_adsp_pil.c134
-rw-r--r--drivers/remoteproc/qcom_common.c96
-rw-r--r--drivers/remoteproc/qcom_common.h22
-rw-r--r--drivers/remoteproc/qcom_mdt_loader.c180
-rw-r--r--drivers/remoteproc/qcom_mdt_loader.h13
-rw-r--r--drivers/remoteproc/qcom_q6v5_pil.c531
-rw-r--r--drivers/remoteproc/qcom_wcnss.c60
-rw-r--r--drivers/remoteproc/remoteproc_core.c52
-rw-r--r--drivers/remoteproc/remoteproc_sysfs.c1
-rw-r--r--drivers/remoteproc/st_remoteproc.c119
-rw-r--r--drivers/remoteproc/st_slim_rproc.c2
-rw-r--r--drivers/remoteproc/wkup_m3_rproc.c2
-rw-r--r--drivers/soc/qcom/Kconfig4
-rw-r--r--drivers/soc/qcom/Makefile1
-rw-r--r--drivers/soc/qcom/mdt_loader.c204
-rw-r--r--include/linux/remoteproc.h6
-rw-r--r--include/linux/soc/qcom/mdt_loader.h18
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
107The following example describes the resources needed to boot control the
108SLPI, 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
10548F: drivers/rpmsg/ 10548F: drivers/rpmsg/
10549F: Documentation/rpmsg.txt 10549F: Documentation/rpmsg.txt
10550F: include/linux/rpmsg.h 10550F: include/linux/rpmsg.h
10551F: include/linux/rpmsg/
10551 10552
10552RENESAS CLOCK DRIVERS 10553RENESAS CLOCK DRIVERS
10553M: Geert Uytterhoeven <geert+renesas@glider.be> 10554M: 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
11if REMOTEPROC 14if 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
35config WKUP_M3_RPROC 38config 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
83config QCOM_MDT_LOADER 88config QCOM_RPROC_COMMON
84 tristate 89 tristate
85 90
86config QCOM_Q6V5_PIL 91config 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
12obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o 12obj-$(CONFIG_WKUP_M3_RPROC) += wkup_m3_rproc.o
13obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o 13obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
14obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o 14obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o
15obj-$(CONFIG_QCOM_MDT_LOADER) += qcom_mdt_loader.o 15obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
16obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o 16obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
17obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o 17obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
18qcom_wcnss_pil-y += qcom_wcnss.o 18qcom_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
154static struct rproc_ops da8xx_rproc_ops = { 154static 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
180static struct rproc_ops omap_rproc_ops = { 180static 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 36struct 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
39struct qcom_adsp { 43struct 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
65static int adsp_load(struct rproc *rproc, const struct firmware *fw) 77static 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
98static const struct rproc_fw_ops adsp_fw_ops = { 85static 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
134disable_regulators: 129disable_px_supply:
130 regulator_disable(adsp->px_supply);
131disable_cx_supply:
135 regulator_disable(adsp->cx_supply); 132 regulator_disable(adsp->cx_supply);
136disable_clocks: 133disable_aggre2_clk:
134 clk_disable_unprepare(adsp->aggre2_clk);
135disable_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
312static int adsp_probe(struct platform_device *pdev) 326static 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
431static 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
438static 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
410static const struct of_device_id adsp_of_match[] = { 445static 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};
415MODULE_DEVICE_TABLE(of, adsp_of_match); 451MODULE_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 */
37struct 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}
46EXPORT_SYMBOL_GPL(qcom_mdt_find_rsc_table);
47
48static 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
57static 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 */
70void 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}
81EXPORT_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 */
88void 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}
93EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
94
95MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
96MODULE_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
7struct 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
15struct resource_table *qcom_mdt_find_rsc_table(struct rproc *rproc,
16 const struct firmware *fw,
17 int *tablesz);
18
19void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
20void 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 */
37struct 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}
46EXPORT_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 */
57int 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}
102EXPORT_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 */
112int 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}
177EXPORT_SYMBOL_GPL(qcom_mdt_load);
178
179MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
180MODULE_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
8struct resource_table * qcom_mdt_find_rsc_table(struct rproc *rproc, const struct firmware *fw, int *tablesz);
9int qcom_mdt_load(struct rproc *rproc, const struct firmware *fw, const char *fw_name);
10
11int 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
95struct reg_info {
96 struct regulator *reg;
97 int uV;
98 int uA;
99};
100
101struct qcom_mss_reg_res {
102 const char *supply;
103 int uV;
104 int uA;
105};
106
107struct 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
96struct q6v5 { 115struct 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
133enum { 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
140static int q6v5_regulator_init(struct q6v5 *qproc) 158static 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
184static 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;
220err:
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
163static int q6v5_regulator_enable(struct q6v5 *qproc) 234static 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); 250static 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;
265err:
266 for (i--; i >= 0; i--)
267 clk_disable_unprepare(clks[i]);
268
269 return rc;
178} 270}
179 271
180static void q6v5_regulator_disable(struct q6v5 *qproc) 272static 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 */ 281static 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
192static int q6v5_load(struct rproc *rproc, const struct firmware *fw) 291static 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
201static const struct rproc_fw_ops q6v5_fw_ops = { 300static 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
379static int q6v5_mpss_validate(struct q6v5 *qproc, const struct firmware *fw) 478static 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
492static 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
435static 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
470release_firmware: 595release_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);
548disable_axi_clk:
549 clk_disable_unprepare(qproc->axi_clk);
550disable_ahb_clk:
551 clk_disable_unprepare(qproc->ahb_clk);
552assert_reset: 685assert_reset:
553 reset_control_assert(qproc->mss_restart); 686 reset_control_assert(qproc->mss_restart);
554disable_vdd: 687disable_vdd:
555 q6v5_regulator_disable(qproc); 688 q6v5_regulator_disable(qproc, qproc->active_regs,
689 qproc->active_reg_count);
690disable_proxy_clk:
691 q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
692 qproc->proxy_clk_count);
693disable_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
705static int q6v5_init_clocks(struct q6v5 *qproc) 845static 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
728static int q6v5_init_reset(struct q6v5 *qproc) 868static int q6v5_init_reset(struct q6v5 *qproc)
@@ -805,12 +945,17 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc)
805 945
806static int q6v5_probe(struct platform_device *pdev) 946static 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
1066static 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
1095static 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
893static const struct of_device_id q6v5_of_match[] = { 1132static 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};
897MODULE_DEVICE_TABLE(of, q6v5_of_match); 1138MODULE_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
104static const struct wcnss_data riva_data = { 103static const struct wcnss_data riva_data = {
@@ -152,34 +151,9 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss,
152static int wcnss_load(struct rproc *rproc, const struct firmware *fw) 151static 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
185static const struct rproc_fw_ops wcnss_fw_ops = { 159static 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
403static 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
412static 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
420static int wcnss_init_regulators(struct qcom_wcnss *wcnss, 377static 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 */
971static void rproc_fw_config_virtio(const struct firmware *fw, void *context) 971static 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
984static int rproc_add_virtio_devices(struct rproc *rproc) 980static 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
28struct st_rproc_config { 39struct 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
58static 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
66static
67void st_rproc_mbox_callback_vq0(struct mbox_client *mbox_client, void *data)
68{
69 st_rproc_mbox_callback(mbox_client->dev, 0);
70}
71
72static
73void st_rproc_mbox_callback_vq1(struct mbox_client *mbox_client, void *data)
74{
75 st_rproc_mbox_callback(mbox_client->dev, 1);
76}
77
78static 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
44static int st_rproc_start(struct rproc *rproc) 94static 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
110static struct rproc_ops st_rproc_ops = { 160static 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
368free_mbox:
369 for (i = 0; i < ST_RPROC_MAX_VRING * MBOX_MAX; i++)
370 mbox_free_channel(ddata->mbox_chan[i]);
371free_clk:
372 clk_unprepare(ddata->clk);
266free_rproc: 373free_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
203static struct rproc_ops slim_rproc_ops = { 203static 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
114static struct rproc_ops wkup_m3_rproc_ops = { 114static 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
13config QCOM_MDT_LOADER
14 tristate
15 select QCOM_SCM
16
13config QCOM_PM 17config 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 @@
1obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o 1obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o
2obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o
2obj-$(CONFIG_QCOM_PM) += spm.o 3obj-$(CONFIG_QCOM_PM) += spm.o
3obj-$(CONFIG_QCOM_SMD) += smd.o 4obj-$(CONFIG_QCOM_SMD) += smd.o
4obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o 5obj-$(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
28static 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 */
48ssize_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}
75EXPORT_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 */
89int 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
196out:
197 kfree(fw_name);
198
199 return ret;
200}
201EXPORT_SYMBOL_GPL(qcom_mdt_load);
202
203MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format");
204MODULE_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
10struct device;
11struct firmware;
12
13ssize_t qcom_mdt_get_size(const struct firmware *fw);
14int 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