xref: /openbmc/linux/sound/soc/intel/catpt/loader.c (revision bef7a78d)
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 (!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