]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/remoteproc/qcom_adsp_pil.c
remoteproc: Introduce Qualcomm ADSP PIL
[mirror_ubuntu-artful-kernel.git] / drivers / remoteproc / qcom_adsp_pil.c
CommitLineData
b9e718e9
BA
1/*
2 * Qualcomm ADSP 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/firmware.h>
19#include <linux/interrupt.h>
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/of_address.h>
23#include <linux/of_device.h>
24#include <linux/platform_device.h>
25#include <linux/qcom_scm.h>
26#include <linux/regulator/consumer.h>
27#include <linux/remoteproc.h>
28#include <linux/soc/qcom/smem.h>
29#include <linux/soc/qcom/smem_state.h>
30
31#include "qcom_mdt_loader.h"
32#include "remoteproc_internal.h"
33
34#define ADSP_CRASH_REASON_SMEM 423
35#define ADSP_FIRMWARE_NAME "adsp.mdt"
36#define ADSP_PAS_ID 1
37
38struct qcom_adsp {
39 struct device *dev;
40 struct rproc *rproc;
41
42 int wdog_irq;
43 int fatal_irq;
44 int ready_irq;
45 int handover_irq;
46 int stop_ack_irq;
47
48 struct qcom_smem_state *state;
49 unsigned stop_bit;
50
51 struct regulator *cx_supply;
52
53 struct completion start_done;
54 struct completion stop_done;
55
56 phys_addr_t mem_phys;
57 phys_addr_t mem_reloc;
58 void *mem_region;
59 size_t mem_size;
60};
61
62static int adsp_load(struct rproc *rproc, const struct firmware *fw)
63{
64 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
65 phys_addr_t fw_addr;
66 size_t fw_size;
67 bool relocate;
68 int ret;
69
70 ret = qcom_scm_pas_init_image(ADSP_PAS_ID, fw->data, fw->size);
71 if (ret) {
72 dev_err(&rproc->dev, "invalid firmware metadata\n");
73 return ret;
74 }
75
76 ret = qcom_mdt_parse(fw, &fw_addr, &fw_size, &relocate);
77 if (ret) {
78 dev_err(&rproc->dev, "failed to parse mdt header\n");
79 return ret;
80 }
81
82 if (relocate) {
83 adsp->mem_reloc = fw_addr;
84
85 ret = qcom_scm_pas_mem_setup(ADSP_PAS_ID, adsp->mem_phys, fw_size);
86 if (ret) {
87 dev_err(&rproc->dev, "unable to setup memory for image\n");
88 return ret;
89 }
90 }
91
92 return qcom_mdt_load(rproc, fw, rproc->firmware);
93}
94
95static const struct rproc_fw_ops adsp_fw_ops = {
96 .find_rsc_table = qcom_mdt_find_rsc_table,
97 .load = adsp_load,
98};
99
100static int adsp_start(struct rproc *rproc)
101{
102 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
103 int ret;
104
105 ret = regulator_enable(adsp->cx_supply);
106 if (ret)
107 return ret;
108
109 ret = qcom_scm_pas_auth_and_reset(ADSP_PAS_ID);
110 if (ret) {
111 dev_err(adsp->dev,
112 "failed to authenticate image and release reset\n");
113 goto disable_regulators;
114 }
115
116 ret = wait_for_completion_timeout(&adsp->start_done,
117 msecs_to_jiffies(5000));
118 if (!ret) {
119 dev_err(adsp->dev, "start timed out\n");
120 qcom_scm_pas_shutdown(ADSP_PAS_ID);
121 ret = -ETIMEDOUT;
122 goto disable_regulators;
123 }
124
125 ret = 0;
126
127disable_regulators:
128 regulator_disable(adsp->cx_supply);
129
130 return ret;
131}
132
133static int adsp_stop(struct rproc *rproc)
134{
135 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
136 int ret;
137
138 qcom_smem_state_update_bits(adsp->state,
139 BIT(adsp->stop_bit),
140 BIT(adsp->stop_bit));
141
142 ret = wait_for_completion_timeout(&adsp->stop_done,
143 msecs_to_jiffies(5000));
144 if (ret == 0)
145 dev_err(adsp->dev, "timed out on wait\n");
146
147 qcom_smem_state_update_bits(adsp->state,
148 BIT(adsp->stop_bit),
149 0);
150
151 ret = qcom_scm_pas_shutdown(ADSP_PAS_ID);
152 if (ret)
153 dev_err(adsp->dev, "failed to shutdown: %d\n", ret);
154
155 return ret;
156}
157
158static void *adsp_da_to_va(struct rproc *rproc, u64 da, int len)
159{
160 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
161 int offset;
162
163 offset = da - adsp->mem_reloc;
164 if (offset < 0 || offset + len > adsp->mem_size)
165 return NULL;
166
167 return adsp->mem_region + offset;
168}
169
170static const struct rproc_ops adsp_ops = {
171 .start = adsp_start,
172 .stop = adsp_stop,
173 .da_to_va = adsp_da_to_va,
174};
175
176static irqreturn_t adsp_wdog_interrupt(int irq, void *dev)
177{
178 struct qcom_adsp *adsp = dev;
179
180 rproc_report_crash(adsp->rproc, RPROC_WATCHDOG);
181
182 return IRQ_HANDLED;
183}
184
185static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
186{
187 struct qcom_adsp *adsp = dev;
188 size_t len;
189 char *msg;
190
191 msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, ADSP_CRASH_REASON_SMEM, &len);
192 if (!IS_ERR(msg) && len > 0 && msg[0])
193 dev_err(adsp->dev, "fatal error received: %s\n", msg);
194
195 rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
196
197 if (!IS_ERR(msg))
198 msg[0] = '\0';
199
200 return IRQ_HANDLED;
201}
202
203static irqreturn_t adsp_ready_interrupt(int irq, void *dev)
204{
205 return IRQ_HANDLED;
206}
207
208static irqreturn_t adsp_handover_interrupt(int irq, void *dev)
209{
210 struct qcom_adsp *adsp = dev;
211
212 complete(&adsp->start_done);
213
214 return IRQ_HANDLED;
215}
216
217static irqreturn_t adsp_stop_ack_interrupt(int irq, void *dev)
218{
219 struct qcom_adsp *adsp = dev;
220
221 complete(&adsp->stop_done);
222
223 return IRQ_HANDLED;
224}
225
226static int adsp_init_regulator(struct qcom_adsp *adsp)
227{
228 adsp->cx_supply = devm_regulator_get(adsp->dev, "cx");
229 if (IS_ERR(adsp->cx_supply))
230 return PTR_ERR(adsp->cx_supply);
231
232 regulator_set_load(adsp->cx_supply, 100000);
233
234 return 0;
235}
236
237static int adsp_request_irq(struct qcom_adsp *adsp,
238 struct platform_device *pdev,
239 const char *name,
240 irq_handler_t thread_fn)
241{
242 int ret;
243
244 ret = platform_get_irq_byname(pdev, name);
245 if (ret < 0) {
246 dev_err(&pdev->dev, "no %s IRQ defined\n", name);
247 return ret;
248 }
249
250 ret = devm_request_threaded_irq(&pdev->dev, ret,
251 NULL, thread_fn,
252 IRQF_ONESHOT,
253 "adsp", adsp);
254 if (ret)
255 dev_err(&pdev->dev, "request %s IRQ failed\n", name);
256
257 return ret;
258}
259
260static int adsp_alloc_memory_region(struct qcom_adsp *adsp)
261{
262 struct device_node *node;
263 struct resource r;
264 int ret;
265
266 node = of_parse_phandle(adsp->dev->of_node, "memory-region", 0);
267 if (!node) {
268 dev_err(adsp->dev, "no memory-region specified\n");
269 return -EINVAL;
270 }
271
272 ret = of_address_to_resource(node, 0, &r);
273 if (ret)
274 return ret;
275
276 adsp->mem_phys = adsp->mem_reloc = r.start;
277 adsp->mem_size = resource_size(&r);
278 adsp->mem_region = devm_ioremap_wc(adsp->dev, adsp->mem_phys, adsp->mem_size);
279 if (!adsp->mem_region) {
280 dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n",
281 &r.start, adsp->mem_size);
282 return -EBUSY;
283 }
284
285 return 0;
286}
287
288static int adsp_probe(struct platform_device *pdev)
289{
290 struct qcom_adsp *adsp;
291 struct rproc *rproc;
292 int ret;
293
294 if (!qcom_scm_is_available())
295 return -EPROBE_DEFER;
296
297 if (!qcom_scm_pas_supported(ADSP_PAS_ID)) {
298 dev_err(&pdev->dev, "PAS is not available for ADSP\n");
299 return -ENXIO;
300 }
301
302 rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops,
303 ADSP_FIRMWARE_NAME, sizeof(*adsp));
304 if (!rproc) {
305 dev_err(&pdev->dev, "unable to allocate remoteproc\n");
306 return -ENOMEM;
307 }
308
309 rproc->fw_ops = &adsp_fw_ops;
310
311 adsp = (struct qcom_adsp *)rproc->priv;
312 adsp->dev = &pdev->dev;
313 adsp->rproc = rproc;
314 platform_set_drvdata(pdev, adsp);
315
316 init_completion(&adsp->start_done);
317 init_completion(&adsp->stop_done);
318
319 ret = adsp_alloc_memory_region(adsp);
320 if (ret)
321 goto free_rproc;
322
323 ret = adsp_init_regulator(adsp);
324 if (ret)
325 goto free_rproc;
326
327 ret = adsp_request_irq(adsp, pdev, "wdog", adsp_wdog_interrupt);
328 if (ret < 0)
329 goto free_rproc;
330 adsp->wdog_irq = ret;
331
332 ret = adsp_request_irq(adsp, pdev, "fatal", adsp_fatal_interrupt);
333 if (ret < 0)
334 goto free_rproc;
335 adsp->fatal_irq = ret;
336
337 ret = adsp_request_irq(adsp, pdev, "ready", adsp_ready_interrupt);
338 if (ret < 0)
339 goto free_rproc;
340 adsp->ready_irq = ret;
341
342 ret = adsp_request_irq(adsp, pdev, "handover", adsp_handover_interrupt);
343 if (ret < 0)
344 goto free_rproc;
345 adsp->handover_irq = ret;
346
347 ret = adsp_request_irq(adsp, pdev, "stop-ack", adsp_stop_ack_interrupt);
348 if (ret < 0)
349 goto free_rproc;
350 adsp->stop_ack_irq = ret;
351
352 adsp->state = qcom_smem_state_get(&pdev->dev, "stop",
353 &adsp->stop_bit);
354 if (IS_ERR(adsp->state)) {
355 ret = PTR_ERR(adsp->state);
356 goto free_rproc;
357 }
358
359 ret = rproc_add(rproc);
360 if (ret)
361 goto free_rproc;
362
363 return 0;
364
365free_rproc:
366 rproc_put(rproc);
367
368 return ret;
369}
370
371static int adsp_remove(struct platform_device *pdev)
372{
373 struct qcom_adsp *adsp = platform_get_drvdata(pdev);
374
375 qcom_smem_state_put(adsp->state);
376 rproc_del(adsp->rproc);
377 rproc_put(adsp->rproc);
378
379 return 0;
380}
381
382static const struct of_device_id adsp_of_match[] = {
383 { .compatible = "qcom,msm8974-adsp-pil" },
384 { .compatible = "qcom,msm8996-adsp-pil" },
385 { },
386};
387
388static struct platform_driver adsp_driver = {
389 .probe = adsp_probe,
390 .remove = adsp_remove,
391 .driver = {
392 .name = "qcom_adsp_pil",
393 .of_match_table = adsp_of_match,
394 },
395};
396
397module_platform_driver(adsp_driver);
398MODULE_DESCRIPTION("Qualcomm MSM8974/MSM8996 ADSP Peripherial Image Loader");
399MODULE_LICENSE("GPL v2");