]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/remoteproc/qcom_adsp_pil.c
nfp: flower: ensure dead neighbour entries are not offloaded
[linux.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         const char *ssr_name;
43         const char *sysmon_name;
44         int ssctl_id;
45 };
46
47 struct qcom_adsp {
48         struct device *dev;
49         struct rproc *rproc;
50
51         int wdog_irq;
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
60         struct clk *xo;
61         struct clk *aggre2_clk;
62
63         struct regulator *cx_supply;
64         struct regulator *px_supply;
65
66         int pas_id;
67         int crash_reason_smem;
68         bool has_aggre2_clk;
69
70         struct completion start_done;
71         struct completion stop_done;
72
73         phys_addr_t mem_phys;
74         phys_addr_t mem_reloc;
75         void *mem_region;
76         size_t mem_size;
77
78         struct qcom_rproc_glink glink_subdev;
79         struct qcom_rproc_subdev smd_subdev;
80         struct qcom_rproc_ssr ssr_subdev;
81         struct qcom_sysmon *sysmon;
82 };
83
84 static int adsp_load(struct rproc *rproc, const struct firmware *fw)
85 {
86         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
87
88         return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
89                              adsp->mem_region, adsp->mem_phys, adsp->mem_size,
90                              &adsp->mem_reloc);
91
92 }
93
94 static int adsp_start(struct rproc *rproc)
95 {
96         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
97         int ret;
98
99         ret = clk_prepare_enable(adsp->xo);
100         if (ret)
101                 return ret;
102
103         ret = clk_prepare_enable(adsp->aggre2_clk);
104         if (ret)
105                 goto disable_xo_clk;
106
107         ret = regulator_enable(adsp->cx_supply);
108         if (ret)
109                 goto disable_aggre2_clk;
110
111         ret = regulator_enable(adsp->px_supply);
112         if (ret)
113                 goto disable_cx_supply;
114
115         ret = qcom_scm_pas_auth_and_reset(adsp->pas_id);
116         if (ret) {
117                 dev_err(adsp->dev,
118                         "failed to authenticate image and release reset\n");
119                 goto disable_px_supply;
120         }
121
122         ret = wait_for_completion_timeout(&adsp->start_done,
123                                           msecs_to_jiffies(5000));
124         if (!ret) {
125                 dev_err(adsp->dev, "start timed out\n");
126                 qcom_scm_pas_shutdown(adsp->pas_id);
127                 ret = -ETIMEDOUT;
128                 goto disable_px_supply;
129         }
130
131         ret = 0;
132
133 disable_px_supply:
134         regulator_disable(adsp->px_supply);
135 disable_cx_supply:
136         regulator_disable(adsp->cx_supply);
137 disable_aggre2_clk:
138         clk_disable_unprepare(adsp->aggre2_clk);
139 disable_xo_clk:
140         clk_disable_unprepare(adsp->xo);
141
142         return ret;
143 }
144
145 static int adsp_stop(struct rproc *rproc)
146 {
147         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
148         int ret;
149
150         qcom_smem_state_update_bits(adsp->state,
151                                     BIT(adsp->stop_bit),
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");
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);
164         if (ret)
165                 dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
166
167         return ret;
168 }
169
170 static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
171 {
172         struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
173         int offset;
174
175         offset = da - adsp->mem_reloc;
176         if (offset < 0 || offset + len > adsp->mem_size)
177                 return NULL;
178
179         return adsp->mem_region + offset;
180 }
181
182 static const struct rproc_ops adsp_ops = {
183         .start = adsp_start,
184         .stop = adsp_stop,
185         .da_to_va = adsp_da_to_va,
186         .parse_fw = qcom_register_dump_segments,
187         .load = adsp_load,
188 };
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)
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         adsp = (struct qcom_adsp *)rproc->priv;
348         adsp->dev = &pdev->dev;
349         adsp->rproc = rproc;
350         adsp->pas_id = desc->pas_id;
351         adsp->crash_reason_smem = desc->crash_reason_smem;
352         adsp->has_aggre2_clk = desc->has_aggre2_clk;
353         platform_set_drvdata(pdev, adsp);
354
355         init_completion(&adsp->start_done);
356         init_completion(&adsp->stop_done);
357
358         ret = adsp_alloc_memory_region(adsp);
359         if (ret)
360                 goto free_rproc;
361
362         ret = adsp_init_clock(adsp);
363         if (ret)
364                 goto free_rproc;
365
366         ret = adsp_init_regulator(adsp);
367         if (ret)
368                 goto free_rproc;
369
370         ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt);
371         if (ret < 0)
372                 goto free_rproc;
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;
400         }
401
402         qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
403         qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
404         qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
405         adsp->sysmon = qcom_add_sysmon_subdev(rproc,
406                                               desc->sysmon_name,
407                                               desc->ssctl_id);
408
409         ret = rproc_add(rproc);
410         if (ret)
411                 goto free_rproc;
412
413         return 0;
414
415 free_rproc:
416         rproc_free(rproc);
417
418         return ret;
419 }
420
421 static int adsp_remove(struct platform_device *pdev)
422 {
423         struct qcom_adsp *adsp = platform_get_drvdata(pdev);
424
425         qcom_smem_state_put(adsp->state);
426         rproc_del(adsp->rproc);
427
428         qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
429         qcom_remove_sysmon_subdev(adsp->sysmon);
430         qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
431         qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
432         rproc_free(adsp->rproc);
433
434         return 0;
435 }
436
437 static const struct adsp_data adsp_resource_init = {
438                 .crash_reason_smem = 423,
439                 .firmware_name = "adsp.mdt",
440                 .pas_id = 1,
441                 .has_aggre2_clk = false,
442                 .ssr_name = "lpass",
443                 .sysmon_name = "adsp",
444                 .ssctl_id = 0x14,
445 };
446
447 static const struct adsp_data slpi_resource_init = {
448                 .crash_reason_smem = 424,
449                 .firmware_name = "slpi.mdt",
450                 .pas_id = 12,
451                 .has_aggre2_clk = true,
452                 .ssr_name = "dsps",
453                 .sysmon_name = "slpi",
454                 .ssctl_id = 0x16,
455 };
456
457 static const struct of_device_id adsp_of_match[] = {
458         { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init},
459         { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init},
460         { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init},
461         { },
462 };
463 MODULE_DEVICE_TABLE(of, adsp_of_match);
464
465 static struct platform_driver adsp_driver = {
466         .probe = adsp_probe,
467         .remove = adsp_remove,
468         .driver = {
469                 .name = "qcom_adsp_pil",
470                 .of_match_table = adsp_of_match,
471         },
472 };
473
474 module_platform_driver(adsp_driver);
475 MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader");
476 MODULE_LICENSE("GPL v2");