1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright(c) 2020 Intel Corporation. All rights reserved.
4 //
5 // Author: Cezary Rojewski <cezary.rojewski@intel.com>
6 //
7
8 #include <linux/dma-mapping.h>
9 #include <linux/firmware.h>
10 #include <linux/slab.h>
11 #include "core.h"
12 #include "registers.h"
13
14 /* FW load (200ms) plus operational delays */
15 #define FW_READY_TIMEOUT_MS 250
16
17 #define FW_SIGNATURE "$SST"
18 #define FW_SIGNATURE_SIZE 4
19
20 struct catpt_fw_hdr {
21 char signature[FW_SIGNATURE_SIZE];
22 u32 file_size;
23 u32 modules;
24 u32 file_format;
25 u32 reserved[4];
26 } __packed;
27
28 struct catpt_fw_mod_hdr {
29 char signature[FW_SIGNATURE_SIZE];
30 u32 mod_size;
31 u32 blocks;
32 u16 slot;
33 u16 module_id;
34 u32 entry_point;
35 u32 persistent_size;
36 u32 scratch_size;
37 } __packed;
38
39 enum catpt_ram_type {
40 CATPT_RAM_TYPE_IRAM = 1,
41 CATPT_RAM_TYPE_DRAM = 2,
42 /* DRAM with module's initial state */
43 CATPT_RAM_TYPE_INSTANCE = 3,
44 };
45
46 struct catpt_fw_block_hdr {
47 u32 ram_type;
48 u32 size;
49 u32 ram_offset;
50 u32 rsvd;
51 } __packed;
52
catpt_sram_init(struct resource * sram,u32 start,u32 size)53 void catpt_sram_init(struct resource *sram, u32 start, u32 size)
54 {
55 sram->start = start;
56 sram->end = start + size - 1;
57 }
58
catpt_sram_free(struct resource * sram)59 void catpt_sram_free(struct resource *sram)
60 {
61 struct resource *res, *save;
62
63 for (res = sram->child; res;) {
64 save = res->sibling;
65 release_resource(res);
66 kfree(res);
67 res = save;
68 }
69 }
70
71 struct resource *
catpt_request_region(struct resource * root,resource_size_t size)72 catpt_request_region(struct resource *root, resource_size_t size)
73 {
74 struct resource *res = root->child;
75 resource_size_t addr = root->start;
76
77 for (;;) {
78 if (res->start - addr >= size)
79 break;
80 addr = res->end + 1;
81 res = res->sibling;
82 if (!res)
83 return NULL;
84 }
85
86 return __request_region(root, addr, size, NULL, 0);
87 }
88
catpt_store_streams_context(struct catpt_dev * cdev,struct dma_chan * chan)89 int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
90 {
91 struct catpt_stream_runtime *stream;
92
93 list_for_each_entry(stream, &cdev->stream_list, node) {
94 u32 off, size;
95 int ret;
96
97 off = stream->persistent->start;
98 size = resource_size(stream->persistent);
99 dev_dbg(cdev->dev, "storing stream %d ctx: off 0x%08x size %d\n",
100 stream->info.stream_hw_id, off, size);
101
102 ret = catpt_dma_memcpy_fromdsp(cdev, chan,
103 cdev->dxbuf_paddr + off,
104 cdev->lpe_base + off,
105 ALIGN(size, 4));
106 if (ret) {
107 dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
108 return ret;
109 }
110 }
111
112 return 0;
113 }
114
catpt_store_module_states(struct catpt_dev * cdev,struct dma_chan * chan)115 int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan)
116 {
117 int i;
118
119 for (i = 0; i < ARRAY_SIZE(cdev->modules); i++) {
120 struct catpt_module_type *type;
121 u32 off;
122 int ret;
123
124 type = &cdev->modules[i];
125 if (!type->loaded || !type->state_size)
126 continue;
127
128 off = type->state_offset;
129 dev_dbg(cdev->dev, "storing mod %d state: off 0x%08x size %d\n",
130 i, off, type->state_size);
131
132 ret = catpt_dma_memcpy_fromdsp(cdev, chan,
133 cdev->dxbuf_paddr + off,
134 cdev->lpe_base + off,
135 ALIGN(type->state_size, 4));
136 if (ret) {
137 dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
138 return ret;
139 }
140 }
141
142 return 0;
143 }
144
catpt_store_memdumps(struct catpt_dev * cdev,struct dma_chan * chan)145 int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
146 {
147 int i;
148
149 for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
150 struct catpt_save_meminfo *info;
151 u32 off;
152 int ret;
153
154 info = &cdev->dx_ctx.meminfo[i];
155 if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
156 continue;
157
158 off = catpt_to_host_offset(info->offset);
159 if (off < cdev->dram.start || off > cdev->dram.end)
160 continue;
161
162 dev_dbg(cdev->dev, "storing memdump: off 0x%08x size %d\n",
163 off, info->size);
164
165 ret = catpt_dma_memcpy_fromdsp(cdev, chan,
166 cdev->dxbuf_paddr + off,
167 cdev->lpe_base + off,
168 ALIGN(info->size, 4));
169 if (ret) {
170 dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
171 return ret;
172 }
173 }
174
175 return 0;
176 }
177
178 static int
catpt_restore_streams_context(struct catpt_dev * cdev,struct dma_chan * chan)179 catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
180 {
181 struct catpt_stream_runtime *stream;
182
183 list_for_each_entry(stream, &cdev->stream_list, node) {
184 u32 off, size;
185 int ret;
186
187 off = stream->persistent->start;
188 size = resource_size(stream->persistent);
189 dev_dbg(cdev->dev, "restoring stream %d ctx: off 0x%08x size %d\n",
190 stream->info.stream_hw_id, off, size);
191
192 ret = catpt_dma_memcpy_todsp(cdev, chan,
193 cdev->lpe_base + off,
194 cdev->dxbuf_paddr + off,
195 ALIGN(size, 4));
196 if (ret) {
197 dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
198 return ret;
199 }
200 }
201
202 return 0;
203 }
204
catpt_restore_memdumps(struct catpt_dev * cdev,struct dma_chan * chan)205 static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
206 {
207 int i;
208
209 for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
210 struct catpt_save_meminfo *info;
211 u32 off;
212 int ret;
213
214 info = &cdev->dx_ctx.meminfo[i];
215 if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
216 continue;
217
218 off = catpt_to_host_offset(info->offset);
219 if (off < cdev->dram.start || off > cdev->dram.end)
220 continue;
221
222 dev_dbg(cdev->dev, "restoring memdump: off 0x%08x size %d\n",
223 off, info->size);
224
225 ret = catpt_dma_memcpy_todsp(cdev, chan,
226 cdev->lpe_base + off,
227 cdev->dxbuf_paddr + off,
228 ALIGN(info->size, 4));
229 if (ret) {
230 dev_err(cdev->dev, "restore block failed: %d\n", ret);
231 return ret;
232 }
233 }
234
235 return 0;
236 }
237
catpt_restore_fwimage(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_block_hdr * blk)238 static int catpt_restore_fwimage(struct catpt_dev *cdev,
239 struct dma_chan *chan, dma_addr_t paddr,
240 struct catpt_fw_block_hdr *blk)
241 {
242 struct resource r1, r2, common;
243 int i;
244
245 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
246 blk, sizeof(*blk), false);
247
248 r1.start = cdev->dram.start + blk->ram_offset;
249 r1.end = r1.start + blk->size - 1;
250 /* advance to data area */
251 paddr += sizeof(*blk);
252
253 for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
254 struct catpt_save_meminfo *info;
255 u32 off;
256 int ret;
257
258 info = &cdev->dx_ctx.meminfo[i];
259
260 if (info->source != CATPT_DX_TYPE_FW_IMAGE)
261 continue;
262
263 off = catpt_to_host_offset(info->offset);
264 if (off < cdev->dram.start || off > cdev->dram.end)
265 continue;
266
267 r2.start = off;
268 r2.end = r2.start + info->size - 1;
269
270 if (!catpt_resource_overlapping(&r2, &r1, &common))
271 continue;
272 /* calculate start offset of common data area */
273 off = common.start - r1.start;
274
275 dev_dbg(cdev->dev, "restoring fwimage: %pr\n", &common);
276
277 ret = catpt_dma_memcpy_todsp(cdev, chan, common.start,
278 paddr + off,
279 resource_size(&common));
280 if (ret) {
281 dev_err(cdev->dev, "memcpy todsp failed: %d\n", ret);
282 return ret;
283 }
284 }
285
286 return 0;
287 }
288
catpt_load_block(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_block_hdr * blk,bool alloc)289 static int catpt_load_block(struct catpt_dev *cdev,
290 struct dma_chan *chan, dma_addr_t paddr,
291 struct catpt_fw_block_hdr *blk, bool alloc)
292 {
293 struct resource *sram, *res;
294 dma_addr_t dst_addr;
295 int ret;
296
297 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
298 blk, sizeof(*blk), false);
299
300 switch (blk->ram_type) {
301 case CATPT_RAM_TYPE_IRAM:
302 sram = &cdev->iram;
303 break;
304 default:
305 sram = &cdev->dram;
306 break;
307 };
308
309 dst_addr = sram->start + blk->ram_offset;
310 if (alloc) {
311 res = __request_region(sram, dst_addr, blk->size, NULL, 0);
312 if (!res)
313 return -EBUSY;
314 }
315
316 /* advance to data area */
317 paddr += sizeof(*blk);
318
319 ret = catpt_dma_memcpy_todsp(cdev, chan, dst_addr, paddr, blk->size);
320 if (ret) {
321 dev_err(cdev->dev, "memcpy error: %d\n", ret);
322 __release_region(sram, dst_addr, blk->size);
323 }
324
325 return ret;
326 }
327
catpt_restore_basefw(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_mod_hdr * basefw)328 static int catpt_restore_basefw(struct catpt_dev *cdev,
329 struct dma_chan *chan, dma_addr_t paddr,
330 struct catpt_fw_mod_hdr *basefw)
331 {
332 u32 offset = sizeof(*basefw);
333 int ret, i;
334
335 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
336 basefw, sizeof(*basefw), false);
337
338 /* restore basefw image */
339 for (i = 0; i < basefw->blocks; i++) {
340 struct catpt_fw_block_hdr *blk;
341
342 blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset);
343
344 switch (blk->ram_type) {
345 case CATPT_RAM_TYPE_IRAM:
346 ret = catpt_load_block(cdev, chan, paddr + offset,
347 blk, false);
348 break;
349 default:
350 ret = catpt_restore_fwimage(cdev, chan, paddr + offset,
351 blk);
352 break;
353 }
354
355 if (ret) {
356 dev_err(cdev->dev, "restore block failed: %d\n", ret);
357 return ret;
358 }
359
360 offset += sizeof(*blk) + blk->size;
361 }
362
363 /* then proceed with memory dumps */
364 ret = catpt_restore_memdumps(cdev, chan);
365 if (ret)
366 dev_err(cdev->dev, "restore memdumps failed: %d\n", ret);
367
368 return ret;
369 }
370
catpt_restore_module(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_mod_hdr * mod)371 static int catpt_restore_module(struct catpt_dev *cdev,
372 struct dma_chan *chan, dma_addr_t paddr,
373 struct catpt_fw_mod_hdr *mod)
374 {
375 u32 offset = sizeof(*mod);
376 int i;
377
378 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
379 mod, sizeof(*mod), false);
380
381 for (i = 0; i < mod->blocks; i++) {
382 struct catpt_fw_block_hdr *blk;
383 int ret;
384
385 blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
386
387 switch (blk->ram_type) {
388 case CATPT_RAM_TYPE_INSTANCE:
389 /* restore module state */
390 ret = catpt_dma_memcpy_todsp(cdev, chan,
391 cdev->lpe_base + blk->ram_offset,
392 cdev->dxbuf_paddr + blk->ram_offset,
393 ALIGN(blk->size, 4));
394 break;
395 default:
396 ret = catpt_load_block(cdev, chan, paddr + offset,
397 blk, false);
398 break;
399 }
400
401 if (ret) {
402 dev_err(cdev->dev, "restore block failed: %d\n", ret);
403 return ret;
404 }
405
406 offset += sizeof(*blk) + blk->size;
407 }
408
409 return 0;
410 }
411
catpt_load_module(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_mod_hdr * mod)412 static int catpt_load_module(struct catpt_dev *cdev,
413 struct dma_chan *chan, dma_addr_t paddr,
414 struct catpt_fw_mod_hdr *mod)
415 {
416 struct catpt_module_type *type;
417 u32 offset = sizeof(*mod);
418 int i;
419
420 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
421 mod, sizeof(*mod), false);
422
423 type = &cdev->modules[mod->module_id];
424
425 for (i = 0; i < mod->blocks; i++) {
426 struct catpt_fw_block_hdr *blk;
427 int ret;
428
429 blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
430
431 ret = catpt_load_block(cdev, chan, paddr + offset, blk, true);
432 if (ret) {
433 dev_err(cdev->dev, "load block failed: %d\n", ret);
434 return ret;
435 }
436
437 /*
438 * Save state window coordinates - these will be
439 * used to capture module state on D0 exit.
440 */
441 if (blk->ram_type == CATPT_RAM_TYPE_INSTANCE) {
442 type->state_offset = blk->ram_offset;
443 type->state_size = blk->size;
444 }
445
446 offset += sizeof(*blk) + blk->size;
447 }
448
449 /* init module type static info */
450 type->loaded = true;
451 /* DSP expects address from module header substracted by 4 */
452 type->entry_point = mod->entry_point - 4;
453 type->persistent_size = mod->persistent_size;
454 type->scratch_size = mod->scratch_size;
455
456 return 0;
457 }
458
catpt_restore_firmware(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_hdr * fw)459 static int catpt_restore_firmware(struct catpt_dev *cdev,
460 struct dma_chan *chan, dma_addr_t paddr,
461 struct catpt_fw_hdr *fw)
462 {
463 u32 offset = sizeof(*fw);
464 int i;
465
466 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
467 fw, sizeof(*fw), false);
468
469 for (i = 0; i < fw->modules; i++) {
470 struct catpt_fw_mod_hdr *mod;
471 int ret;
472
473 mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
474 if (strncmp(fw->signature, mod->signature,
475 FW_SIGNATURE_SIZE)) {
476 dev_err(cdev->dev, "module signature mismatch\n");
477 return -EINVAL;
478 }
479
480 if (mod->module_id > CATPT_MODID_LAST)
481 return -EINVAL;
482
483 switch (mod->module_id) {
484 case CATPT_MODID_BASE_FW:
485 ret = catpt_restore_basefw(cdev, chan, paddr + offset,
486 mod);
487 break;
488 default:
489 ret = catpt_restore_module(cdev, chan, paddr + offset,
490 mod);
491 break;
492 }
493
494 if (ret) {
495 dev_err(cdev->dev, "restore module failed: %d\n", ret);
496 return ret;
497 }
498
499 offset += sizeof(*mod) + mod->mod_size;
500 }
501
502 return 0;
503 }
504
catpt_load_firmware(struct catpt_dev * cdev,struct dma_chan * chan,dma_addr_t paddr,struct catpt_fw_hdr * fw)505 static int catpt_load_firmware(struct catpt_dev *cdev,
506 struct dma_chan *chan, dma_addr_t paddr,
507 struct catpt_fw_hdr *fw)
508 {
509 u32 offset = sizeof(*fw);
510 int i;
511
512 print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
513 fw, sizeof(*fw), false);
514
515 for (i = 0; i < fw->modules; i++) {
516 struct catpt_fw_mod_hdr *mod;
517 int ret;
518
519 mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
520 if (strncmp(fw->signature, mod->signature,
521 FW_SIGNATURE_SIZE)) {
522 dev_err(cdev->dev, "module signature mismatch\n");
523 return -EINVAL;
524 }
525
526 if (mod->module_id > CATPT_MODID_LAST)
527 return -EINVAL;
528
529 ret = catpt_load_module(cdev, chan, paddr + offset, mod);
530 if (ret) {
531 dev_err(cdev->dev, "load module failed: %d\n", ret);
532 return ret;
533 }
534
535 offset += sizeof(*mod) + mod->mod_size;
536 }
537
538 return 0;
539 }
540
catpt_load_image(struct catpt_dev * cdev,struct dma_chan * chan,const char * name,const char * signature,bool restore)541 static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan,
542 const char *name, const char *signature,
543 bool restore)
544 {
545 struct catpt_fw_hdr *fw;
546 struct firmware *img;
547 dma_addr_t paddr;
548 void *vaddr;
549 int ret;
550
551 ret = request_firmware((const struct firmware **)&img, name, cdev->dev);
552 if (ret)
553 return ret;
554
555 fw = (struct catpt_fw_hdr *)img->data;
556 if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) {
557 dev_err(cdev->dev, "firmware signature mismatch\n");
558 ret = -EINVAL;
559 goto release_fw;
560 }
561
562 vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL);
563 if (!vaddr) {
564 ret = -ENOMEM;
565 goto release_fw;
566 }
567
568 memcpy(vaddr, img->data, img->size);
569 fw = (struct catpt_fw_hdr *)vaddr;
570 if (restore)
571 ret = catpt_restore_firmware(cdev, chan, paddr, fw);
572 else
573 ret = catpt_load_firmware(cdev, chan, paddr, fw);
574
575 dma_free_coherent(cdev->dev, img->size, vaddr, paddr);
576 release_fw:
577 release_firmware(img);
578 return ret;
579 }
580
catpt_load_images(struct catpt_dev * cdev,bool restore)581 static int catpt_load_images(struct catpt_dev *cdev, bool restore)
582 {
583 static const char *const names[] = {
584 "intel/IntcSST1.bin",
585 "intel/IntcSST2.bin",
586 };
587 struct dma_chan *chan;
588 int ret;
589
590 chan = catpt_dma_request_config_chan(cdev);
591 if (IS_ERR(chan))
592 return PTR_ERR(chan);
593
594 ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1],
595 FW_SIGNATURE, restore);
596 if (ret)
597 goto release_dma_chan;
598
599 if (!restore)
600 goto release_dma_chan;
601 ret = catpt_restore_streams_context(cdev, chan);
602 if (ret)
603 dev_err(cdev->dev, "restore streams ctx failed: %d\n", ret);
604 release_dma_chan:
605 dma_release_channel(chan);
606 return ret;
607 }
608
catpt_boot_firmware(struct catpt_dev * cdev,bool restore)609 int catpt_boot_firmware(struct catpt_dev *cdev, bool restore)
610 {
611 int ret;
612
613 catpt_dsp_stall(cdev, true);
614
615 ret = catpt_load_images(cdev, restore);
616 if (ret) {
617 dev_err(cdev->dev, "load binaries failed: %d\n", ret);
618 return ret;
619 }
620
621 reinit_completion(&cdev->fw_ready);
622 catpt_dsp_stall(cdev, false);
623
624 ret = wait_for_completion_timeout(&cdev->fw_ready,
625 msecs_to_jiffies(FW_READY_TIMEOUT_MS));
626 if (!ret) {
627 dev_err(cdev->dev, "firmware ready timeout\n");
628 return -ETIMEDOUT;
629 }
630
631 /* update sram pg & clock once done booting */
632 catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
633 catpt_dsp_update_srampge(cdev, &cdev->iram, cdev->spec->iram_mask);
634
635 return catpt_dsp_update_lpclock(cdev);
636 }
637
catpt_first_boot_firmware(struct catpt_dev * cdev)638 int catpt_first_boot_firmware(struct catpt_dev *cdev)
639 {
640 struct resource *res;
641 int ret;
642
643 ret = catpt_boot_firmware(cdev, false);
644 if (ret) {
645 dev_err(cdev->dev, "basefw boot failed: %d\n", ret);
646 return ret;
647 }
648
649 /* restrict FW Core dump area */
650 __request_region(&cdev->dram, 0, 0x200, NULL, 0);
651 /* restrict entire area following BASE_FW - highest offset in DRAM */
652 for (res = cdev->dram.child; res->sibling; res = res->sibling)
653 ;
654 __request_region(&cdev->dram, res->end + 1,
655 cdev->dram.end - res->end, NULL, 0);
656
657 ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
658 if (ret)
659 return CATPT_IPC_ERROR(ret);
660
661 ret = catpt_arm_stream_templates(cdev);
662 if (ret) {
663 dev_err(cdev->dev, "arm templates failed: %d\n", ret);
664 return ret;
665 }
666
667 /* update dram pg for scratch and restricted regions */
668 catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
669
670 return 0;
671 }
672