• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2016-2018 Linaro Ltd.
4  * Copyright (C) 2014 Sony Mobile Communications AB
5  * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
6  */
7 #include <linux/iopoll.h>
8 #include <linux/kernel.h>
9 #include <linux/mfd/syscon.h>
10 #include <linux/module.h>
11 #include <linux/of_reserved_mem.h>
12 #include <linux/platform_device.h>
13 #include <linux/regmap.h>
14 #include <linux/reset.h>
15 #include <linux/soc/qcom/mdt_loader.h>
16 #include "qcom_common.h"
17 #include "qcom_q6v5.h"
18 
19 #define WCSS_CRASH_REASON		421
20 
21 /* Q6SS Register Offsets */
22 #define Q6SS_RESET_REG		0x014
23 #define Q6SS_GFMUX_CTL_REG		0x020
24 #define Q6SS_PWR_CTL_REG		0x030
25 #define Q6SS_MEM_PWR_CTL		0x0B0
26 
27 /* AXI Halt Register Offsets */
28 #define AXI_HALTREQ_REG			0x0
29 #define AXI_HALTACK_REG			0x4
30 #define AXI_IDLE_REG			0x8
31 
32 #define HALT_ACK_TIMEOUT_MS		100
33 
34 /* Q6SS_RESET */
35 #define Q6SS_STOP_CORE			BIT(0)
36 #define Q6SS_CORE_ARES			BIT(1)
37 #define Q6SS_BUS_ARES_ENABLE		BIT(2)
38 
39 /* Q6SS_GFMUX_CTL */
40 #define Q6SS_CLK_ENABLE			BIT(1)
41 
42 /* Q6SS_PWR_CTL */
43 #define Q6SS_L2DATA_STBY_N		BIT(18)
44 #define Q6SS_SLP_RET_N			BIT(19)
45 #define Q6SS_CLAMP_IO			BIT(20)
46 #define QDSS_BHS_ON			BIT(21)
47 
48 /* Q6SS parameters */
49 #define Q6SS_LDO_BYP		BIT(25)
50 #define Q6SS_BHS_ON		BIT(24)
51 #define Q6SS_CLAMP_WL		BIT(21)
52 #define Q6SS_CLAMP_QMC_MEM		BIT(22)
53 #define HALT_CHECK_MAX_LOOPS		200
54 #define Q6SS_XO_CBCR		GENMASK(5, 3)
55 
56 /* Q6SS config/status registers */
57 #define TCSR_GLOBAL_CFG0	0x0
58 #define TCSR_GLOBAL_CFG1	0x4
59 #define SSCAON_CONFIG		0x8
60 #define SSCAON_STATUS		0xc
61 #define Q6SS_BHS_STATUS		0x78
62 #define Q6SS_RST_EVB		0x10
63 
64 #define BHS_EN_REST_ACK		BIT(0)
65 #define SSCAON_ENABLE		BIT(13)
66 #define SSCAON_BUS_EN		BIT(15)
67 #define SSCAON_BUS_MUX_MASK	GENMASK(18, 16)
68 
69 #define MEM_BANKS		19
70 #define TCSR_WCSS_CLK_MASK	0x1F
71 #define TCSR_WCSS_CLK_ENABLE	0x14
72 
73 struct q6v5_wcss {
74 	struct device *dev;
75 
76 	void __iomem *reg_base;
77 	void __iomem *rmb_base;
78 
79 	struct regmap *halt_map;
80 	u32 halt_q6;
81 	u32 halt_wcss;
82 	u32 halt_nc;
83 
84 	struct reset_control *wcss_aon_reset;
85 	struct reset_control *wcss_reset;
86 	struct reset_control *wcss_q6_reset;
87 
88 	struct qcom_q6v5 q6v5;
89 
90 	phys_addr_t mem_phys;
91 	phys_addr_t mem_reloc;
92 	void *mem_region;
93 	size_t mem_size;
94 };
95 
q6v5_wcss_reset(struct q6v5_wcss * wcss)96 static int q6v5_wcss_reset(struct q6v5_wcss *wcss)
97 {
98 	int ret;
99 	u32 val;
100 	int i;
101 
102 	/* Assert resets, stop core */
103 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
104 	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
105 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
106 
107 	/* BHS require xo cbcr to be enabled */
108 	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
109 	val |= 0x1;
110 	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
111 
112 	/* Read CLKOFF bit to go low indicating CLK is enabled */
113 	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
114 				 val, !(val & BIT(31)), 1,
115 				 HALT_CHECK_MAX_LOOPS);
116 	if (ret) {
117 		dev_err(wcss->dev,
118 			"xo cbcr enabling timed out (rc:%d)\n", ret);
119 		return ret;
120 	}
121 	/* Enable power block headswitch and wait for it to stabilize */
122 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
123 	val |= Q6SS_BHS_ON;
124 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
125 	udelay(1);
126 
127 	/* Put LDO in bypass mode */
128 	val |= Q6SS_LDO_BYP;
129 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
130 
131 	/* Deassert Q6 compiler memory clamp */
132 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
133 	val &= ~Q6SS_CLAMP_QMC_MEM;
134 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
135 
136 	/* Deassert memory peripheral sleep and L2 memory standby */
137 	val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
138 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
139 
140 	/* Turn on L1, L2, ETB and JU memories 1 at a time */
141 	val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
142 	for (i = MEM_BANKS; i >= 0; i--) {
143 		val |= BIT(i);
144 		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
145 		/*
146 		 * Read back value to ensure the write is done then
147 		 * wait for 1us for both memory peripheral and data
148 		 * array to turn on.
149 		 */
150 		val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
151 		udelay(1);
152 	}
153 	/* Remove word line clamp */
154 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
155 	val &= ~Q6SS_CLAMP_WL;
156 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
157 
158 	/* Remove IO clamp */
159 	val &= ~Q6SS_CLAMP_IO;
160 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
161 
162 	/* Bring core out of reset */
163 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
164 	val &= ~Q6SS_CORE_ARES;
165 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
166 
167 	/* Turn on core clock */
168 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
169 	val |= Q6SS_CLK_ENABLE;
170 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
171 
172 	/* Start core execution */
173 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
174 	val &= ~Q6SS_STOP_CORE;
175 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
176 
177 	return 0;
178 }
179 
q6v5_wcss_start(struct rproc * rproc)180 static int q6v5_wcss_start(struct rproc *rproc)
181 {
182 	struct q6v5_wcss *wcss = rproc->priv;
183 	int ret;
184 
185 	qcom_q6v5_prepare(&wcss->q6v5);
186 
187 	/* Release Q6 and WCSS reset */
188 	ret = reset_control_deassert(wcss->wcss_reset);
189 	if (ret) {
190 		dev_err(wcss->dev, "wcss_reset failed\n");
191 		return ret;
192 	}
193 
194 	ret = reset_control_deassert(wcss->wcss_q6_reset);
195 	if (ret) {
196 		dev_err(wcss->dev, "wcss_q6_reset failed\n");
197 		goto wcss_reset;
198 	}
199 
200 	/* Lithium configuration - clock gating and bus arbitration */
201 	ret = regmap_update_bits(wcss->halt_map,
202 				 wcss->halt_nc + TCSR_GLOBAL_CFG0,
203 				 TCSR_WCSS_CLK_MASK,
204 				 TCSR_WCSS_CLK_ENABLE);
205 	if (ret)
206 		goto wcss_q6_reset;
207 
208 	ret = regmap_update_bits(wcss->halt_map,
209 				 wcss->halt_nc + TCSR_GLOBAL_CFG1,
210 				 1, 0);
211 	if (ret)
212 		goto wcss_q6_reset;
213 
214 	/* Write bootaddr to EVB so that Q6WCSS will jump there after reset */
215 	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
216 
217 	ret = q6v5_wcss_reset(wcss);
218 	if (ret)
219 		goto wcss_q6_reset;
220 
221 	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
222 	if (ret == -ETIMEDOUT)
223 		dev_err(wcss->dev, "start timed out\n");
224 
225 	return ret;
226 
227 wcss_q6_reset:
228 	reset_control_assert(wcss->wcss_q6_reset);
229 
230 wcss_reset:
231 	reset_control_assert(wcss->wcss_reset);
232 
233 	return ret;
234 }
235 
q6v5_wcss_halt_axi_port(struct q6v5_wcss * wcss,struct regmap * halt_map,u32 offset)236 static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,
237 				    struct regmap *halt_map,
238 				    u32 offset)
239 {
240 	unsigned long timeout;
241 	unsigned int val;
242 	int ret;
243 
244 	/* Check if we're already idle */
245 	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
246 	if (!ret && val)
247 		return;
248 
249 	/* Assert halt request */
250 	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
251 
252 	/* Wait for halt */
253 	timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
254 	for (;;) {
255 		ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
256 		if (ret || val || time_after(jiffies, timeout))
257 			break;
258 
259 		msleep(1);
260 	}
261 
262 	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
263 	if (ret || !val)
264 		dev_err(wcss->dev, "port failed halt\n");
265 
266 	/* Clear halt request (port will remain halted until reset) */
267 	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
268 }
269 
q6v5_wcss_powerdown(struct q6v5_wcss * wcss)270 static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)
271 {
272 	int ret;
273 	u32 val;
274 
275 	/* 1 - Assert WCSS/Q6 HALTREQ */
276 	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
277 
278 	/* 2 - Enable WCSSAON_CONFIG */
279 	val = readl(wcss->rmb_base + SSCAON_CONFIG);
280 	val |= SSCAON_ENABLE;
281 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
282 
283 	/* 3 - Set SSCAON_CONFIG */
284 	val |= SSCAON_BUS_EN;
285 	val &= ~SSCAON_BUS_MUX_MASK;
286 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
287 
288 	/* 4 - SSCAON_CONFIG 1 */
289 	val |= BIT(1);
290 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
291 
292 	/* 5 - wait for SSCAON_STATUS */
293 	ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,
294 				 val, (val & 0xffff) == 0x400, 1000,
295 				 HALT_CHECK_MAX_LOOPS);
296 	if (ret) {
297 		dev_err(wcss->dev,
298 			"can't get SSCAON_STATUS rc:%d)\n", ret);
299 		return ret;
300 	}
301 
302 	/* 6 - De-assert WCSS_AON reset */
303 	reset_control_assert(wcss->wcss_aon_reset);
304 
305 	/* 7 - Disable WCSSAON_CONFIG 13 */
306 	val = readl(wcss->rmb_base + SSCAON_CONFIG);
307 	val &= ~SSCAON_ENABLE;
308 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
309 
310 	/* 8 - De-assert WCSS/Q6 HALTREQ */
311 	reset_control_assert(wcss->wcss_reset);
312 
313 	return 0;
314 }
315 
q6v5_q6_powerdown(struct q6v5_wcss * wcss)316 static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)
317 {
318 	int ret;
319 	u32 val;
320 	int i;
321 
322 	/* 1 - Halt Q6 bus interface */
323 	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);
324 
325 	/* 2 - Disable Q6 Core clock */
326 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
327 	val &= ~Q6SS_CLK_ENABLE;
328 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
329 
330 	/* 3 - Clamp I/O */
331 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
332 	val |= Q6SS_CLAMP_IO;
333 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
334 
335 	/* 4 - Clamp WL */
336 	val |= QDSS_BHS_ON;
337 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
338 
339 	/* 5 - Clear Erase standby */
340 	val &= ~Q6SS_L2DATA_STBY_N;
341 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
342 
343 	/* 6 - Clear Sleep RTN */
344 	val &= ~Q6SS_SLP_RET_N;
345 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
346 
347 	/* 7 - turn off Q6 memory foot/head switch one bank at a time */
348 	for (i = 0; i < 20; i++) {
349 		val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
350 		val &= ~BIT(i);
351 		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
352 		mdelay(1);
353 	}
354 
355 	/* 8 - Assert QMC memory RTN */
356 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
357 	val |= Q6SS_CLAMP_QMC_MEM;
358 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
359 
360 	/* 9 - Turn off BHS */
361 	val &= ~Q6SS_BHS_ON;
362 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
363 	udelay(1);
364 
365 	/* 10 - Wait till BHS Reset is done */
366 	ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,
367 				 val, !(val & BHS_EN_REST_ACK), 1000,
368 				 HALT_CHECK_MAX_LOOPS);
369 	if (ret) {
370 		dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);
371 		return ret;
372 	}
373 
374 	/* 11 -  Assert WCSS reset */
375 	reset_control_assert(wcss->wcss_reset);
376 
377 	/* 12 - Assert Q6 reset */
378 	reset_control_assert(wcss->wcss_q6_reset);
379 
380 	return 0;
381 }
382 
q6v5_wcss_stop(struct rproc * rproc)383 static int q6v5_wcss_stop(struct rproc *rproc)
384 {
385 	struct q6v5_wcss *wcss = rproc->priv;
386 	int ret;
387 
388 	/* WCSS powerdown */
389 	ret = qcom_q6v5_request_stop(&wcss->q6v5);
390 	if (ret == -ETIMEDOUT) {
391 		dev_err(wcss->dev, "timed out on wait\n");
392 		return ret;
393 	}
394 
395 	ret = q6v5_wcss_powerdown(wcss);
396 	if (ret)
397 		return ret;
398 
399 	/* Q6 Power down */
400 	ret = q6v5_q6_powerdown(wcss);
401 	if (ret)
402 		return ret;
403 
404 	qcom_q6v5_unprepare(&wcss->q6v5);
405 
406 	return 0;
407 }
408 
q6v5_wcss_da_to_va(struct rproc * rproc,u64 da,int len)409 static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, int len)
410 {
411 	struct q6v5_wcss *wcss = rproc->priv;
412 	int offset;
413 
414 	offset = da - wcss->mem_reloc;
415 	if (offset < 0 || offset + len > wcss->mem_size)
416 		return NULL;
417 
418 	return wcss->mem_region + offset;
419 }
420 
q6v5_wcss_load(struct rproc * rproc,const struct firmware * fw)421 static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
422 {
423 	struct q6v5_wcss *wcss = rproc->priv;
424 
425 	return qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
426 				     0, wcss->mem_region, wcss->mem_phys,
427 				     wcss->mem_size, &wcss->mem_reloc);
428 }
429 
430 static const struct rproc_ops q6v5_wcss_ops = {
431 	.start = q6v5_wcss_start,
432 	.stop = q6v5_wcss_stop,
433 	.da_to_va = q6v5_wcss_da_to_va,
434 	.load = q6v5_wcss_load,
435 	.get_boot_addr = rproc_elf_get_boot_addr,
436 };
437 
q6v5_wcss_init_reset(struct q6v5_wcss * wcss)438 static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss)
439 {
440 	struct device *dev = wcss->dev;
441 
442 	wcss->wcss_aon_reset = devm_reset_control_get(dev, "wcss_aon_reset");
443 	if (IS_ERR(wcss->wcss_aon_reset)) {
444 		dev_err(wcss->dev, "unable to acquire wcss_aon_reset\n");
445 		return PTR_ERR(wcss->wcss_aon_reset);
446 	}
447 
448 	wcss->wcss_reset = devm_reset_control_get(dev, "wcss_reset");
449 	if (IS_ERR(wcss->wcss_reset)) {
450 		dev_err(wcss->dev, "unable to acquire wcss_reset\n");
451 		return PTR_ERR(wcss->wcss_reset);
452 	}
453 
454 	wcss->wcss_q6_reset = devm_reset_control_get(dev, "wcss_q6_reset");
455 	if (IS_ERR(wcss->wcss_q6_reset)) {
456 		dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");
457 		return PTR_ERR(wcss->wcss_q6_reset);
458 	}
459 
460 	return 0;
461 }
462 
q6v5_wcss_init_mmio(struct q6v5_wcss * wcss,struct platform_device * pdev)463 static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
464 			       struct platform_device *pdev)
465 {
466 	struct of_phandle_args args;
467 	struct resource *res;
468 	int ret;
469 
470 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
471 	wcss->reg_base = devm_ioremap_resource(&pdev->dev, res);
472 	if (IS_ERR(wcss->reg_base))
473 		return PTR_ERR(wcss->reg_base);
474 
475 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb");
476 	wcss->rmb_base = devm_ioremap_resource(&pdev->dev, res);
477 	if (IS_ERR(wcss->rmb_base))
478 		return PTR_ERR(wcss->rmb_base);
479 
480 	ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
481 					       "qcom,halt-regs", 3, 0, &args);
482 	if (ret < 0) {
483 		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
484 		return -EINVAL;
485 	}
486 
487 	wcss->halt_map = syscon_node_to_regmap(args.np);
488 	of_node_put(args.np);
489 	if (IS_ERR(wcss->halt_map))
490 		return PTR_ERR(wcss->halt_map);
491 
492 	wcss->halt_q6 = args.args[0];
493 	wcss->halt_wcss = args.args[1];
494 	wcss->halt_nc = args.args[2];
495 
496 	return 0;
497 }
498 
q6v5_alloc_memory_region(struct q6v5_wcss * wcss)499 static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)
500 {
501 	struct reserved_mem *rmem = NULL;
502 	struct device_node *node;
503 	struct device *dev = wcss->dev;
504 
505 	node = of_parse_phandle(dev->of_node, "memory-region", 0);
506 	if (node)
507 		rmem = of_reserved_mem_lookup(node);
508 	of_node_put(node);
509 
510 	if (!rmem) {
511 		dev_err(dev, "unable to acquire memory-region\n");
512 		return -EINVAL;
513 	}
514 
515 	wcss->mem_phys = rmem->base;
516 	wcss->mem_reloc = rmem->base;
517 	wcss->mem_size = rmem->size;
518 	wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
519 	if (!wcss->mem_region) {
520 		dev_err(dev, "unable to map memory region: %pa+%pa\n",
521 			&rmem->base, &rmem->size);
522 		return -EBUSY;
523 	}
524 
525 	return 0;
526 }
527 
q6v5_wcss_probe(struct platform_device * pdev)528 static int q6v5_wcss_probe(struct platform_device *pdev)
529 {
530 	struct q6v5_wcss *wcss;
531 	struct rproc *rproc;
532 	int ret;
533 
534 	rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_wcss_ops,
535 			    "IPQ8074/q6_fw.mdt", sizeof(*wcss));
536 	if (!rproc) {
537 		dev_err(&pdev->dev, "failed to allocate rproc\n");
538 		return -ENOMEM;
539 	}
540 
541 	wcss = rproc->priv;
542 	wcss->dev = &pdev->dev;
543 
544 	ret = q6v5_wcss_init_mmio(wcss, pdev);
545 	if (ret)
546 		goto free_rproc;
547 
548 	ret = q6v5_alloc_memory_region(wcss);
549 	if (ret)
550 		goto free_rproc;
551 
552 	ret = q6v5_wcss_init_reset(wcss);
553 	if (ret)
554 		goto free_rproc;
555 
556 	ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, WCSS_CRASH_REASON, NULL);
557 	if (ret)
558 		goto free_rproc;
559 
560 	ret = rproc_add(rproc);
561 	if (ret)
562 		goto free_rproc;
563 
564 	platform_set_drvdata(pdev, rproc);
565 
566 	return 0;
567 
568 free_rproc:
569 	rproc_free(rproc);
570 
571 	return ret;
572 }
573 
q6v5_wcss_remove(struct platform_device * pdev)574 static int q6v5_wcss_remove(struct platform_device *pdev)
575 {
576 	struct rproc *rproc = platform_get_drvdata(pdev);
577 
578 	rproc_del(rproc);
579 	rproc_free(rproc);
580 
581 	return 0;
582 }
583 
584 static const struct of_device_id q6v5_wcss_of_match[] = {
585 	{ .compatible = "qcom,ipq8074-wcss-pil" },
586 	{ },
587 };
588 MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
589 
590 static struct platform_driver q6v5_wcss_driver = {
591 	.probe = q6v5_wcss_probe,
592 	.remove = q6v5_wcss_remove,
593 	.driver = {
594 		.name = "qcom-q6v5-wcss-pil",
595 		.of_match_table = q6v5_wcss_of_match,
596 	},
597 };
598 module_platform_driver(q6v5_wcss_driver);
599 
600 MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
601 MODULE_LICENSE("GPL v2");
602