Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/intel/catpt/loader.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
//
3
// Copyright(c) 2020 Intel Corporation
4
//
5
// Author: Cezary Rojewski <[email protected]>
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 (!resource_intersection(&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 = 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
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
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
}
672
673