Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec
[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 };
42
43 struct qcom_adsp {
44         struct device *dev;
45         struct rproc *rproc;
46
47         int wdog_irq;
48         int fatal_irq;
49         int ready_irq;
50         int handover_irq;
51         int stop_ack_irq;
52
53         struct qcom_smem_state *state;
54         unsigned stop_bit;
55
56         struct clk *xo;
57         struct clk *aggre2_clk;
58
59         struct regulator *cx_supply;
60         struct regulator *px_supply;
61
62         int pas_id;
63         int crash_reason_smem;
64         bool has_aggre2_clk;
65
66         struct completion start_done;
67         struct completion stop_done;
68
69         phys_addr_t mem_phys;
70         phys_addr_t mem_reloc;
71         void *mem_region;
72         size_t mem_size;
73
74         struct qcom_rproc_subdev smd_subdev;
75 };
76
77 static int adsp_load(struct rproc *rproc, const struct firmware *fw)
78 {
79         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
80
81         return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
82                              adsp->mem_region, adsp->mem_phys, adsp->mem_size);
83 }
84
85 static const struct rproc_fw_ops adsp_fw_ops = {
86         .find_rsc_table = qcom_mdt_find_rsc_table,
87         .load = adsp_load,
88 };
89
90 static int adsp_start(struct rproc *rproc)
91 {
92         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
93         int ret;
94
95         ret = clk_prepare_enable(adsp->xo);
96         if (ret)
97                 return ret;
98
99         ret = clk_prepare_enable(adsp->aggre2_clk);
100         if (ret)
101                 goto disable_xo_clk;
102
103         ret = regulator_enable(adsp->cx_supply);
104         if (ret)
105                 goto disable_aggre2_clk;
106
107         ret = regulator_enable(adsp->px_supply);
108         if (ret)
109                 goto disable_cx_supply;
110
111         ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
112         if (ret) {
113                 dev_err(adsp->dev,
114                         "failed to authenticate image and release reset\n");
115                 goto disable_px_supply;
116         }
117
118         ret = wait_for_completion_timeout(&adsp->start_done,
119                                           msecs_to_jiffies(5000));
120         if (!ret) {
121                 dev_err(adsp->dev, "start timed out\n");
122                 qcom_scm_pas_shutdown(adsp->pas_id);
123                 ret = -ETIMEDOUT;
124                 goto disable_px_supply;
125         }
126
127         ret = 0;
128
129 disable_px_supply:
130         regulator_disable(adsp->px_supply);
131 disable_cx_supply:
132         regulator_disable(adsp->cx_supply);
133 disable_aggre2_clk:
134         clk_disable_unprepare(adsp->aggre2_clk);
135 disable_xo_clk:
136         clk_disable_unprepare(adsp->xo);
137
138         return ret;
139 }
140
141 static int adsp_stop(struct rproc *rproc)
142 {
143         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
144         int ret;
145
146         qcom_smem_state_update_bits(adsp->state,
147                                     BIT(adsp->stop_bit),
148                                     BIT(adsp->stop_bit));
149
150         ret = wait_for_completion_timeout(&adsp->stop_done,
151                                           msecs_to_jiffies(5000));
152         if (ret == 0)
153                 dev_err(adsp->dev, "timed out on wait\n");
154
155         qcom_smem_state_update_bits(adsp->state,
156                                     BIT(adsp->stop_bit),
157                                     0);
158
159         ret = qcom_scm_pas_shutdown(adsp->pas_id);
160         if (ret)
161                 dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
162
163         return ret;
164 }
165
166 static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
167 {
168         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
169         int offset;
170
171         offset = da - adsp->mem_reloc;
172         if (offset < 0 || offset + len > adsp->mem_size)
173                 return NULL;
174
175         return adsp->mem_region + offset;
176 }
177
178 static const struct rproc_ops adsp_ops = {
179         .start = adsp_start,
180         .stop = adsp_stop,
181         .da_to_va = adsp_da_to_va,
182 };
183
184 static irqreturn_t adsp_wdog_interrupt(int irq, void *dev)
185 {
186         struct qcom_adsp *adsp = dev;
187
188         rproc_report_crash(adsp->rproc, RPROC_WATCHDOG);
189
190         return IRQ_HANDLED;
191 }
192
193 static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
194 {
195         struct qcom_adsp *adsp = dev;
196         size_t len;
197         char *msg;
198
199         msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, adsp->crash_reason_smem, &len);
200         if (!IS_ERR(msg) && len > 0 && msg[0])
201                 dev_err(adsp->dev, "fatal error received: %s\n", msg);
202
203         rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
204
205         if (!IS_ERR(msg))
206                 msg[0] = '\0';
207
208         return IRQ_HANDLED;
209 }
210
211 static irqreturn_t adsp_ready_interrupt(int irq, void *dev)
212 {
213         return IRQ_HANDLED;
214 }
215
216 static irqreturn_t adsp_handover_interrupt(int irq, void *dev)
217 {
218         struct qcom_adsp *adsp = dev;
219
220         complete(&adsp->start_done);
221
222         return IRQ_HANDLED;
223 }
224
225 static irqreturn_t adsp_stop_ack_interrupt(int irq, void *dev)
226 {
227         struct qcom_adsp *adsp = dev;
228
229         complete(&adsp->stop_done);
230
231         return IRQ_HANDLED;
232 }
233
234 static int adsp_init_clock(struct qcom_adsp *adsp)
235 {
236         int ret;
237
238         adsp->xo = devm_clk_get(adsp->dev, "xo");
239         if (IS_ERR(adsp->xo)) {
240                 ret = PTR_ERR(adsp->xo);
241                 if (ret != -EPROBE_DEFER)
242                         dev_err(adsp->dev, "failed to get xo clock");
243                 return ret;
244         }
245
246         if (adsp->has_aggre2_clk) {
247                 adsp->aggre2_clk = devm_clk_get(adsp->dev, "aggre2");
248                 if (IS_ERR(adsp->aggre2_clk)) {
249                         ret = PTR_ERR(adsp->aggre2_clk);
250                         if (ret != -EPROBE_DEFER)
251                                 dev_err(adsp->dev,
252                                         "failed to get aggre2 clock");
253                         return ret;
254                 }
255         }
256
257         return 0;
258 }
259
260 static int adsp_init_regulator(struct qcom_adsp *adsp)
261 {
262         adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
263         if (IS_ERR(adsp->cx_supply))
264                 return PTR_ERR(adsp->cx_supply);
265
266         regulator_set_load(adsp->cx_supply, 100000);
267
268         adsp->px_supply = devm_regulator_get(adsp->dev, "px");
269         if (IS_ERR(adsp->px_supply))
270                 return PTR_ERR(adsp->px_supply);
271
272         return 0;
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_smd_subdev(rproc, &adsp->smd_subdev);
405
406         ret = rproc_add(rproc);
407         if (ret)
408                 goto free_rproc;
409
410         return 0;
411
412 free_rproc:
413         rproc_free(rproc);
414
415         return ret;
416 }
417
418 static int adsp_remove(struct platform_device *pdev)
419 {
420         struct qcom_adsp *adsp = platform_get_drvdata(pdev);
421
422         qcom_smem_state_put(adsp->state);
423         rproc_del(adsp->rproc);
424
425         qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
426         rproc_free(adsp->rproc);
427
428         return 0;
429 }
430
431 static const struct adsp_data adsp_resource_init = {
432                 .crash_reason_smem = 423,
433                 .firmware_name = "adsp.mdt",
434                 .pas_id = 1,
435                 .has_aggre2_clk = false,
436 };
437
438 static const struct adsp_data slpi_resource_init = {
439                 .crash_reason_smem = 424,
440                 .firmware_name = "slpi.mdt",
441                 .pas_id = 12,
442                 .has_aggre2_clk = true,
443 };
444
445 static const struct of_device_id adsp_of_match[] = {
446         { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
447         { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
448         { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
449         { },
450 };
451 MODULE_DEVICE_TABLE(of, adsp_of_match);
452
453 static struct platform_driver adsp_driver = {
454         .probe = adsp_probe,
455         .remove = adsp_remove,
456         .driver = {
457                 .name = "qcom_adsp_pil",
458                 .of_match_table = adsp_of_match,
459         },
460 };
461
462 module_platform_driver(adsp_driver);
463 MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader");
464 MODULE_LICENSE("GPL v2");