1 /*
2  * cnl-sst-dsp.c - CNL SST library generic function
3  *
4  * Copyright (C) 2016-17, Intel Corporation.
5  * Author: Guneshwor Singh <guneshwor.o.singh@intel.com>
6  *
7  * Modified from:
8  *	SKL SST library generic function
9  *	Copyright (C) 2014-15, Intel Corporation.
10  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as version 2, as
14  * published by the Free Software Foundation.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22  */
23 #include <linux/device.h>
24 #include "../common/sst-dsp.h"
25 #include "../common/sst-ipc.h"
26 #include "../common/sst-dsp-priv.h"
27 #include "cnl-sst-dsp.h"
28 
29 /* various timeout values */
30 #define CNL_DSP_PU_TO		50
31 #define CNL_DSP_PD_TO		50
32 #define CNL_DSP_RESET_TO	50
33 
34 static int
35 cnl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
36 {
37 	/* update bits */
38 	sst_dsp_shim_update_bits_unlocked(ctx,
39 			CNL_ADSP_REG_ADSPCS, CNL_ADSPCS_CRST(core_mask),
40 			CNL_ADSPCS_CRST(core_mask));
41 
42 	/* poll with timeout to check if operation successful */
43 	return sst_dsp_register_poll(ctx,
44 			CNL_ADSP_REG_ADSPCS,
45 			CNL_ADSPCS_CRST(core_mask),
46 			CNL_ADSPCS_CRST(core_mask),
47 			CNL_DSP_RESET_TO,
48 			"Set reset");
49 }
50 
51 static int
52 cnl_dsp_core_unset_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
53 {
54 	/* update bits */
55 	sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
56 					CNL_ADSPCS_CRST(core_mask), 0);
57 
58 	/* poll with timeout to check if operation successful */
59 	return sst_dsp_register_poll(ctx,
60 			CNL_ADSP_REG_ADSPCS,
61 			CNL_ADSPCS_CRST(core_mask),
62 			0,
63 			CNL_DSP_RESET_TO,
64 			"Unset reset");
65 }
66 
67 static bool is_cnl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask)
68 {
69 	int val;
70 	bool is_enable;
71 
72 	val = sst_dsp_shim_read_unlocked(ctx, CNL_ADSP_REG_ADSPCS);
73 
74 	is_enable = (val & CNL_ADSPCS_CPA(core_mask)) &&
75 			(val & CNL_ADSPCS_SPA(core_mask)) &&
76 			!(val & CNL_ADSPCS_CRST(core_mask)) &&
77 			!(val & CNL_ADSPCS_CSTALL(core_mask));
78 
79 	dev_dbg(ctx->dev, "DSP core(s) enabled? %d: core_mask %#x\n",
80 		is_enable, core_mask);
81 
82 	return is_enable;
83 }
84 
85 static int cnl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask)
86 {
87 	/* stall core */
88 	sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
89 			CNL_ADSPCS_CSTALL(core_mask),
90 			CNL_ADSPCS_CSTALL(core_mask));
91 
92 	/* set reset state */
93 	return cnl_dsp_core_set_reset_state(ctx, core_mask);
94 }
95 
96 static int cnl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask)
97 {
98 	int ret;
99 
100 	/* unset reset state */
101 	ret = cnl_dsp_core_unset_reset_state(ctx, core_mask);
102 	if (ret < 0)
103 		return ret;
104 
105 	/* run core */
106 	sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
107 				CNL_ADSPCS_CSTALL(core_mask), 0);
108 
109 	if (!is_cnl_dsp_core_enable(ctx, core_mask)) {
110 		cnl_dsp_reset_core(ctx, core_mask);
111 		dev_err(ctx->dev, "DSP core mask %#x enable failed\n",
112 			core_mask);
113 		ret = -EIO;
114 	}
115 
116 	return ret;
117 }
118 
119 static int cnl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask)
120 {
121 	/* update bits */
122 	sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
123 					  CNL_ADSPCS_SPA(core_mask),
124 					  CNL_ADSPCS_SPA(core_mask));
125 
126 	/* poll with timeout to check if operation successful */
127 	return sst_dsp_register_poll(ctx, CNL_ADSP_REG_ADSPCS,
128 				    CNL_ADSPCS_CPA(core_mask),
129 				    CNL_ADSPCS_CPA(core_mask),
130 				    CNL_DSP_PU_TO,
131 				    "Power up");
132 }
133 
134 static int cnl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask)
135 {
136 	/* update bits */
137 	sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPCS,
138 					CNL_ADSPCS_SPA(core_mask), 0);
139 
140 	/* poll with timeout to check if operation successful */
141 	return sst_dsp_register_poll(ctx,
142 			CNL_ADSP_REG_ADSPCS,
143 			CNL_ADSPCS_CPA(core_mask),
144 			0,
145 			CNL_DSP_PD_TO,
146 			"Power down");
147 }
148 
149 int cnl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask)
150 {
151 	int ret;
152 
153 	/* power up */
154 	ret = cnl_dsp_core_power_up(ctx, core_mask);
155 	if (ret < 0) {
156 		dev_dbg(ctx->dev, "DSP core mask %#x power up failed",
157 			core_mask);
158 		return ret;
159 	}
160 
161 	return cnl_dsp_start_core(ctx, core_mask);
162 }
163 
164 int cnl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask)
165 {
166 	int ret;
167 
168 	ret = cnl_dsp_reset_core(ctx, core_mask);
169 	if (ret < 0) {
170 		dev_err(ctx->dev, "DSP core mask %#x reset failed\n",
171 			core_mask);
172 		return ret;
173 	}
174 
175 	/* power down core*/
176 	ret = cnl_dsp_core_power_down(ctx, core_mask);
177 	if (ret < 0) {
178 		dev_err(ctx->dev, "DSP core mask %#x power down failed\n",
179 			core_mask);
180 		return ret;
181 	}
182 
183 	if (is_cnl_dsp_core_enable(ctx, core_mask)) {
184 		dev_err(ctx->dev, "DSP core mask %#x disable failed\n",
185 			core_mask);
186 		ret = -EIO;
187 	}
188 
189 	return ret;
190 }
191 
192 irqreturn_t cnl_dsp_sst_interrupt(int irq, void *dev_id)
193 {
194 	struct sst_dsp *ctx = dev_id;
195 	u32 val;
196 	irqreturn_t ret = IRQ_NONE;
197 
198 	spin_lock(&ctx->spinlock);
199 
200 	val = sst_dsp_shim_read_unlocked(ctx, CNL_ADSP_REG_ADSPIS);
201 	ctx->intr_status = val;
202 
203 	if (val == 0xffffffff) {
204 		spin_unlock(&ctx->spinlock);
205 		return IRQ_NONE;
206 	}
207 
208 	if (val & CNL_ADSPIS_IPC) {
209 		cnl_ipc_int_disable(ctx);
210 		ret = IRQ_WAKE_THREAD;
211 	}
212 
213 	spin_unlock(&ctx->spinlock);
214 
215 	return ret;
216 }
217 
218 void cnl_dsp_free(struct sst_dsp *dsp)
219 {
220 	cnl_ipc_int_disable(dsp);
221 
222 	free_irq(dsp->irq, dsp);
223 	cnl_ipc_op_int_disable(dsp);
224 	cnl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK);
225 }
226 EXPORT_SYMBOL_GPL(cnl_dsp_free);
227 
228 void cnl_ipc_int_enable(struct sst_dsp *ctx)
229 {
230 	sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_ADSPIC,
231 				 CNL_ADSPIC_IPC, CNL_ADSPIC_IPC);
232 }
233 
234 void cnl_ipc_int_disable(struct sst_dsp *ctx)
235 {
236 	sst_dsp_shim_update_bits_unlocked(ctx, CNL_ADSP_REG_ADSPIC,
237 					  CNL_ADSPIC_IPC, 0);
238 }
239 
240 void cnl_ipc_op_int_enable(struct sst_dsp *ctx)
241 {
242 	/* enable IPC DONE interrupt */
243 	sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
244 				 CNL_ADSP_REG_HIPCCTL_DONE,
245 				 CNL_ADSP_REG_HIPCCTL_DONE);
246 
247 	/* enable IPC BUSY interrupt */
248 	sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
249 				 CNL_ADSP_REG_HIPCCTL_BUSY,
250 				 CNL_ADSP_REG_HIPCCTL_BUSY);
251 }
252 
253 void cnl_ipc_op_int_disable(struct sst_dsp *ctx)
254 {
255 	/* disable IPC DONE interrupt */
256 	sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
257 				 CNL_ADSP_REG_HIPCCTL_DONE, 0);
258 
259 	/* disable IPC BUSY interrupt */
260 	sst_dsp_shim_update_bits(ctx, CNL_ADSP_REG_HIPCCTL,
261 				 CNL_ADSP_REG_HIPCCTL_BUSY, 0);
262 }
263 
264 bool cnl_ipc_int_status(struct sst_dsp *ctx)
265 {
266 	return sst_dsp_shim_read_unlocked(ctx, CNL_ADSP_REG_ADSPIS) &
267 							CNL_ADSPIS_IPC;
268 }
269 
270 void cnl_ipc_free(struct sst_generic_ipc *ipc)
271 {
272 	cnl_ipc_op_int_disable(ipc->dsp);
273 	sst_ipc_fini(ipc);
274 }
275