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