18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright(c) 2020 Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Author: Cezary Rojewski <cezary.rojewski@intel.com>
68c2ecf20Sopenharmony_ci//
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
98c2ecf20Sopenharmony_ci#include <linux/firmware.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include "core.h"
128c2ecf20Sopenharmony_ci#include "registers.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/* FW load (200ms) plus operational delays */
158c2ecf20Sopenharmony_ci#define FW_READY_TIMEOUT_MS	250
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define FW_SIGNATURE		"$SST"
188c2ecf20Sopenharmony_ci#define FW_SIGNATURE_SIZE	4
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct catpt_fw_hdr {
218c2ecf20Sopenharmony_ci	char signature[FW_SIGNATURE_SIZE];
228c2ecf20Sopenharmony_ci	u32 file_size;
238c2ecf20Sopenharmony_ci	u32 modules;
248c2ecf20Sopenharmony_ci	u32 file_format;
258c2ecf20Sopenharmony_ci	u32 reserved[4];
268c2ecf20Sopenharmony_ci} __packed;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct catpt_fw_mod_hdr {
298c2ecf20Sopenharmony_ci	char signature[FW_SIGNATURE_SIZE];
308c2ecf20Sopenharmony_ci	u32 mod_size;
318c2ecf20Sopenharmony_ci	u32 blocks;
328c2ecf20Sopenharmony_ci	u16 slot;
338c2ecf20Sopenharmony_ci	u16 module_id;
348c2ecf20Sopenharmony_ci	u32 entry_point;
358c2ecf20Sopenharmony_ci	u32 persistent_size;
368c2ecf20Sopenharmony_ci	u32 scratch_size;
378c2ecf20Sopenharmony_ci} __packed;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cienum catpt_ram_type {
408c2ecf20Sopenharmony_ci	CATPT_RAM_TYPE_IRAM = 1,
418c2ecf20Sopenharmony_ci	CATPT_RAM_TYPE_DRAM = 2,
428c2ecf20Sopenharmony_ci	/* DRAM with module's initial state */
438c2ecf20Sopenharmony_ci	CATPT_RAM_TYPE_INSTANCE = 3,
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct catpt_fw_block_hdr {
478c2ecf20Sopenharmony_ci	u32 ram_type;
488c2ecf20Sopenharmony_ci	u32 size;
498c2ecf20Sopenharmony_ci	u32 ram_offset;
508c2ecf20Sopenharmony_ci	u32 rsvd;
518c2ecf20Sopenharmony_ci} __packed;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_civoid catpt_sram_init(struct resource *sram, u32 start, u32 size)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	sram->start = start;
568c2ecf20Sopenharmony_ci	sram->end = start + size - 1;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_civoid catpt_sram_free(struct resource *sram)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct resource *res, *save;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	for (res = sram->child; res;) {
648c2ecf20Sopenharmony_ci		save = res->sibling;
658c2ecf20Sopenharmony_ci		release_resource(res);
668c2ecf20Sopenharmony_ci		kfree(res);
678c2ecf20Sopenharmony_ci		res = save;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistruct resource *
728c2ecf20Sopenharmony_cicatpt_request_region(struct resource *root, resource_size_t size)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct resource *res = root->child;
758c2ecf20Sopenharmony_ci	resource_size_t addr = root->start;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	for (;;) {
788c2ecf20Sopenharmony_ci		if (res->start - addr >= size)
798c2ecf20Sopenharmony_ci			break;
808c2ecf20Sopenharmony_ci		addr = res->end + 1;
818c2ecf20Sopenharmony_ci		res = res->sibling;
828c2ecf20Sopenharmony_ci		if (!res)
838c2ecf20Sopenharmony_ci			return NULL;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return __request_region(root, addr, size, NULL, 0);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciint catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct catpt_stream_runtime *stream;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &cdev->stream_list, node) {
948c2ecf20Sopenharmony_ci		u32 off, size;
958c2ecf20Sopenharmony_ci		int ret;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		off = stream->persistent->start;
988c2ecf20Sopenharmony_ci		size = resource_size(stream->persistent);
998c2ecf20Sopenharmony_ci		dev_dbg(cdev->dev, "storing stream %d ctx: off 0x%08x size %d\n",
1008c2ecf20Sopenharmony_ci			stream->info.stream_hw_id, off, size);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		ret = catpt_dma_memcpy_fromdsp(cdev, chan,
1038c2ecf20Sopenharmony_ci					       cdev->dxbuf_paddr + off,
1048c2ecf20Sopenharmony_ci					       cdev->lpe_base + off,
1058c2ecf20Sopenharmony_ci					       ALIGN(size, 4));
1068c2ecf20Sopenharmony_ci		if (ret) {
1078c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
1088c2ecf20Sopenharmony_ci			return ret;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int i;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cdev->modules); i++) {
1208c2ecf20Sopenharmony_ci		struct catpt_module_type *type;
1218c2ecf20Sopenharmony_ci		u32 off;
1228c2ecf20Sopenharmony_ci		int ret;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		type = &cdev->modules[i];
1258c2ecf20Sopenharmony_ci		if (!type->loaded || !type->state_size)
1268c2ecf20Sopenharmony_ci			continue;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		off = type->state_offset;
1298c2ecf20Sopenharmony_ci		dev_dbg(cdev->dev, "storing mod %d state: off 0x%08x size %d\n",
1308c2ecf20Sopenharmony_ci			i, off, type->state_size);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		ret = catpt_dma_memcpy_fromdsp(cdev, chan,
1338c2ecf20Sopenharmony_ci					       cdev->dxbuf_paddr + off,
1348c2ecf20Sopenharmony_ci					       cdev->lpe_base + off,
1358c2ecf20Sopenharmony_ci					       ALIGN(type->state_size, 4));
1368c2ecf20Sopenharmony_ci		if (ret) {
1378c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
1388c2ecf20Sopenharmony_ci			return ret;
1398c2ecf20Sopenharmony_ci		}
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciint catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int i;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
1508c2ecf20Sopenharmony_ci		struct catpt_save_meminfo *info;
1518c2ecf20Sopenharmony_ci		u32 off;
1528c2ecf20Sopenharmony_ci		int ret;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		info = &cdev->dx_ctx.meminfo[i];
1558c2ecf20Sopenharmony_ci		if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
1568c2ecf20Sopenharmony_ci			continue;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		off = catpt_to_host_offset(info->offset);
1598c2ecf20Sopenharmony_ci		if (off < cdev->dram.start || off > cdev->dram.end)
1608c2ecf20Sopenharmony_ci			continue;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci		dev_dbg(cdev->dev, "storing memdump: off 0x%08x size %d\n",
1638c2ecf20Sopenharmony_ci			off, info->size);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		ret = catpt_dma_memcpy_fromdsp(cdev, chan,
1668c2ecf20Sopenharmony_ci					       cdev->dxbuf_paddr + off,
1678c2ecf20Sopenharmony_ci					       cdev->lpe_base + off,
1688c2ecf20Sopenharmony_ci					       ALIGN(info->size, 4));
1698c2ecf20Sopenharmony_ci		if (ret) {
1708c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
1718c2ecf20Sopenharmony_ci			return ret;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic int
1798c2ecf20Sopenharmony_cicatpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct catpt_stream_runtime *stream;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &cdev->stream_list, node) {
1848c2ecf20Sopenharmony_ci		u32 off, size;
1858c2ecf20Sopenharmony_ci		int ret;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		off = stream->persistent->start;
1888c2ecf20Sopenharmony_ci		size = resource_size(stream->persistent);
1898c2ecf20Sopenharmony_ci		dev_dbg(cdev->dev, "restoring stream %d ctx: off 0x%08x size %d\n",
1908c2ecf20Sopenharmony_ci			stream->info.stream_hw_id, off, size);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		ret = catpt_dma_memcpy_todsp(cdev, chan,
1938c2ecf20Sopenharmony_ci					     cdev->lpe_base + off,
1948c2ecf20Sopenharmony_ci					     cdev->dxbuf_paddr + off,
1958c2ecf20Sopenharmony_ci					     ALIGN(size, 4));
1968c2ecf20Sopenharmony_ci		if (ret) {
1978c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret);
1988c2ecf20Sopenharmony_ci			return ret;
1998c2ecf20Sopenharmony_ci		}
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	int i;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
2108c2ecf20Sopenharmony_ci		struct catpt_save_meminfo *info;
2118c2ecf20Sopenharmony_ci		u32 off;
2128c2ecf20Sopenharmony_ci		int ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		info = &cdev->dx_ctx.meminfo[i];
2158c2ecf20Sopenharmony_ci		if (info->source != CATPT_DX_TYPE_MEMORY_DUMP)
2168c2ecf20Sopenharmony_ci			continue;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		off = catpt_to_host_offset(info->offset);
2198c2ecf20Sopenharmony_ci		if (off < cdev->dram.start || off > cdev->dram.end)
2208c2ecf20Sopenharmony_ci			continue;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		dev_dbg(cdev->dev, "restoring memdump: off 0x%08x size %d\n",
2238c2ecf20Sopenharmony_ci			off, info->size);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		ret = catpt_dma_memcpy_todsp(cdev, chan,
2268c2ecf20Sopenharmony_ci					     cdev->lpe_base + off,
2278c2ecf20Sopenharmony_ci					     cdev->dxbuf_paddr + off,
2288c2ecf20Sopenharmony_ci					     ALIGN(info->size, 4));
2298c2ecf20Sopenharmony_ci		if (ret) {
2308c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "restore block failed: %d\n", ret);
2318c2ecf20Sopenharmony_ci			return ret;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci	}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic int catpt_restore_fwimage(struct catpt_dev *cdev,
2398c2ecf20Sopenharmony_ci				 struct dma_chan *chan, dma_addr_t paddr,
2408c2ecf20Sopenharmony_ci				 struct catpt_fw_block_hdr *blk)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct resource r1, r2, common;
2438c2ecf20Sopenharmony_ci	int i;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
2468c2ecf20Sopenharmony_ci			     blk, sizeof(*blk), false);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	r1.start = cdev->dram.start + blk->ram_offset;
2498c2ecf20Sopenharmony_ci	r1.end = r1.start + blk->size - 1;
2508c2ecf20Sopenharmony_ci	/* advance to data area */
2518c2ecf20Sopenharmony_ci	paddr += sizeof(*blk);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) {
2548c2ecf20Sopenharmony_ci		struct catpt_save_meminfo *info;
2558c2ecf20Sopenharmony_ci		u32 off;
2568c2ecf20Sopenharmony_ci		int ret;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		info = &cdev->dx_ctx.meminfo[i];
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		if (info->source != CATPT_DX_TYPE_FW_IMAGE)
2618c2ecf20Sopenharmony_ci			continue;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci		off = catpt_to_host_offset(info->offset);
2648c2ecf20Sopenharmony_ci		if (off < cdev->dram.start || off > cdev->dram.end)
2658c2ecf20Sopenharmony_ci			continue;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		r2.start = off;
2688c2ecf20Sopenharmony_ci		r2.end = r2.start + info->size - 1;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci		if (!catpt_resource_overlapping(&r2, &r1, &common))
2718c2ecf20Sopenharmony_ci			continue;
2728c2ecf20Sopenharmony_ci		/* calculate start offset of common data area */
2738c2ecf20Sopenharmony_ci		off = common.start - r1.start;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		dev_dbg(cdev->dev, "restoring fwimage: %pr\n", &common);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		ret = catpt_dma_memcpy_todsp(cdev, chan, common.start,
2788c2ecf20Sopenharmony_ci					     paddr + off,
2798c2ecf20Sopenharmony_ci					     resource_size(&common));
2808c2ecf20Sopenharmony_ci		if (ret) {
2818c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "memcpy todsp failed: %d\n", ret);
2828c2ecf20Sopenharmony_ci			return ret;
2838c2ecf20Sopenharmony_ci		}
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int catpt_load_block(struct catpt_dev *cdev,
2908c2ecf20Sopenharmony_ci			    struct dma_chan *chan, dma_addr_t paddr,
2918c2ecf20Sopenharmony_ci			    struct catpt_fw_block_hdr *blk, bool alloc)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct resource *sram, *res;
2948c2ecf20Sopenharmony_ci	dma_addr_t dst_addr;
2958c2ecf20Sopenharmony_ci	int ret;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
2988c2ecf20Sopenharmony_ci			     blk, sizeof(*blk), false);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	switch (blk->ram_type) {
3018c2ecf20Sopenharmony_ci	case CATPT_RAM_TYPE_IRAM:
3028c2ecf20Sopenharmony_ci		sram = &cdev->iram;
3038c2ecf20Sopenharmony_ci		break;
3048c2ecf20Sopenharmony_ci	default:
3058c2ecf20Sopenharmony_ci		sram = &cdev->dram;
3068c2ecf20Sopenharmony_ci		break;
3078c2ecf20Sopenharmony_ci	};
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	dst_addr = sram->start + blk->ram_offset;
3108c2ecf20Sopenharmony_ci	if (alloc) {
3118c2ecf20Sopenharmony_ci		res = __request_region(sram, dst_addr, blk->size, NULL, 0);
3128c2ecf20Sopenharmony_ci		if (!res)
3138c2ecf20Sopenharmony_ci			return -EBUSY;
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* advance to data area */
3178c2ecf20Sopenharmony_ci	paddr += sizeof(*blk);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ret = catpt_dma_memcpy_todsp(cdev, chan, dst_addr, paddr, blk->size);
3208c2ecf20Sopenharmony_ci	if (ret) {
3218c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "memcpy error: %d\n", ret);
3228c2ecf20Sopenharmony_ci		__release_region(sram, dst_addr, blk->size);
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	return ret;
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic int catpt_restore_basefw(struct catpt_dev *cdev,
3298c2ecf20Sopenharmony_ci				struct dma_chan *chan, dma_addr_t paddr,
3308c2ecf20Sopenharmony_ci				struct catpt_fw_mod_hdr *basefw)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	u32 offset = sizeof(*basefw);
3338c2ecf20Sopenharmony_ci	int ret, i;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
3368c2ecf20Sopenharmony_ci			     basefw, sizeof(*basefw), false);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* restore basefw image */
3398c2ecf20Sopenharmony_ci	for (i = 0; i < basefw->blocks; i++) {
3408c2ecf20Sopenharmony_ci		struct catpt_fw_block_hdr *blk;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci		blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci		switch (blk->ram_type) {
3458c2ecf20Sopenharmony_ci		case CATPT_RAM_TYPE_IRAM:
3468c2ecf20Sopenharmony_ci			ret = catpt_load_block(cdev, chan, paddr + offset,
3478c2ecf20Sopenharmony_ci					       blk, false);
3488c2ecf20Sopenharmony_ci			break;
3498c2ecf20Sopenharmony_ci		default:
3508c2ecf20Sopenharmony_ci			ret = catpt_restore_fwimage(cdev, chan, paddr + offset,
3518c2ecf20Sopenharmony_ci						    blk);
3528c2ecf20Sopenharmony_ci			break;
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		if (ret) {
3568c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "restore block failed: %d\n", ret);
3578c2ecf20Sopenharmony_ci			return ret;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		offset += sizeof(*blk) + blk->size;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* then proceed with memory dumps */
3648c2ecf20Sopenharmony_ci	ret = catpt_restore_memdumps(cdev, chan);
3658c2ecf20Sopenharmony_ci	if (ret)
3668c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "restore memdumps failed: %d\n", ret);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return ret;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int catpt_restore_module(struct catpt_dev *cdev,
3728c2ecf20Sopenharmony_ci				struct dma_chan *chan, dma_addr_t paddr,
3738c2ecf20Sopenharmony_ci				struct catpt_fw_mod_hdr *mod)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	u32 offset = sizeof(*mod);
3768c2ecf20Sopenharmony_ci	int i;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
3798c2ecf20Sopenharmony_ci			     mod, sizeof(*mod), false);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (i = 0; i < mod->blocks; i++) {
3828c2ecf20Sopenharmony_ci		struct catpt_fw_block_hdr *blk;
3838c2ecf20Sopenharmony_ci		int ret;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		switch (blk->ram_type) {
3888c2ecf20Sopenharmony_ci		case CATPT_RAM_TYPE_INSTANCE:
3898c2ecf20Sopenharmony_ci			/* restore module state */
3908c2ecf20Sopenharmony_ci			ret = catpt_dma_memcpy_todsp(cdev, chan,
3918c2ecf20Sopenharmony_ci					cdev->lpe_base + blk->ram_offset,
3928c2ecf20Sopenharmony_ci					cdev->dxbuf_paddr + blk->ram_offset,
3938c2ecf20Sopenharmony_ci					ALIGN(blk->size, 4));
3948c2ecf20Sopenharmony_ci			break;
3958c2ecf20Sopenharmony_ci		default:
3968c2ecf20Sopenharmony_ci			ret = catpt_load_block(cdev, chan, paddr + offset,
3978c2ecf20Sopenharmony_ci					       blk, false);
3988c2ecf20Sopenharmony_ci			break;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		if (ret) {
4028c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "restore block failed: %d\n", ret);
4038c2ecf20Sopenharmony_ci			return ret;
4048c2ecf20Sopenharmony_ci		}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci		offset += sizeof(*blk) + blk->size;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return 0;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int catpt_load_module(struct catpt_dev *cdev,
4138c2ecf20Sopenharmony_ci			     struct dma_chan *chan, dma_addr_t paddr,
4148c2ecf20Sopenharmony_ci			     struct catpt_fw_mod_hdr *mod)
4158c2ecf20Sopenharmony_ci{
4168c2ecf20Sopenharmony_ci	struct catpt_module_type *type;
4178c2ecf20Sopenharmony_ci	u32 offset = sizeof(*mod);
4188c2ecf20Sopenharmony_ci	int i;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
4218c2ecf20Sopenharmony_ci			     mod, sizeof(*mod), false);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	type = &cdev->modules[mod->module_id];
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	for (i = 0; i < mod->blocks; i++) {
4268c2ecf20Sopenharmony_ci		struct catpt_fw_block_hdr *blk;
4278c2ecf20Sopenharmony_ci		int ret;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci		blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		ret = catpt_load_block(cdev, chan, paddr + offset, blk, true);
4328c2ecf20Sopenharmony_ci		if (ret) {
4338c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "load block failed: %d\n", ret);
4348c2ecf20Sopenharmony_ci			return ret;
4358c2ecf20Sopenharmony_ci		}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		/*
4388c2ecf20Sopenharmony_ci		 * Save state window coordinates - these will be
4398c2ecf20Sopenharmony_ci		 * used to capture module state on D0 exit.
4408c2ecf20Sopenharmony_ci		 */
4418c2ecf20Sopenharmony_ci		if (blk->ram_type == CATPT_RAM_TYPE_INSTANCE) {
4428c2ecf20Sopenharmony_ci			type->state_offset = blk->ram_offset;
4438c2ecf20Sopenharmony_ci			type->state_size = blk->size;
4448c2ecf20Sopenharmony_ci		}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci		offset += sizeof(*blk) + blk->size;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	/* init module type static info */
4508c2ecf20Sopenharmony_ci	type->loaded = true;
4518c2ecf20Sopenharmony_ci	/* DSP expects address from module header substracted by 4 */
4528c2ecf20Sopenharmony_ci	type->entry_point = mod->entry_point - 4;
4538c2ecf20Sopenharmony_ci	type->persistent_size = mod->persistent_size;
4548c2ecf20Sopenharmony_ci	type->scratch_size = mod->scratch_size;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return 0;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic int catpt_restore_firmware(struct catpt_dev *cdev,
4608c2ecf20Sopenharmony_ci				  struct dma_chan *chan, dma_addr_t paddr,
4618c2ecf20Sopenharmony_ci				  struct catpt_fw_hdr *fw)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	u32 offset = sizeof(*fw);
4648c2ecf20Sopenharmony_ci	int i;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
4678c2ecf20Sopenharmony_ci			     fw, sizeof(*fw), false);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	for (i = 0; i < fw->modules; i++) {
4708c2ecf20Sopenharmony_ci		struct catpt_fw_mod_hdr *mod;
4718c2ecf20Sopenharmony_ci		int ret;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci		mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
4748c2ecf20Sopenharmony_ci		if (strncmp(fw->signature, mod->signature,
4758c2ecf20Sopenharmony_ci			    FW_SIGNATURE_SIZE)) {
4768c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "module signature mismatch\n");
4778c2ecf20Sopenharmony_ci			return -EINVAL;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (mod->module_id > CATPT_MODID_LAST)
4818c2ecf20Sopenharmony_ci			return -EINVAL;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		switch (mod->module_id) {
4848c2ecf20Sopenharmony_ci		case CATPT_MODID_BASE_FW:
4858c2ecf20Sopenharmony_ci			ret = catpt_restore_basefw(cdev, chan, paddr + offset,
4868c2ecf20Sopenharmony_ci						   mod);
4878c2ecf20Sopenharmony_ci			break;
4888c2ecf20Sopenharmony_ci		default:
4898c2ecf20Sopenharmony_ci			ret = catpt_restore_module(cdev, chan, paddr + offset,
4908c2ecf20Sopenharmony_ci						   mod);
4918c2ecf20Sopenharmony_ci			break;
4928c2ecf20Sopenharmony_ci		}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		if (ret) {
4958c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "restore module failed: %d\n", ret);
4968c2ecf20Sopenharmony_ci			return ret;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		offset += sizeof(*mod) + mod->mod_size;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	return 0;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic int catpt_load_firmware(struct catpt_dev *cdev,
5068c2ecf20Sopenharmony_ci			       struct dma_chan *chan, dma_addr_t paddr,
5078c2ecf20Sopenharmony_ci			       struct catpt_fw_hdr *fw)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	u32 offset = sizeof(*fw);
5108c2ecf20Sopenharmony_ci	int i;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4,
5138c2ecf20Sopenharmony_ci			     fw, sizeof(*fw), false);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	for (i = 0; i < fw->modules; i++) {
5168c2ecf20Sopenharmony_ci		struct catpt_fw_mod_hdr *mod;
5178c2ecf20Sopenharmony_ci		int ret;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset);
5208c2ecf20Sopenharmony_ci		if (strncmp(fw->signature, mod->signature,
5218c2ecf20Sopenharmony_ci			    FW_SIGNATURE_SIZE)) {
5228c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "module signature mismatch\n");
5238c2ecf20Sopenharmony_ci			return -EINVAL;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		if (mod->module_id > CATPT_MODID_LAST)
5278c2ecf20Sopenharmony_ci			return -EINVAL;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		ret = catpt_load_module(cdev, chan, paddr + offset, mod);
5308c2ecf20Sopenharmony_ci		if (ret) {
5318c2ecf20Sopenharmony_ci			dev_err(cdev->dev, "load module failed: %d\n", ret);
5328c2ecf20Sopenharmony_ci			return ret;
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		offset += sizeof(*mod) + mod->mod_size;
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return 0;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan,
5428c2ecf20Sopenharmony_ci			    const char *name, const char *signature,
5438c2ecf20Sopenharmony_ci			    bool restore)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct catpt_fw_hdr *fw;
5468c2ecf20Sopenharmony_ci	struct firmware *img;
5478c2ecf20Sopenharmony_ci	dma_addr_t paddr;
5488c2ecf20Sopenharmony_ci	void *vaddr;
5498c2ecf20Sopenharmony_ci	int ret;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	ret = request_firmware((const struct firmware **)&img, name, cdev->dev);
5528c2ecf20Sopenharmony_ci	if (ret)
5538c2ecf20Sopenharmony_ci		return ret;
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	fw = (struct catpt_fw_hdr *)img->data;
5568c2ecf20Sopenharmony_ci	if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) {
5578c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "firmware signature mismatch\n");
5588c2ecf20Sopenharmony_ci		ret = -EINVAL;
5598c2ecf20Sopenharmony_ci		goto release_fw;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL);
5638c2ecf20Sopenharmony_ci	if (!vaddr) {
5648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5658c2ecf20Sopenharmony_ci		goto release_fw;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	memcpy(vaddr, img->data, img->size);
5698c2ecf20Sopenharmony_ci	fw = (struct catpt_fw_hdr *)vaddr;
5708c2ecf20Sopenharmony_ci	if (restore)
5718c2ecf20Sopenharmony_ci		ret = catpt_restore_firmware(cdev, chan, paddr, fw);
5728c2ecf20Sopenharmony_ci	else
5738c2ecf20Sopenharmony_ci		ret = catpt_load_firmware(cdev, chan, paddr, fw);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	dma_free_coherent(cdev->dev, img->size, vaddr, paddr);
5768c2ecf20Sopenharmony_cirelease_fw:
5778c2ecf20Sopenharmony_ci	release_firmware(img);
5788c2ecf20Sopenharmony_ci	return ret;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic int catpt_load_images(struct catpt_dev *cdev, bool restore)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	static const char *const names[] = {
5848c2ecf20Sopenharmony_ci		"intel/IntcSST1.bin",
5858c2ecf20Sopenharmony_ci		"intel/IntcSST2.bin",
5868c2ecf20Sopenharmony_ci	};
5878c2ecf20Sopenharmony_ci	struct dma_chan *chan;
5888c2ecf20Sopenharmony_ci	int ret;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	chan = catpt_dma_request_config_chan(cdev);
5918c2ecf20Sopenharmony_ci	if (IS_ERR(chan))
5928c2ecf20Sopenharmony_ci		return PTR_ERR(chan);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1],
5958c2ecf20Sopenharmony_ci			       FW_SIGNATURE, restore);
5968c2ecf20Sopenharmony_ci	if (ret)
5978c2ecf20Sopenharmony_ci		goto release_dma_chan;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (!restore)
6008c2ecf20Sopenharmony_ci		goto release_dma_chan;
6018c2ecf20Sopenharmony_ci	ret = catpt_restore_streams_context(cdev, chan);
6028c2ecf20Sopenharmony_ci	if (ret)
6038c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "restore streams ctx failed: %d\n", ret);
6048c2ecf20Sopenharmony_cirelease_dma_chan:
6058c2ecf20Sopenharmony_ci	dma_release_channel(chan);
6068c2ecf20Sopenharmony_ci	return ret;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ciint catpt_boot_firmware(struct catpt_dev *cdev, bool restore)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	int ret;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	catpt_dsp_stall(cdev, true);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	ret = catpt_load_images(cdev, restore);
6168c2ecf20Sopenharmony_ci	if (ret) {
6178c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "load binaries failed: %d\n", ret);
6188c2ecf20Sopenharmony_ci		return ret;
6198c2ecf20Sopenharmony_ci	}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	reinit_completion(&cdev->fw_ready);
6228c2ecf20Sopenharmony_ci	catpt_dsp_stall(cdev, false);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	ret = wait_for_completion_timeout(&cdev->fw_ready,
6258c2ecf20Sopenharmony_ci			msecs_to_jiffies(FW_READY_TIMEOUT_MS));
6268c2ecf20Sopenharmony_ci	if (!ret) {
6278c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "firmware ready timeout\n");
6288c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* update sram pg & clock once done booting */
6328c2ecf20Sopenharmony_ci	catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
6338c2ecf20Sopenharmony_ci	catpt_dsp_update_srampge(cdev, &cdev->iram, cdev->spec->iram_mask);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return catpt_dsp_update_lpclock(cdev);
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ciint catpt_first_boot_firmware(struct catpt_dev *cdev)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct resource *res;
6418c2ecf20Sopenharmony_ci	int ret;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	ret = catpt_boot_firmware(cdev, false);
6448c2ecf20Sopenharmony_ci	if (ret) {
6458c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "basefw boot failed: %d\n", ret);
6468c2ecf20Sopenharmony_ci		return ret;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	/* restrict FW Core dump area */
6508c2ecf20Sopenharmony_ci	__request_region(&cdev->dram, 0, 0x200, NULL, 0);
6518c2ecf20Sopenharmony_ci	/* restrict entire area following BASE_FW - highest offset in DRAM */
6528c2ecf20Sopenharmony_ci	for (res = cdev->dram.child; res->sibling; res = res->sibling)
6538c2ecf20Sopenharmony_ci		;
6548c2ecf20Sopenharmony_ci	__request_region(&cdev->dram, res->end + 1,
6558c2ecf20Sopenharmony_ci			 cdev->dram.end - res->end, NULL, 0);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer);
6588c2ecf20Sopenharmony_ci	if (ret)
6598c2ecf20Sopenharmony_ci		return CATPT_IPC_ERROR(ret);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	ret = catpt_arm_stream_templates(cdev);
6628c2ecf20Sopenharmony_ci	if (ret) {
6638c2ecf20Sopenharmony_ci		dev_err(cdev->dev, "arm templates failed: %d\n", ret);
6648c2ecf20Sopenharmony_ci		return ret;
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	/* update dram pg for scratch and restricted regions */
6688c2ecf20Sopenharmony_ci	catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	return 0;
6718c2ecf20Sopenharmony_ci}
672