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