brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 696d843 Raw
672 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2//3// Copyright(c) 2020 Intel Corporation4//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	25016 17#define FW_SIGNATURE		"$SST"18#define FW_SIGNATURE_SIZE	419 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 int179catpt_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 be439		 * 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	else573		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