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