xref: /linux/drivers/remoteproc/qcom_q6v5_wcss.c (revision da5b2ad1c2f18834cb1ce429e2e5a5cf5cbdf21b)
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/clk.h>
8 #include <linux/delay.h>
9 #include <linux/io.h>
10 #include <linux/iopoll.h>
11 #include <linux/kernel.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
14 #include <linux/of_address.h>
15 #include <linux/of_reserved_mem.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <linux/regulator/consumer.h>
19 #include <linux/reset.h>
20 #include <linux/soc/qcom/mdt_loader.h>
21 #include "qcom_common.h"
22 #include "qcom_pil_info.h"
23 #include "qcom_q6v5.h"
24 
25 #define WCSS_CRASH_REASON		421
26 
27 /* Q6SS Register Offsets */
28 #define Q6SS_RESET_REG		0x014
29 #define Q6SS_GFMUX_CTL_REG		0x020
30 #define Q6SS_PWR_CTL_REG		0x030
31 #define Q6SS_MEM_PWR_CTL		0x0B0
32 #define Q6SS_STRAP_ACC			0x110
33 #define Q6SS_CGC_OVERRIDE		0x034
34 #define Q6SS_BCR_REG			0x6000
35 
36 /* AXI Halt Register Offsets */
37 #define AXI_HALTREQ_REG			0x0
38 #define AXI_HALTACK_REG			0x4
39 #define AXI_IDLE_REG			0x8
40 
41 #define HALT_ACK_TIMEOUT_MS		100
42 
43 /* Q6SS_RESET */
44 #define Q6SS_STOP_CORE			BIT(0)
45 #define Q6SS_CORE_ARES			BIT(1)
46 #define Q6SS_BUS_ARES_ENABLE		BIT(2)
47 
48 /* Q6SS_BRC_RESET */
49 #define Q6SS_BRC_BLK_ARES		BIT(0)
50 
51 /* Q6SS_GFMUX_CTL */
52 #define Q6SS_CLK_ENABLE			BIT(1)
53 #define Q6SS_SWITCH_CLK_SRC		BIT(8)
54 
55 /* Q6SS_PWR_CTL */
56 #define Q6SS_L2DATA_STBY_N		BIT(18)
57 #define Q6SS_SLP_RET_N			BIT(19)
58 #define Q6SS_CLAMP_IO			BIT(20)
59 #define QDSS_BHS_ON			BIT(21)
60 #define QDSS_Q6_MEMORIES		GENMASK(15, 0)
61 
62 /* Q6SS parameters */
63 #define Q6SS_LDO_BYP		BIT(25)
64 #define Q6SS_BHS_ON		BIT(24)
65 #define Q6SS_CLAMP_WL		BIT(21)
66 #define Q6SS_CLAMP_QMC_MEM		BIT(22)
67 #define HALT_CHECK_MAX_LOOPS		200
68 #define Q6SS_XO_CBCR		GENMASK(5, 3)
69 #define Q6SS_SLEEP_CBCR		GENMASK(5, 2)
70 
71 /* Q6SS config/status registers */
72 #define TCSR_GLOBAL_CFG0	0x0
73 #define TCSR_GLOBAL_CFG1	0x4
74 #define SSCAON_CONFIG		0x8
75 #define SSCAON_STATUS		0xc
76 #define Q6SS_BHS_STATUS		0x78
77 #define Q6SS_RST_EVB		0x10
78 
79 #define BHS_EN_REST_ACK		BIT(0)
80 #define SSCAON_ENABLE		BIT(13)
81 #define SSCAON_BUS_EN		BIT(15)
82 #define SSCAON_BUS_MUX_MASK	GENMASK(18, 16)
83 
84 #define MEM_BANKS		19
85 #define TCSR_WCSS_CLK_MASK	0x1F
86 #define TCSR_WCSS_CLK_ENABLE	0x14
87 
88 #define MAX_HALT_REG		3
89 enum {
90 	WCSS_IPQ8074,
91 	WCSS_QCS404,
92 };
93 
94 struct wcss_data {
95 	const char *firmware_name;
96 	unsigned int crash_reason_smem;
97 	u32 version;
98 	bool aon_reset_required;
99 	bool wcss_q6_reset_required;
100 	const char *ssr_name;
101 	const char *sysmon_name;
102 	int ssctl_id;
103 	const struct rproc_ops *ops;
104 	bool requires_force_stop;
105 };
106 
107 struct q6v5_wcss {
108 	struct device *dev;
109 
110 	void __iomem *reg_base;
111 	void __iomem *rmb_base;
112 
113 	struct regmap *halt_map;
114 	u32 halt_q6;
115 	u32 halt_wcss;
116 	u32 halt_nc;
117 
118 	struct clk *xo;
119 	struct clk *ahbfabric_cbcr_clk;
120 	struct clk *gcc_abhs_cbcr;
121 	struct clk *gcc_axim_cbcr;
122 	struct clk *lcc_csr_cbcr;
123 	struct clk *ahbs_cbcr;
124 	struct clk *tcm_slave_cbcr;
125 	struct clk *qdsp6ss_abhm_cbcr;
126 	struct clk *qdsp6ss_sleep_cbcr;
127 	struct clk *qdsp6ss_axim_cbcr;
128 	struct clk *qdsp6ss_xo_cbcr;
129 	struct clk *qdsp6ss_core_gfmux;
130 	struct clk *lcc_bcr_sleep;
131 	struct regulator *cx_supply;
132 	struct qcom_sysmon *sysmon;
133 
134 	struct reset_control *wcss_aon_reset;
135 	struct reset_control *wcss_reset;
136 	struct reset_control *wcss_q6_reset;
137 	struct reset_control *wcss_q6_bcr_reset;
138 
139 	struct qcom_q6v5 q6v5;
140 
141 	phys_addr_t mem_phys;
142 	phys_addr_t mem_reloc;
143 	void *mem_region;
144 	size_t mem_size;
145 
146 	unsigned int crash_reason_smem;
147 	u32 version;
148 	bool requires_force_stop;
149 
150 	struct qcom_rproc_glink glink_subdev;
151 	struct qcom_rproc_pdm pdm_subdev;
152 	struct qcom_rproc_ssr ssr_subdev;
153 };
154 
155 static int q6v5_wcss_reset(struct q6v5_wcss *wcss)
156 {
157 	int ret;
158 	u32 val;
159 	int i;
160 
161 	/* Assert resets, stop core */
162 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
163 	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
164 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
165 
166 	/* BHS require xo cbcr to be enabled */
167 	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
168 	val |= 0x1;
169 	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
170 
171 	/* Read CLKOFF bit to go low indicating CLK is enabled */
172 	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
173 				 val, !(val & BIT(31)), 1,
174 				 HALT_CHECK_MAX_LOOPS);
175 	if (ret) {
176 		dev_err(wcss->dev,
177 			"xo cbcr enabling timed out (rc:%d)\n", ret);
178 		return ret;
179 	}
180 	/* Enable power block headswitch and wait for it to stabilize */
181 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
182 	val |= Q6SS_BHS_ON;
183 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
184 	udelay(1);
185 
186 	/* Put LDO in bypass mode */
187 	val |= Q6SS_LDO_BYP;
188 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
189 
190 	/* Deassert Q6 compiler memory clamp */
191 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
192 	val &= ~Q6SS_CLAMP_QMC_MEM;
193 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
194 
195 	/* Deassert memory peripheral sleep and L2 memory standby */
196 	val |= Q6SS_L2DATA_STBY_N | Q6SS_SLP_RET_N;
197 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
198 
199 	/* Turn on L1, L2, ETB and JU memories 1 at a time */
200 	val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
201 	for (i = MEM_BANKS; i >= 0; i--) {
202 		val |= BIT(i);
203 		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
204 		/*
205 		 * Read back value to ensure the write is done then
206 		 * wait for 1us for both memory peripheral and data
207 		 * array to turn on.
208 		 */
209 		val |= readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
210 		udelay(1);
211 	}
212 	/* Remove word line clamp */
213 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
214 	val &= ~Q6SS_CLAMP_WL;
215 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
216 
217 	/* Remove IO clamp */
218 	val &= ~Q6SS_CLAMP_IO;
219 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
220 
221 	/* Bring core out of reset */
222 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
223 	val &= ~Q6SS_CORE_ARES;
224 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
225 
226 	/* Turn on core clock */
227 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
228 	val |= Q6SS_CLK_ENABLE;
229 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
230 
231 	/* Start core execution */
232 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
233 	val &= ~Q6SS_STOP_CORE;
234 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
235 
236 	return 0;
237 }
238 
239 static int q6v5_wcss_start(struct rproc *rproc)
240 {
241 	struct q6v5_wcss *wcss = rproc->priv;
242 	int ret;
243 
244 	qcom_q6v5_prepare(&wcss->q6v5);
245 
246 	/* Release Q6 and WCSS reset */
247 	ret = reset_control_deassert(wcss->wcss_reset);
248 	if (ret) {
249 		dev_err(wcss->dev, "wcss_reset failed\n");
250 		return ret;
251 	}
252 
253 	ret = reset_control_deassert(wcss->wcss_q6_reset);
254 	if (ret) {
255 		dev_err(wcss->dev, "wcss_q6_reset failed\n");
256 		goto wcss_reset;
257 	}
258 
259 	/* Lithium configuration - clock gating and bus arbitration */
260 	ret = regmap_update_bits(wcss->halt_map,
261 				 wcss->halt_nc + TCSR_GLOBAL_CFG0,
262 				 TCSR_WCSS_CLK_MASK,
263 				 TCSR_WCSS_CLK_ENABLE);
264 	if (ret)
265 		goto wcss_q6_reset;
266 
267 	ret = regmap_update_bits(wcss->halt_map,
268 				 wcss->halt_nc + TCSR_GLOBAL_CFG1,
269 				 1, 0);
270 	if (ret)
271 		goto wcss_q6_reset;
272 
273 	/* Write bootaddr to EVB so that Q6WCSS will jump there after reset */
274 	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
275 
276 	ret = q6v5_wcss_reset(wcss);
277 	if (ret)
278 		goto wcss_q6_reset;
279 
280 	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
281 	if (ret == -ETIMEDOUT)
282 		dev_err(wcss->dev, "start timed out\n");
283 
284 	return ret;
285 
286 wcss_q6_reset:
287 	reset_control_assert(wcss->wcss_q6_reset);
288 
289 wcss_reset:
290 	reset_control_assert(wcss->wcss_reset);
291 
292 	return ret;
293 }
294 
295 static int q6v5_wcss_qcs404_power_on(struct q6v5_wcss *wcss)
296 {
297 	unsigned long val;
298 	int ret, idx;
299 
300 	/* Toggle the restart */
301 	reset_control_assert(wcss->wcss_reset);
302 	usleep_range(200, 300);
303 	reset_control_deassert(wcss->wcss_reset);
304 	usleep_range(200, 300);
305 
306 	/* Enable GCC_WDSP_Q6SS_AHBS_CBCR clock */
307 	ret = clk_prepare_enable(wcss->gcc_abhs_cbcr);
308 	if (ret)
309 		return ret;
310 
311 	/* Remove reset to the WCNSS QDSP6SS */
312 	reset_control_deassert(wcss->wcss_q6_bcr_reset);
313 
314 	/* Enable Q6SSTOP_AHBFABRIC_CBCR clock */
315 	ret = clk_prepare_enable(wcss->ahbfabric_cbcr_clk);
316 	if (ret)
317 		goto disable_gcc_abhs_cbcr_clk;
318 
319 	/* Enable the LCCCSR CBC clock, Q6SSTOP_Q6SSTOP_LCC_CSR_CBCR clock */
320 	ret = clk_prepare_enable(wcss->lcc_csr_cbcr);
321 	if (ret)
322 		goto disable_ahbfabric_cbcr_clk;
323 
324 	/* Enable the Q6AHBS CBC, Q6SSTOP_Q6SS_AHBS_CBCR clock */
325 	ret = clk_prepare_enable(wcss->ahbs_cbcr);
326 	if (ret)
327 		goto disable_csr_cbcr_clk;
328 
329 	/* Enable the TCM slave CBC, Q6SSTOP_Q6SS_TCM_SLAVE_CBCR clock */
330 	ret = clk_prepare_enable(wcss->tcm_slave_cbcr);
331 	if (ret)
332 		goto disable_ahbs_cbcr_clk;
333 
334 	/* Enable the Q6SS AHB master CBC, Q6SSTOP_Q6SS_AHBM_CBCR clock */
335 	ret = clk_prepare_enable(wcss->qdsp6ss_abhm_cbcr);
336 	if (ret)
337 		goto disable_tcm_slave_cbcr_clk;
338 
339 	/* Enable the Q6SS AXI master CBC, Q6SSTOP_Q6SS_AXIM_CBCR clock */
340 	ret = clk_prepare_enable(wcss->qdsp6ss_axim_cbcr);
341 	if (ret)
342 		goto disable_abhm_cbcr_clk;
343 
344 	/* Enable the Q6SS XO CBC */
345 	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
346 	val |= BIT(0);
347 	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
348 	/* Read CLKOFF bit to go low indicating CLK is enabled */
349 	ret = readl_poll_timeout(wcss->reg_base + Q6SS_XO_CBCR,
350 				 val, !(val & BIT(31)), 1,
351 				 HALT_CHECK_MAX_LOOPS);
352 	if (ret) {
353 		dev_err(wcss->dev,
354 			"xo cbcr enabling timed out (rc:%d)\n", ret);
355 		goto disable_xo_cbcr_clk;
356 	}
357 
358 	writel(0, wcss->reg_base + Q6SS_CGC_OVERRIDE);
359 
360 	/* Enable QDSP6 sleep clock clock */
361 	val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);
362 	val |= BIT(0);
363 	writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);
364 
365 	/* Enable the Enable the Q6 AXI clock, GCC_WDSP_Q6SS_AXIM_CBCR*/
366 	ret = clk_prepare_enable(wcss->gcc_axim_cbcr);
367 	if (ret)
368 		goto disable_sleep_cbcr_clk;
369 
370 	/* Assert resets, stop core */
371 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
372 	val |= Q6SS_CORE_ARES | Q6SS_BUS_ARES_ENABLE | Q6SS_STOP_CORE;
373 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
374 
375 	/* Program the QDSP6SS PWR_CTL register */
376 	writel(0x01700000, wcss->reg_base + Q6SS_PWR_CTL_REG);
377 
378 	writel(0x03700000, wcss->reg_base + Q6SS_PWR_CTL_REG);
379 
380 	writel(0x03300000, wcss->reg_base + Q6SS_PWR_CTL_REG);
381 
382 	writel(0x033C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);
383 
384 	/*
385 	 * Enable memories by turning on the QDSP6 memory foot/head switch, one
386 	 * bank at a time to avoid in-rush current
387 	 */
388 	for (idx = 28; idx >= 0; idx--) {
389 		writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) |
390 			(1 << idx)), wcss->reg_base + Q6SS_MEM_PWR_CTL);
391 	}
392 
393 	writel(0x031C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);
394 	writel(0x030C0000, wcss->reg_base + Q6SS_PWR_CTL_REG);
395 
396 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
397 	val &= ~Q6SS_CORE_ARES;
398 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
399 
400 	/* Enable the Q6 core clock at the GFM, Q6SSTOP_QDSP6SS_GFMUX_CTL */
401 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
402 	val |= Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC;
403 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
404 
405 	/* Enable sleep clock branch needed for BCR circuit */
406 	ret = clk_prepare_enable(wcss->lcc_bcr_sleep);
407 	if (ret)
408 		goto disable_core_gfmux_clk;
409 
410 	return 0;
411 
412 disable_core_gfmux_clk:
413 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
414 	val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);
415 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
416 	clk_disable_unprepare(wcss->gcc_axim_cbcr);
417 disable_sleep_cbcr_clk:
418 	val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);
419 	val &= ~Q6SS_CLK_ENABLE;
420 	writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);
421 disable_xo_cbcr_clk:
422 	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
423 	val &= ~Q6SS_CLK_ENABLE;
424 	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
425 	clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);
426 disable_abhm_cbcr_clk:
427 	clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);
428 disable_tcm_slave_cbcr_clk:
429 	clk_disable_unprepare(wcss->tcm_slave_cbcr);
430 disable_ahbs_cbcr_clk:
431 	clk_disable_unprepare(wcss->ahbs_cbcr);
432 disable_csr_cbcr_clk:
433 	clk_disable_unprepare(wcss->lcc_csr_cbcr);
434 disable_ahbfabric_cbcr_clk:
435 	clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);
436 disable_gcc_abhs_cbcr_clk:
437 	clk_disable_unprepare(wcss->gcc_abhs_cbcr);
438 
439 	return ret;
440 }
441 
442 static inline int q6v5_wcss_qcs404_reset(struct q6v5_wcss *wcss)
443 {
444 	unsigned long val;
445 
446 	writel(0x80800000, wcss->reg_base + Q6SS_STRAP_ACC);
447 
448 	/* Start core execution */
449 	val = readl(wcss->reg_base + Q6SS_RESET_REG);
450 	val &= ~Q6SS_STOP_CORE;
451 	writel(val, wcss->reg_base + Q6SS_RESET_REG);
452 
453 	return 0;
454 }
455 
456 static int q6v5_qcs404_wcss_start(struct rproc *rproc)
457 {
458 	struct q6v5_wcss *wcss = rproc->priv;
459 	int ret;
460 
461 	ret = clk_prepare_enable(wcss->xo);
462 	if (ret)
463 		return ret;
464 
465 	ret = regulator_enable(wcss->cx_supply);
466 	if (ret)
467 		goto disable_xo_clk;
468 
469 	qcom_q6v5_prepare(&wcss->q6v5);
470 
471 	ret = q6v5_wcss_qcs404_power_on(wcss);
472 	if (ret) {
473 		dev_err(wcss->dev, "wcss clk_enable failed\n");
474 		goto disable_cx_supply;
475 	}
476 
477 	writel(rproc->bootaddr >> 4, wcss->reg_base + Q6SS_RST_EVB);
478 
479 	q6v5_wcss_qcs404_reset(wcss);
480 
481 	ret = qcom_q6v5_wait_for_start(&wcss->q6v5, 5 * HZ);
482 	if (ret == -ETIMEDOUT) {
483 		dev_err(wcss->dev, "start timed out\n");
484 		goto disable_cx_supply;
485 	}
486 
487 	return 0;
488 
489 disable_cx_supply:
490 	regulator_disable(wcss->cx_supply);
491 disable_xo_clk:
492 	clk_disable_unprepare(wcss->xo);
493 
494 	return ret;
495 }
496 
497 static void q6v5_wcss_halt_axi_port(struct q6v5_wcss *wcss,
498 				    struct regmap *halt_map,
499 				    u32 offset)
500 {
501 	unsigned long timeout;
502 	unsigned int val;
503 	int ret;
504 
505 	/* Check if we're already idle */
506 	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
507 	if (!ret && val)
508 		return;
509 
510 	/* Assert halt request */
511 	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 1);
512 
513 	/* Wait for halt */
514 	timeout = jiffies + msecs_to_jiffies(HALT_ACK_TIMEOUT_MS);
515 	for (;;) {
516 		ret = regmap_read(halt_map, offset + AXI_HALTACK_REG, &val);
517 		if (ret || val || time_after(jiffies, timeout))
518 			break;
519 
520 		msleep(1);
521 	}
522 
523 	ret = regmap_read(halt_map, offset + AXI_IDLE_REG, &val);
524 	if (ret || !val)
525 		dev_err(wcss->dev, "port failed halt\n");
526 
527 	/* Clear halt request (port will remain halted until reset) */
528 	regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0);
529 }
530 
531 static int q6v5_qcs404_wcss_shutdown(struct q6v5_wcss *wcss)
532 {
533 	unsigned long val;
534 	int ret;
535 
536 	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
537 
538 	/* assert clamps to avoid MX current inrush */
539 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
540 	val |= (Q6SS_CLAMP_IO | Q6SS_CLAMP_WL | Q6SS_CLAMP_QMC_MEM);
541 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
542 
543 	/* Disable memories by turning off memory foot/headswitch */
544 	writel((readl(wcss->reg_base + Q6SS_MEM_PWR_CTL) &
545 		~QDSS_Q6_MEMORIES),
546 		wcss->reg_base + Q6SS_MEM_PWR_CTL);
547 
548 	/* Clear the BHS_ON bit */
549 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
550 	val &= ~Q6SS_BHS_ON;
551 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
552 
553 	clk_disable_unprepare(wcss->ahbfabric_cbcr_clk);
554 	clk_disable_unprepare(wcss->lcc_csr_cbcr);
555 	clk_disable_unprepare(wcss->tcm_slave_cbcr);
556 	clk_disable_unprepare(wcss->qdsp6ss_abhm_cbcr);
557 	clk_disable_unprepare(wcss->qdsp6ss_axim_cbcr);
558 
559 	val = readl(wcss->reg_base + Q6SS_SLEEP_CBCR);
560 	val &= ~BIT(0);
561 	writel(val, wcss->reg_base + Q6SS_SLEEP_CBCR);
562 
563 	val = readl(wcss->reg_base + Q6SS_XO_CBCR);
564 	val &= ~BIT(0);
565 	writel(val, wcss->reg_base + Q6SS_XO_CBCR);
566 
567 	clk_disable_unprepare(wcss->ahbs_cbcr);
568 	clk_disable_unprepare(wcss->lcc_bcr_sleep);
569 
570 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
571 	val &= ~(Q6SS_CLK_ENABLE | Q6SS_SWITCH_CLK_SRC);
572 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
573 
574 	clk_disable_unprepare(wcss->gcc_abhs_cbcr);
575 
576 	ret = reset_control_assert(wcss->wcss_reset);
577 	if (ret) {
578 		dev_err(wcss->dev, "wcss_reset failed\n");
579 		return ret;
580 	}
581 	usleep_range(200, 300);
582 
583 	ret = reset_control_deassert(wcss->wcss_reset);
584 	if (ret) {
585 		dev_err(wcss->dev, "wcss_reset failed\n");
586 		return ret;
587 	}
588 	usleep_range(200, 300);
589 
590 	clk_disable_unprepare(wcss->gcc_axim_cbcr);
591 
592 	return 0;
593 }
594 
595 static int q6v5_wcss_powerdown(struct q6v5_wcss *wcss)
596 {
597 	int ret;
598 	u32 val;
599 
600 	/* 1 - Assert WCSS/Q6 HALTREQ */
601 	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_wcss);
602 
603 	/* 2 - Enable WCSSAON_CONFIG */
604 	val = readl(wcss->rmb_base + SSCAON_CONFIG);
605 	val |= SSCAON_ENABLE;
606 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
607 
608 	/* 3 - Set SSCAON_CONFIG */
609 	val |= SSCAON_BUS_EN;
610 	val &= ~SSCAON_BUS_MUX_MASK;
611 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
612 
613 	/* 4 - SSCAON_CONFIG 1 */
614 	val |= BIT(1);
615 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
616 
617 	/* 5 - wait for SSCAON_STATUS */
618 	ret = readl_poll_timeout(wcss->rmb_base + SSCAON_STATUS,
619 				 val, (val & 0xffff) == 0x400, 1000,
620 				 HALT_CHECK_MAX_LOOPS);
621 	if (ret) {
622 		dev_err(wcss->dev,
623 			"can't get SSCAON_STATUS rc:%d)\n", ret);
624 		return ret;
625 	}
626 
627 	/* 6 - De-assert WCSS_AON reset */
628 	reset_control_assert(wcss->wcss_aon_reset);
629 
630 	/* 7 - Disable WCSSAON_CONFIG 13 */
631 	val = readl(wcss->rmb_base + SSCAON_CONFIG);
632 	val &= ~SSCAON_ENABLE;
633 	writel(val, wcss->rmb_base + SSCAON_CONFIG);
634 
635 	/* 8 - De-assert WCSS/Q6 HALTREQ */
636 	reset_control_assert(wcss->wcss_reset);
637 
638 	return 0;
639 }
640 
641 static int q6v5_q6_powerdown(struct q6v5_wcss *wcss)
642 {
643 	int ret;
644 	u32 val;
645 	int i;
646 
647 	/* 1 - Halt Q6 bus interface */
648 	q6v5_wcss_halt_axi_port(wcss, wcss->halt_map, wcss->halt_q6);
649 
650 	/* 2 - Disable Q6 Core clock */
651 	val = readl(wcss->reg_base + Q6SS_GFMUX_CTL_REG);
652 	val &= ~Q6SS_CLK_ENABLE;
653 	writel(val, wcss->reg_base + Q6SS_GFMUX_CTL_REG);
654 
655 	/* 3 - Clamp I/O */
656 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
657 	val |= Q6SS_CLAMP_IO;
658 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
659 
660 	/* 4 - Clamp WL */
661 	val |= QDSS_BHS_ON;
662 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
663 
664 	/* 5 - Clear Erase standby */
665 	val &= ~Q6SS_L2DATA_STBY_N;
666 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
667 
668 	/* 6 - Clear Sleep RTN */
669 	val &= ~Q6SS_SLP_RET_N;
670 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
671 
672 	/* 7 - turn off Q6 memory foot/head switch one bank at a time */
673 	for (i = 0; i < 20; i++) {
674 		val = readl(wcss->reg_base + Q6SS_MEM_PWR_CTL);
675 		val &= ~BIT(i);
676 		writel(val, wcss->reg_base + Q6SS_MEM_PWR_CTL);
677 		mdelay(1);
678 	}
679 
680 	/* 8 - Assert QMC memory RTN */
681 	val = readl(wcss->reg_base + Q6SS_PWR_CTL_REG);
682 	val |= Q6SS_CLAMP_QMC_MEM;
683 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
684 
685 	/* 9 - Turn off BHS */
686 	val &= ~Q6SS_BHS_ON;
687 	writel(val, wcss->reg_base + Q6SS_PWR_CTL_REG);
688 	udelay(1);
689 
690 	/* 10 - Wait till BHS Reset is done */
691 	ret = readl_poll_timeout(wcss->reg_base + Q6SS_BHS_STATUS,
692 				 val, !(val & BHS_EN_REST_ACK), 1000,
693 				 HALT_CHECK_MAX_LOOPS);
694 	if (ret) {
695 		dev_err(wcss->dev, "BHS_STATUS not OFF (rc:%d)\n", ret);
696 		return ret;
697 	}
698 
699 	/* 11 -  Assert WCSS reset */
700 	reset_control_assert(wcss->wcss_reset);
701 
702 	/* 12 - Assert Q6 reset */
703 	reset_control_assert(wcss->wcss_q6_reset);
704 
705 	return 0;
706 }
707 
708 static int q6v5_wcss_stop(struct rproc *rproc)
709 {
710 	struct q6v5_wcss *wcss = rproc->priv;
711 	int ret;
712 
713 	/* WCSS powerdown */
714 	if (wcss->requires_force_stop) {
715 		ret = qcom_q6v5_request_stop(&wcss->q6v5, NULL);
716 		if (ret == -ETIMEDOUT) {
717 			dev_err(wcss->dev, "timed out on wait\n");
718 			return ret;
719 		}
720 	}
721 
722 	if (wcss->version == WCSS_QCS404) {
723 		ret = q6v5_qcs404_wcss_shutdown(wcss);
724 		if (ret)
725 			return ret;
726 	} else {
727 		ret = q6v5_wcss_powerdown(wcss);
728 		if (ret)
729 			return ret;
730 
731 		/* Q6 Power down */
732 		ret = q6v5_q6_powerdown(wcss);
733 		if (ret)
734 			return ret;
735 	}
736 
737 	qcom_q6v5_unprepare(&wcss->q6v5);
738 
739 	return 0;
740 }
741 
742 static void *q6v5_wcss_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem)
743 {
744 	struct q6v5_wcss *wcss = rproc->priv;
745 	int offset;
746 
747 	offset = da - wcss->mem_reloc;
748 	if (offset < 0 || offset + len > wcss->mem_size)
749 		return NULL;
750 
751 	return wcss->mem_region + offset;
752 }
753 
754 static int q6v5_wcss_load(struct rproc *rproc, const struct firmware *fw)
755 {
756 	struct q6v5_wcss *wcss = rproc->priv;
757 	int ret;
758 
759 	ret = qcom_mdt_load_no_init(wcss->dev, fw, rproc->firmware,
760 				    0, wcss->mem_region, wcss->mem_phys,
761 				    wcss->mem_size, &wcss->mem_reloc);
762 	if (ret)
763 		return ret;
764 
765 	qcom_pil_info_store("wcnss", wcss->mem_phys, wcss->mem_size);
766 
767 	return ret;
768 }
769 
770 static const struct rproc_ops q6v5_wcss_ipq8074_ops = {
771 	.start = q6v5_wcss_start,
772 	.stop = q6v5_wcss_stop,
773 	.da_to_va = q6v5_wcss_da_to_va,
774 	.load = q6v5_wcss_load,
775 	.get_boot_addr = rproc_elf_get_boot_addr,
776 };
777 
778 static const struct rproc_ops q6v5_wcss_qcs404_ops = {
779 	.start = q6v5_qcs404_wcss_start,
780 	.stop = q6v5_wcss_stop,
781 	.da_to_va = q6v5_wcss_da_to_va,
782 	.load = q6v5_wcss_load,
783 	.get_boot_addr = rproc_elf_get_boot_addr,
784 	.parse_fw = qcom_register_dump_segments,
785 };
786 
787 static int q6v5_wcss_init_reset(struct q6v5_wcss *wcss,
788 				const struct wcss_data *desc)
789 {
790 	struct device *dev = wcss->dev;
791 
792 	if (desc->aon_reset_required) {
793 		wcss->wcss_aon_reset = devm_reset_control_get_exclusive(dev, "wcss_aon_reset");
794 		if (IS_ERR(wcss->wcss_aon_reset)) {
795 			dev_err(wcss->dev, "fail to acquire wcss_aon_reset\n");
796 			return PTR_ERR(wcss->wcss_aon_reset);
797 		}
798 	}
799 
800 	wcss->wcss_reset = devm_reset_control_get_exclusive(dev, "wcss_reset");
801 	if (IS_ERR(wcss->wcss_reset)) {
802 		dev_err(wcss->dev, "unable to acquire wcss_reset\n");
803 		return PTR_ERR(wcss->wcss_reset);
804 	}
805 
806 	if (desc->wcss_q6_reset_required) {
807 		wcss->wcss_q6_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_reset");
808 		if (IS_ERR(wcss->wcss_q6_reset)) {
809 			dev_err(wcss->dev, "unable to acquire wcss_q6_reset\n");
810 			return PTR_ERR(wcss->wcss_q6_reset);
811 		}
812 	}
813 
814 	wcss->wcss_q6_bcr_reset = devm_reset_control_get_exclusive(dev, "wcss_q6_bcr_reset");
815 	if (IS_ERR(wcss->wcss_q6_bcr_reset)) {
816 		dev_err(wcss->dev, "unable to acquire wcss_q6_bcr_reset\n");
817 		return PTR_ERR(wcss->wcss_q6_bcr_reset);
818 	}
819 
820 	return 0;
821 }
822 
823 static int q6v5_wcss_init_mmio(struct q6v5_wcss *wcss,
824 			       struct platform_device *pdev)
825 {
826 	unsigned int halt_reg[MAX_HALT_REG] = {0};
827 	struct device_node *syscon;
828 	struct resource *res;
829 	int ret;
830 
831 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6");
832 	if (!res)
833 		return -EINVAL;
834 
835 	wcss->reg_base = devm_ioremap(&pdev->dev, res->start,
836 				      resource_size(res));
837 	if (!wcss->reg_base)
838 		return -ENOMEM;
839 
840 	if (wcss->version == WCSS_IPQ8074) {
841 		wcss->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb");
842 		if (IS_ERR(wcss->rmb_base))
843 			return PTR_ERR(wcss->rmb_base);
844 	}
845 
846 	syscon = of_parse_phandle(pdev->dev.of_node,
847 				  "qcom,halt-regs", 0);
848 	if (!syscon) {
849 		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
850 		return -EINVAL;
851 	}
852 
853 	wcss->halt_map = syscon_node_to_regmap(syscon);
854 	of_node_put(syscon);
855 	if (IS_ERR(wcss->halt_map))
856 		return PTR_ERR(wcss->halt_map);
857 
858 	ret = of_property_read_variable_u32_array(pdev->dev.of_node,
859 						  "qcom,halt-regs",
860 						  halt_reg, 0,
861 						  MAX_HALT_REG);
862 	if (ret < 0) {
863 		dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n");
864 		return -EINVAL;
865 	}
866 
867 	wcss->halt_q6 = halt_reg[0];
868 	wcss->halt_wcss = halt_reg[1];
869 	wcss->halt_nc = halt_reg[2];
870 
871 	return 0;
872 }
873 
874 static int q6v5_alloc_memory_region(struct q6v5_wcss *wcss)
875 {
876 	struct reserved_mem *rmem = NULL;
877 	struct device_node *node;
878 	struct device *dev = wcss->dev;
879 
880 	node = of_parse_phandle(dev->of_node, "memory-region", 0);
881 	if (node)
882 		rmem = of_reserved_mem_lookup(node);
883 	of_node_put(node);
884 
885 	if (!rmem) {
886 		dev_err(dev, "unable to acquire memory-region\n");
887 		return -EINVAL;
888 	}
889 
890 	wcss->mem_phys = rmem->base;
891 	wcss->mem_reloc = rmem->base;
892 	wcss->mem_size = rmem->size;
893 	wcss->mem_region = devm_ioremap_wc(dev, wcss->mem_phys, wcss->mem_size);
894 	if (!wcss->mem_region) {
895 		dev_err(dev, "unable to map memory region: %pa+%pa\n",
896 			&rmem->base, &rmem->size);
897 		return -EBUSY;
898 	}
899 
900 	return 0;
901 }
902 
903 static int q6v5_wcss_init_clock(struct q6v5_wcss *wcss)
904 {
905 	int ret;
906 
907 	wcss->xo = devm_clk_get(wcss->dev, "xo");
908 	if (IS_ERR(wcss->xo)) {
909 		ret = PTR_ERR(wcss->xo);
910 		if (ret != -EPROBE_DEFER)
911 			dev_err(wcss->dev, "failed to get xo clock");
912 		return ret;
913 	}
914 
915 	wcss->gcc_abhs_cbcr = devm_clk_get(wcss->dev, "gcc_abhs_cbcr");
916 	if (IS_ERR(wcss->gcc_abhs_cbcr)) {
917 		ret = PTR_ERR(wcss->gcc_abhs_cbcr);
918 		if (ret != -EPROBE_DEFER)
919 			dev_err(wcss->dev, "failed to get gcc abhs clock");
920 		return ret;
921 	}
922 
923 	wcss->gcc_axim_cbcr = devm_clk_get(wcss->dev, "gcc_axim_cbcr");
924 	if (IS_ERR(wcss->gcc_axim_cbcr)) {
925 		ret = PTR_ERR(wcss->gcc_axim_cbcr);
926 		if (ret != -EPROBE_DEFER)
927 			dev_err(wcss->dev, "failed to get gcc axim clock\n");
928 		return ret;
929 	}
930 
931 	wcss->ahbfabric_cbcr_clk = devm_clk_get(wcss->dev,
932 						"lcc_ahbfabric_cbc");
933 	if (IS_ERR(wcss->ahbfabric_cbcr_clk)) {
934 		ret = PTR_ERR(wcss->ahbfabric_cbcr_clk);
935 		if (ret != -EPROBE_DEFER)
936 			dev_err(wcss->dev, "failed to get ahbfabric clock\n");
937 		return ret;
938 	}
939 
940 	wcss->lcc_csr_cbcr = devm_clk_get(wcss->dev, "tcsr_lcc_cbc");
941 	if (IS_ERR(wcss->lcc_csr_cbcr)) {
942 		ret = PTR_ERR(wcss->lcc_csr_cbcr);
943 		if (ret != -EPROBE_DEFER)
944 			dev_err(wcss->dev, "failed to get csr cbcr clk\n");
945 		return ret;
946 	}
947 
948 	wcss->ahbs_cbcr = devm_clk_get(wcss->dev,
949 				       "lcc_abhs_cbc");
950 	if (IS_ERR(wcss->ahbs_cbcr)) {
951 		ret = PTR_ERR(wcss->ahbs_cbcr);
952 		if (ret != -EPROBE_DEFER)
953 			dev_err(wcss->dev, "failed to get ahbs_cbcr clk\n");
954 		return ret;
955 	}
956 
957 	wcss->tcm_slave_cbcr = devm_clk_get(wcss->dev,
958 					    "lcc_tcm_slave_cbc");
959 	if (IS_ERR(wcss->tcm_slave_cbcr)) {
960 		ret = PTR_ERR(wcss->tcm_slave_cbcr);
961 		if (ret != -EPROBE_DEFER)
962 			dev_err(wcss->dev, "failed to get tcm cbcr clk\n");
963 		return ret;
964 	}
965 
966 	wcss->qdsp6ss_abhm_cbcr = devm_clk_get(wcss->dev, "lcc_abhm_cbc");
967 	if (IS_ERR(wcss->qdsp6ss_abhm_cbcr)) {
968 		ret = PTR_ERR(wcss->qdsp6ss_abhm_cbcr);
969 		if (ret != -EPROBE_DEFER)
970 			dev_err(wcss->dev, "failed to get abhm cbcr clk\n");
971 		return ret;
972 	}
973 
974 	wcss->qdsp6ss_axim_cbcr = devm_clk_get(wcss->dev, "lcc_axim_cbc");
975 	if (IS_ERR(wcss->qdsp6ss_axim_cbcr)) {
976 		ret = PTR_ERR(wcss->qdsp6ss_axim_cbcr);
977 		if (ret != -EPROBE_DEFER)
978 			dev_err(wcss->dev, "failed to get axim cbcr clk\n");
979 		return ret;
980 	}
981 
982 	wcss->lcc_bcr_sleep = devm_clk_get(wcss->dev, "lcc_bcr_sleep");
983 	if (IS_ERR(wcss->lcc_bcr_sleep)) {
984 		ret = PTR_ERR(wcss->lcc_bcr_sleep);
985 		if (ret != -EPROBE_DEFER)
986 			dev_err(wcss->dev, "failed to get bcr cbcr clk\n");
987 		return ret;
988 	}
989 
990 	return 0;
991 }
992 
993 static int q6v5_wcss_init_regulator(struct q6v5_wcss *wcss)
994 {
995 	wcss->cx_supply = devm_regulator_get(wcss->dev, "cx");
996 	if (IS_ERR(wcss->cx_supply))
997 		return PTR_ERR(wcss->cx_supply);
998 
999 	regulator_set_load(wcss->cx_supply, 100000);
1000 
1001 	return 0;
1002 }
1003 
1004 static int q6v5_wcss_probe(struct platform_device *pdev)
1005 {
1006 	const struct wcss_data *desc;
1007 	struct q6v5_wcss *wcss;
1008 	struct rproc *rproc;
1009 	int ret;
1010 
1011 	desc = device_get_match_data(&pdev->dev);
1012 	if (!desc)
1013 		return -EINVAL;
1014 
1015 	rproc = devm_rproc_alloc(&pdev->dev, pdev->name, desc->ops,
1016 				 desc->firmware_name, sizeof(*wcss));
1017 	if (!rproc) {
1018 		dev_err(&pdev->dev, "failed to allocate rproc\n");
1019 		return -ENOMEM;
1020 	}
1021 
1022 	wcss = rproc->priv;
1023 	wcss->dev = &pdev->dev;
1024 	wcss->version = desc->version;
1025 
1026 	wcss->version = desc->version;
1027 	wcss->requires_force_stop = desc->requires_force_stop;
1028 
1029 	ret = q6v5_wcss_init_mmio(wcss, pdev);
1030 	if (ret)
1031 		return ret;
1032 
1033 	ret = q6v5_alloc_memory_region(wcss);
1034 	if (ret)
1035 		return ret;
1036 
1037 	if (wcss->version == WCSS_QCS404) {
1038 		ret = q6v5_wcss_init_clock(wcss);
1039 		if (ret)
1040 			return ret;
1041 
1042 		ret = q6v5_wcss_init_regulator(wcss);
1043 		if (ret)
1044 			return ret;
1045 	}
1046 
1047 	ret = q6v5_wcss_init_reset(wcss, desc);
1048 	if (ret)
1049 		return ret;
1050 
1051 	ret = qcom_q6v5_init(&wcss->q6v5, pdev, rproc, desc->crash_reason_smem, NULL, NULL);
1052 	if (ret)
1053 		return ret;
1054 
1055 	qcom_add_glink_subdev(rproc, &wcss->glink_subdev, "q6wcss");
1056 	qcom_add_pdm_subdev(rproc, &wcss->pdm_subdev);
1057 	qcom_add_ssr_subdev(rproc, &wcss->ssr_subdev, "q6wcss");
1058 
1059 	if (desc->ssctl_id)
1060 		wcss->sysmon = qcom_add_sysmon_subdev(rproc,
1061 						      desc->sysmon_name,
1062 						      desc->ssctl_id);
1063 
1064 	ret = rproc_add(rproc);
1065 	if (ret)
1066 		return ret;
1067 
1068 	platform_set_drvdata(pdev, rproc);
1069 
1070 	return 0;
1071 }
1072 
1073 static void q6v5_wcss_remove(struct platform_device *pdev)
1074 {
1075 	struct rproc *rproc = platform_get_drvdata(pdev);
1076 	struct q6v5_wcss *wcss = rproc->priv;
1077 
1078 	qcom_q6v5_deinit(&wcss->q6v5);
1079 	qcom_remove_pdm_subdev(rproc, &wcss->pdm_subdev);
1080 	rproc_del(rproc);
1081 }
1082 
1083 static const struct wcss_data wcss_ipq8074_res_init = {
1084 	.firmware_name = "IPQ8074/q6_fw.mdt",
1085 	.crash_reason_smem = WCSS_CRASH_REASON,
1086 	.aon_reset_required = true,
1087 	.wcss_q6_reset_required = true,
1088 	.ops = &q6v5_wcss_ipq8074_ops,
1089 	.requires_force_stop = true,
1090 };
1091 
1092 static const struct wcss_data wcss_qcs404_res_init = {
1093 	.crash_reason_smem = WCSS_CRASH_REASON,
1094 	.firmware_name = "wcnss.mdt",
1095 	.version = WCSS_QCS404,
1096 	.aon_reset_required = false,
1097 	.wcss_q6_reset_required = false,
1098 	.ssr_name = "mpss",
1099 	.sysmon_name = "wcnss",
1100 	.ssctl_id = 0x12,
1101 	.ops = &q6v5_wcss_qcs404_ops,
1102 	.requires_force_stop = false,
1103 };
1104 
1105 static const struct of_device_id q6v5_wcss_of_match[] = {
1106 	{ .compatible = "qcom,ipq8074-wcss-pil", .data = &wcss_ipq8074_res_init },
1107 	{ .compatible = "qcom,qcs404-wcss-pil", .data = &wcss_qcs404_res_init },
1108 	{ },
1109 };
1110 MODULE_DEVICE_TABLE(of, q6v5_wcss_of_match);
1111 
1112 static struct platform_driver q6v5_wcss_driver = {
1113 	.probe = q6v5_wcss_probe,
1114 	.remove_new = q6v5_wcss_remove,
1115 	.driver = {
1116 		.name = "qcom-q6v5-wcss-pil",
1117 		.of_match_table = q6v5_wcss_of_match,
1118 	},
1119 };
1120 module_platform_driver(q6v5_wcss_driver);
1121 
1122 MODULE_DESCRIPTION("Hexagon WCSS Peripheral Image Loader");
1123 MODULE_LICENSE("GPL v2");
1124