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