Commit | Line | Data |
---|---|---|
92eb4f62 JK |
1 | /* |
2 | * bxt-sst.c - DSP library functions for BXT platform | |
3 | * | |
4 | * Copyright (C) 2015-16 Intel Corp | |
5 | * Author:Rafal Redzimski <rafal.f.redzimski@intel.com> | |
6 | * Jeeja KP <jeeja.kp@intel.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; version 2 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | */ | |
17 | ||
18 | #include <linux/module.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/firmware.h> | |
21 | #include <linux/device.h> | |
22 | ||
23 | #include "../common/sst-dsp.h" | |
24 | #include "../common/sst-dsp-priv.h" | |
25 | #include "skl-sst-ipc.h" | |
26 | ||
27 | #define BXT_BASEFW_TIMEOUT 3000 | |
28 | #define BXT_INIT_TIMEOUT 500 | |
29 | #define BXT_IPC_PURGE_FW 0x01004000 | |
30 | ||
31 | #define BXT_ROM_INIT 0x5 | |
32 | #define BXT_ADSP_SRAM0_BASE 0x80000 | |
33 | ||
34 | /* Firmware status window */ | |
35 | #define BXT_ADSP_FW_STATUS BXT_ADSP_SRAM0_BASE | |
36 | #define BXT_ADSP_ERROR_CODE (BXT_ADSP_FW_STATUS + 0x4) | |
37 | ||
38 | #define BXT_ADSP_SRAM1_BASE 0xA0000 | |
39 | ||
e68aca08 J |
40 | #define BXT_INSTANCE_ID 0 |
41 | #define BXT_BASE_FW_MODULE_ID 0 | |
42 | ||
92eb4f62 JK |
43 | static unsigned int bxt_get_errorcode(struct sst_dsp *ctx) |
44 | { | |
45 | return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE); | |
46 | } | |
47 | ||
e68aca08 J |
48 | /* |
49 | * First boot sequence has some extra steps. Core 0 waits for power | |
50 | * status on core 1, so power up core 1 also momentarily, keep it in | |
51 | * reset/stall and then turn it off | |
52 | */ | |
92eb4f62 JK |
53 | static int sst_bxt_prepare_fw(struct sst_dsp *ctx, |
54 | const void *fwdata, u32 fwsize) | |
55 | { | |
56 | int stream_tag, ret, i; | |
57 | u32 reg; | |
58 | ||
59 | stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab); | |
e68aca08 | 60 | if (stream_tag <= 0) { |
92eb4f62 JK |
61 | dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n", |
62 | stream_tag); | |
63 | return stream_tag; | |
64 | } | |
65 | ||
66 | ctx->dsp_ops.stream_tag = stream_tag; | |
67 | memcpy(ctx->dmab.area, fwdata, fwsize); | |
68 | ||
e68aca08 J |
69 | /* Step 1: Power up core 0 and core1 */ |
70 | ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK | | |
71 | SKL_DSP_CORE_MASK(1)); | |
2023576d | 72 | if (ret < 0) { |
e68aca08 | 73 | dev_err(ctx->dev, "dsp core0/1 power up failed\n"); |
2023576d SV |
74 | goto base_fw_load_failed; |
75 | } | |
76 | ||
e68aca08 | 77 | /* Step 2: Purge FW request */ |
92eb4f62 | 78 | sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY | |
2023576d | 79 | (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9))); |
92eb4f62 | 80 | |
e68aca08 | 81 | /* Step 3: Unset core0 reset state & unstall/run core0 */ |
052f103c | 82 | ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK); |
92eb4f62 | 83 | if (ret < 0) { |
2023576d | 84 | dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret); |
92eb4f62 JK |
85 | ret = -EIO; |
86 | goto base_fw_load_failed; | |
87 | } | |
88 | ||
e68aca08 | 89 | /* Step 4: Wait for DONE Bit */ |
92eb4f62 JK |
90 | for (i = BXT_INIT_TIMEOUT; i > 0; --i) { |
91 | reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE); | |
92 | ||
93 | if (reg & SKL_ADSP_REG_HIPCIE_DONE) { | |
94 | sst_dsp_shim_update_bits_forced(ctx, | |
95 | SKL_ADSP_REG_HIPCIE, | |
96 | SKL_ADSP_REG_HIPCIE_DONE, | |
97 | SKL_ADSP_REG_HIPCIE_DONE); | |
98 | break; | |
99 | } | |
100 | mdelay(1); | |
101 | } | |
102 | if (!i) { | |
103 | dev_info(ctx->dev, "Waiting for HIPCIE done, reg: 0x%x\n", reg); | |
104 | sst_dsp_shim_update_bits(ctx, SKL_ADSP_REG_HIPCIE, | |
105 | SKL_ADSP_REG_HIPCIE_DONE, | |
106 | SKL_ADSP_REG_HIPCIE_DONE); | |
107 | } | |
108 | ||
e68aca08 J |
109 | /* Step 5: power down core1 */ |
110 | ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); | |
111 | if (ret < 0) { | |
112 | dev_err(ctx->dev, "dsp core1 power down failed\n"); | |
113 | goto base_fw_load_failed; | |
114 | } | |
115 | ||
116 | /* Step 6: Enable Interrupt */ | |
92eb4f62 JK |
117 | skl_ipc_int_enable(ctx); |
118 | skl_ipc_op_int_enable(ctx); | |
119 | ||
e68aca08 | 120 | /* Step 7: Wait for ROM init */ |
92eb4f62 JK |
121 | for (i = BXT_INIT_TIMEOUT; i > 0; --i) { |
122 | if (SKL_FW_INIT == | |
123 | (sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) & | |
124 | SKL_FW_STS_MASK)) { | |
125 | ||
126 | dev_info(ctx->dev, "ROM loaded, continue FW loading\n"); | |
127 | break; | |
128 | } | |
129 | mdelay(1); | |
130 | } | |
131 | if (!i) { | |
132 | dev_err(ctx->dev, "Timeout for ROM init, HIPCIE: 0x%x\n", reg); | |
133 | ret = -EIO; | |
134 | goto base_fw_load_failed; | |
135 | } | |
136 | ||
137 | return ret; | |
138 | ||
139 | base_fw_load_failed: | |
140 | ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag); | |
052f103c | 141 | skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); |
c7872267 | 142 | skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); |
92eb4f62 JK |
143 | return ret; |
144 | } | |
145 | ||
146 | static int sst_transfer_fw_host_dma(struct sst_dsp *ctx) | |
147 | { | |
148 | int ret; | |
149 | ||
150 | ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag); | |
151 | ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK, | |
152 | BXT_ROM_INIT, BXT_BASEFW_TIMEOUT, "Firmware boot"); | |
153 | ||
154 | ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag); | |
155 | ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag); | |
156 | ||
157 | return ret; | |
158 | } | |
159 | ||
3467a64d VK |
160 | #define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000 |
161 | ||
92eb4f62 JK |
162 | static int bxt_load_base_firmware(struct sst_dsp *ctx) |
163 | { | |
bf242d19 | 164 | struct firmware stripped_fw; |
92eb4f62 JK |
165 | struct skl_sst *skl = ctx->thread_context; |
166 | int ret; | |
167 | ||
fdfa82ee | 168 | ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev); |
92eb4f62 JK |
169 | if (ret < 0) { |
170 | dev_err(ctx->dev, "Request firmware failed %d\n", ret); | |
171 | goto sst_load_base_firmware_failed; | |
172 | } | |
173 | ||
bf242d19 VK |
174 | /* check for extended manifest */ |
175 | if (ctx->fw == NULL) | |
176 | goto sst_load_base_firmware_failed; | |
177 | ||
3467a64d VK |
178 | ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET); |
179 | if (ret < 0) | |
180 | goto sst_load_base_firmware_failed; | |
bf242d19 VK |
181 | |
182 | stripped_fw.data = ctx->fw->data; | |
183 | stripped_fw.size = ctx->fw->size; | |
184 | skl_dsp_strip_extended_manifest(&stripped_fw); | |
185 | ||
186 | ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); | |
92eb4f62 JK |
187 | /* Retry Enabling core and ROM load. Retry seemed to help */ |
188 | if (ret < 0) { | |
bf242d19 | 189 | ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size); |
92eb4f62 | 190 | if (ret < 0) { |
2023576d SV |
191 | dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", |
192 | sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), | |
193 | sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); | |
194 | ||
92eb4f62 JK |
195 | dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret); |
196 | goto sst_load_base_firmware_failed; | |
197 | } | |
198 | } | |
199 | ||
200 | ret = sst_transfer_fw_host_dma(ctx); | |
201 | if (ret < 0) { | |
202 | dev_err(ctx->dev, "Transfer firmware failed %d\n", ret); | |
203 | dev_info(ctx->dev, "Error code=0x%x: FW status=0x%x\n", | |
204 | sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), | |
205 | sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); | |
206 | ||
052f103c | 207 | skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); |
92eb4f62 JK |
208 | } else { |
209 | dev_dbg(ctx->dev, "Firmware download successful\n"); | |
210 | ret = wait_event_timeout(skl->boot_wait, skl->boot_complete, | |
211 | msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); | |
212 | if (ret == 0) { | |
213 | dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n"); | |
052f103c | 214 | skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK); |
92eb4f62 JK |
215 | ret = -EIO; |
216 | } else { | |
92eb4f62 | 217 | ret = 0; |
1665c177 | 218 | skl->fw_loaded = true; |
92eb4f62 JK |
219 | } |
220 | } | |
221 | ||
222 | sst_load_base_firmware_failed: | |
fdfa82ee | 223 | release_firmware(ctx->fw); |
92eb4f62 JK |
224 | return ret; |
225 | } | |
226 | ||
052f103c | 227 | static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id) |
92eb4f62 JK |
228 | { |
229 | struct skl_sst *skl = ctx->thread_context; | |
230 | int ret; | |
e68aca08 J |
231 | struct skl_ipc_dxstate_info dx; |
232 | unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); | |
92eb4f62 | 233 | |
1665c177 | 234 | if (skl->fw_loaded == false) { |
c7872267 | 235 | skl->boot_complete = false; |
1665c177 J |
236 | ret = bxt_load_base_firmware(ctx); |
237 | if (ret < 0) | |
238 | dev_err(ctx->dev, "reload fw failed: %d\n", ret); | |
92eb4f62 JK |
239 | return ret; |
240 | } | |
241 | ||
e68aca08 J |
242 | /* If core 0 is being turned on, turn on core 1 as well */ |
243 | if (core_id == SKL_DSP_CORE0_ID) | |
244 | ret = skl_dsp_core_power_up(ctx, core_mask | | |
245 | SKL_DSP_CORE_MASK(1)); | |
246 | else | |
247 | ret = skl_dsp_core_power_up(ctx, core_mask); | |
248 | ||
249 | if (ret < 0) | |
250 | goto err; | |
251 | ||
252 | if (core_id == SKL_DSP_CORE0_ID) { | |
253 | ||
254 | /* | |
255 | * Enable interrupt after SPA is set and before | |
256 | * DSP is unstalled | |
257 | */ | |
258 | skl_ipc_int_enable(ctx); | |
259 | skl_ipc_op_int_enable(ctx); | |
260 | skl->boot_complete = false; | |
92eb4f62 | 261 | } |
92eb4f62 | 262 | |
e68aca08 J |
263 | ret = skl_dsp_start_core(ctx, core_mask); |
264 | if (ret < 0) | |
265 | goto err; | |
92eb4f62 | 266 | |
e68aca08 J |
267 | if (core_id == SKL_DSP_CORE0_ID) { |
268 | ret = wait_event_timeout(skl->boot_wait, | |
269 | skl->boot_complete, | |
270 | msecs_to_jiffies(SKL_IPC_BOOT_MSECS)); | |
271 | ||
272 | /* If core 1 was turned on for booting core 0, turn it off */ | |
273 | skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1)); | |
274 | if (ret == 0) { | |
275 | dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__); | |
276 | dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n", | |
277 | sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE), | |
278 | sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS)); | |
279 | dev_err(ctx->dev, "Failed to set core0 to D0 state\n"); | |
280 | ret = -EIO; | |
281 | goto err; | |
282 | } | |
92eb4f62 JK |
283 | } |
284 | ||
e68aca08 J |
285 | /* Tell FW if additional core in now On */ |
286 | ||
287 | if (core_id != SKL_DSP_CORE0_ID) { | |
288 | dx.core_mask = core_mask; | |
289 | dx.dx_mask = core_mask; | |
290 | ||
291 | ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID, | |
292 | BXT_BASE_FW_MODULE_ID, &dx); | |
293 | if (ret < 0) { | |
294 | dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n", | |
295 | core_id, ret); | |
296 | goto err; | |
297 | } | |
92eb4f62 JK |
298 | } |
299 | ||
e68aca08 | 300 | skl->cores.state[core_id] = SKL_DSP_RUNNING; |
92eb4f62 | 301 | return 0; |
e68aca08 J |
302 | err: |
303 | if (core_id == SKL_DSP_CORE0_ID) | |
304 | core_mask |= SKL_DSP_CORE_MASK(1); | |
305 | skl_dsp_disable_core(ctx, core_mask); | |
306 | ||
307 | return ret; | |
92eb4f62 JK |
308 | } |
309 | ||
052f103c | 310 | static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id) |
92eb4f62 | 311 | { |
e68aca08 | 312 | int ret; |
92eb4f62 JK |
313 | struct skl_ipc_dxstate_info dx; |
314 | struct skl_sst *skl = ctx->thread_context; | |
e68aca08 | 315 | unsigned int core_mask = SKL_DSP_CORE_MASK(core_id); |
92eb4f62 | 316 | |
e68aca08 | 317 | dx.core_mask = core_mask; |
92eb4f62 JK |
318 | dx.dx_mask = SKL_IPC_D3_MASK; |
319 | ||
e68aca08 J |
320 | dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n", |
321 | dx.core_mask, dx.dx_mask); | |
92eb4f62 | 322 | |
e68aca08 J |
323 | ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID, |
324 | BXT_BASE_FW_MODULE_ID, &dx); | |
325 | if (ret < 0) | |
326 | dev_err(ctx->dev, | |
327 | "Failed to set DSP to D3:core id = %d;Continue reset\n", | |
328 | core_id); | |
92eb4f62 | 329 | |
e68aca08 | 330 | ret = skl_dsp_disable_core(ctx, core_mask); |
92eb4f62 | 331 | if (ret < 0) { |
e68aca08 J |
332 | dev_err(ctx->dev, "Failed to disable core %d", ret); |
333 | return ret; | |
92eb4f62 | 334 | } |
e68aca08 | 335 | skl->cores.state[core_id] = SKL_DSP_RESET; |
92eb4f62 JK |
336 | return 0; |
337 | } | |
338 | ||
339 | static struct skl_dsp_fw_ops bxt_fw_ops = { | |
340 | .set_state_D0 = bxt_set_dsp_D0, | |
341 | .set_state_D3 = bxt_set_dsp_D3, | |
342 | .load_fw = bxt_load_base_firmware, | |
343 | .get_fw_errcode = bxt_get_errorcode, | |
344 | }; | |
345 | ||
346 | static struct sst_ops skl_ops = { | |
347 | .irq_handler = skl_dsp_sst_interrupt, | |
348 | .write = sst_shim32_write, | |
349 | .read = sst_shim32_read, | |
350 | .ram_read = sst_memcpy_fromio_32, | |
351 | .ram_write = sst_memcpy_toio_32, | |
352 | .free = skl_dsp_free, | |
353 | }; | |
354 | ||
355 | static struct sst_dsp_device skl_dev = { | |
356 | .thread = skl_dsp_irq_thread_handler, | |
357 | .ops = &skl_ops, | |
358 | }; | |
359 | ||
360 | int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq, | |
361 | const char *fw_name, struct skl_dsp_loader_ops dsp_ops, | |
362 | struct skl_sst **dsp) | |
363 | { | |
364 | struct skl_sst *skl; | |
365 | struct sst_dsp *sst; | |
366 | int ret; | |
367 | ||
368 | skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL); | |
369 | if (skl == NULL) | |
370 | return -ENOMEM; | |
371 | ||
372 | skl->dev = dev; | |
373 | skl_dev.thread_context = skl; | |
3467a64d | 374 | INIT_LIST_HEAD(&skl->uuid_list); |
92eb4f62 JK |
375 | |
376 | skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq); | |
377 | if (!skl->dsp) { | |
378 | dev_err(skl->dev, "skl_dsp_ctx_init failed\n"); | |
379 | return -ENODEV; | |
380 | } | |
381 | ||
382 | sst = skl->dsp; | |
383 | sst->fw_name = fw_name; | |
384 | sst->dsp_ops = dsp_ops; | |
385 | sst->fw_ops = bxt_fw_ops; | |
386 | sst->addr.lpe = mmio_base; | |
387 | sst->addr.shim = mmio_base; | |
388 | ||
389 | sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ), | |
390 | SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ); | |
391 | ||
b914bb55 | 392 | INIT_LIST_HEAD(&sst->module_list); |
92eb4f62 JK |
393 | ret = skl_ipc_init(dev, skl); |
394 | if (ret) | |
395 | return ret; | |
396 | ||
052f103c | 397 | skl->cores.count = 2; |
92eb4f62 JK |
398 | skl->boot_complete = false; |
399 | init_waitqueue_head(&skl->boot_wait); | |
400 | ||
401 | ret = sst->fw_ops.load_fw(sst); | |
402 | if (ret < 0) { | |
403 | dev_err(dev, "Load base fw failed: %x", ret); | |
404 | return ret; | |
405 | } | |
406 | ||
052f103c J |
407 | skl_dsp_init_core_state(sst); |
408 | ||
92eb4f62 JK |
409 | if (dsp) |
410 | *dsp = skl; | |
411 | ||
412 | return 0; | |
413 | } | |
414 | EXPORT_SYMBOL_GPL(bxt_sst_dsp_init); | |
415 | ||
416 | ||
417 | void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx) | |
418 | { | |
3467a64d | 419 | skl_freeup_uuid_list(ctx); |
92eb4f62 JK |
420 | skl_ipc_free(&ctx->ipc); |
421 | ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp); | |
422 | ||
423 | if (ctx->dsp->addr.lpe) | |
424 | iounmap(ctx->dsp->addr.lpe); | |
425 | ||
426 | ctx->dsp->ops->free(ctx->dsp); | |
427 | } | |
428 | EXPORT_SYMBOL_GPL(bxt_sst_dsp_cleanup); | |
429 | ||
430 | MODULE_LICENSE("GPL v2"); | |
431 | MODULE_DESCRIPTION("Intel Broxton IPC driver"); |