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