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