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