]> git.ipfire.org Git - thirdparty/openwrt.git/blob
97130b59e40913d306cb3f9e9cfdac3ebcd814d3
[thirdparty/openwrt.git] /
1 From 3f61ff1fb5c90f8b6c28a3a2b4a29203000ee585 Mon Sep 17 00:00:00 2001
2 From: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
3 Date: Fri, 10 Nov 2023 14:49:37 +0530
4 Subject: [PATCH] remoteproc: qcom: Add Hexagon based multipd rproc
5 driver
6
7 It adds support to bring up remoteproc's on multipd model.
8 Pd means protection domain. It's similar to process in Linux.
9 Here QDSP6 processor runs each wifi radio functionality on a
10 separate process. One process can't access other process
11 resources, so this is termed as PD i.e protection domain.
12
13 Here we have two pd's called root and user pd. We can correlate
14 Root pd as root and user pd as user in linux. Root pd has more
15 privileges than user pd. Root will provide services to user pd.
16
17 >From remoteproc driver perspective, root pd corresponds to QDSP6
18 processor bring up and user pd corresponds to Wifi radio (WCSS)
19 bring up.
20
21 Here WCSS(user) PD is dependent on Q6(root) PD, so first
22 q6 pd should be up before wcss pd. After wcss pd goes down,
23 q6 pd should be turned off.
24
25 APPS QDSP6
26 ------- -------------
27 | | Crash notification | | ------
28 | |<---------------------|----------|-------|User|
29 | | | | |->|PD1 |
30 | | | ------- | | ------
31 | | | | | | |
32 |Root | Start/stop Q6 | | R | | |
33 |PD |<---------------------|->| | | |
34 |rproc| Crash notification | | O | | |
35 | | | | | | |
36 |User |Start/stop UserPD1 | | O | | |
37 |PD1 |----------------------|->| |-|----|
38 |rproc| | | T | | |
39 | | | | | | |
40 |User |Start/stop UserPD2 | | P | | |
41 |PD2 |----------------------|->| |-|----|
42 |rproc| | | D | | |
43 | | | ------- | | ------
44 | | Crash notification | | |->|User|
45 | |<---------------------|----------|-------|PD2 |
46 ------- | | ------
47 ------------
48
49 IPQ5332, IPQ9574 supports multipd remoteproc driver.
50
51 Signed-off-by: Manikanta Mylavarapu <quic_mmanikan@quicinc.com>
52 ---
53 drivers/remoteproc/Kconfig | 19 +
54 drivers/remoteproc/Makefile | 1 +
55 drivers/remoteproc/qcom_q6v5_mpd.c | 802 +++++++++++++++++++++++++++++
56 3 files changed, 822 insertions(+)
57 create mode 100644 drivers/remoteproc/qcom_q6v5_mpd.c
58
59 --- a/drivers/remoteproc/Kconfig
60 +++ b/drivers/remoteproc/Kconfig
61 @@ -234,6 +234,25 @@ config QCOM_Q6V5_PAS
62 CDSP (Compute DSP), MPSS (Modem Peripheral SubSystem), and
63 SLPI (Sensor Low Power Island).
64
65 +config QCOM_Q6V5_MPD
66 + tristate "Qualcomm Hexagon based MPD model Peripheral Image Loader"
67 + depends on OF && ARCH_QCOM
68 + depends on QCOM_SMEM
69 + depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n
70 + depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
71 + depends on QCOM_SYSMON || QCOM_SYSMON=n
72 + depends on RPMSG_QCOM_GLINK || RPMSG_QCOM_GLINK=n
73 + depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
74 + select QCOM_MDT_LOADER
75 + select QCOM_PIL_INFO
76 + select QCOM_Q6V5_COMMON
77 + select QCOM_RPROC_COMMON
78 + select QCOM_SCM
79 + help
80 + Say y here to support the Qualcomm Secure Peripheral Image Loader
81 + for the Hexagon based MultiPD model remote processors on e.g. IPQ5018.
82 + This is trustZone wireless subsystem.
83 +
84 config QCOM_Q6V5_WCSS
85 tristate "Qualcomm Hexagon based WCSS Peripheral Image Loader"
86 depends on OF && ARCH_QCOM
87 --- a/drivers/remoteproc/Makefile
88 +++ b/drivers/remoteproc/Makefile
89 @@ -25,6 +25,7 @@ obj-$(CONFIG_QCOM_PIL_INFO) += qcom_pil
90 obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
91 obj-$(CONFIG_QCOM_Q6V5_COMMON) += qcom_q6v5.o
92 obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o
93 +obj-$(CONFIG_QCOM_Q6V5_MPD) += qcom_q6v5_mpd.o
94 obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o
95 obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o
96 obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o
97 --- /dev/null
98 +++ b/drivers/remoteproc/qcom_q6v5_mpd.c
99 @@ -0,0 +1,800 @@
100 +// SPDX-License-Identifier: GPL-2.0
101 +/*
102 + * Copyright (C) 2016-2018 Linaro Ltd.
103 + * Copyright (C) 2014 Sony Mobile Communications AB
104 + * Copyright (c) 2012-2018, 2021 The Linux Foundation. All rights reserved.
105 + */
106 +#include <linux/clk.h>
107 +#include <linux/delay.h>
108 +#include <linux/firmware/qcom/qcom_scm.h>
109 +#include <linux/interrupt.h>
110 +#include <linux/io.h>
111 +#include <linux/iopoll.h>
112 +#include <linux/kernel.h>
113 +#include <linux/module.h>
114 +#include <linux/of_address.h>
115 +#include <linux/of_device.h>
116 +#include <linux/of_reserved_mem.h>
117 +#include <linux/platform_device.h>
118 +#include <linux/reset.h>
119 +#include <linux/soc/qcom/mdt_loader.h>
120 +#include <linux/soc/qcom/smem.h>
121 +#include <linux/soc/qcom/smem_state.h>
122 +#include "qcom_common.h"
123 +#include "qcom_q6v5.h"
124 +
125 +#include "remoteproc_internal.h"
126 +
127 +#define WCSS_CRASH_REASON 421
128 +#define WCSS_SMEM_HOST 1
129 +
130 +#define WCNSS_PAS_ID 6
131 +#define MPD_WCNSS_PAS_ID 0xD
132 +
133 +#define BUF_SIZE 35
134 +
135 +#define MAX_FIRMWARE 3
136 +
137 +#define RPD_SWID MPD_WCNSS_PAS_ID
138 +#define UPD_SWID 0x12
139 +#define REMOTE_PID 1
140 +#define UPD_BOOT_INFO_SMEM_SIZE 4096
141 +#define UPD_BOOT_INFO_HEADER_TYPE 0x2
142 +#define UPD_BOOT_INFO_SMEM_ID 507
143 +#define VERSION2 2
144 +
145 +static LIST_HEAD(upd_rproc_list);
146 +enum {
147 + Q6_IPQ,
148 + WCSS_IPQ,
149 +};
150 +
151 +/**
152 + * struct userpd_boot_info_header - header of user pd bootinfo
153 + * @type: type of bootinfo passing over smem
154 + * @length: length of header in bytes
155 + */
156 +struct userpd_boot_info_header {
157 + u8 type;
158 + u8 length;
159 +};
160 +
161 +/**
162 + * struct userpd_boot_info - holds info required to boot user pd
163 + * @header: pointer to header
164 + * @pid: unique id represents each user pd process
165 + * @bootaddr: load address of user pd firmware
166 + * @data_size: user pd firmware memory size
167 + */
168 +struct userpd_boot_info {
169 + struct userpd_boot_info_header header;
170 + u8 pid;
171 + u32 bootaddr;
172 + u32 data_size;
173 +} __packed;
174 +
175 +struct q6_wcss {
176 + struct device *dev;
177 + struct qcom_rproc_glink glink_subdev;
178 + struct qcom_rproc_ssr ssr_subdev;
179 + struct qcom_q6v5 q6;
180 + phys_addr_t mem_phys;
181 + phys_addr_t mem_reloc;
182 + void *mem_region;
183 + size_t mem_size;
184 + u8 pd_asid;
185 + const struct wcss_data *desc;
186 + const char **firmware;
187 + u32 version;
188 +};
189 +
190 +struct wcss_data {
191 + u32 pasid;
192 + bool share_upd_info_to_q6;
193 +};
194 +
195 +/**
196 + * qcom_get_pd_asid() - get the pd asid number from PD spawn bit
197 + * @rproc: rproc handle
198 + *
199 + * Returns asid on success
200 + */
201 +static u8 qcom_get_pd_asid(struct rproc *rproc)
202 +{
203 + struct q6_wcss *wcss = rproc->priv;
204 + u8 bit = wcss->q6.spawn_bit;
205 +
206 + return bit / 8;
207 +}
208 +
209 +static int q6_wcss_start(struct rproc *rproc)
210 +{
211 + struct q6_wcss *wcss = rproc->priv;
212 + int ret;
213 + const struct wcss_data *desc = wcss->desc;
214 +
215 + qcom_q6v5_prepare(&wcss->q6);
216 +
217 + ret = qcom_scm_pas_auth_and_reset(desc->pasid);
218 + if (ret) {
219 + dev_err(wcss->dev, "wcss_reset failed\n");
220 + return ret;
221 + }
222 +
223 + ret = qcom_q6v5_wait_for_start(&wcss->q6, 5 * HZ);
224 + if (ret == -ETIMEDOUT)
225 + dev_err(wcss->dev, "start timed out\n");
226 +
227 + return ret;
228 +}
229 +
230 +static int q6_wcss_spawn_pd(struct rproc *rproc)
231 +{
232 + int ret;
233 + struct q6_wcss *wcss = rproc->priv;
234 +
235 + ret = qcom_q6v5_request_spawn(&wcss->q6);
236 + if (ret == -ETIMEDOUT) {
237 + dev_err(wcss->dev, "%s spawn timedout\n", rproc->name);
238 + return ret;
239 + }
240 +
241 + ret = qcom_q6v5_wait_for_start(&wcss->q6, msecs_to_jiffies(10000));
242 + if (ret == -ETIMEDOUT) {
243 + dev_err(wcss->dev, "%s start timedout\n", rproc->name);
244 + wcss->q6.running = false;
245 + return ret;
246 + }
247 + wcss->q6.running = true;
248 + return ret;
249 +}
250 +
251 +static int wcss_pd_start(struct rproc *rproc)
252 +{
253 + struct q6_wcss *wcss = rproc->priv;
254 + u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
255 + int ret;
256 +
257 + ret = qcom_scm_msa_lock(pasid);
258 + if (ret) {
259 + dev_err(wcss->dev, "failed to power up pd\n");
260 + return ret;
261 + }
262 +
263 + if (wcss->q6.spawn_bit) {
264 + ret = q6_wcss_spawn_pd(rproc);
265 + if (ret)
266 + return ret;
267 + }
268 +
269 + return ret;
270 +}
271 +
272 +static int q6_wcss_stop(struct rproc *rproc)
273 +{
274 + struct q6_wcss *wcss = rproc->priv;
275 + const struct wcss_data *desc = wcss->desc;
276 + int ret;
277 +
278 + ret = qcom_scm_pas_shutdown(desc->pasid);
279 + if (ret) {
280 + dev_err(wcss->dev, "not able to shutdown\n");
281 + return ret;
282 + }
283 + qcom_q6v5_unprepare(&wcss->q6);
284 +
285 + return 0;
286 +}
287 +
288 +/**
289 + * wcss_pd_stop() - Stop WCSS user pd
290 + * @rproc: rproc handle
291 + *
292 + * Stop root pd after user pd down. Root pd
293 + * is used to provide services to user pd, so
294 + * keeping root pd alive when user pd is down
295 + * is invalid.
296 + * ---------------------------------------------
297 + *
298 + * -----------
299 + * |-------->| User PD1 |
300 + * | -----------
301 + * |
302 + * |
303 + * ----- | -----------
304 + * | Q6 |---------------->| User Pd2 |
305 + * ----- | -----------
306 + * |
307 + * |
308 + * | -----------
309 + * |--------->| User Pd3 |
310 + * -----------
311 + * ----------------------------------------------
312 + */
313 +static int wcss_pd_stop(struct rproc *rproc)
314 +{
315 + struct q6_wcss *wcss = rproc->priv;
316 + struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
317 + u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
318 + int ret;
319 +
320 + if (rproc->state != RPROC_CRASHED && wcss->q6.stop_bit) {
321 + ret = qcom_q6v5_request_stop(&wcss->q6, NULL);
322 + if (ret) {
323 + dev_err(&rproc->dev, "pd not stopped\n");
324 + return ret;
325 + }
326 + }
327 +
328 + ret = qcom_scm_msa_unlock(pasid);
329 + if (ret) {
330 + dev_err(wcss->dev, "failed to power down pd\n");
331 + return ret;
332 + }
333 +
334 + rproc_shutdown(rpd_rproc);
335 +
336 + return 0;
337 +}
338 +
339 +static void *q6_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len,
340 + bool *is_iomem)
341 +{
342 + struct q6_wcss *wcss = rproc->priv;
343 + int offset;
344 +
345 + offset = da - wcss->mem_reloc;
346 + if (offset < 0 || offset + len > wcss->mem_size)
347 + return NULL;
348 +
349 + return wcss->mem_region + offset;
350 +}
351 +
352 +/**
353 + * share_upd_bootinfo_to_q6() - Share userpd boot info to Q6 root pd
354 + * @rproc: rproc handle
355 + *
356 + * Q6 needs user pd parameters like loadaddress and
357 + * PIL size to authenticate user pd with underlying
358 + * security software. If authenticatoin success then
359 + * only Q6 spawns user pd and sends spawn ack to rproc
360 + * driver. This API is passing userpd boot info to Q6
361 + * over SMEM.
362 + *
363 + * User pd boot-info format mentioned below
364 + * <Version> <No of elements passing over smem> <Header type> <Header Length>
365 + * <Process Id> <Load address> <firmware mem Size>
366 + *
367 + * Returns 0 on success else negative value on failure.
368 + */
369 +static int share_upd_bootinfo_to_q6(struct rproc *rproc)
370 +{
371 + int ret;
372 + size_t size;
373 + u16 cnt = 0, version;
374 + void *ptr;
375 + struct q6_wcss *wcss = rproc->priv, *upd_wcss;
376 + struct rproc *upd_rproc;
377 + struct userpd_boot_info upd_bootinfo = {0};
378 + const struct firmware *fw;
379 +
380 + ret = qcom_smem_alloc(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID,
381 + UPD_BOOT_INFO_SMEM_SIZE);
382 + if (ret && ret != -EEXIST) {
383 + dev_err(wcss->dev,
384 + "failed to allocate q6 bootinfo smem segment\n");
385 + return ret;
386 + }
387 +
388 + ptr = qcom_smem_get(REMOTE_PID, UPD_BOOT_INFO_SMEM_ID, &size);
389 + if (IS_ERR(ptr) || size != UPD_BOOT_INFO_SMEM_SIZE) {
390 + dev_err(wcss->dev,
391 + "Unable to acquire smp2p item(%d) ret:%ld\n",
392 + UPD_BOOT_INFO_SMEM_ID, PTR_ERR(ptr));
393 + return PTR_ERR(ptr);
394 + }
395 +
396 + /*Version*/
397 + version = VERSION2;
398 + memcpy_toio(ptr, &version, sizeof(version));
399 + ptr += sizeof(version);
400 +
401 + list_for_each_entry(upd_rproc, &upd_rproc_list, node)
402 + cnt++;
403 +
404 + /* No of elements */
405 + cnt = (sizeof(upd_bootinfo) * cnt);
406 + memcpy_toio(ptr, &cnt, sizeof(u16));
407 + ptr += sizeof(u16);
408 +
409 + list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
410 + upd_wcss = upd_rproc->priv;
411 +
412 + /* TYPE */
413 + upd_bootinfo.header.type = UPD_BOOT_INFO_HEADER_TYPE;
414 +
415 + /* LENGTH */
416 + upd_bootinfo.header.length =
417 + sizeof(upd_bootinfo) - sizeof(upd_bootinfo.header);
418 +
419 + /* Process ID */
420 + upd_bootinfo.pid = upd_wcss->pd_asid + 1;
421 +
422 + ret = request_firmware(&fw, upd_rproc->firmware, upd_wcss->dev);
423 + if (ret < 0) {
424 + dev_err(upd_wcss->dev, "request_firmware failed: %d\n", ret);
425 + return ret;
426 + }
427 +
428 + /* Load address */
429 + upd_bootinfo.bootaddr = rproc_get_boot_addr(upd_rproc, fw);
430 +
431 + /* Firmware mem size */
432 + upd_bootinfo.data_size = qcom_mdt_get_size(fw);
433 +
434 + release_firmware(fw);
435 +
436 + /* copy into smem */
437 + memcpy_toio(ptr, &upd_bootinfo, sizeof(upd_bootinfo));
438 + ptr += sizeof(upd_bootinfo);
439 + }
440 + return 0;
441 +}
442 +
443 +static int q6_wcss_load(struct rproc *rproc, const struct firmware *fw)
444 +{
445 + struct q6_wcss *wcss = rproc->priv;
446 + const struct firmware *fw_hdl;
447 + int ret;
448 + const struct wcss_data *desc = wcss->desc;
449 + int loop;
450 +
451 + /* Share user pd boot info to Q6 remote processor */
452 + if (desc->share_upd_info_to_q6) {
453 + ret = share_upd_bootinfo_to_q6(rproc);
454 + if (ret) {
455 + dev_err(wcss->dev,
456 + "user pd boot info sharing with q6 failed %d\n",
457 + ret);
458 + return ret;
459 + }
460 + }
461 +
462 + ret = qcom_mdt_load(wcss->dev, fw, rproc->firmware,
463 + desc->pasid, wcss->mem_region,
464 + wcss->mem_phys, wcss->mem_size,
465 + &wcss->mem_reloc);
466 + if (ret)
467 + return ret;
468 +
469 + for (loop = 1; loop < MAX_FIRMWARE; loop++) {
470 + if (!wcss->firmware[loop])
471 + continue;
472 +
473 + ret = request_firmware(&fw_hdl, wcss->firmware[loop],
474 + wcss->dev);
475 + if (ret)
476 + continue;
477 +
478 + ret = qcom_mdt_load_no_init(wcss->dev, fw_hdl,
479 + wcss->firmware[loop], 0,
480 + wcss->mem_region,
481 + wcss->mem_phys,
482 + wcss->mem_size,
483 + &wcss->mem_reloc);
484 +
485 + release_firmware(fw_hdl);
486 +
487 + if (ret) {
488 + dev_err(wcss->dev,
489 + "can't load %s ret:%d\n", wcss->firmware[loop], ret);
490 + return ret;
491 + }
492 + }
493 + return 0;
494 +}
495 +
496 +/**
497 + * wcss_pd_load() - Load WCSS user pd firmware
498 + * @rproc: rproc handle
499 + * @fw: firmware handle
500 + *
501 + * User pd get services from root pd. So first
502 + * bring up root pd and then load userpd firmware.
503 + * ---------------------------------------------
504 + *
505 + * -----------
506 + * |-------->| User PD1 |
507 + * | -----------
508 + * |
509 + * |
510 + * ----- | -----------
511 + * | Q6 |---------------->| User Pd2 |
512 + * ----- | -----------
513 + * |
514 + * |
515 + * | -----------
516 + * |--------->| User Pd3 |
517 + * -----------
518 + * ----------------------------------------------
519 + *
520 + */
521 +static int wcss_pd_load(struct rproc *rproc, const struct firmware *fw)
522 +{
523 + struct q6_wcss *wcss = rproc->priv;
524 + struct rproc *rpd_rproc = dev_get_drvdata(wcss->dev->parent);
525 + u32 pasid = (wcss->pd_asid << 8) | UPD_SWID;
526 + int ret;
527 +
528 + ret = rproc_boot(rpd_rproc);
529 + if (ret)
530 + return ret;
531 +
532 + return qcom_mdt_load(wcss->dev, fw, rproc->firmware,
533 + pasid, wcss->mem_region,
534 + wcss->mem_phys, wcss->mem_size,
535 + &wcss->mem_reloc);
536 +}
537 +
538 +static unsigned long q6_wcss_panic(struct rproc *rproc)
539 +{
540 + struct q6_wcss *wcss = rproc->priv;
541 +
542 + return qcom_q6v5_panic(&wcss->q6);
543 +}
544 +
545 +static const struct rproc_ops wcss_ops = {
546 + .start = wcss_pd_start,
547 + .stop = wcss_pd_stop,
548 + .load = wcss_pd_load,
549 + .get_boot_addr = rproc_elf_get_boot_addr,
550 +};
551 +
552 +static const struct rproc_ops q6_wcss_ops = {
553 + .start = q6_wcss_start,
554 + .stop = q6_wcss_stop,
555 + .da_to_va = q6_wcss_da_to_va,
556 + .load = q6_wcss_load,
557 + .get_boot_addr = rproc_elf_get_boot_addr,
558 + .panic = q6_wcss_panic,
559 +};
560 +
561 +static int q6_alloc_memory_region(struct q6_wcss *wcss)
562 +{
563 + struct reserved_mem *rmem = NULL;
564 + struct device_node *node;
565 + struct device *dev = wcss->dev;
566 +
567 + if (wcss->version == Q6_IPQ) {
568 + node = of_parse_phandle(dev->of_node, "memory-region", 0);
569 + if (node)
570 + rmem = of_reserved_mem_lookup(node);
571 +
572 + of_node_put(node);
573 +
574 + if (!rmem) {
575 + dev_err(dev, "unable to acquire memory-region\n");
576 + return -EINVAL;
577 + }
578 + } else {
579 + struct rproc *rpd_rproc = dev_get_drvdata(dev->parent);
580 + struct q6_wcss *rpd_wcss = rpd_rproc->priv;
581 +
582 + wcss->mem_phys = rpd_wcss->mem_phys;
583 + wcss->mem_reloc = rpd_wcss->mem_reloc;
584 + wcss->mem_size = rpd_wcss->mem_size;
585 + wcss->mem_region = rpd_wcss->mem_region;
586 + return 0;
587 + }
588 +
589 + wcss->mem_phys = rmem->base;
590 + wcss->mem_reloc = rmem->base;
591 + wcss->mem_size = rmem->size;
592 + wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
593 + if (!wcss->mem_region) {
594 + dev_err(dev, "unable to map memory region: %pa+%pa\n",
595 + &rmem->base, &rmem->size);
596 + return -EBUSY;
597 + }
598 +
599 + return 0;
600 +}
601 +
602 +static int q6_get_inbound_irq(struct qcom_q6v5 *q6,
603 + struct platform_device *pdev,
604 + const char *int_name,
605 + int index, int *pirq,
606 + irqreturn_t (*handler)(int irq, void *data))
607 +{
608 + int ret, irq;
609 + char *interrupt, *tmp = (char *)int_name;
610 + struct q6_wcss *wcss = q6->rproc->priv;
611 +
612 + irq = platform_get_irq(pdev, index);
613 + if (irq < 0)
614 + return irq;
615 +
616 + *pirq = irq;
617 +
618 + interrupt = devm_kzalloc(&pdev->dev, BUF_SIZE, GFP_KERNEL);
619 + if (!interrupt)
620 + return -ENOMEM;
621 +
622 + snprintf(interrupt, BUF_SIZE, "q6v5_wcss_userpd%d_%s", wcss->pd_asid, tmp);
623 +
624 + ret = devm_request_threaded_irq(&pdev->dev, *pirq,
625 + NULL, handler,
626 + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
627 + interrupt, q6);
628 + if (ret)
629 + return dev_err_probe(&pdev->dev, ret,
630 + "failed to acquire %s irq\n", interrupt);
631 + return 0;
632 +}
633 +
634 +static int q6_get_outbound_irq(struct qcom_q6v5 *q6,
635 + struct platform_device *pdev,
636 + const char *int_name)
637 +{
638 + struct qcom_smem_state *tmp_state;
639 + unsigned bit;
640 +
641 + tmp_state = qcom_smem_state_get(&pdev->dev, int_name, &bit);
642 + if (IS_ERR(tmp_state))
643 + return dev_err_probe(&pdev->dev, PTR_ERR(tmp_state),
644 + "failed to acquire %s state\n", int_name);
645 +
646 + if (!strcmp(int_name, "stop")) {
647 + q6->state = tmp_state;
648 + q6->stop_bit = bit;
649 + } else if (!strcmp(int_name, "spawn")) {
650 + q6->spawn_state = tmp_state;
651 + q6->spawn_bit = bit;
652 + }
653 +
654 + return 0;
655 +}
656 +
657 +static int init_irq(struct qcom_q6v5 *q6,
658 + struct platform_device *pdev, struct rproc *rproc,
659 + int crash_reason, const char *load_state,
660 + void (*handover)(struct qcom_q6v5 *q6))
661 +{
662 + int ret;
663 + struct q6_wcss *wcss = rproc->priv;
664 +
665 + q6->rproc = rproc;
666 + q6->dev = &pdev->dev;
667 + q6->crash_reason = crash_reason;
668 + q6->handover = handover;
669 +
670 + init_completion(&q6->start_done);
671 + init_completion(&q6->stop_done);
672 + init_completion(&q6->spawn_done);
673 +
674 + ret = q6_get_outbound_irq(q6, pdev, "stop");
675 + if (ret)
676 + return ret;
677 +
678 + ret = q6_get_outbound_irq(q6, pdev, "spawn");
679 + if (ret)
680 + return ret;
681 +
682 + /* Get pd_asid to prepare interrupt names */
683 + wcss->pd_asid = qcom_get_pd_asid(rproc);
684 +
685 + ret = q6_get_inbound_irq(q6, pdev, "fatal", 0, &q6->fatal_irq,
686 + q6v5_fatal_interrupt);
687 + if (ret)
688 + return ret;
689 +
690 + ret = q6_get_inbound_irq(q6, pdev, "ready", 1, &q6->ready_irq,
691 + q6v5_ready_interrupt);
692 + if (ret)
693 + return ret;
694 +
695 + ret = q6_get_inbound_irq(q6, pdev, "stop-ack", 3, &q6->stop_irq,
696 + q6v5_stop_interrupt);
697 + if (ret)
698 + return ret;
699 +
700 + ret = q6_get_inbound_irq(q6, pdev, "spawn-ack", 2, &q6->spawn_irq,
701 + q6v5_spawn_interrupt);
702 + if (ret)
703 + return ret;
704 + return 0;
705 +}
706 +
707 +static void q6_release_resources(void)
708 +{
709 + struct rproc *upd_rproc;
710 +
711 + /* Release userpd resources */
712 + list_for_each_entry(upd_rproc, &upd_rproc_list, node) {
713 + rproc_del(upd_rproc);
714 + rproc_free(upd_rproc);
715 + }
716 +}
717 +
718 +static int q6_register_userpd(struct platform_device *pdev,
719 + struct device_node *userpd_np)
720 +{
721 + struct q6_wcss *wcss;
722 + struct rproc *rproc = NULL;
723 + int ret;
724 + struct platform_device *userpd_pdev;
725 + const char *firmware_name = NULL;
726 + const char *label = NULL;
727 +
728 + ret = of_property_read_string(userpd_np, "firmware-name",
729 + &firmware_name);
730 + if (ret < 0) {
731 + /* All userpd's who want to register as rproc must have firmware.
732 + * Other than userpd like glink they don't need any firmware.
733 + * So for glink child simply return success.
734 + */
735 + if (ret == -EINVAL) {
736 + /* Confirming userpd_np is glink node or not */
737 + if (!of_property_read_string(userpd_np, "label", &label))
738 + return 0;
739 + }
740 + return ret;
741 + }
742 +
743 + dev_info(&pdev->dev, "%s node found\n", userpd_np->name);
744 +
745 + userpd_pdev = of_platform_device_create(userpd_np, userpd_np->name,
746 + &pdev->dev);
747 + if (!userpd_pdev)
748 + return dev_err_probe(&pdev->dev, -ENODEV,
749 + "failed to create %s platform device\n",
750 + userpd_np->name);
751 +
752 + userpd_pdev->dev.driver = pdev->dev.driver;
753 + rproc = rproc_alloc(&userpd_pdev->dev, userpd_pdev->name, &wcss_ops,
754 + firmware_name, sizeof(*wcss));
755 + if (!rproc) {
756 + ret = -ENOMEM;
757 + goto free_rproc;
758 + }
759 +
760 + wcss = rproc->priv;
761 + wcss->dev = &userpd_pdev->dev;
762 + wcss->version = WCSS_IPQ;
763 +
764 + ret = q6_alloc_memory_region(wcss);
765 + if (ret)
766 + goto free_rproc;
767 +
768 + ret = init_irq(&wcss->q6, userpd_pdev, rproc,
769 + WCSS_CRASH_REASON, NULL, NULL);
770 + if (ret)
771 + goto free_rproc;
772 +
773 + rproc->auto_boot = false;
774 + ret = rproc_add(rproc);
775 + if (ret)
776 + goto free_rproc;
777 +
778 + list_add(&rproc->node, &upd_rproc_list);
779 + platform_set_drvdata(userpd_pdev, rproc);
780 + qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, userpd_pdev->name);
781 + return 0;
782 +
783 +free_rproc:
784 + kfree(rproc);
785 + return ret;
786 +}
787 +
788 +static int q6_wcss_probe(struct platform_device *pdev)
789 +{
790 + const struct wcss_data *desc;
791 + struct q6_wcss *wcss;
792 + struct rproc *rproc;
793 + int ret;
794 + const char **firmware;
795 + struct device_node *userpd_np;
796 + const struct rproc_ops *ops = &q6_wcss_ops;
797 +
798 + desc = of_device_get_match_data(&pdev->dev);
799 + if (!desc)
800 + return -EINVAL;
801 +
802 + firmware = devm_kcalloc(&pdev->dev, MAX_FIRMWARE,
803 + sizeof(*firmware), GFP_KERNEL);
804 + if (!firmware)
805 + return -ENOMEM;
806 +
807 + ret = of_property_read_string_array(pdev->dev.of_node, "firmware-name",
808 + firmware, MAX_FIRMWARE);
809 + if (ret < 0)
810 + return ret;
811 +
812 + rproc = rproc_alloc(&pdev->dev, pdev->name, ops,
813 + firmware[0], sizeof(*wcss));
814 + if (!rproc)
815 + return -ENOMEM;
816 +
817 + wcss = rproc->priv;
818 + wcss->dev = &pdev->dev;
819 + wcss->desc = desc;
820 + wcss->firmware = firmware;
821 + wcss->version = Q6_IPQ;
822 +
823 + ret = q6_alloc_memory_region(wcss);
824 + if (ret)
825 + goto free_rproc;
826 +
827 + ret = qcom_q6v5_init(&wcss->q6, pdev, rproc,
828 + WCSS_CRASH_REASON, NULL, NULL);
829 + if (ret)
830 + goto free_rproc;
831 +
832 + qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
833 + qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
834 +
835 + rproc->auto_boot = false;
836 + ret = rproc_add(rproc);
837 + if (ret)
838 + goto free_rproc;
839 +
840 + platform_set_drvdata(pdev, rproc);
841 +
842 + /* Iterate over userpd child's and register with rproc */
843 + for_each_available_child_of_node(pdev->dev.of_node, userpd_np) {
844 + ret = q6_register_userpd(pdev, userpd_np);
845 + if (ret) {
846 + /* release resources of successfully allocated userpd rproc's */
847 + q6_release_resources();
848 + return dev_err_probe(&pdev->dev, ret,
849 + "Failed to register userpd(%s)\n",
850 + userpd_np->name);
851 + }
852 + }
853 + return 0;
854 +
855 +free_rproc:
856 + rproc_free(rproc);
857 +
858 + return ret;
859 +}
860 +
861 +static void q6_wcss_remove(struct platform_device *pdev)
862 +{
863 + struct rproc *rproc = platform_get_drvdata(pdev);
864 + struct q6_wcss *wcss = rproc->priv;
865 +
866 + qcom_q6v5_deinit(&wcss->q6);
867 +
868 + rproc_del(rproc);
869 + rproc_free(rproc);
870 +}
871 +
872 +static const struct wcss_data q6_ipq5332_res_init = {
873 + .pasid = MPD_WCNSS_PAS_ID,
874 + .share_upd_info_to_q6 = true,
875 +};
876 +
877 +static const struct wcss_data q6_ipq9574_res_init = {
878 + .pasid = WCNSS_PAS_ID,
879 +};
880 +
881 +static const struct of_device_id q6_wcss_of_match[] = {
882 + { .compatible = "qcom,ipq5332-q6-mpd", .data = &q6_ipq5332_res_init },
883 + { .compatible = "qcom,ipq9574-q6-mpd", .data = &q6_ipq9574_res_init },
884 + { },
885 +};
886 +MODULE_DEVICE_TABLE(of, q6_wcss_of_match);
887 +
888 +static struct platform_driver q6_wcss_driver = {
889 + .probe = q6_wcss_probe,
890 + .remove_new = q6_wcss_remove,
891 + .driver = {
892 + .name = "qcom-q6-mpd",
893 + .of_match_table = q6_wcss_of_match,
894 + },
895 +};
896 +module_platform_driver(q6_wcss_driver);
897 +
898 +MODULE_DESCRIPTION("Hexagon WCSS Multipd Peripheral Image Loader");
899 +MODULE_LICENSE("GPL v2");