diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-18 19:42:04 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-18 19:42:04 -0400 |
commit | c54fc8658b501b412d006886ebe3e8543a30a122 (patch) | |
tree | beb6a843a1c838f0499d8417706296a8c97e0f0e | |
parent | 6eaac34ff30e189fda28110298ca9fbfb2f51e28 (diff) | |
parent | b2201ee554a5811f569f31280b0079e7d6177606 (diff) |
Merge tag 'rproc-v4.19' of git://github.com/andersson/remoteproc
Pull remoteproc updates from Bjorn Andersson:
"This adds support for pre-start and post-shutdown hooks for remoteproc
subdevices, refactors the Qualcomm Hexagon support to allow reuse
between several drivers, makes authentication in the MDT file loader
optional, migrates a few format strings to use %pK and migrates the
Davinci driver to use the reset framework"
* tag 'rproc-v4.19' of git://github.com/andersson/remoteproc:
remoteproc/davinci: use the reset framework
remoteproc/davinci: Mark error recovery as disabled
remoteproc: st_slim: replace "%p" with "%pK"
remoteproc: replace "%p" with "%pK"
remoteproc: qcom: fix Q6V5_WCSS dependencies
remoteproc: Reset table_ptr in rproc_start() failure paths
remoteproc: qcom: q6v5-pil: fix modem hang on SDM845 after axis2 clk unvote
remoteproc: qcom q6v5: fix modular build
remoteproc: Introduce prepare and unprepare for subdevices
remoteproc: rename subdev probe and remove functions
remoteproc: Make client initialize ops in rproc_subdev
remoteproc: Make start and stop in subdev optional
remoteproc: Rename subdev functions to start/stop
remoteproc: qcom: Introduce Hexagon V5 based WCSS driver
remoteproc: qcom: q6v5-pil: Use common q6v5 helpers
remoteproc: qcom: adsp: Use common q6v5 helpers
remoteproc: q6v5: Extract common resource handling
remoteproc: qcom: mdt_loader: Make the firmware authentication optional
-rw-r--r-- | Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | 7 | ||||
-rw-r--r-- | drivers/remoteproc/Kconfig | 23 | ||||
-rw-r--r-- | drivers/remoteproc/Makefile | 2 | ||||
-rw-r--r-- | drivers/remoteproc/da8xx_remoteproc.c | 37 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_adsp_pil.c | 156 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_common.c | 26 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5.c | 252 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5.h | 46 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_pil.c | 158 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_wcss.c | 601 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_sysmon.c | 5 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_core.c | 117 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_debugfs.c | 4 | ||||
-rw-r--r-- | drivers/remoteproc/remoteproc_virtio.c | 2 | ||||
-rw-r--r-- | drivers/remoteproc/st_slim_rproc.c | 3 | ||||
-rw-r--r-- | drivers/soc/qcom/mdt_loader.c | 87 | ||||
-rw-r--r-- | include/linux/remoteproc.h | 19 | ||||
-rw-r--r-- | include/linux/soc/qcom/mdt_loader.h | 4 |
18 files changed, 1188 insertions, 361 deletions
diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index d90182425450..601dd9f389aa 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | |||
@@ -8,6 +8,7 @@ on the Qualcomm Hexagon core. | |||
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,ipq8074-wcss-pil" | ||
11 | "qcom,msm8916-mss-pil", | 12 | "qcom,msm8916-mss-pil", |
12 | "qcom,msm8974-mss-pil" | 13 | "qcom,msm8974-mss-pil" |
13 | "qcom,msm8996-mss-pil" | 14 | "qcom,msm8996-mss-pil" |
@@ -50,11 +51,15 @@ on the Qualcomm Hexagon core. | |||
50 | Usage: required | 51 | Usage: required |
51 | Value type: <phandle> | 52 | Value type: <phandle> |
52 | Definition: reference to the reset-controller for the modem sub-system | 53 | Definition: reference to the reset-controller for the modem sub-system |
54 | reference to the list of 3 reset-controllers for the | ||
55 | wcss sub-system | ||
53 | 56 | ||
54 | - reset-names: | 57 | - reset-names: |
55 | Usage: required | 58 | Usage: required |
56 | Value type: <stringlist> | 59 | Value type: <stringlist> |
57 | Definition: must be "mss_restart" | 60 | Definition: must be "mss_restart" for the modem sub-system |
61 | Definition: must be "wcss_aon_reset", "wcss_reset", "wcss_q6_reset" | ||
62 | for the wcss syb-system | ||
58 | 63 | ||
59 | - cx-supply: | 64 | - cx-supply: |
60 | - mss-supply: | 65 | - mss-supply: |
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index cd1c168fd188..052d4dd347f9 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig | |||
@@ -93,6 +93,7 @@ config QCOM_ADSP_PIL | |||
93 | depends on QCOM_SYSMON || QCOM_SYSMON=n | 93 | depends on QCOM_SYSMON || QCOM_SYSMON=n |
94 | select MFD_SYSCON | 94 | select MFD_SYSCON |
95 | select QCOM_MDT_LOADER | 95 | select QCOM_MDT_LOADER |
96 | select QCOM_Q6V5_COMMON | ||
96 | select QCOM_RPROC_COMMON | 97 | select QCOM_RPROC_COMMON |
97 | select QCOM_SCM | 98 | select QCOM_SCM |
98 | help | 99 | help |
@@ -102,6 +103,11 @@ config QCOM_ADSP_PIL | |||
102 | config QCOM_RPROC_COMMON | 103 | config QCOM_RPROC_COMMON |
103 | tristate | 104 | tristate |
104 | 105 | ||
106 | config QCOM_Q6V5_COMMON | ||
107 | tristate | ||
108 | depends on ARCH_QCOM | ||
109 | depends on QCOM_SMEM | ||
110 | |||
105 | config QCOM_Q6V5_PIL | 111 | config QCOM_Q6V5_PIL |
106 | tristate "Qualcomm Hexagon V5 Peripherial Image Loader" | 112 | tristate "Qualcomm Hexagon V5 Peripherial Image Loader" |
107 | depends on OF && ARCH_QCOM | 113 | depends on OF && ARCH_QCOM |
@@ -110,12 +116,29 @@ config QCOM_Q6V5_PIL | |||
110 | depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n | 116 | depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n |
111 | depends on QCOM_SYSMON || QCOM_SYSMON=n | 117 | depends on QCOM_SYSMON || QCOM_SYSMON=n |
112 | select MFD_SYSCON | 118 | select MFD_SYSCON |
119 | select QCOM_Q6V5_COMMON | ||
113 | select QCOM_RPROC_COMMON | 120 | select QCOM_RPROC_COMMON |
114 | select QCOM_SCM | 121 | select QCOM_SCM |
115 | help | 122 | help |
116 | Say y here to support the Qualcomm Peripherial Image Loader for the | 123 | Say y here to support the Qualcomm Peripherial Image Loader for the |
117 | Hexagon V5 based remote processors. | 124 | Hexagon V5 based remote processors. |
118 | 125 | ||
126 | config QCOM_Q6V5_WCSS | ||
127 | tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader" | ||
128 | depends on OF && ARCH_QCOM | ||
129 | depends on QCOM_SMEM | ||
130 | depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) | ||
131 | depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n | ||
132 | depends on QCOM_SYSMON || QCOM_SYSMON=n | ||
133 | select MFD_SYSCON | ||
134 | select QCOM_MDT_LOADER | ||
135 | select QCOM_Q6V5_COMMON | ||
136 | select QCOM_RPROC_COMMON | ||
137 | select QCOM_SCM | ||
138 | help | ||
139 | Say y here to support the Qualcomm Peripheral Image Loader for the | ||
140 | Hexagon V5 based WCSS remote processors. | ||
141 | |||
119 | config QCOM_SYSMON | 142 | config QCOM_SYSMON |
120 | tristate "Qualcomm sysmon driver" | 143 | tristate "Qualcomm sysmon driver" |
121 | depends on RPMSG | 144 | depends on RPMSG |
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 02627ede8d4a..03332fa7e2ee 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile | |||
@@ -16,7 +16,9 @@ obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o | |||
16 | obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o | 16 | obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o |
17 | obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o | 17 | obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o |
18 | obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o | 18 | obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o |
19 | obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o | ||
19 | obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o | 20 | obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o |
21 | obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o | ||
20 | obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o | 22 | obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o |
21 | obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o | 23 | obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o |
22 | qcom_wcnss_pil-y += qcom_wcnss.o | 24 | qcom_wcnss_pil-y += qcom_wcnss.o |
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index b668e32996e2..e230bef71be1 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | #include <linux/bitops.h> | 11 | #include <linux/bitops.h> |
12 | #include <linux/clk.h> | 12 | #include <linux/clk.h> |
13 | #include <linux/reset.h> | ||
13 | #include <linux/err.h> | 14 | #include <linux/err.h> |
14 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
15 | #include <linux/io.h> | 16 | #include <linux/io.h> |
@@ -20,8 +21,6 @@ | |||
20 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
21 | #include <linux/remoteproc.h> | 22 | #include <linux/remoteproc.h> |
22 | 23 | ||
23 | #include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */ | ||
24 | |||
25 | #include "remoteproc_internal.h" | 24 | #include "remoteproc_internal.h" |
26 | 25 | ||
27 | static char *da8xx_fw_name; | 26 | static char *da8xx_fw_name; |
@@ -72,6 +71,7 @@ struct da8xx_rproc { | |||
72 | struct da8xx_rproc_mem *mem; | 71 | struct da8xx_rproc_mem *mem; |
73 | int num_mems; | 72 | int num_mems; |
74 | struct clk *dsp_clk; | 73 | struct clk *dsp_clk; |
74 | struct reset_control *dsp_reset; | ||
75 | void (*ack_fxn)(struct irq_data *data); | 75 | void (*ack_fxn)(struct irq_data *data); |
76 | struct irq_data *irq_data; | 76 | struct irq_data *irq_data; |
77 | void __iomem *chipsig; | 77 | void __iomem *chipsig; |
@@ -138,6 +138,7 @@ static int da8xx_rproc_start(struct rproc *rproc) | |||
138 | struct device *dev = rproc->dev.parent; | 138 | struct device *dev = rproc->dev.parent; |
139 | struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; | 139 | struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv; |
140 | struct clk *dsp_clk = drproc->dsp_clk; | 140 | struct clk *dsp_clk = drproc->dsp_clk; |
141 | struct reset_control *dsp_reset = drproc->dsp_reset; | ||
141 | int ret; | 142 | int ret; |
142 | 143 | ||
143 | /* hw requires the start (boot) address be on 1KB boundary */ | 144 | /* hw requires the start (boot) address be on 1KB boundary */ |
@@ -155,7 +156,12 @@ static int da8xx_rproc_start(struct rproc *rproc) | |||
155 | return ret; | 156 | return ret; |
156 | } | 157 | } |
157 | 158 | ||
158 | davinci_clk_reset_deassert(dsp_clk); | 159 | ret = reset_control_deassert(dsp_reset); |
160 | if (ret) { | ||
161 | dev_err(dev, "reset_control_deassert() failed: %d\n", ret); | ||
162 | clk_disable_unprepare(dsp_clk); | ||
163 | return ret; | ||
164 | } | ||
159 | 165 | ||
160 | return 0; | 166 | return 0; |
161 | } | 167 | } |
@@ -163,8 +169,15 @@ static int da8xx_rproc_start(struct rproc *rproc) | |||
163 | static int da8xx_rproc_stop(struct rproc *rproc) | 169 | static int da8xx_rproc_stop(struct rproc *rproc) |
164 | { | 170 | { |
165 | struct da8xx_rproc *drproc = rproc->priv; | 171 | struct da8xx_rproc *drproc = rproc->priv; |
172 | struct device *dev = rproc->dev.parent; | ||
173 | int ret; | ||
174 | |||
175 | ret = reset_control_assert(drproc->dsp_reset); | ||
176 | if (ret) { | ||
177 | dev_err(dev, "reset_control_assert() failed: %d\n", ret); | ||
178 | return ret; | ||
179 | } | ||
166 | 180 | ||
167 | davinci_clk_reset_assert(drproc->dsp_clk); | ||
168 | clk_disable_unprepare(drproc->dsp_clk); | 181 | clk_disable_unprepare(drproc->dsp_clk); |
169 | 182 | ||
170 | return 0; | 183 | return 0; |
@@ -232,6 +245,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev) | |||
232 | struct resource *bootreg_res; | 245 | struct resource *bootreg_res; |
233 | struct resource *chipsig_res; | 246 | struct resource *chipsig_res; |
234 | struct clk *dsp_clk; | 247 | struct clk *dsp_clk; |
248 | struct reset_control *dsp_reset; | ||
235 | void __iomem *chipsig; | 249 | void __iomem *chipsig; |
236 | void __iomem *bootreg; | 250 | void __iomem *bootreg; |
237 | int irq; | 251 | int irq; |
@@ -268,6 +282,15 @@ static int da8xx_rproc_probe(struct platform_device *pdev) | |||
268 | return PTR_ERR(dsp_clk); | 282 | return PTR_ERR(dsp_clk); |
269 | } | 283 | } |
270 | 284 | ||
285 | dsp_reset = devm_reset_control_get_exclusive(dev, NULL); | ||
286 | if (IS_ERR(dsp_reset)) { | ||
287 | if (PTR_ERR(dsp_reset) != -EPROBE_DEFER) | ||
288 | dev_err(dev, "unable to get reset control: %ld\n", | ||
289 | PTR_ERR(dsp_reset)); | ||
290 | |||
291 | return PTR_ERR(dsp_reset); | ||
292 | } | ||
293 | |||
271 | if (dev->of_node) { | 294 | if (dev->of_node) { |
272 | ret = of_reserved_mem_device_init(dev); | 295 | ret = of_reserved_mem_device_init(dev); |
273 | if (ret) { | 296 | if (ret) { |
@@ -284,9 +307,13 @@ static int da8xx_rproc_probe(struct platform_device *pdev) | |||
284 | goto free_mem; | 307 | goto free_mem; |
285 | } | 308 | } |
286 | 309 | ||
310 | /* error recovery is not supported at present */ | ||
311 | rproc->recovery_disabled = true; | ||
312 | |||
287 | drproc = rproc->priv; | 313 | drproc = rproc->priv; |
288 | drproc->rproc = rproc; | 314 | drproc->rproc = rproc; |
289 | drproc->dsp_clk = dsp_clk; | 315 | drproc->dsp_clk = dsp_clk; |
316 | drproc->dsp_reset = dsp_reset; | ||
290 | rproc->has_iommu = false; | 317 | rproc->has_iommu = false; |
291 | 318 | ||
292 | ret = da8xx_rproc_get_internal_memories(pdev, drproc); | 319 | ret = da8xx_rproc_get_internal_memories(pdev, drproc); |
@@ -309,7 +336,7 @@ static int da8xx_rproc_probe(struct platform_device *pdev) | |||
309 | * *not* in reset, but da8xx_rproc_start() needs the DSP to be | 336 | * *not* in reset, but da8xx_rproc_start() needs the DSP to be |
310 | * held in reset at the time it is called. | 337 | * held in reset at the time it is called. |
311 | */ | 338 | */ |
312 | ret = davinci_clk_reset_assert(drproc->dsp_clk); | 339 | ret = reset_control_assert(dsp_reset); |
313 | if (ret) | 340 | if (ret) |
314 | goto free_rproc; | 341 | goto free_rproc; |
315 | 342 | ||
diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c index 89a86ce07f99..d4339a6da616 100644 --- a/drivers/remoteproc/qcom_adsp_pil.c +++ b/drivers/remoteproc/qcom_adsp_pil.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/soc/qcom/smem_state.h> | 31 | #include <linux/soc/qcom/smem_state.h> |
32 | 32 | ||
33 | #include "qcom_common.h" | 33 | #include "qcom_common.h" |
34 | #include "qcom_q6v5.h" | ||
34 | #include "remoteproc_internal.h" | 35 | #include "remoteproc_internal.h" |
35 | 36 | ||
36 | struct adsp_data { | 37 | struct adsp_data { |
@@ -48,14 +49,7 @@ struct qcom_adsp { | |||
48 | struct device *dev; | 49 | struct device *dev; |
49 | struct rproc *rproc; | 50 | struct rproc *rproc; |
50 | 51 | ||
51 | int wdog_irq; | 52 | struct qcom_q6v5 q6v5; |
52 | int fatal_irq; | ||
53 | int ready_irq; | ||
54 | int handover_irq; | ||
55 | int stop_ack_irq; | ||
56 | |||
57 | struct qcom_smem_state *state; | ||
58 | unsigned stop_bit; | ||
59 | 53 | ||
60 | struct clk *xo; | 54 | struct clk *xo; |
61 | struct clk *aggre2_clk; | 55 | struct clk *aggre2_clk; |
@@ -96,6 +90,8 @@ static int adsp_start(struct rproc *rproc) | |||
96 | struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; | 90 | struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; |
97 | int ret; | 91 | int ret; |
98 | 92 | ||
93 | qcom_q6v5_prepare(&adsp->q6v5); | ||
94 | |||
99 | ret = clk_prepare_enable(adsp->xo); | 95 | ret = clk_prepare_enable(adsp->xo); |
100 | if (ret) | 96 | if (ret) |
101 | return ret; | 97 | return ret; |
@@ -119,16 +115,14 @@ static int adsp_start(struct rproc *rproc) | |||
119 | goto disable_px_supply; | 115 | goto disable_px_supply; |
120 | } | 116 | } |
121 | 117 | ||
122 | ret = wait_for_completion_timeout(&adsp->start_done, | 118 | ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); |
123 | msecs_to_jiffies(5000)); | 119 | if (ret == -ETIMEDOUT) { |
124 | if (!ret) { | ||
125 | dev_err(adsp->dev, "start timed out\n"); | 120 | dev_err(adsp->dev, "start timed out\n"); |
126 | qcom_scm_pas_shutdown(adsp->pas_id); | 121 | qcom_scm_pas_shutdown(adsp->pas_id); |
127 | ret = -ETIMEDOUT; | ||
128 | goto disable_px_supply; | 122 | goto disable_px_supply; |
129 | } | 123 | } |
130 | 124 | ||
131 | ret = 0; | 125 | return 0; |
132 | 126 | ||
133 | disable_px_supply: | 127 | disable_px_supply: |
134 | regulator_disable(adsp->px_supply); | 128 | regulator_disable(adsp->px_supply); |
@@ -142,28 +136,34 @@ disable_xo_clk: | |||
142 | return ret; | 136 | return ret; |
143 | } | 137 | } |
144 | 138 | ||
139 | static void qcom_pas_handover(struct qcom_q6v5 *q6v5) | ||
140 | { | ||
141 | struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); | ||
142 | |||
143 | regulator_disable(adsp->px_supply); | ||
144 | regulator_disable(adsp->cx_supply); | ||
145 | clk_disable_unprepare(adsp->aggre2_clk); | ||
146 | clk_disable_unprepare(adsp->xo); | ||
147 | } | ||
148 | |||
145 | static int adsp_stop(struct rproc *rproc) | 149 | static int adsp_stop(struct rproc *rproc) |
146 | { | 150 | { |
147 | struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; | 151 | struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; |
152 | int handover; | ||
148 | int ret; | 153 | int ret; |
149 | 154 | ||
150 | qcom_smem_state_update_bits(adsp->state, | 155 | ret = qcom_q6v5_request_stop(&adsp->q6v5); |
151 | BIT(adsp->stop_bit), | 156 | if (ret == -ETIMEDOUT) |
152 | BIT(adsp->stop_bit)); | ||
153 | |||
154 | ret = wait_for_completion_timeout(&adsp->stop_done, | ||
155 | msecs_to_jiffies(5000)); | ||
156 | if (ret == 0) | ||
157 | dev_err(adsp->dev, "timed out on wait\n"); | 157 | dev_err(adsp->dev, "timed out on wait\n"); |
158 | 158 | ||
159 | qcom_smem_state_update_bits(adsp->state, | ||
160 | BIT(adsp->stop_bit), | ||
161 | 0); | ||
162 | |||
163 | ret = qcom_scm_pas_shutdown(adsp->pas_id); | 159 | ret = qcom_scm_pas_shutdown(adsp->pas_id); |
164 | if (ret) | 160 | if (ret) |
165 | dev_err(adsp->dev, "failed to shutdown: %d\n", ret); | 161 | dev_err(adsp->dev, "failed to shutdown: %d\n", ret); |
166 | 162 | ||
163 | handover = qcom_q6v5_unprepare(&adsp->q6v5); | ||
164 | if (handover) | ||
165 | qcom_pas_handover(&adsp->q6v5); | ||
166 | |||
167 | return ret; | 167 | return ret; |
168 | } | 168 | } |
169 | 169 | ||
@@ -187,53 +187,6 @@ static const struct rproc_ops adsp_ops = { | |||
187 | .load = adsp_load, | 187 | .load = adsp_load, |
188 | }; | 188 | }; |
189 | 189 | ||
190 | static irqreturn_t adsp_wdog_interrupt(int irq, void *dev) | ||
191 | { | ||
192 | struct qcom_adsp *adsp = dev; | ||
193 | |||
194 | rproc_report_crash(adsp->rproc, RPROC_WATCHDOG); | ||
195 | |||
196 | return IRQ_HANDLED; | ||
197 | } | ||
198 | |||
199 | static irqreturn_t adsp_fatal_interrupt(int irq, void *dev) | ||
200 | { | ||
201 | struct qcom_adsp *adsp = dev; | ||
202 | size_t len; | ||
203 | char *msg; | ||
204 | |||
205 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, adsp->crash_reason_smem, &len); | ||
206 | if (!IS_ERR(msg) && len > 0 && msg[0]) | ||
207 | dev_err(adsp->dev, "fatal error received: %s\n", msg); | ||
208 | |||
209 | rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR); | ||
210 | |||
211 | return IRQ_HANDLED; | ||
212 | } | ||
213 | |||
214 | static irqreturn_t adsp_ready_interrupt(int irq, void *dev) | ||
215 | { | ||
216 | return IRQ_HANDLED; | ||
217 | } | ||
218 | |||
219 | static irqreturn_t adsp_handover_interrupt(int irq, void *dev) | ||
220 | { | ||
221 | struct qcom_adsp *adsp = dev; | ||
222 | |||
223 | complete(&adsp->start_done); | ||
224 | |||
225 | return IRQ_HANDLED; | ||
226 | } | ||
227 | |||
228 | static irqreturn_t adsp_stop_ack_interrupt(int irq, void *dev) | ||
229 | { | ||
230 | struct qcom_adsp *adsp = dev; | ||
231 | |||
232 | complete(&adsp->stop_done); | ||
233 | |||
234 | return IRQ_HANDLED; | ||
235 | } | ||
236 | |||
237 | static int adsp_init_clock(struct qcom_adsp *adsp) | 190 | static int adsp_init_clock(struct qcom_adsp *adsp) |
238 | { | 191 | { |
239 | int ret; | 192 | int ret; |
@@ -272,29 +225,6 @@ static int adsp_init_regulator(struct qcom_adsp *adsp) | |||
272 | return PTR_ERR_OR_ZERO(adsp->px_supply); | 225 | return PTR_ERR_OR_ZERO(adsp->px_supply); |
273 | } | 226 | } |
274 | 227 | ||
275 | static int adsp_request_irq(struct qcom_adsp *adsp, | ||
276 | struct platform_device *pdev, | ||
277 | const char *name, | ||
278 | irq_handler_t thread_fn) | ||
279 | { | ||
280 | int ret; | ||
281 | |||
282 | ret = platform_get_irq_byname(pdev, name); | ||
283 | if (ret < 0) { | ||
284 | dev_err(&pdev->dev, "no %s IRQ defined\n", name); | ||
285 | return ret; | ||
286 | } | ||
287 | |||
288 | ret = devm_request_threaded_irq(&pdev->dev, ret, | ||
289 | NULL, thread_fn, | ||
290 | IRQF_ONESHOT, | ||
291 | "adsp", adsp); | ||
292 | if (ret) | ||
293 | dev_err(&pdev->dev, "request %s IRQ failed\n", name); | ||
294 | |||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | static int adsp_alloc_memory_region(struct qcom_adsp *adsp) | 228 | static int adsp_alloc_memory_region(struct qcom_adsp *adsp) |
299 | { | 229 | { |
300 | struct device_node *node; | 230 | struct device_node *node; |
@@ -348,13 +278,9 @@ static int adsp_probe(struct platform_device *pdev) | |||
348 | adsp->dev = &pdev->dev; | 278 | adsp->dev = &pdev->dev; |
349 | adsp->rproc = rproc; | 279 | adsp->rproc = rproc; |
350 | adsp->pas_id = desc->pas_id; | 280 | adsp->pas_id = desc->pas_id; |
351 | adsp->crash_reason_smem = desc->crash_reason_smem; | ||
352 | adsp->has_aggre2_clk = desc->has_aggre2_clk; | 281 | adsp->has_aggre2_clk = desc->has_aggre2_clk; |
353 | platform_set_drvdata(pdev, adsp); | 282 | platform_set_drvdata(pdev, adsp); |
354 | 283 | ||
355 | init_completion(&adsp->start_done); | ||
356 | init_completion(&adsp->stop_done); | ||
357 | |||
358 | ret = adsp_alloc_memory_region(adsp); | 284 | ret = adsp_alloc_memory_region(adsp); |
359 | if (ret) | 285 | if (ret) |
360 | goto free_rproc; | 286 | goto free_rproc; |
@@ -367,37 +293,10 @@ static int adsp_probe(struct platform_device *pdev) | |||
367 | if (ret) | 293 | if (ret) |
368 | goto free_rproc; | 294 | goto free_rproc; |
369 | 295 | ||
370 | ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt); | 296 | ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, |
371 | if (ret < 0) | 297 | qcom_pas_handover); |
372 | goto free_rproc; | 298 | if (ret) |
373 | adsp->wdog_irq = ret; | ||
374 | |||
375 | ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt); | ||
376 | if (ret < 0) | ||
377 | goto free_rproc; | ||
378 | adsp->fatal_irq = ret; | ||
379 | |||
380 | ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt); | ||
381 | if (ret < 0) | ||
382 | goto free_rproc; | ||
383 | adsp->ready_irq = ret; | ||
384 | |||
385 | ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt); | ||
386 | if (ret < 0) | ||
387 | goto free_rproc; | ||
388 | adsp->handover_irq = ret; | ||
389 | |||
390 | ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt); | ||
391 | if (ret < 0) | ||
392 | goto free_rproc; | ||
393 | adsp->stop_ack_irq = ret; | ||
394 | |||
395 | adsp->state = qcom_smem_state_get(&pdev->dev, "stop", | ||
396 | &adsp->stop_bit); | ||
397 | if (IS_ERR(adsp->state)) { | ||
398 | ret = PTR_ERR(adsp->state); | ||
399 | goto free_rproc; | 299 | goto free_rproc; |
400 | } | ||
401 | 300 | ||
402 | qcom_add_glink_subdev(rproc, &adsp->glink_subdev); | 301 | qcom_add_glink_subdev(rproc, &adsp->glink_subdev); |
403 | qcom_add_smd_subdev(rproc, &adsp->smd_subdev); | 302 | qcom_add_smd_subdev(rproc, &adsp->smd_subdev); |
@@ -422,7 +321,6 @@ static int adsp_remove(struct platform_device *pdev) | |||
422 | { | 321 | { |
423 | struct qcom_adsp *adsp = platform_get_drvdata(pdev); | 322 | struct qcom_adsp *adsp = platform_get_drvdata(pdev); |
424 | 323 | ||
425 | qcom_smem_state_put(adsp->state); | ||
426 | rproc_del(adsp->rproc); | 324 | rproc_del(adsp->rproc); |
427 | 325 | ||
428 | qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); | 326 | qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); |
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index acfc99f82fb8..6f77840140bf 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c | |||
@@ -33,7 +33,7 @@ | |||
33 | 33 | ||
34 | static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); | 34 | static BLOCKING_NOTIFIER_HEAD(ssr_notifiers); |
35 | 35 | ||
36 | static int glink_subdev_probe(struct rproc_subdev *subdev) | 36 | static int glink_subdev_start(struct rproc_subdev *subdev) |
37 | { | 37 | { |
38 | struct qcom_rproc_glink *glink = to_glink_subdev(subdev); | 38 | struct qcom_rproc_glink *glink = to_glink_subdev(subdev); |
39 | 39 | ||
@@ -42,7 +42,7 @@ static int glink_subdev_probe(struct rproc_subdev *subdev) | |||
42 | return PTR_ERR_OR_ZERO(glink->edge); | 42 | return PTR_ERR_OR_ZERO(glink->edge); |
43 | } | 43 | } |
44 | 44 | ||
45 | static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed) | 45 | static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed) |
46 | { | 46 | { |
47 | struct qcom_rproc_glink *glink = to_glink_subdev(subdev); | 47 | struct qcom_rproc_glink *glink = to_glink_subdev(subdev); |
48 | 48 | ||
@@ -64,7 +64,10 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) | |||
64 | return; | 64 | return; |
65 | 65 | ||
66 | glink->dev = dev; | 66 | glink->dev = dev; |
67 | rproc_add_subdev(rproc, &glink->subdev, glink_subdev_probe, glink_subdev_remove); | 67 | glink->subdev.start = glink_subdev_start; |
68 | glink->subdev.stop = glink_subdev_stop; | ||
69 | |||
70 | rproc_add_subdev(rproc, &glink->subdev); | ||
68 | } | 71 | } |
69 | EXPORT_SYMBOL_GPL(qcom_add_glink_subdev); | 72 | EXPORT_SYMBOL_GPL(qcom_add_glink_subdev); |
70 | 73 | ||
@@ -126,7 +129,7 @@ int qcom_register_dump_segments(struct rproc *rproc, | |||
126 | } | 129 | } |
127 | EXPORT_SYMBOL_GPL(qcom_register_dump_segments); | 130 | EXPORT_SYMBOL_GPL(qcom_register_dump_segments); |
128 | 131 | ||
129 | static int smd_subdev_probe(struct rproc_subdev *subdev) | 132 | static int smd_subdev_start(struct rproc_subdev *subdev) |
130 | { | 133 | { |
131 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); | 134 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); |
132 | 135 | ||
@@ -135,7 +138,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev) | |||
135 | return PTR_ERR_OR_ZERO(smd->edge); | 138 | return PTR_ERR_OR_ZERO(smd->edge); |
136 | } | 139 | } |
137 | 140 | ||
138 | static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed) | 141 | static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed) |
139 | { | 142 | { |
140 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); | 143 | struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); |
141 | 144 | ||
@@ -157,7 +160,10 @@ void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) | |||
157 | return; | 160 | return; |
158 | 161 | ||
159 | smd->dev = dev; | 162 | smd->dev = dev; |
160 | rproc_add_subdev(rproc, &smd->subdev, smd_subdev_probe, smd_subdev_remove); | 163 | smd->subdev.start = smd_subdev_start; |
164 | smd->subdev.stop = smd_subdev_stop; | ||
165 | |||
166 | rproc_add_subdev(rproc, &smd->subdev); | ||
161 | } | 167 | } |
162 | EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); | 168 | EXPORT_SYMBOL_GPL(qcom_add_smd_subdev); |
163 | 169 | ||
@@ -202,11 +208,6 @@ void qcom_unregister_ssr_notifier(struct notifier_block *nb) | |||
202 | } | 208 | } |
203 | EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); | 209 | EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); |
204 | 210 | ||
205 | static int ssr_notify_start(struct rproc_subdev *subdev) | ||
206 | { | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) | 211 | static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) |
211 | { | 212 | { |
212 | struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); | 213 | struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); |
@@ -227,8 +228,9 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, | |||
227 | const char *ssr_name) | 228 | const char *ssr_name) |
228 | { | 229 | { |
229 | ssr->name = ssr_name; | 230 | ssr->name = ssr_name; |
231 | ssr->subdev.stop = ssr_notify_stop; | ||
230 | 232 | ||
231 | rproc_add_subdev(rproc, &ssr->subdev, ssr_notify_start, ssr_notify_stop); | 233 | rproc_add_subdev(rproc, &ssr->subdev); |
232 | } | 234 | } |
233 | EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); | 235 | EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev); |
234 | 236 | ||
diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c new file mode 100644 index 000000000000..61a760ee4aac --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5.c | |||
@@ -0,0 +1,252 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Qualcomm Peripheral Image Loader for Q6V5 | ||
4 | * | ||
5 | * Copyright (C) 2016-2018 Linaro Ltd. | ||
6 | * Copyright (C) 2014 Sony Mobile Communications AB | ||
7 | * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. | ||
8 | */ | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/soc/qcom/smem.h> | ||
14 | #include <linux/soc/qcom/smem_state.h> | ||
15 | #include <linux/remoteproc.h> | ||
16 | #include "qcom_q6v5.h" | ||
17 | |||
18 | /** | ||
19 | * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start | ||
20 | * @q6v5: reference to qcom_q6v5 context to be reinitialized | ||
21 | * | ||
22 | * Return: 0 on success, negative errno on failure | ||
23 | */ | ||
24 | int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5) | ||
25 | { | ||
26 | reinit_completion(&q6v5->start_done); | ||
27 | reinit_completion(&q6v5->stop_done); | ||
28 | |||
29 | q6v5->running = true; | ||
30 | q6v5->handover_issued = false; | ||
31 | |||
32 | enable_irq(q6v5->handover_irq); | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); | ||
37 | |||
38 | /** | ||
39 | * qcom_q6v5_unprepare() - unprepare the qcom_q6v5 context after stop | ||
40 | * @q6v5: reference to qcom_q6v5 context to be unprepared | ||
41 | * | ||
42 | * Return: 0 on success, 1 if handover hasn't yet been called | ||
43 | */ | ||
44 | int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5) | ||
45 | { | ||
46 | disable_irq(q6v5->handover_irq); | ||
47 | |||
48 | return !q6v5->handover_issued; | ||
49 | } | ||
50 | EXPORT_SYMBOL_GPL(qcom_q6v5_unprepare); | ||
51 | |||
52 | static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) | ||
53 | { | ||
54 | struct qcom_q6v5 *q6v5 = data; | ||
55 | size_t len; | ||
56 | char *msg; | ||
57 | |||
58 | /* Sometimes the stop triggers a watchdog rather than a stop-ack */ | ||
59 | if (!q6v5->running) { | ||
60 | complete(&q6v5->stop_done); | ||
61 | return IRQ_HANDLED; | ||
62 | } | ||
63 | |||
64 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); | ||
65 | if (!IS_ERR(msg) && len > 0 && msg[0]) | ||
66 | dev_err(q6v5->dev, "watchdog received: %s\n", msg); | ||
67 | else | ||
68 | dev_err(q6v5->dev, "watchdog without message\n"); | ||
69 | |||
70 | rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG); | ||
71 | |||
72 | return IRQ_HANDLED; | ||
73 | } | ||
74 | |||
75 | static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) | ||
76 | { | ||
77 | struct qcom_q6v5 *q6v5 = data; | ||
78 | size_t len; | ||
79 | char *msg; | ||
80 | |||
81 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); | ||
82 | if (!IS_ERR(msg) && len > 0 && msg[0]) | ||
83 | dev_err(q6v5->dev, "fatal error received: %s\n", msg); | ||
84 | else | ||
85 | dev_err(q6v5->dev, "fatal error without message\n"); | ||
86 | |||
87 | rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR); | ||
88 | |||
89 | return IRQ_HANDLED; | ||
90 | } | ||
91 | |||
92 | static irqreturn_t q6v5_ready_interrupt(int irq, void *data) | ||
93 | { | ||
94 | struct qcom_q6v5 *q6v5 = data; | ||
95 | |||
96 | complete(&q6v5->start_done); | ||
97 | |||
98 | return IRQ_HANDLED; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * qcom_q6v5_wait_for_start() - wait for remote processor start signal | ||
103 | * @q6v5: reference to qcom_q6v5 context | ||
104 | * @timeout: timeout to wait for the event, in jiffies | ||
105 | * | ||
106 | * qcom_q6v5_unprepare() should not be called when this function fails. | ||
107 | * | ||
108 | * Return: 0 on success, -ETIMEDOUT on timeout | ||
109 | */ | ||
110 | int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout) | ||
111 | { | ||
112 | int ret; | ||
113 | |||
114 | ret = wait_for_completion_timeout(&q6v5->start_done, timeout); | ||
115 | if (!ret) | ||
116 | disable_irq(q6v5->handover_irq); | ||
117 | |||
118 | return !ret ? -ETIMEDOUT : 0; | ||
119 | } | ||
120 | EXPORT_SYMBOL_GPL(qcom_q6v5_wait_for_start); | ||
121 | |||
122 | static irqreturn_t q6v5_handover_interrupt(int irq, void *data) | ||
123 | { | ||
124 | struct qcom_q6v5 *q6v5 = data; | ||
125 | |||
126 | if (q6v5->handover) | ||
127 | q6v5->handover(q6v5); | ||
128 | |||
129 | q6v5->handover_issued = true; | ||
130 | |||
131 | return IRQ_HANDLED; | ||
132 | } | ||
133 | |||
134 | static irqreturn_t q6v5_stop_interrupt(int irq, void *data) | ||
135 | { | ||
136 | struct qcom_q6v5 *q6v5 = data; | ||
137 | |||
138 | complete(&q6v5->stop_done); | ||
139 | |||
140 | return IRQ_HANDLED; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * qcom_q6v5_request_stop() - request the remote processor to stop | ||
145 | * @q6v5: reference to qcom_q6v5 context | ||
146 | * | ||
147 | * Return: 0 on success, negative errno on failure | ||
148 | */ | ||
149 | int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5) | ||
150 | { | ||
151 | int ret; | ||
152 | |||
153 | q6v5->running = false; | ||
154 | |||
155 | qcom_smem_state_update_bits(q6v5->state, | ||
156 | BIT(q6v5->stop_bit), BIT(q6v5->stop_bit)); | ||
157 | |||
158 | ret = wait_for_completion_timeout(&q6v5->stop_done, 5 * HZ); | ||
159 | |||
160 | qcom_smem_state_update_bits(q6v5->state, BIT(q6v5->stop_bit), 0); | ||
161 | |||
162 | return ret == 0 ? -ETIMEDOUT : 0; | ||
163 | } | ||
164 | EXPORT_SYMBOL_GPL(qcom_q6v5_request_stop); | ||
165 | |||
166 | /** | ||
167 | * qcom_q6v5_init() - initializer of the q6v5 common struct | ||
168 | * @q6v5: handle to be initialized | ||
169 | * @pdev: platform_device reference for acquiring resources | ||
170 | * @rproc: associated remoteproc instance | ||
171 | * @crash_reason: SMEM id for crash reason string, or 0 if none | ||
172 | * @handover: function to be called when proxy resources should be released | ||
173 | * | ||
174 | * Return: 0 on success, negative errno on failure | ||
175 | */ | ||
176 | int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, | ||
177 | struct rproc *rproc, int crash_reason, | ||
178 | void (*handover)(struct qcom_q6v5 *q6v5)) | ||
179 | { | ||
180 | int ret; | ||
181 | |||
182 | q6v5->rproc = rproc; | ||
183 | q6v5->dev = &pdev->dev; | ||
184 | q6v5->crash_reason = crash_reason; | ||
185 | q6v5->handover = handover; | ||
186 | |||
187 | init_completion(&q6v5->start_done); | ||
188 | init_completion(&q6v5->stop_done); | ||
189 | |||
190 | q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog"); | ||
191 | ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq, | ||
192 | NULL, q6v5_wdog_interrupt, | ||
193 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
194 | "q6v5 wdog", q6v5); | ||
195 | if (ret) { | ||
196 | dev_err(&pdev->dev, "failed to acquire wdog IRQ\n"); | ||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | q6v5->fatal_irq = platform_get_irq_byname(pdev, "fatal"); | ||
201 | ret = devm_request_threaded_irq(&pdev->dev, q6v5->fatal_irq, | ||
202 | NULL, q6v5_fatal_interrupt, | ||
203 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
204 | "q6v5 fatal", q6v5); | ||
205 | if (ret) { | ||
206 | dev_err(&pdev->dev, "failed to acquire fatal IRQ\n"); | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | q6v5->ready_irq = platform_get_irq_byname(pdev, "ready"); | ||
211 | ret = devm_request_threaded_irq(&pdev->dev, q6v5->ready_irq, | ||
212 | NULL, q6v5_ready_interrupt, | ||
213 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
214 | "q6v5 ready", q6v5); | ||
215 | if (ret) { | ||
216 | dev_err(&pdev->dev, "failed to acquire ready IRQ\n"); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | q6v5->handover_irq = platform_get_irq_byname(pdev, "handover"); | ||
221 | ret = devm_request_threaded_irq(&pdev->dev, q6v5->handover_irq, | ||
222 | NULL, q6v5_handover_interrupt, | ||
223 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
224 | "q6v5 handover", q6v5); | ||
225 | if (ret) { | ||
226 | dev_err(&pdev->dev, "failed to acquire handover IRQ\n"); | ||
227 | return ret; | ||
228 | } | ||
229 | disable_irq(q6v5->handover_irq); | ||
230 | |||
231 | q6v5->stop_irq = platform_get_irq_byname(pdev, "stop-ack"); | ||
232 | ret = devm_request_threaded_irq(&pdev->dev, q6v5->stop_irq, | ||
233 | NULL, q6v5_stop_interrupt, | ||
234 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
235 | "q6v5 stop", q6v5); | ||
236 | if (ret) { | ||
237 | dev_err(&pdev->dev, "failed to acquire stop-ack IRQ\n"); | ||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | q6v5->state = qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); | ||
242 | if (IS_ERR(q6v5->state)) { | ||
243 | dev_err(&pdev->dev, "failed to acquire stop state\n"); | ||
244 | return PTR_ERR(q6v5->state); | ||
245 | } | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | EXPORT_SYMBOL_GPL(qcom_q6v5_init); | ||
250 | |||
251 | MODULE_LICENSE("GPL v2"); | ||
252 | MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Q6V5"); | ||
diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h new file mode 100644 index 000000000000..7ac92c1e0f49 --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | |||
3 | #ifndef __QCOM_Q6V5_H__ | ||
4 | #define __QCOM_Q6V5_H__ | ||
5 | |||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/completion.h> | ||
8 | |||
9 | struct rproc; | ||
10 | struct qcom_smem_state; | ||
11 | |||
12 | struct qcom_q6v5 { | ||
13 | struct device *dev; | ||
14 | struct rproc *rproc; | ||
15 | |||
16 | struct qcom_smem_state *state; | ||
17 | unsigned stop_bit; | ||
18 | |||
19 | int wdog_irq; | ||
20 | int fatal_irq; | ||
21 | int ready_irq; | ||
22 | int handover_irq; | ||
23 | int stop_irq; | ||
24 | |||
25 | bool handover_issued; | ||
26 | |||
27 | struct completion start_done; | ||
28 | struct completion stop_done; | ||
29 | |||
30 | int crash_reason; | ||
31 | |||
32 | bool running; | ||
33 | |||
34 | void (*handover)(struct qcom_q6v5 *q6v5); | ||
35 | }; | ||
36 | |||
37 | int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, | ||
38 | struct rproc *rproc, int crash_reason, | ||
39 | void (*handover)(struct qcom_q6v5 *q6v5)); | ||
40 | |||
41 | int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); | ||
42 | int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); | ||
43 | int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5); | ||
44 | int qcom_q6v5_wait_for_start(struct qcom_q6v5 *q6v5, int timeout); | ||
45 | |||
46 | #endif | ||
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c index 2bf8e7c49f2a..d7a4b9eca5d2 100644 --- a/drivers/remoteproc/qcom_q6v5_pil.c +++ b/drivers/remoteproc/qcom_q6v5_pil.c | |||
@@ -30,12 +30,11 @@ | |||
30 | #include <linux/remoteproc.h> | 30 | #include <linux/remoteproc.h> |
31 | #include <linux/reset.h> | 31 | #include <linux/reset.h> |
32 | #include <linux/soc/qcom/mdt_loader.h> | 32 | #include <linux/soc/qcom/mdt_loader.h> |
33 | #include <linux/soc/qcom/smem.h> | ||
34 | #include <linux/soc/qcom/smem_state.h> | ||
35 | #include <linux/iopoll.h> | 33 | #include <linux/iopoll.h> |
36 | 34 | ||
37 | #include "remoteproc_internal.h" | 35 | #include "remoteproc_internal.h" |
38 | #include "qcom_common.h" | 36 | #include "qcom_common.h" |
37 | #include "qcom_q6v5.h" | ||
39 | 38 | ||
40 | #include <linux/qcom_scm.h> | 39 | #include <linux/qcom_scm.h> |
41 | 40 | ||
@@ -151,12 +150,7 @@ struct q6v5 { | |||
151 | 150 | ||
152 | struct reset_control *mss_restart; | 151 | struct reset_control *mss_restart; |
153 | 152 | ||
154 | struct qcom_smem_state *state; | 153 | struct qcom_q6v5 q6v5; |
155 | unsigned stop_bit; | ||
156 | |||
157 | int handover_irq; | ||
158 | |||
159 | bool proxy_unvoted; | ||
160 | 154 | ||
161 | struct clk *active_clks[8]; | 155 | struct clk *active_clks[8]; |
162 | struct clk *reset_clks[4]; | 156 | struct clk *reset_clks[4]; |
@@ -170,8 +164,6 @@ struct q6v5 { | |||
170 | int active_reg_count; | 164 | int active_reg_count; |
171 | int proxy_reg_count; | 165 | int proxy_reg_count; |
172 | 166 | ||
173 | struct completion start_done; | ||
174 | struct completion stop_done; | ||
175 | bool running; | 167 | bool running; |
176 | 168 | ||
177 | phys_addr_t mba_phys; | 169 | phys_addr_t mba_phys; |
@@ -798,9 +790,7 @@ static int q6v5_start(struct rproc *rproc) | |||
798 | int xfermemop_ret; | 790 | int xfermemop_ret; |
799 | int ret; | 791 | int ret; |
800 | 792 | ||
801 | qproc->proxy_unvoted = false; | 793 | qcom_q6v5_prepare(&qproc->q6v5); |
802 | |||
803 | enable_irq(qproc->handover_irq); | ||
804 | 794 | ||
805 | ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, | 795 | ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, |
806 | qproc->proxy_reg_count); | 796 | qproc->proxy_reg_count); |
@@ -875,11 +865,9 @@ static int q6v5_start(struct rproc *rproc) | |||
875 | if (ret) | 865 | if (ret) |
876 | goto reclaim_mpss; | 866 | goto reclaim_mpss; |
877 | 867 | ||
878 | ret = wait_for_completion_timeout(&qproc->start_done, | 868 | ret = qcom_q6v5_wait_for_start(&qproc->q6v5, msecs_to_jiffies(5000)); |
879 | msecs_to_jiffies(5000)); | 869 | if (ret == -ETIMEDOUT) { |
880 | if (ret == 0) { | ||
881 | dev_err(qproc->dev, "start timed out\n"); | 870 | dev_err(qproc->dev, "start timed out\n"); |
882 | ret = -ETIMEDOUT; | ||
883 | goto reclaim_mpss; | 871 | goto reclaim_mpss; |
884 | } | 872 | } |
885 | 873 | ||
@@ -933,7 +921,7 @@ disable_proxy_reg: | |||
933 | qproc->proxy_reg_count); | 921 | qproc->proxy_reg_count); |
934 | 922 | ||
935 | disable_irqs: | 923 | disable_irqs: |
936 | disable_irq(qproc->handover_irq); | 924 | qcom_q6v5_unprepare(&qproc->q6v5); |
937 | 925 | ||
938 | return ret; | 926 | return ret; |
939 | } | 927 | } |
@@ -946,16 +934,10 @@ static int q6v5_stop(struct rproc *rproc) | |||
946 | 934 | ||
947 | qproc->running = false; | 935 | qproc->running = false; |
948 | 936 | ||
949 | qcom_smem_state_update_bits(qproc->state, | 937 | ret = qcom_q6v5_request_stop(&qproc->q6v5); |
950 | BIT(qproc->stop_bit), BIT(qproc->stop_bit)); | 938 | if (ret == -ETIMEDOUT) |
951 | |||
952 | ret = wait_for_completion_timeout(&qproc->stop_done, | ||
953 | msecs_to_jiffies(5000)); | ||
954 | if (ret == 0) | ||
955 | dev_err(qproc->dev, "timed out on wait\n"); | 939 | dev_err(qproc->dev, "timed out on wait\n"); |
956 | 940 | ||
957 | qcom_smem_state_update_bits(qproc->state, BIT(qproc->stop_bit), 0); | ||
958 | |||
959 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); | 941 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); |
960 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); | 942 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); |
961 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); | 943 | q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); |
@@ -976,9 +958,8 @@ static int q6v5_stop(struct rproc *rproc) | |||
976 | 958 | ||
977 | q6v5_reset_assert(qproc); | 959 | q6v5_reset_assert(qproc); |
978 | 960 | ||
979 | disable_irq(qproc->handover_irq); | 961 | ret = qcom_q6v5_unprepare(&qproc->q6v5); |
980 | 962 | if (ret) { | |
981 | if (!qproc->proxy_unvoted) { | ||
982 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, | 963 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, |
983 | qproc->proxy_clk_count); | 964 | qproc->proxy_clk_count); |
984 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | 965 | q6v5_regulator_disable(qproc, qproc->proxy_regs, |
@@ -1014,74 +995,14 @@ static const struct rproc_ops q6v5_ops = { | |||
1014 | .load = q6v5_load, | 995 | .load = q6v5_load, |
1015 | }; | 996 | }; |
1016 | 997 | ||
1017 | static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev) | 998 | static void qcom_msa_handover(struct qcom_q6v5 *q6v5) |
1018 | { | ||
1019 | struct q6v5 *qproc = dev; | ||
1020 | size_t len; | ||
1021 | char *msg; | ||
1022 | |||
1023 | /* Sometimes the stop triggers a watchdog rather than a stop-ack */ | ||
1024 | if (!qproc->running) { | ||
1025 | complete(&qproc->stop_done); | ||
1026 | return IRQ_HANDLED; | ||
1027 | } | ||
1028 | |||
1029 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len); | ||
1030 | if (!IS_ERR(msg) && len > 0 && msg[0]) | ||
1031 | dev_err(qproc->dev, "watchdog received: %s\n", msg); | ||
1032 | else | ||
1033 | dev_err(qproc->dev, "watchdog without message\n"); | ||
1034 | |||
1035 | rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); | ||
1036 | |||
1037 | return IRQ_HANDLED; | ||
1038 | } | ||
1039 | |||
1040 | static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev) | ||
1041 | { | ||
1042 | struct q6v5 *qproc = dev; | ||
1043 | size_t len; | ||
1044 | char *msg; | ||
1045 | |||
1046 | msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, MPSS_CRASH_REASON_SMEM, &len); | ||
1047 | if (!IS_ERR(msg) && len > 0 && msg[0]) | ||
1048 | dev_err(qproc->dev, "fatal error received: %s\n", msg); | ||
1049 | else | ||
1050 | dev_err(qproc->dev, "fatal error without message\n"); | ||
1051 | |||
1052 | rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); | ||
1053 | |||
1054 | return IRQ_HANDLED; | ||
1055 | } | ||
1056 | |||
1057 | static irqreturn_t q6v5_ready_interrupt(int irq, void *dev) | ||
1058 | { | ||
1059 | struct q6v5 *qproc = dev; | ||
1060 | |||
1061 | complete(&qproc->start_done); | ||
1062 | return IRQ_HANDLED; | ||
1063 | } | ||
1064 | |||
1065 | static irqreturn_t q6v5_handover_interrupt(int irq, void *dev) | ||
1066 | { | 999 | { |
1067 | struct q6v5 *qproc = dev; | 1000 | struct q6v5 *qproc = container_of(q6v5, struct q6v5, q6v5); |
1068 | 1001 | ||
1069 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, | 1002 | q6v5_clk_disable(qproc->dev, qproc->proxy_clks, |
1070 | qproc->proxy_clk_count); | 1003 | qproc->proxy_clk_count); |
1071 | q6v5_regulator_disable(qproc, qproc->proxy_regs, | 1004 | q6v5_regulator_disable(qproc, qproc->proxy_regs, |
1072 | qproc->proxy_reg_count); | 1005 | qproc->proxy_reg_count); |
1073 | |||
1074 | qproc->proxy_unvoted = true; | ||
1075 | |||
1076 | return IRQ_HANDLED; | ||
1077 | } | ||
1078 | |||
1079 | static irqreturn_t q6v5_stop_ack_interrupt(int irq, void *dev) | ||
1080 | { | ||
1081 | struct q6v5 *qproc = dev; | ||
1082 | |||
1083 | complete(&qproc->stop_done); | ||
1084 | return IRQ_HANDLED; | ||
1085 | } | 1006 | } |
1086 | 1007 | ||
1087 | static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) | 1008 | static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) |
@@ -1154,30 +1075,6 @@ static int q6v5_init_reset(struct q6v5 *qproc) | |||
1154 | return 0; | 1075 | return 0; |
1155 | } | 1076 | } |
1156 | 1077 | ||
1157 | static int q6v5_request_irq(struct q6v5 *qproc, | ||
1158 | struct platform_device *pdev, | ||
1159 | const char *name, | ||
1160 | irq_handler_t thread_fn) | ||
1161 | { | ||
1162 | int irq; | ||
1163 | int ret; | ||
1164 | |||
1165 | irq = platform_get_irq_byname(pdev, name); | ||
1166 | if (irq < 0) { | ||
1167 | dev_err(&pdev->dev, "no %s IRQ defined\n", name); | ||
1168 | return irq; | ||
1169 | } | ||
1170 | |||
1171 | ret = devm_request_threaded_irq(&pdev->dev, irq, | ||
1172 | NULL, thread_fn, | ||
1173 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
1174 | "q6v5", qproc); | ||
1175 | if (ret) | ||
1176 | dev_err(&pdev->dev, "request %s IRQ failed\n", name); | ||
1177 | |||
1178 | return ret ? : irq; | ||
1179 | } | ||
1180 | |||
1181 | static int q6v5_alloc_memory_region(struct q6v5 *qproc) | 1078 | static int q6v5_alloc_memory_region(struct q6v5 *qproc) |
1182 | { | 1079 | { |
1183 | struct device_node *child; | 1080 | struct device_node *child; |
@@ -1247,9 +1144,6 @@ static int q6v5_probe(struct platform_device *pdev) | |||
1247 | qproc->rproc = rproc; | 1144 | qproc->rproc = rproc; |
1248 | platform_set_drvdata(pdev, qproc); | 1145 | platform_set_drvdata(pdev, qproc); |
1249 | 1146 | ||
1250 | init_completion(&qproc->start_done); | ||
1251 | init_completion(&qproc->stop_done); | ||
1252 | |||
1253 | ret = q6v5_init_mem(qproc, pdev); | 1147 | ret = q6v5_init_mem(qproc, pdev); |
1254 | if (ret) | 1148 | if (ret) |
1255 | goto free_rproc; | 1149 | goto free_rproc; |
@@ -1305,33 +1199,12 @@ static int q6v5_probe(struct platform_device *pdev) | |||
1305 | qproc->version = desc->version; | 1199 | qproc->version = desc->version; |
1306 | qproc->has_alt_reset = desc->has_alt_reset; | 1200 | qproc->has_alt_reset = desc->has_alt_reset; |
1307 | qproc->need_mem_protection = desc->need_mem_protection; | 1201 | qproc->need_mem_protection = desc->need_mem_protection; |
1308 | ret = q6v5_request_irq(qproc, pdev, "wdog", q6v5_wdog_interrupt); | ||
1309 | if (ret < 0) | ||
1310 | goto free_rproc; | ||
1311 | 1202 | ||
1312 | ret = q6v5_request_irq(qproc, pdev, "fatal", q6v5_fatal_interrupt); | 1203 | ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, |
1313 | if (ret < 0) | 1204 | qcom_msa_handover); |
1314 | goto free_rproc; | 1205 | if (ret) |
1315 | |||
1316 | ret = q6v5_request_irq(qproc, pdev, "ready", q6v5_ready_interrupt); | ||
1317 | if (ret < 0) | ||
1318 | goto free_rproc; | ||
1319 | |||
1320 | ret = q6v5_request_irq(qproc, pdev, "handover", q6v5_handover_interrupt); | ||
1321 | if (ret < 0) | ||
1322 | goto free_rproc; | ||
1323 | qproc->handover_irq = ret; | ||
1324 | disable_irq(qproc->handover_irq); | ||
1325 | |||
1326 | ret = q6v5_request_irq(qproc, pdev, "stop-ack", q6v5_stop_ack_interrupt); | ||
1327 | if (ret < 0) | ||
1328 | goto free_rproc; | 1206 | goto free_rproc; |
1329 | 1207 | ||
1330 | qproc->state = qcom_smem_state_get(&pdev->dev, "stop", &qproc->stop_bit); | ||
1331 | if (IS_ERR(qproc->state)) { | ||
1332 | ret = PTR_ERR(qproc->state); | ||
1333 | goto free_rproc; | ||
1334 | } | ||
1335 | qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); | 1208 | qproc->mpss_perm = BIT(QCOM_SCM_VMID_HLOS); |
1336 | qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); | 1209 | qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); |
1337 | qcom_add_glink_subdev(rproc, &qproc->glink_subdev); | 1210 | qcom_add_glink_subdev(rproc, &qproc->glink_subdev); |
@@ -1370,7 +1243,6 @@ static const struct rproc_hexagon_res sdm845_mss = { | |||
1370 | .hexagon_mba_image = "mba.mbn", | 1243 | .hexagon_mba_image = "mba.mbn", |
1371 | .proxy_clk_names = (char*[]){ | 1244 | .proxy_clk_names = (char*[]){ |
1372 | "xo", | 1245 | "xo", |
1373 | "axis2", | ||
1374 | "prng", | 1246 | "prng", |
1375 | NULL | 1247 | NULL |
1376 | }, | 1248 | }, |
diff --git a/drivers/remoteproc/qcom_q6v5_wcss.c b/drivers/remoteproc/qcom_q6v5_wcss.c new file mode 100644 index 000000000000..f93e1e4a1cc0 --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5_wcss.c | |||
@@ -0,0 +1,601 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2016-2018 Linaro Ltd. | ||
4 | * Copyright (C) 2014 Sony Mobile Communications AB | ||
5 | * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. | ||
6 | */ | ||
7 | #include <linux/iopoll.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/mfd/syscon.h> | ||
10 | #include <linux/module.h> | ||
11 | #include <linux/of_reserved_mem.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/regmap.h> | ||
14 | #include <linux/reset.h> | ||
15 | #include <linux/soc/qcom/mdt_loader.h> | ||
16 | #include "qcom_common.h" | ||
17 | #include "qcom_q6v5.h" | ||
18 | |||
19 | #define WCSS_CRASH_REASON 421 | ||
20 | |||
21 | /* Q6SS Register Offsets */ | ||
22 | #define Q6SS_RESET_REG 0x014 | ||
23 | #define Q6SS_GFMUX_CTL_REG 0x020 | ||
24 | #define Q6SS_PWR_CTL_REG 0x030 | ||
25 | #define Q6SS_MEM_PWR_CTL 0x0B0 | ||
26 | |||
27 | /* AXI Halt Register Offsets */ | ||
28 | #define AXI_HALTREQ_REG 0x0 | ||
29 | #define AXI_HALTACK_REG 0x4 | ||
30 | #define AXI_IDLE_REG 0x8 | ||
31 | |||
32 | #define HALT_ACK_TIMEOUT_MS 100 | ||
33 | |||
34 | /* Q6SS_RESET */ | ||
35 | #define Q6SS_STOP_CORE BIT(0) | ||
36 | #define Q6SS_CORE_ARES BIT(1) | ||
37 | #define Q6SS_BUS_ARES_ENABLE BIT(2) | ||
38 | |||
39 | /* Q6SS_GFMUX_CTL */ | ||
40 | #define Q6SS_CLK_ENABLE BIT(1) | ||
41 | |||
42 | /* Q6SS_PWR_CTL */ | ||
43 | #define Q6SS_L2DATA_STBY_N BIT(18) | ||
44 | #define Q6SS_SLP_RET_N BIT(19) | ||
45 | #define Q6SS_CLAMP_IO BIT(20) | ||
46 | #define QDSS_BHS_ON BIT(21) | ||
47 | |||
48 | /* Q6SS parameters */ | ||
49 | #define Q6SS_LDO_BYP BIT(25) | ||
50 | #define Q6SS_BHS_ON BIT(24) | ||
51 | #define Q6SS_CLAMP_WL BIT(21) | ||
52 | #define Q6SS_CLAMP_QMC_MEM BIT(22) | ||
53 | #define HALT_CHECK_MAX_LOOPS 200 | ||
54 | #define Q6SS_XO_CBCR GENMASK(5, 3) | ||
55 | |||
56 | /* Q6SS config/status registers */ | ||
57 | #define TCSR_GLOBAL_CFG0 0x0 | ||
58 | #define TCSR_GLOBAL_CFG1 0x4 | ||
59 | #define SSCAON_CONFIG 0x8 | ||
60 | #define SSCAON_STATUS 0xc | ||
61 | #define Q6SS_BHS_STATUS 0x78 | ||
62 | #define Q6SS_RST_EVB 0x10 | ||
63 | |||
64 | #define BHS_EN_REST_ACK BIT(0) | ||
65 | #define SSCAON_ENABLE BIT(13) | ||
66 | #define SSCAON_BUS_EN BIT(15) | ||
67 | #define SSCAON_BUS_MUX_MASK GENMASK(18, 16) | ||
68 | |||
69 | #define MEM_BANKS 19 | ||
70 | #define TCSR_WCSS_CLK_MASK 0x1F | ||
71 | #define TCSR_WCSS_CLK_ENABLE 0x14 | ||
72 | |||
73 | struct q6v5_wcss { | ||
74 | struct device *dev; | ||
75 | |||
76 | void __iomem *reg_base; | ||
77 | void __iomem *rmb_base; | ||
78 | |||
79 | struct regmap *halt_map; | ||
80 | u32 halt_q6; | ||
81 | u32 halt_wcss; | ||
82 | u32 halt_nc; | ||
83 | |||
84 | struct reset_control *wcss_aon_reset; | ||
85 | struct reset_control *wcss_reset; | ||
86 | struct reset_control *wcss_q6_reset; | ||
87 | |||
88 | struct qcom_q6v5 q6v5; | ||
89 | |||
90 | phys_addr_t mem_phys; | ||
91 | phys_addr_t mem_reloc; | ||
92 | void *mem_region; | ||
93 | size_t mem_size; | ||
94 | }; | ||
95 | |||
96 | static int q6v5_wcss_reset(struct q6v5_wcss *wcss) | ||
97 | { | ||
98 | int ret; | ||
99 | u32 val; | ||
100 | int i; | ||
101 | |||
102 | /* Assert resets, stop core */ | ||
103 | val = readl(wcss->reg_base + Q6SS_RESET_REG); | ||
104 | val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE; | ||
105 | writel(val, wcss->reg_base + Q6SS_RESET_REG); | ||
106 | |||
107 | /* BHS require xo cbcr to be enabled */ | ||
108 | val = readl(wcss->reg_base + Q6SS_XO_CBCR); | ||
109 | val |= 0x1; | ||
110 | writel(val, wcss->reg_base + Q6SS_XO_CBCR); | ||
111 | |||
112 | /* Read CLKOFF bit to go low indicating CLK is enabled */ | ||
113 | ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR, | ||
114 | val, !(val & BIT(31)), 1, | ||
115 | HALT_CHECK_MAX_LOOPS); | ||
116 | if (ret) { | ||
117 | dev_err(wcss->dev, | ||
118 | "xo cbcr enabling timed out (rc:%d)\n", ret); | ||
119 | return ret; | ||
120 | } | ||
121 | /* Enable power block headswitch and wait for it to stabilize */ | ||
122 | val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
123 | val |= Q6SS_BHS_ON; | ||
124 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
125 | udelay(1); | ||
126 | |||
127 | /* Put LDO in bypass mode */ | ||
128 | val |= Q6SS_LDO_BYP; | ||
129 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
130 | |||
131 | /* Deassert Q6 compiler memory clamp */ | ||
132 | val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
133 | val &= ~Q6SS_CLAMP_QMC_MEM; | ||
134 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
135 | |||
136 | /* Deassert memory peripheral sleep and L2 memory standby */ | ||
137 | val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N; | ||
138 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
139 | |||
140 | /* Turn on L1, L2, ETB and JU memories 1 at a time */ | ||
141 | val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); | ||
142 | for (i = MEM_BANKS; i >= 0; i--) { | ||
143 | val |= BIT(i); | ||
144 | writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL); | ||
145 | /* | ||
146 | * Read back value to ensure the write is done then | ||
147 | * wait for 1us for both memory peripheral and data | ||
148 | * array to turn on. | ||
149 | */ | ||
150 | val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); | ||
151 | udelay(1); | ||
152 | } | ||
153 | /* Remove word line clamp */ | ||
154 | val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
155 | val &= ~Q6SS_CLAMP_WL; | ||
156 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
157 | |||
158 | /* Remove IO clamp */ | ||
159 | val &= ~Q6SS_CLAMP_IO; | ||
160 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
161 | |||
162 | /* Bring core out of reset */ | ||
163 | val = readl(wcss->reg_base + Q6SS_RESET_REG); | ||
164 | val &= ~Q6SS_CORE_ARES; | ||
165 | writel(val, wcss->reg_base + Q6SS_RESET_REG); | ||
166 | |||
167 | /* Turn on core clock */ | ||
168 | val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG); | ||
169 | val |= Q6SS_CLK_ENABLE; | ||
170 | writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG); | ||
171 | |||
172 | /* Start core execution */ | ||
173 | val = readl(wcss->reg_base + Q6SS_RESET_REG); | ||
174 | val &= ~Q6SS_STOP_CORE; | ||
175 | writel(val, wcss->reg_base + Q6SS_RESET_REG); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int q6v5_wcss_start(struct rproc *rproc) | ||
181 | { | ||
182 | struct q6v5_wcss *wcss = rproc->priv; | ||
183 | int ret; | ||
184 | |||
185 | qcom_q6v5_prepare(&wcss->q6v5); | ||
186 | |||
187 | /* Release Q6 and WCSS reset */ | ||
188 | ret = reset_control_deassert(wcss->wcss_reset); | ||
189 | if (ret) { | ||
190 | dev_err(wcss->dev, "wcss_reset failed\n"); | ||
191 | return ret; | ||
192 | } | ||
193 | |||
194 | ret = reset_control_deassert(wcss->wcss_q6_reset); | ||
195 | if (ret) { | ||
196 | dev_err(wcss->dev, "wcss_q6_reset failed\n"); | ||
197 | goto wcss_reset; | ||
198 | } | ||
199 | |||
200 | /* Lithium configuration - clock gating and bus arbitration */ | ||
201 | ret = regmap_update_bits(wcss->halt_map, | ||
202 | wcss->halt_nc + TCSR_GLOBAL_CFG0, | ||
203 | TCSR_WCSS_CLK_MASK, | ||
204 | TCSR_WCSS_CLK_ENABLE); | ||
205 | if (ret) | ||
206 | goto wcss_q6_reset; | ||
207 | |||
208 | ret = regmap_update_bits(wcss->halt_map, | ||
209 | wcss->halt_nc + TCSR_GLOBAL_CFG1, | ||
210 | 1, 0); | ||
211 | if (ret) | ||
212 | goto wcss_q6_reset; | ||
213 | |||
214 | /* Write bootaddr to EVB so that Q6WCSS will jump there after reset */ | ||
215 | writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB); | ||
216 | |||
217 | ret = q6v5_wcss_reset(wcss); | ||
218 | if (ret) | ||
219 | goto wcss_q6_reset; | ||
220 | |||
221 | ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ); | ||
222 | if (ret == -ETIMEDOUT) | ||
223 | dev_err(wcss->dev, "start timed out\n"); | ||
224 | |||
225 | return ret; | ||
226 | |||
227 | wcss_q6_reset: | ||
228 | reset_control_assert(wcss->wcss_q6_reset); | ||
229 | |||
230 | wcss_reset: | ||
231 | reset_control_assert(wcss->wcss_reset); | ||
232 | |||
233 | return ret; | ||
234 | } | ||
235 | |||
236 | static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss, | ||
237 | struct regmap *halt_map, | ||
238 | u32 offset) | ||
239 | { | ||
240 | unsigned long timeout; | ||
241 | unsigned int val; | ||
242 | int ret; | ||
243 | |||
244 | /* Check if we're already idle */ | ||
245 | ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); | ||
246 | if (!ret && val) | ||
247 | return; | ||
248 | |||
249 | /* Assert halt request */ | ||
250 | regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1); | ||
251 | |||
252 | /* Wait for halt */ | ||
253 | timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS); | ||
254 | for (;;) { | ||
255 | ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val); | ||
256 | if (ret || val || time_after(jiffies, timeout)) | ||
257 | break; | ||
258 | |||
259 | msleep(1); | ||
260 | } | ||
261 | |||
262 | ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val); | ||
263 | if (ret || !val) | ||
264 | dev_err(wcss->dev, "port failed halt\n"); | ||
265 | |||
266 | /* Clear halt request (port will remain halted until reset) */ | ||
267 | regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); | ||
268 | } | ||
269 | |||
270 | static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss) | ||
271 | { | ||
272 | int ret; | ||
273 | u32 val; | ||
274 | |||
275 | /* 1 - Assert WCSS/Q6 HALTREQ */ | ||
276 | q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss); | ||
277 | |||
278 | /* 2 - Enable WCSSAON_CONFIG */ | ||
279 | val = readl(wcss->rmb_base + SSCAON_CONFIG); | ||
280 | val |= SSCAON_ENABLE; | ||
281 | writel(val, wcss->rmb_base + SSCAON_CONFIG); | ||
282 | |||
283 | /* 3 - Set SSCAON_CONFIG */ | ||
284 | val |= SSCAON_BUS_EN; | ||
285 | val &= ~SSCAON_BUS_MUX_MASK; | ||
286 | writel(val, wcss->rmb_base + SSCAON_CONFIG); | ||
287 | |||
288 | /* 4 - SSCAON_CONFIG 1 */ | ||
289 | val |= BIT(1); | ||
290 | writel(val, wcss->rmb_base + SSCAON_CONFIG); | ||
291 | |||
292 | /* 5 - wait for SSCAON_STATUS */ | ||
293 | ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS, | ||
294 | val, (val & 0xffff) == 0x400, 1000, | ||
295 | HALT_CHECK_MAX_LOOPS); | ||
296 | if (ret) { | ||
297 | dev_err(wcss->dev, | ||
298 | "can't get SSCAON_STATUS rc:%d)\n", ret); | ||
299 | return ret; | ||
300 | } | ||
301 | |||
302 | /* 6 - De-assert WCSS_AON reset */ | ||
303 | reset_control_assert(wcss->wcss_aon_reset); | ||
304 | |||
305 | /* 7 - Disable WCSSAON_CONFIG 13 */ | ||
306 | val = readl(wcss->rmb_base + SSCAON_CONFIG); | ||
307 | val &= ~SSCAON_ENABLE; | ||
308 | writel(val, wcss->rmb_base + SSCAON_CONFIG); | ||
309 | |||
310 | /* 8 - De-assert WCSS/Q6 HALTREQ */ | ||
311 | reset_control_assert(wcss->wcss_reset); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int q6v5_q6_powerdown(struct q6v5_wcss *wcss) | ||
317 | { | ||
318 | int ret; | ||
319 | u32 val; | ||
320 | int i; | ||
321 | |||
322 | /* 1 - Halt Q6 bus interface */ | ||
323 | q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6); | ||
324 | |||
325 | /* 2 - Disable Q6 Core clock */ | ||
326 | val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG); | ||
327 | val &= ~Q6SS_CLK_ENABLE; | ||
328 | writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG); | ||
329 | |||
330 | /* 3 - Clamp I/O */ | ||
331 | val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
332 | val |= Q6SS_CLAMP_IO; | ||
333 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
334 | |||
335 | /* 4 - Clamp WL */ | ||
336 | val |= QDSS_BHS_ON; | ||
337 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
338 | |||
339 | /* 5 - Clear Erase standby */ | ||
340 | val &= ~Q6SS_L2DATA_STBY_N; | ||
341 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
342 | |||
343 | /* 6 - Clear Sleep RTN */ | ||
344 | val &= ~Q6SS_SLP_RET_N; | ||
345 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
346 | |||
347 | /* 7 - turn off Q6 memory foot/head switch one bank at a time */ | ||
348 | for (i = 0; i < 20; i++) { | ||
349 | val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL); | ||
350 | val &= ~BIT(i); | ||
351 | writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL); | ||
352 | mdelay(1); | ||
353 | } | ||
354 | |||
355 | /* 8 - Assert QMC memory RTN */ | ||
356 | val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
357 | val |= Q6SS_CLAMP_QMC_MEM; | ||
358 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
359 | |||
360 | /* 9 - Turn off BHS */ | ||
361 | val &= ~Q6SS_BHS_ON; | ||
362 | writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG); | ||
363 | udelay(1); | ||
364 | |||
365 | /* 10 - Wait till BHS Reset is done */ | ||
366 | ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS, | ||
367 | val, !(val & BHS_EN_REST_ACK), 1000, | ||
368 | HALT_CHECK_MAX_LOOPS); | ||
369 | if (ret) { | ||
370 | dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret); | ||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | /* 11 - Assert WCSS reset */ | ||
375 | reset_control_assert(wcss->wcss_reset); | ||
376 | |||
377 | /* 12 - Assert Q6 reset */ | ||
378 | reset_control_assert(wcss->wcss_q6_reset); | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int q6v5_wcss_stop(struct rproc *rproc) | ||
384 | { | ||
385 | struct q6v5_wcss *wcss = rproc->priv; | ||
386 | int ret; | ||
387 | |||
388 | /* WCSS powerdown */ | ||
389 | ret = qcom_q6v5_request_stop(&wcss->q6v5); | ||
390 | if (ret == -ETIMEDOUT) { | ||
391 | dev_err(wcss->dev, "timed out on wait\n"); | ||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | ret = q6v5_wcss_powerdown(wcss); | ||
396 | if (ret) | ||
397 | return ret; | ||
398 | |||
399 | /* Q6 Power down */ | ||
400 | ret = q6v5_q6_powerdown(wcss); | ||
401 | if (ret) | ||
402 | return ret; | ||
403 | |||
404 | qcom_q6v5_unprepare(&wcss->q6v5); | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len) | ||
410 | { | ||
411 | struct q6v5_wcss *wcss = rproc->priv; | ||
412 | int offset; | ||
413 | |||
414 | offset = da - wcss->mem_reloc; | ||
415 | if (offset < 0 || offset + len > wcss->mem_size) | ||
416 | return NULL; | ||
417 | |||
418 | return wcss->mem_region + offset; | ||
419 | } | ||
420 | |||
421 | static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw) | ||
422 | { | ||
423 | struct q6v5_wcss *wcss = rproc->priv; | ||
424 | |||
425 | return qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware, | ||
426 | 0, wcss->mem_region, wcss->mem_phys, | ||
427 | wcss->mem_size, &wcss->mem_reloc); | ||
428 | } | ||
429 | |||
430 | static const struct rproc_ops q6v5_wcss_ops = { | ||
431 | .start = q6v5_wcss_start, | ||
432 | .stop = q6v5_wcss_stop, | ||
433 | .da_to_va = q6v5_wcss_da_to_va, | ||
434 | .load = q6v5_wcss_load, | ||
435 | .get_boot_addr = rproc_elf_get_boot_addr, | ||
436 | }; | ||
437 | |||
438 | static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss) | ||
439 | { | ||
440 | struct device *dev = wcss->dev; | ||
441 | |||
442 | wcss->wcss_aon_reset = devm_reset_control_get(dev, "wcss_aon_reset"); | ||
443 | if (IS_ERR(wcss->wcss_aon_reset)) { | ||
444 | dev_err(wcss->dev, "unable to acquire wcss_aon_reset\n"); | ||
445 | return PTR_ERR(wcss->wcss_aon_reset); | ||
446 | } | ||
447 | |||
448 | wcss->wcss_reset = devm_reset_control_get(dev, "wcss_reset"); | ||
449 | if (IS_ERR(wcss->wcss_reset)) { | ||
450 | dev_err(wcss->dev, "unable to acquire wcss_reset\n"); | ||
451 | return PTR_ERR(wcss->wcss_reset); | ||
452 | } | ||
453 | |||
454 | wcss->wcss_q6_reset = devm_reset_control_get(dev, "wcss_q6_reset"); | ||
455 | if (IS_ERR(wcss->wcss_q6_reset)) { | ||
456 | dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n"); | ||
457 | return PTR_ERR(wcss->wcss_q6_reset); | ||
458 | } | ||
459 | |||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss, | ||
464 | struct platform_device *pdev) | ||
465 | { | ||
466 | struct of_phandle_args args; | ||
467 | struct resource *res; | ||
468 | int ret; | ||
469 | |||
470 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6"); | ||
471 | wcss->reg_base = devm_ioremap_resource(&pdev->dev, res); | ||
472 | if (IS_ERR(wcss->reg_base)) | ||
473 | return PTR_ERR(wcss->reg_base); | ||
474 | |||
475 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb"); | ||
476 | wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res); | ||
477 | if (IS_ERR(wcss->rmb_base)) | ||
478 | return PTR_ERR(wcss->rmb_base); | ||
479 | |||
480 | ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, | ||
481 | "qcom,halt-regs", 3, 0, &args); | ||
482 | if (ret < 0) { | ||
483 | dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n"); | ||
484 | return -EINVAL; | ||
485 | } | ||
486 | |||
487 | wcss->halt_map = syscon_node_to_regmap(args.np); | ||
488 | of_node_put(args.np); | ||
489 | if (IS_ERR(wcss->halt_map)) | ||
490 | return PTR_ERR(wcss->halt_map); | ||
491 | |||
492 | wcss->halt_q6 = args.args[0]; | ||
493 | wcss->halt_wcss = args.args[1]; | ||
494 | wcss->halt_nc = args.args[2]; | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss) | ||
500 | { | ||
501 | struct reserved_mem *rmem = NULL; | ||
502 | struct device_node *node; | ||
503 | struct device *dev = wcss->dev; | ||
504 | |||
505 | node = of_parse_phandle(dev->of_node, "memory-region", 0); | ||
506 | if (node) | ||
507 | rmem = of_reserved_mem_lookup(node); | ||
508 | of_node_put(node); | ||
509 | |||
510 | if (!rmem) { | ||
511 | dev_err(dev, "unable to acquire memory-region\n"); | ||
512 | return -EINVAL; | ||
513 | } | ||
514 | |||
515 | wcss->mem_phys = rmem->base; | ||
516 | wcss->mem_reloc = rmem->base; | ||
517 | wcss->mem_size = rmem->size; | ||
518 | wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size); | ||
519 | if (!wcss->mem_region) { | ||
520 | dev_err(dev, "unable to map memory region: %pa+%pa\n", | ||
521 | &rmem->base, &rmem->size); | ||
522 | return -EBUSY; | ||
523 | } | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | static int q6v5_wcss_probe(struct platform_device *pdev) | ||
529 | { | ||
530 | struct q6v5_wcss *wcss; | ||
531 | struct rproc *rproc; | ||
532 | int ret; | ||
533 | |||
534 | rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_wcss_ops, | ||
535 | "IPQ8074/q6_fw.mdt", sizeof(*wcss)); | ||
536 | if (!rproc) { | ||
537 | dev_err(&pdev->dev, "failed to allocate rproc\n"); | ||
538 | return -ENOMEM; | ||
539 | } | ||
540 | |||
541 | wcss = rproc->priv; | ||
542 | wcss->dev = &pdev->dev; | ||
543 | |||
544 | ret = q6v5_wcss_init_mmio(wcss, pdev); | ||
545 | if (ret) | ||
546 | goto free_rproc; | ||
547 | |||
548 | ret = q6v5_alloc_memory_region(wcss); | ||
549 | if (ret) | ||
550 | goto free_rproc; | ||
551 | |||
552 | ret = q6v5_wcss_init_reset(wcss); | ||
553 | if (ret) | ||
554 | goto free_rproc; | ||
555 | |||
556 | ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, WCSS_CRASH_REASON, NULL); | ||
557 | if (ret) | ||
558 | goto free_rproc; | ||
559 | |||
560 | ret = rproc_add(rproc); | ||
561 | if (ret) | ||
562 | goto free_rproc; | ||
563 | |||
564 | platform_set_drvdata(pdev, rproc); | ||
565 | |||
566 | return 0; | ||
567 | |||
568 | free_rproc: | ||
569 | rproc_free(rproc); | ||
570 | |||
571 | return ret; | ||
572 | } | ||
573 | |||
574 | static int q6v5_wcss_remove(struct platform_device *pdev) | ||
575 | { | ||
576 | struct rproc *rproc = platform_get_drvdata(pdev); | ||
577 | |||
578 | rproc_del(rproc); | ||
579 | rproc_free(rproc); | ||
580 | |||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | static const struct of_device_id q6v5_wcss_of_match[] = { | ||
585 | { .compatible = "qcom,ipq8074-wcss-pil" }, | ||
586 | { }, | ||
587 | }; | ||
588 | MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match); | ||
589 | |||
590 | static struct platform_driver q6v5_wcss_driver = { | ||
591 | .probe = q6v5_wcss_probe, | ||
592 | .remove = q6v5_wcss_remove, | ||
593 | .driver = { | ||
594 | .name = "qcom-q6v5-wcss-pil", | ||
595 | .of_match_table = q6v5_wcss_of_match, | ||
596 | }, | ||
597 | }; | ||
598 | module_platform_driver(q6v5_wcss_driver); | ||
599 | |||
600 | MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader"); | ||
601 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index f085545d7da5..e976a602b015 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c | |||
@@ -469,7 +469,10 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, | |||
469 | 469 | ||
470 | qmi_add_lookup(&sysmon->qmi, 43, 0, 0); | 470 | qmi_add_lookup(&sysmon->qmi, 43, 0, 0); |
471 | 471 | ||
472 | rproc_add_subdev(rproc, &sysmon->subdev, sysmon_start, sysmon_stop); | 472 | sysmon->subdev.start = sysmon_start; |
473 | sysmon->subdev.stop = sysmon_stop; | ||
474 | |||
475 | rproc_add_subdev(rproc, &sysmon->subdev); | ||
473 | 476 | ||
474 | sysmon->nb.notifier_call = sysmon_notify; | 477 | sysmon->nb.notifier_call = sysmon_notify; |
475 | blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); | 478 | blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb); |
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index a9609d971f7f..aa6206706fe3 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c | |||
@@ -241,7 +241,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) | |||
241 | if (notifyid > rproc->max_notifyid) | 241 | if (notifyid > rproc->max_notifyid) |
242 | rproc->max_notifyid = notifyid; | 242 | rproc->max_notifyid = notifyid; |
243 | 243 | ||
244 | dev_dbg(dev, "vring%d: va %p dma %pad size 0x%x idr %d\n", | 244 | dev_dbg(dev, "vring%d: va %pK dma %pad size 0x%x idr %d\n", |
245 | i, va, &dma, size, notifyid); | 245 | i, va, &dma, size, notifyid); |
246 | 246 | ||
247 | rvring->va = va; | 247 | rvring->va = va; |
@@ -301,14 +301,14 @@ void rproc_free_vring(struct rproc_vring *rvring) | |||
301 | rsc->vring[idx].notifyid = -1; | 301 | rsc->vring[idx].notifyid = -1; |
302 | } | 302 | } |
303 | 303 | ||
304 | static int rproc_vdev_do_probe(struct rproc_subdev *subdev) | 304 | static int rproc_vdev_do_start(struct rproc_subdev *subdev) |
305 | { | 305 | { |
306 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); | 306 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); |
307 | 307 | ||
308 | return rproc_add_virtio_dev(rvdev, rvdev->id); | 308 | return rproc_add_virtio_dev(rvdev, rvdev->id); |
309 | } | 309 | } |
310 | 310 | ||
311 | static void rproc_vdev_do_remove(struct rproc_subdev *subdev, bool crashed) | 311 | static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed) |
312 | { | 312 | { |
313 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); | 313 | struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); |
314 | 314 | ||
@@ -399,8 +399,10 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, | |||
399 | 399 | ||
400 | list_add_tail(&rvdev->node, &rproc->rvdevs); | 400 | list_add_tail(&rvdev->node, &rproc->rvdevs); |
401 | 401 | ||
402 | rproc_add_subdev(rproc, &rvdev->subdev, | 402 | rvdev->subdev.start = rproc_vdev_do_start; |
403 | rproc_vdev_do_probe, rproc_vdev_do_remove); | 403 | rvdev->subdev.stop = rproc_vdev_do_stop; |
404 | |||
405 | rproc_add_subdev(rproc, &rvdev->subdev); | ||
404 | 406 | ||
405 | return 0; | 407 | return 0; |
406 | 408 | ||
@@ -497,7 +499,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc, | |||
497 | 499 | ||
498 | rproc->num_traces++; | 500 | rproc->num_traces++; |
499 | 501 | ||
500 | dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", | 502 | dev_dbg(dev, "%s added: va %pK, da 0x%x, len 0x%x\n", |
501 | name, ptr, rsc->da, rsc->len); | 503 | name, ptr, rsc->da, rsc->len); |
502 | 504 | ||
503 | return 0; | 505 | return 0; |
@@ -635,7 +637,7 @@ static int rproc_handle_carveout(struct rproc *rproc, | |||
635 | goto free_carv; | 637 | goto free_carv; |
636 | } | 638 | } |
637 | 639 | ||
638 | dev_dbg(dev, "carveout va %p, dma %pad, len 0x%x\n", | 640 | dev_dbg(dev, "carveout va %pK, dma %pad, len 0x%x\n", |
639 | va, &dma, rsc->len); | 641 | va, &dma, rsc->len); |
640 | 642 | ||
641 | /* | 643 | /* |
@@ -774,32 +776,72 @@ static int rproc_handle_resources(struct rproc *rproc, | |||
774 | return ret; | 776 | return ret; |
775 | } | 777 | } |
776 | 778 | ||
777 | static int rproc_probe_subdevices(struct rproc *rproc) | 779 | static int rproc_prepare_subdevices(struct rproc *rproc) |
778 | { | 780 | { |
779 | struct rproc_subdev *subdev; | 781 | struct rproc_subdev *subdev; |
780 | int ret; | 782 | int ret; |
781 | 783 | ||
782 | list_for_each_entry(subdev, &rproc->subdevs, node) { | 784 | list_for_each_entry(subdev, &rproc->subdevs, node) { |
783 | ret = subdev->probe(subdev); | 785 | if (subdev->prepare) { |
784 | if (ret) | 786 | ret = subdev->prepare(subdev); |
785 | goto unroll_registration; | 787 | if (ret) |
788 | goto unroll_preparation; | ||
789 | } | ||
790 | } | ||
791 | |||
792 | return 0; | ||
793 | |||
794 | unroll_preparation: | ||
795 | list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) { | ||
796 | if (subdev->unprepare) | ||
797 | subdev->unprepare(subdev); | ||
798 | } | ||
799 | |||
800 | return ret; | ||
801 | } | ||
802 | |||
803 | static int rproc_start_subdevices(struct rproc *rproc) | ||
804 | { | ||
805 | struct rproc_subdev *subdev; | ||
806 | int ret; | ||
807 | |||
808 | list_for_each_entry(subdev, &rproc->subdevs, node) { | ||
809 | if (subdev->start) { | ||
810 | ret = subdev->start(subdev); | ||
811 | if (ret) | ||
812 | goto unroll_registration; | ||
813 | } | ||
786 | } | 814 | } |
787 | 815 | ||
788 | return 0; | 816 | return 0; |
789 | 817 | ||
790 | unroll_registration: | 818 | unroll_registration: |
791 | list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) | 819 | list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) { |
792 | subdev->remove(subdev, true); | 820 | if (subdev->stop) |
821 | subdev->stop(subdev, true); | ||
822 | } | ||
793 | 823 | ||
794 | return ret; | 824 | return ret; |
795 | } | 825 | } |
796 | 826 | ||
797 | static void rproc_remove_subdevices(struct rproc *rproc, bool crashed) | 827 | static void rproc_stop_subdevices(struct rproc *rproc, bool crashed) |
798 | { | 828 | { |
799 | struct rproc_subdev *subdev; | 829 | struct rproc_subdev *subdev; |
800 | 830 | ||
801 | list_for_each_entry_reverse(subdev, &rproc->subdevs, node) | 831 | list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { |
802 | subdev->remove(subdev, crashed); | 832 | if (subdev->stop) |
833 | subdev->stop(subdev, crashed); | ||
834 | } | ||
835 | } | ||
836 | |||
837 | static void rproc_unprepare_subdevices(struct rproc *rproc) | ||
838 | { | ||
839 | struct rproc_subdev *subdev; | ||
840 | |||
841 | list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { | ||
842 | if (subdev->unprepare) | ||
843 | subdev->unprepare(subdev); | ||
844 | } | ||
803 | } | 845 | } |
804 | 846 | ||
805 | /** | 847 | /** |
@@ -894,20 +936,26 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw) | |||
894 | rproc->table_ptr = loaded_table; | 936 | rproc->table_ptr = loaded_table; |
895 | } | 937 | } |
896 | 938 | ||
939 | ret = rproc_prepare_subdevices(rproc); | ||
940 | if (ret) { | ||
941 | dev_err(dev, "failed to prepare subdevices for %s: %d\n", | ||
942 | rproc->name, ret); | ||
943 | goto reset_table_ptr; | ||
944 | } | ||
945 | |||
897 | /* power up the remote processor */ | 946 | /* power up the remote processor */ |
898 | ret = rproc->ops->start(rproc); | 947 | ret = rproc->ops->start(rproc); |
899 | if (ret) { | 948 | if (ret) { |
900 | dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret); | 949 | dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret); |
901 | return ret; | 950 | goto unprepare_subdevices; |
902 | } | 951 | } |
903 | 952 | ||
904 | /* probe any subdevices for the remote processor */ | 953 | /* Start any subdevices for the remote processor */ |
905 | ret = rproc_probe_subdevices(rproc); | 954 | ret = rproc_start_subdevices(rproc); |
906 | if (ret) { | 955 | if (ret) { |
907 | dev_err(dev, "failed to probe subdevices for %s: %d\n", | 956 | dev_err(dev, "failed to probe subdevices for %s: %d\n", |
908 | rproc->name, ret); | 957 | rproc->name, ret); |
909 | rproc->ops->stop(rproc); | 958 | goto stop_rproc; |
910 | return ret; | ||
911 | } | 959 | } |
912 | 960 | ||
913 | rproc->state = RPROC_RUNNING; | 961 | rproc->state = RPROC_RUNNING; |
@@ -915,6 +963,15 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw) | |||
915 | dev_info(dev, "remote processor %s is now up\n", rproc->name); | 963 | dev_info(dev, "remote processor %s is now up\n", rproc->name); |
916 | 964 | ||
917 | return 0; | 965 | return 0; |
966 | |||
967 | stop_rproc: | ||
968 | rproc->ops->stop(rproc); | ||
969 | unprepare_subdevices: | ||
970 | rproc_unprepare_subdevices(rproc); | ||
971 | reset_table_ptr: | ||
972 | rproc->table_ptr = rproc->cached_table; | ||
973 | |||
974 | return ret; | ||
918 | } | 975 | } |
919 | 976 | ||
920 | /* | 977 | /* |
@@ -1014,8 +1071,8 @@ static int rproc_stop(struct rproc *rproc, bool crashed) | |||
1014 | struct device *dev = &rproc->dev; | 1071 | struct device *dev = &rproc->dev; |
1015 | int ret; | 1072 | int ret; |
1016 | 1073 | ||
1017 | /* remove any subdevices for the remote processor */ | 1074 | /* Stop any subdevices for the remote processor */ |
1018 | rproc_remove_subdevices(rproc, crashed); | 1075 | rproc_stop_subdevices(rproc, crashed); |
1019 | 1076 | ||
1020 | /* the installed resource table is no longer accessible */ | 1077 | /* the installed resource table is no longer accessible */ |
1021 | rproc->table_ptr = rproc->cached_table; | 1078 | rproc->table_ptr = rproc->cached_table; |
@@ -1027,6 +1084,8 @@ static int rproc_stop(struct rproc *rproc, bool crashed) | |||
1027 | return ret; | 1084 | return ret; |
1028 | } | 1085 | } |
1029 | 1086 | ||
1087 | rproc_unprepare_subdevices(rproc); | ||
1088 | |||
1030 | rproc->state = RPROC_OFFLINE; | 1089 | rproc->state = RPROC_OFFLINE; |
1031 | 1090 | ||
1032 | dev_info(dev, "stopped remote processor %s\n", rproc->name); | 1091 | dev_info(dev, "stopped remote processor %s\n", rproc->name); |
@@ -1657,17 +1716,11 @@ EXPORT_SYMBOL(rproc_del); | |||
1657 | * rproc_add_subdev() - add a subdevice to a remoteproc | 1716 | * rproc_add_subdev() - add a subdevice to a remoteproc |
1658 | * @rproc: rproc handle to add the subdevice to | 1717 | * @rproc: rproc handle to add the subdevice to |
1659 | * @subdev: subdev handle to register | 1718 | * @subdev: subdev handle to register |
1660 | * @probe: function to call when the rproc boots | 1719 | * |
1661 | * @remove: function to call when the rproc shuts down | 1720 | * Caller is responsible for populating optional subdevice function pointers. |
1662 | */ | 1721 | */ |
1663 | void rproc_add_subdev(struct rproc *rproc, | 1722 | void rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev) |
1664 | struct rproc_subdev *subdev, | ||
1665 | int (*probe)(struct rproc_subdev *subdev), | ||
1666 | void (*remove)(struct rproc_subdev *subdev, bool crashed)) | ||
1667 | { | 1723 | { |
1668 | subdev->probe = probe; | ||
1669 | subdev->remove = remove; | ||
1670 | |||
1671 | list_add_tail(&subdev->node, &rproc->subdevs); | 1724 | list_add_tail(&subdev->node, &rproc->subdevs); |
1672 | } | 1725 | } |
1673 | EXPORT_SYMBOL(rproc_add_subdev); | 1726 | EXPORT_SYMBOL(rproc_add_subdev); |
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index a20488336aa0..a5c29f2764a3 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c | |||
@@ -231,7 +231,7 @@ static int rproc_rsc_table_show(struct seq_file *seq, void *p) | |||
231 | } | 231 | } |
232 | break; | 232 | break; |
233 | default: | 233 | default: |
234 | seq_printf(seq, "Unknown resource type found: %d [hdr: %p]\n", | 234 | seq_printf(seq, "Unknown resource type found: %d [hdr: %pK]\n", |
235 | hdr->type, hdr); | 235 | hdr->type, hdr); |
236 | break; | 236 | break; |
237 | } | 237 | } |
@@ -260,7 +260,7 @@ static int rproc_carveouts_show(struct seq_file *seq, void *p) | |||
260 | 260 | ||
261 | list_for_each_entry(carveout, &rproc->carveouts, node) { | 261 | list_for_each_entry(carveout, &rproc->carveouts, node) { |
262 | seq_puts(seq, "Carveout memory entry:\n"); | 262 | seq_puts(seq, "Carveout memory entry:\n"); |
263 | seq_printf(seq, "\tVirtual address: %p\n", carveout->va); | 263 | seq_printf(seq, "\tVirtual address: %pK\n", carveout->va); |
264 | seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); | 264 | seq_printf(seq, "\tDMA address: %pad\n", &carveout->dma); |
265 | seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); | 265 | seq_printf(seq, "\tDevice address: 0x%x\n", carveout->da); |
266 | seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len); | 266 | seq_printf(seq, "\tLength: 0x%x Bytes\n\n", carveout->len); |
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c index b0633fd4c041..bbecd44df7e8 100644 --- a/drivers/remoteproc/remoteproc_virtio.c +++ b/drivers/remoteproc/remoteproc_virtio.c | |||
@@ -96,7 +96,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev, | |||
96 | size = vring_size(len, rvring->align); | 96 | size = vring_size(len, rvring->align); |
97 | memset(addr, 0, size); | 97 | memset(addr, 0, size); |
98 | 98 | ||
99 | dev_dbg(dev, "vring%d: va %p qsz %d notifyid %d\n", | 99 | dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n", |
100 | id, addr, len, rvring->notifyid); | 100 | id, addr, len, rvring->notifyid); |
101 | 101 | ||
102 | /* | 102 | /* |
diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index 1ffb1f0c43d6..d711d9430a4f 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c | |||
@@ -195,7 +195,8 @@ static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len) | |||
195 | } | 195 | } |
196 | } | 196 | } |
197 | 197 | ||
198 | dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va); | 198 | dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%pK\n", |
199 | da, len, va); | ||
199 | 200 | ||
200 | return va; | 201 | return va; |
201 | } | 202 | } |
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index dc09d7ac905f..1c488024c698 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c | |||
@@ -74,23 +74,10 @@ ssize_t qcom_mdt_get_size(const struct firmware *fw) | |||
74 | } | 74 | } |
75 | EXPORT_SYMBOL_GPL(qcom_mdt_get_size); | 75 | EXPORT_SYMBOL_GPL(qcom_mdt_get_size); |
76 | 76 | ||
77 | /** | 77 | static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, |
78 | * qcom_mdt_load() - load the firmware which header is loaded as fw | 78 | const char *firmware, int pas_id, void *mem_region, |
79 | * @dev: device handle to associate resources with | 79 | phys_addr_t mem_phys, size_t mem_size, |
80 | * @fw: firmware object for the mdt file | 80 | phys_addr_t *reloc_base, bool pas_init) |
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 | * @reloc_base: adjusted physical address after relocation | ||
87 | * | ||
88 | * Returns 0 on success, negative errno otherwise. | ||
89 | */ | ||
90 | int qcom_mdt_load(struct device *dev, const struct firmware *fw, | ||
91 | const char *firmware, int pas_id, void *mem_region, | ||
92 | phys_addr_t mem_phys, size_t mem_size, | ||
93 | phys_addr_t *reloc_base) | ||
94 | { | 81 | { |
95 | const struct elf32_phdr *phdrs; | 82 | const struct elf32_phdr *phdrs; |
96 | const struct elf32_phdr *phdr; | 83 | const struct elf32_phdr *phdr; |
@@ -121,10 +108,12 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, | |||
121 | if (!fw_name) | 108 | if (!fw_name) |
122 | return -ENOMEM; | 109 | return -ENOMEM; |
123 | 110 | ||
124 | ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); | 111 | if (pas_init) { |
125 | if (ret) { | 112 | ret = qcom_scm_pas_init_image(pas_id, fw->data, fw->size); |
126 | dev_err(dev, "invalid firmware metadata\n"); | 113 | if (ret) { |
127 | goto out; | 114 | dev_err(dev, "invalid firmware metadata\n"); |
115 | goto out; | ||
116 | } | ||
128 | } | 117 | } |
129 | 118 | ||
130 | for (i = 0; i < ehdr->e_phnum; i++) { | 119 | for (i = 0; i < ehdr->e_phnum; i++) { |
@@ -144,10 +133,13 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, | |||
144 | } | 133 | } |
145 | 134 | ||
146 | if (relocate) { | 135 | if (relocate) { |
147 | ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); | 136 | if (pas_init) { |
148 | if (ret) { | 137 | ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, |
149 | dev_err(dev, "unable to setup relocation\n"); | 138 | max_addr - min_addr); |
150 | goto out; | 139 | if (ret) { |
140 | dev_err(dev, "unable to setup relocation\n"); | ||
141 | goto out; | ||
142 | } | ||
151 | } | 143 | } |
152 | 144 | ||
153 | /* | 145 | /* |
@@ -202,7 +194,52 @@ out: | |||
202 | 194 | ||
203 | return ret; | 195 | return ret; |
204 | } | 196 | } |
197 | |||
198 | /** | ||
199 | * qcom_mdt_load() - load the firmware which header is loaded as fw | ||
200 | * @dev: device handle to associate resources with | ||
201 | * @fw: firmware object for the mdt file | ||
202 | * @firmware: name of the firmware, for construction of segment file names | ||
203 | * @pas_id: PAS identifier | ||
204 | * @mem_region: allocated memory region to load firmware into | ||
205 | * @mem_phys: physical address of allocated memory region | ||
206 | * @mem_size: size of the allocated memory region | ||
207 | * @reloc_base: adjusted physical address after relocation | ||
208 | * | ||
209 | * Returns 0 on success, negative errno otherwise. | ||
210 | */ | ||
211 | int qcom_mdt_load(struct device *dev, const struct firmware *fw, | ||
212 | const char *firmware, int pas_id, void *mem_region, | ||
213 | phys_addr_t mem_phys, size_t mem_size, | ||
214 | phys_addr_t *reloc_base) | ||
215 | { | ||
216 | return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, | ||
217 | mem_size, reloc_base, true); | ||
218 | } | ||
205 | EXPORT_SYMBOL_GPL(qcom_mdt_load); | 219 | EXPORT_SYMBOL_GPL(qcom_mdt_load); |
206 | 220 | ||
221 | /** | ||
222 | * qcom_mdt_load_no_init() - load the firmware which header is loaded as fw | ||
223 | * @dev: device handle to associate resources with | ||
224 | * @fw: firmware object for the mdt file | ||
225 | * @firmware: name of the firmware, for construction of segment file names | ||
226 | * @pas_id: PAS identifier | ||
227 | * @mem_region: allocated memory region to load firmware into | ||
228 | * @mem_phys: physical address of allocated memory region | ||
229 | * @mem_size: size of the allocated memory region | ||
230 | * @reloc_base: adjusted physical address after relocation | ||
231 | * | ||
232 | * Returns 0 on success, negative errno otherwise. | ||
233 | */ | ||
234 | int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, | ||
235 | const char *firmware, int pas_id, | ||
236 | void *mem_region, phys_addr_t mem_phys, | ||
237 | size_t mem_size, phys_addr_t *reloc_base) | ||
238 | { | ||
239 | return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, | ||
240 | mem_size, reloc_base, false); | ||
241 | } | ||
242 | EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); | ||
243 | |||
207 | MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); | 244 | MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); |
208 | MODULE_LICENSE("GPL v2"); | 245 | MODULE_LICENSE("GPL v2"); |
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h index dfdaede9139e..e3c5d856b6da 100644 --- a/include/linux/remoteproc.h +++ b/include/linux/remoteproc.h | |||
@@ -477,15 +477,19 @@ struct rproc { | |||
477 | /** | 477 | /** |
478 | * struct rproc_subdev - subdevice tied to a remoteproc | 478 | * struct rproc_subdev - subdevice tied to a remoteproc |
479 | * @node: list node related to the rproc subdevs list | 479 | * @node: list node related to the rproc subdevs list |
480 | * @probe: probe function, called as the rproc is started | 480 | * @prepare: prepare function, called before the rproc is started |
481 | * @remove: remove function, called as the rproc is being stopped, the @crashed | 481 | * @start: start function, called after the rproc has been started |
482 | * parameter indicates if this originates from the a recovery | 482 | * @stop: stop function, called before the rproc is stopped; the @crashed |
483 | * parameter indicates if this originates from a recovery | ||
484 | * @unprepare: unprepare function, called after the rproc has been stopped | ||
483 | */ | 485 | */ |
484 | struct rproc_subdev { | 486 | struct rproc_subdev { |
485 | struct list_head node; | 487 | struct list_head node; |
486 | 488 | ||
487 | int (*probe)(struct rproc_subdev *subdev); | 489 | int (*prepare)(struct rproc_subdev *subdev); |
488 | void (*remove)(struct rproc_subdev *subdev, bool crashed); | 490 | int (*start)(struct rproc_subdev *subdev); |
491 | void (*stop)(struct rproc_subdev *subdev, bool crashed); | ||
492 | void (*unprepare)(struct rproc_subdev *subdev); | ||
489 | }; | 493 | }; |
490 | 494 | ||
491 | /* we currently support only two vrings per rvdev */ | 495 | /* we currently support only two vrings per rvdev */ |
@@ -566,10 +570,7 @@ static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev) | |||
566 | return rvdev->rproc; | 570 | return rvdev->rproc; |
567 | } | 571 | } |
568 | 572 | ||
569 | void rproc_add_subdev(struct rproc *rproc, | 573 | void rproc_add_subdev(struct rproc *rproc, struct rproc_subdev *subdev); |
570 | struct rproc_subdev *subdev, | ||
571 | int (*probe)(struct rproc_subdev *subdev), | ||
572 | void (*remove)(struct rproc_subdev *subdev, bool crashed)); | ||
573 | 574 | ||
574 | void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev); | 575 | void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev); |
575 | 576 | ||
diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 5b98bbdabc25..944b06aefb0f 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h | |||
@@ -17,4 +17,8 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, | |||
17 | phys_addr_t mem_phys, size_t mem_size, | 17 | phys_addr_t mem_phys, size_t mem_size, |
18 | phys_addr_t *reloc_base); | 18 | phys_addr_t *reloc_base); |
19 | 19 | ||
20 | int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, | ||
21 | const char *fw_name, int pas_id, void *mem_region, | ||
22 | phys_addr_t mem_phys, size_t mem_size, | ||
23 | phys_addr_t *reloc_base); | ||
20 | #endif | 24 | #endif |