GNU Linux-libre 5.10.76-gnu1
[releases.git] / sound / soc / intel / catpt / loader.c
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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 = reject_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
581 static int catpt_load_images(struct catpt_dev *cdev, bool restore)
582 {
583         static const char *const names[] = {
584                 "/*(DEBLOBBED)*/",
585                 "/*(DEBLOBBED)*/",
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
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
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 }