GNU Linux-libre 4.14.257-gnu1
[releases.git] / sound / soc / intel / skylake / cnl-sst-dsp.c
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 }