Merge tag 'rpmsg-v4.14' of git://github.com/andersson/remoteproc
[sfrench/cifs-2.6.git] / drivers / remoteproc / qcom_adsp_pil.c
1 /*
2  * Qualcomm ADSP/SLPI Peripheral Image Loader for MSM8974 and MSM8996
3  *
4  * Copyright (C) 2016 Linaro Ltd
5  * Copyright (C) 2014 Sony Mobile Communications AB
6  * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  */
17
18 #include <linux/clk.h>
19 #include <linux/firmware.h>
20 #include <linux/interrupt.h>
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/of_address.h>
24 #include <linux/of_device.h>
25 #include <linux/platform_device.h>
26 #include <linux/qcom_scm.h>
27 #include <linux/regulator/consumer.h>
28 #include <linux/remoteproc.h>
29 #include <linux/soc/qcom/mdt_loader.h>
30 #include <linux/soc/qcom/smem.h>
31 #include <linux/soc/qcom/smem_state.h>
32
33 #include "qcom_common.h"
34 #include "remoteproc_internal.h"
35
36 struct adsp_data {
37         int crash_reason_smem;
38         const char *firmware_name;
39         int pas_id;
40         bool has_aggre2_clk;
41         const char *ssr_name;
42 };
43
44 struct qcom_adsp {
45         struct device *dev;
46         struct rproc *rproc;
47
48         int wdog_irq;
49         int fatal_irq;
50         int ready_irq;
51         int handover_irq;
52         int stop_ack_irq;
53
54         struct qcom_smem_state *state;
55         unsigned stop_bit;
56
57         struct clk *xo;
58         struct clk *aggre2_clk;
59
60         struct regulator *cx_supply;
61         struct regulator *px_supply;
62
63         int pas_id;
64         int crash_reason_smem;
65         bool has_aggre2_clk;
66
67         struct completion start_done;
68         struct completion stop_done;
69
70         phys_addr_t mem_phys;
71         phys_addr_t mem_reloc;
72         void *mem_region;
73         size_t mem_size;
74
75         struct qcom_rproc_glink glink_subdev;
76         struct qcom_rproc_subdev smd_subdev;
77         struct qcom_rproc_ssr ssr_subdev;
78 };
79
80 static int adsp_load(struct rproc *rproc, const struct firmware *fw)
81 {
82         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
83
84         return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
85                              adsp->mem_region, adsp->mem_phys, adsp->mem_size);
86 }
87
88 static const struct rproc_fw_ops adsp_fw_ops = {
89         .find_rsc_table = qcom_mdt_find_rsc_table,
90         .load = adsp_load,
91 };
92
93 static int adsp_start(struct rproc *rproc)
94 {
95         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
96         int ret;
97
98         ret = clk_prepare_enable(adsp->xo);
99         if (ret)
100                 return ret;
101
102         ret = clk_prepare_enable(adsp->aggre2_clk);
103         if (ret)
104                 goto disable_xo_clk;
105
106         ret = regulator_enable(adsp->cx_supply);
107         if (ret)
108                 goto disable_aggre2_clk;
109
110         ret = regulator_enable(adsp->px_supply);
111         if (ret)
112                 goto disable_cx_supply;
113
114         ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
115         if (ret) {
116                 dev_err(adsp->dev,
117                         "failed to authenticate image and release reset\n");
118                 goto disable_px_supply;
119         }
120
121         ret = wait_for_completion_timeout(&adsp->start_done,
122                                           msecs_to_jiffies(5000));
123         if (!ret) {
124                 dev_err(adsp->dev, "start timed out\n");
125                 qcom_scm_pas_shutdown(adsp->pas_id);
126                 ret = -ETIMEDOUT;
127                 goto disable_px_supply;
128         }
129
130         ret = 0;
131
132 disable_px_supply:
133         regulator_disable(adsp->px_supply);
134 disable_cx_supply:
135         regulator_disable(adsp->cx_supply);
136 disable_aggre2_clk:
137         clk_disable_unprepare(adsp->aggre2_clk);
138 disable_xo_clk:
139         clk_disable_unprepare(adsp->xo);
140
141         return ret;
142 }
143
144 static int adsp_stop(struct rproc *rproc)
145 {
146         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
147         int ret;
148
149         qcom_smem_state_update_bits(adsp->state,
150                                     BIT(adsp->stop_bit),
151                                     BIT(adsp->stop_bit));
152
153         ret = wait_for_completion_timeout(&adsp->stop_done,
154                                           msecs_to_jiffies(5000));
155         if (ret == 0)
156                 dev_err(adsp->dev, "timed out on wait\n");
157
158         qcom_smem_state_update_bits(adsp->state,
159                                     BIT(adsp->stop_bit),
160                                     0);
161
162         ret = qcom_scm_pas_shutdown(adsp->pas_id);
163         if (ret)
164                 dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
165
166         return ret;
167 }
168
169 static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
170 {
171         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
172         int offset;
173
174         offset = da - adsp->mem_reloc;
175         if (offset < 0 || offset + len > adsp->mem_size)
176                 return NULL;
177
178         return adsp->mem_region + offset;
179 }
180
181 static const struct rproc_ops adsp_ops = {
182         .start = adsp_start,
183         .stop = adsp_stop,
184         .da_to_va = adsp_da_to_va,
185 };
186
187 static irqreturn_t adsp_wdog_interrupt(int irq, void *dev)
188 {
189         struct qcom_adsp *adsp = dev;
190
191         rproc_report_crash(adsp->rproc, RPROC_WATCHDOG);
192
193         return IRQ_HANDLED;
194 }
195
196 static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
197 {
198         struct qcom_adsp *adsp = dev;
199         size_t len;
200         char *msg;
201
202         msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, adsp->crash_reason_smem, &len);
203         if (!IS_ERR(msg) && len > 0 && msg[0])
204                 dev_err(adsp->dev, "fatal error received: %s\n", msg);
205
206         rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
207
208         if (!IS_ERR(msg))
209                 msg[0] = '\0';
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)
238 {
239         int ret;
240
241         adsp->xo = devm_clk_get(adsp->dev, "xo");
242         if (IS_ERR(adsp->xo)) {
243                 ret = PTR_ERR(adsp->xo);
244                 if (ret != -EPROBE_DEFER)
245                         dev_err(adsp->dev, "failed to get xo clock");
246                 return ret;
247         }
248
249         if (adsp->has_aggre2_clk) {
250                 adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2");
251                 if (IS_ERR(adsp->aggre2_clk)) {
252                         ret = PTR_ERR(adsp->aggre2_clk);
253                         if (ret != -EPROBE_DEFER)
254                                 dev_err(adsp->dev,
255                                         "failed to get aggre2 clock");
256                         return ret;
257                 }
258         }
259
260         return 0;
261 }
262
263 static int adsp_init_regulator(struct qcom_adsp *adsp)
264 {
265         adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
266         if (IS_ERR(adsp->cx_supply))
267                 return PTR_ERR(adsp->cx_supply);
268
269         regulator_set_load(adsp->cx_supply, 100000);
270
271         adsp->px_supply = devm_regulator_get(adsp->dev, "px");
272         return PTR_ERR_OR_ZERO(adsp->px_supply);
273 }
274
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)
299 {
300         struct device_node *node;
301         struct resource r;
302         int ret;
303
304         node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
305         if (!node) {
306                 dev_err(adsp->dev, "no memory-region specified\n");
307                 return -EINVAL;
308         }
309
310         ret = of_address_to_resource(node, 0, &r);
311         if (ret)
312                 return ret;
313
314         adsp->mem_phys = adsp->mem_reloc = r.start;
315         adsp->mem_size = resource_size(&r);
316         adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size);
317         if (!adsp->mem_region) {
318                 dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
319                         &r.start, adsp->mem_size);
320                 return -EBUSY;
321         }
322
323         return 0;
324 }
325
326 static int adsp_probe(struct platform_device *pdev)
327 {
328         const struct adsp_data *desc;
329         struct qcom_adsp *adsp;
330         struct rproc *rproc;
331         int ret;
332
333         desc = of_device_get_match_data(&pdev->dev);
334         if (!desc)
335                 return -EINVAL;
336
337         if (!qcom_scm_is_available())
338                 return -EPROBE_DEFER;
339
340         rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
341                             desc->firmware_name, sizeof(*adsp));
342         if (!rproc) {
343                 dev_err(&pdev->dev, "unable to allocate remoteproc\n");
344                 return -ENOMEM;
345         }
346
347         rproc->fw_ops = &adsp_fw_ops;
348
349         adsp = (struct qcom_adsp *)rproc->priv;
350         adsp->dev = &pdev->dev;
351         adsp->rproc = rproc;
352         adsp->pas_id = desc->pas_id;
353         adsp->crash_reason_smem = desc->crash_reason_smem;
354         adsp->has_aggre2_clk = desc->has_aggre2_clk;
355         platform_set_drvdata(pdev, adsp);
356
357         init_completion(&adsp->start_done);
358         init_completion(&adsp->stop_done);
359
360         ret = adsp_alloc_memory_region(adsp);
361         if (ret)
362                 goto free_rproc;
363
364         ret = adsp_init_clock(adsp);
365         if (ret)
366                 goto free_rproc;
367
368         ret = adsp_init_regulator(adsp);
369         if (ret)
370                 goto free_rproc;
371
372         ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt);
373         if (ret < 0)
374                 goto free_rproc;
375         adsp->wdog_irq = ret;
376
377         ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt);
378         if (ret < 0)
379                 goto free_rproc;
380         adsp->fatal_irq = ret;
381
382         ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt);
383         if (ret < 0)
384                 goto free_rproc;
385         adsp->ready_irq = ret;
386
387         ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt);
388         if (ret < 0)
389                 goto free_rproc;
390         adsp->handover_irq = ret;
391
392         ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt);
393         if (ret < 0)
394                 goto free_rproc;
395         adsp->stop_ack_irq = ret;
396
397         adsp->state = qcom_smem_state_get(&pdev->dev, "stop",
398                                           &adsp->stop_bit);
399         if (IS_ERR(adsp->state)) {
400                 ret = PTR_ERR(adsp->state);
401                 goto free_rproc;
402         }
403
404         qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
405         qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
406         qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
407
408         ret = rproc_add(rproc);
409         if (ret)
410                 goto free_rproc;
411
412         return 0;
413
414 free_rproc:
415         rproc_free(rproc);
416
417         return ret;
418 }
419
420 static int adsp_remove(struct platform_device *pdev)
421 {
422         struct qcom_adsp *adsp = platform_get_drvdata(pdev);
423
424         qcom_smem_state_put(adsp->state);
425         rproc_del(adsp->rproc);
426
427         qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
428         qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
429         qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
430         rproc_free(adsp->rproc);
431
432         return 0;
433 }
434
435 static const struct adsp_data adsp_resource_init = {
436                 .crash_reason_smem = 423,
437                 .firmware_name = "adsp.mdt",
438                 .pas_id = 1,
439                 .has_aggre2_clk = false,
440                 .ssr_name = "lpass",
441 };
442
443 static const struct adsp_data slpi_resource_init = {
444                 .crash_reason_smem = 424,
445                 .firmware_name = "slpi.mdt",
446                 .pas_id = 12,
447                 .has_aggre2_clk = true,
448                 .ssr_name = "dsps",
449 };
450
451 static const struct of_device_id adsp_of_match[] = {
452         { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
453         { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
454         { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
455         { },
456 };
457 MODULE_DEVICE_TABLE(of, adsp_of_match);
458
459 static struct platform_driver adsp_driver = {
460         .probe = adsp_probe,
461         .remove = adsp_remove,
462         .driver = {
463                 .name = "qcom_adsp_pil",
464                 .of_match_table = adsp_of_match,
465         },
466 };
467
468 module_platform_driver(adsp_driver);
469 MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader");
470 MODULE_LICENSE("GPL v2");