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
20struct 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
28struct 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
39enum 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
46struct catpt_fw_block_hdr {
47	u32 ram_type;
48	u32 size;
49	u32 ram_offset;
50	u32 rsvd;
51} __packed;
52
53void catpt_sram_init(struct resource *sram, u32 start, u32 size)
54{
55	sram->start = start;
56	sram->end = start + size - 1;
57}
58
59void 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
71struct resource *
72catpt_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
89int 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
115int 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
145int 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
178static int
179catpt_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
205static 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
238static 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
289static 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
328static 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
371static 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
412static 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
459static 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
505static 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
541static 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);
576release_fw:
577	release_firmware(img);
578	return ret;
579}
580
581static 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);
604release_dma_chan:
605	dma_release_channel(chan);
606	return ret;
607}
608
609int 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
638int 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