• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
adsp_load(struct rproc * rproc,const struct firmware * fw)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 
adsp_start(struct rproc * rproc)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 
adsp_stop(struct rproc * rproc)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 
adsp_da_to_va(struct rproc * rproc,u64 da,int len)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 
adsp_wdog_interrupt(int irq,void * dev)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 
adsp_fatal_interrupt(int irq,void * dev)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 
adsp_ready_interrupt(int irq,void * dev)214 static irqreturn_t adsp_ready_interrupt(int irq, void *dev)
215 {
216 	return IRQ_HANDLED;
217 }
218 
adsp_handover_interrupt(int irq,void * dev)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 
adsp_stop_ack_interrupt(int irq,void * dev)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 
adsp_init_clock(struct qcom_adsp * adsp)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 
adsp_init_regulator(struct qcom_adsp * adsp)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 
adsp_request_irq(struct qcom_adsp * adsp,struct platform_device * pdev,const char * name,irq_handler_t thread_fn)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 
adsp_alloc_memory_region(struct qcom_adsp * adsp)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 
adsp_probe(struct platform_device * pdev)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 
adsp_remove(struct platform_device * pdev)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");
471