170cd5254SLiam Girdwood // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 270cd5254SLiam Girdwood // 370cd5254SLiam Girdwood // This file is provided under a dual BSD/GPLv2 license. When using or 470cd5254SLiam Girdwood // redistributing this file, you may do so under either license. 570cd5254SLiam Girdwood // 670cd5254SLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved. 770cd5254SLiam Girdwood // 870cd5254SLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 970cd5254SLiam Girdwood // 1070cd5254SLiam Girdwood // Generic firmware loader. 1170cd5254SLiam Girdwood // 1270cd5254SLiam Girdwood 1370cd5254SLiam Girdwood #include <linux/firmware.h> 1470cd5254SLiam Girdwood #include <sound/sof.h> 1570cd5254SLiam Girdwood #include "ops.h" 1670cd5254SLiam Girdwood 1770cd5254SLiam Girdwood static int get_ext_windows(struct snd_sof_dev *sdev, 1870cd5254SLiam Girdwood struct sof_ipc_ext_data_hdr *ext_hdr) 1970cd5254SLiam Girdwood { 2070cd5254SLiam Girdwood struct sof_ipc_window *w = 2170cd5254SLiam Girdwood container_of(ext_hdr, struct sof_ipc_window, ext_hdr); 2270cd5254SLiam Girdwood 2370cd5254SLiam Girdwood if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) 2470cd5254SLiam Girdwood return -EINVAL; 2570cd5254SLiam Girdwood 2670cd5254SLiam Girdwood /* keep a local copy of the data */ 27b11c5b5eSGustavo A. R. Silva sdev->info_window = kmemdup(w, struct_size(w, window, w->num_windows), 28b11c5b5eSGustavo A. R. Silva GFP_KERNEL); 2970cd5254SLiam Girdwood if (!sdev->info_window) 3070cd5254SLiam Girdwood return -ENOMEM; 3170cd5254SLiam Girdwood 3270cd5254SLiam Girdwood return 0; 3370cd5254SLiam Girdwood } 3470cd5254SLiam Girdwood 3570cd5254SLiam Girdwood /* parse the extended FW boot data structures from FW boot message */ 3670cd5254SLiam Girdwood int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) 3770cd5254SLiam Girdwood { 3870cd5254SLiam Girdwood struct sof_ipc_ext_data_hdr *ext_hdr; 3970cd5254SLiam Girdwood void *ext_data; 4070cd5254SLiam Girdwood int ret = 0; 4170cd5254SLiam Girdwood 4270cd5254SLiam Girdwood ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); 4370cd5254SLiam Girdwood if (!ext_data) 4470cd5254SLiam Girdwood return -ENOMEM; 4570cd5254SLiam Girdwood 4670cd5254SLiam Girdwood /* get first header */ 4770cd5254SLiam Girdwood snd_sof_dsp_block_read(sdev, bar, offset, ext_data, 4870cd5254SLiam Girdwood sizeof(*ext_hdr)); 4970cd5254SLiam Girdwood ext_hdr = ext_data; 5070cd5254SLiam Girdwood 5170cd5254SLiam Girdwood while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { 5270cd5254SLiam Girdwood /* read in ext structure */ 5370cd5254SLiam Girdwood offset += sizeof(*ext_hdr); 5470cd5254SLiam Girdwood snd_sof_dsp_block_read(sdev, bar, offset, 5570cd5254SLiam Girdwood (void *)((u8 *)ext_data + sizeof(*ext_hdr)), 5670cd5254SLiam Girdwood ext_hdr->hdr.size - sizeof(*ext_hdr)); 5770cd5254SLiam Girdwood 5870cd5254SLiam Girdwood dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", 5970cd5254SLiam Girdwood ext_hdr->type, ext_hdr->hdr.size); 6070cd5254SLiam Girdwood 6170cd5254SLiam Girdwood /* process structure data */ 6270cd5254SLiam Girdwood switch (ext_hdr->type) { 6370cd5254SLiam Girdwood case SOF_IPC_EXT_DMA_BUFFER: 6470cd5254SLiam Girdwood break; 6570cd5254SLiam Girdwood case SOF_IPC_EXT_WINDOW: 6670cd5254SLiam Girdwood ret = get_ext_windows(sdev, ext_hdr); 6770cd5254SLiam Girdwood break; 6870cd5254SLiam Girdwood default: 6970cd5254SLiam Girdwood break; 7070cd5254SLiam Girdwood } 7170cd5254SLiam Girdwood 7270cd5254SLiam Girdwood if (ret < 0) { 7370cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to parse ext data type %d\n", 7470cd5254SLiam Girdwood ext_hdr->type); 7570cd5254SLiam Girdwood break; 7670cd5254SLiam Girdwood } 7770cd5254SLiam Girdwood 7870cd5254SLiam Girdwood /* move to next header */ 7970cd5254SLiam Girdwood offset += ext_hdr->hdr.size; 8070cd5254SLiam Girdwood snd_sof_dsp_block_read(sdev, bar, offset, ext_data, 8170cd5254SLiam Girdwood sizeof(*ext_hdr)); 8270cd5254SLiam Girdwood ext_hdr = ext_data; 8370cd5254SLiam Girdwood } 8470cd5254SLiam Girdwood 8570cd5254SLiam Girdwood kfree(ext_data); 8670cd5254SLiam Girdwood return ret; 8770cd5254SLiam Girdwood } 8870cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_fw_parse_ext_data); 8970cd5254SLiam Girdwood 9070cd5254SLiam Girdwood /* generic module parser for mmaped DSPs */ 9170cd5254SLiam Girdwood int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, 9270cd5254SLiam Girdwood struct snd_sof_mod_hdr *module) 9370cd5254SLiam Girdwood { 9470cd5254SLiam Girdwood struct snd_sof_blk_hdr *block; 9570cd5254SLiam Girdwood int count; 9670cd5254SLiam Girdwood u32 offset; 9770cd5254SLiam Girdwood size_t remaining; 9870cd5254SLiam Girdwood 9970cd5254SLiam Girdwood dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", 10070cd5254SLiam Girdwood module->size, module->num_blocks, module->type); 10170cd5254SLiam Girdwood 10270cd5254SLiam Girdwood block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); 10370cd5254SLiam Girdwood 10470cd5254SLiam Girdwood /* module->size doesn't include header size */ 10570cd5254SLiam Girdwood remaining = module->size; 10670cd5254SLiam Girdwood for (count = 0; count < module->num_blocks; count++) { 10770cd5254SLiam Girdwood /* check for wrap */ 10870cd5254SLiam Girdwood if (remaining < sizeof(*block)) { 10970cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 11070cd5254SLiam Girdwood return -EINVAL; 11170cd5254SLiam Girdwood } 11270cd5254SLiam Girdwood 11370cd5254SLiam Girdwood /* minus header size of block */ 11470cd5254SLiam Girdwood remaining -= sizeof(*block); 11570cd5254SLiam Girdwood 11670cd5254SLiam Girdwood if (block->size == 0) { 11770cd5254SLiam Girdwood dev_warn(sdev->dev, 11870cd5254SLiam Girdwood "warning: block %d size zero\n", count); 11970cd5254SLiam Girdwood dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", 12070cd5254SLiam Girdwood block->type, block->offset); 12170cd5254SLiam Girdwood continue; 12270cd5254SLiam Girdwood } 12370cd5254SLiam Girdwood 12470cd5254SLiam Girdwood switch (block->type) { 12570cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_RSRVD0: 12670cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_SRAM...SOF_FW_BLK_TYPE_RSRVD14: 12770cd5254SLiam Girdwood continue; /* not handled atm */ 12870cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_IRAM: 12970cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_DRAM: 13070cd5254SLiam Girdwood offset = block->offset; 13170cd5254SLiam Girdwood break; 13270cd5254SLiam Girdwood default: 13370cd5254SLiam Girdwood dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", 13470cd5254SLiam Girdwood block->type, count); 13570cd5254SLiam Girdwood return -EINVAL; 13670cd5254SLiam Girdwood } 13770cd5254SLiam Girdwood 13870cd5254SLiam Girdwood dev_dbg(sdev->dev, 13970cd5254SLiam Girdwood "block %d type 0x%x size 0x%x ==> offset 0x%x\n", 14070cd5254SLiam Girdwood count, block->type, block->size, offset); 14170cd5254SLiam Girdwood 14270cd5254SLiam Girdwood /* checking block->size to avoid unaligned access */ 14370cd5254SLiam Girdwood if (block->size % sizeof(u32)) { 14470cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid block size 0x%x\n", 14570cd5254SLiam Girdwood block->size); 14670cd5254SLiam Girdwood return -EINVAL; 14770cd5254SLiam Girdwood } 14870cd5254SLiam Girdwood snd_sof_dsp_block_write(sdev, sdev->mmio_bar, offset, 14970cd5254SLiam Girdwood block + 1, block->size); 15070cd5254SLiam Girdwood 15170cd5254SLiam Girdwood if (remaining < block->size) { 15270cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 15370cd5254SLiam Girdwood return -EINVAL; 15470cd5254SLiam Girdwood } 15570cd5254SLiam Girdwood 15670cd5254SLiam Girdwood /* minus body size of block */ 15770cd5254SLiam Girdwood remaining -= block->size; 15870cd5254SLiam Girdwood /* next block */ 15970cd5254SLiam Girdwood block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) 16070cd5254SLiam Girdwood + block->size); 16170cd5254SLiam Girdwood } 16270cd5254SLiam Girdwood 16370cd5254SLiam Girdwood return 0; 16470cd5254SLiam Girdwood } 16570cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_parse_module_memcpy); 16670cd5254SLiam Girdwood 16770cd5254SLiam Girdwood static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw) 16870cd5254SLiam Girdwood { 16970cd5254SLiam Girdwood struct snd_sof_fw_header *header; 17070cd5254SLiam Girdwood 17170cd5254SLiam Girdwood /* Read the header information from the data pointer */ 17270cd5254SLiam Girdwood header = (struct snd_sof_fw_header *)fw->data; 17370cd5254SLiam Girdwood 17470cd5254SLiam Girdwood /* verify FW sig */ 17570cd5254SLiam Girdwood if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { 17670cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid firmware signature\n"); 17770cd5254SLiam Girdwood return -EINVAL; 17870cd5254SLiam Girdwood } 17970cd5254SLiam Girdwood 18070cd5254SLiam Girdwood /* check size is valid */ 18170cd5254SLiam Girdwood if (fw->size != header->file_size + sizeof(*header)) { 18270cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", 18370cd5254SLiam Girdwood fw->size, header->file_size + sizeof(*header)); 18470cd5254SLiam Girdwood return -EINVAL; 18570cd5254SLiam Girdwood } 18670cd5254SLiam Girdwood 18770cd5254SLiam Girdwood dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", 18870cd5254SLiam Girdwood header->file_size, header->num_modules, 18970cd5254SLiam Girdwood header->abi, sizeof(*header)); 19070cd5254SLiam Girdwood 19170cd5254SLiam Girdwood return 0; 19270cd5254SLiam Girdwood } 19370cd5254SLiam Girdwood 19470cd5254SLiam Girdwood static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw) 19570cd5254SLiam Girdwood { 19670cd5254SLiam Girdwood struct snd_sof_fw_header *header; 19770cd5254SLiam Girdwood struct snd_sof_mod_hdr *module; 19870cd5254SLiam Girdwood int (*load_module)(struct snd_sof_dev *sof_dev, 19970cd5254SLiam Girdwood struct snd_sof_mod_hdr *hdr); 20070cd5254SLiam Girdwood int ret, count; 20170cd5254SLiam Girdwood size_t remaining; 20270cd5254SLiam Girdwood 20370cd5254SLiam Girdwood header = (struct snd_sof_fw_header *)fw->data; 20470cd5254SLiam Girdwood load_module = sof_ops(sdev)->load_module; 20570cd5254SLiam Girdwood if (!load_module) 20670cd5254SLiam Girdwood return -EINVAL; 20770cd5254SLiam Girdwood 20870cd5254SLiam Girdwood /* parse each module */ 20970cd5254SLiam Girdwood module = (struct snd_sof_mod_hdr *)((u8 *)(fw->data) + sizeof(*header)); 21070cd5254SLiam Girdwood remaining = fw->size - sizeof(*header); 21170cd5254SLiam Girdwood /* check for wrap */ 21270cd5254SLiam Girdwood if (remaining > fw->size) { 21370cd5254SLiam Girdwood dev_err(sdev->dev, "error: fw size smaller than header size\n"); 21470cd5254SLiam Girdwood return -EINVAL; 21570cd5254SLiam Girdwood } 21670cd5254SLiam Girdwood 21770cd5254SLiam Girdwood for (count = 0; count < header->num_modules; count++) { 21870cd5254SLiam Girdwood /* check for wrap */ 21970cd5254SLiam Girdwood if (remaining < sizeof(*module)) { 22070cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 22170cd5254SLiam Girdwood return -EINVAL; 22270cd5254SLiam Girdwood } 22370cd5254SLiam Girdwood 22470cd5254SLiam Girdwood /* minus header size of module */ 22570cd5254SLiam Girdwood remaining -= sizeof(*module); 22670cd5254SLiam Girdwood 22770cd5254SLiam Girdwood /* module */ 22870cd5254SLiam Girdwood ret = load_module(sdev, module); 22970cd5254SLiam Girdwood if (ret < 0) { 23070cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid module %d\n", count); 23170cd5254SLiam Girdwood return ret; 23270cd5254SLiam Girdwood } 23370cd5254SLiam Girdwood 23470cd5254SLiam Girdwood if (remaining < module->size) { 23570cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 23670cd5254SLiam Girdwood return -EINVAL; 23770cd5254SLiam Girdwood } 23870cd5254SLiam Girdwood 23970cd5254SLiam Girdwood /* minus body size of module */ 24070cd5254SLiam Girdwood remaining -= module->size; 24170cd5254SLiam Girdwood module = (struct snd_sof_mod_hdr *)((u8 *)module 24270cd5254SLiam Girdwood + sizeof(*module) + module->size); 24370cd5254SLiam Girdwood } 24470cd5254SLiam Girdwood 24570cd5254SLiam Girdwood return 0; 24670cd5254SLiam Girdwood } 24770cd5254SLiam Girdwood 24870cd5254SLiam Girdwood int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) 24970cd5254SLiam Girdwood { 25070cd5254SLiam Girdwood struct snd_sof_pdata *plat_data = sdev->pdata; 25170cd5254SLiam Girdwood const char *fw_filename; 25270cd5254SLiam Girdwood int ret; 25370cd5254SLiam Girdwood 25470cd5254SLiam Girdwood /* set code loading condition to true */ 25570cd5254SLiam Girdwood sdev->code_loading = 1; 25670cd5254SLiam Girdwood 25770cd5254SLiam Girdwood /* Don't request firmware again if firmware is already requested */ 25870cd5254SLiam Girdwood if (plat_data->fw) 25970cd5254SLiam Girdwood return 0; 26070cd5254SLiam Girdwood 26170cd5254SLiam Girdwood fw_filename = kasprintf(GFP_KERNEL, "%s/%s", 26270cd5254SLiam Girdwood plat_data->fw_filename_prefix, 26370cd5254SLiam Girdwood plat_data->fw_filename); 26470cd5254SLiam Girdwood if (!fw_filename) 26570cd5254SLiam Girdwood return -ENOMEM; 26670cd5254SLiam Girdwood 26770cd5254SLiam Girdwood ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); 26870cd5254SLiam Girdwood 26970cd5254SLiam Girdwood if (ret < 0) { 27070cd5254SLiam Girdwood dev_err(sdev->dev, "error: request firmware %s failed err: %d\n", 27170cd5254SLiam Girdwood fw_filename, ret); 27270cd5254SLiam Girdwood } 27370cd5254SLiam Girdwood 27470cd5254SLiam Girdwood kfree(fw_filename); 27570cd5254SLiam Girdwood 27670cd5254SLiam Girdwood return ret; 27770cd5254SLiam Girdwood } 27870cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_raw); 27970cd5254SLiam Girdwood 28070cd5254SLiam Girdwood int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 28170cd5254SLiam Girdwood { 28270cd5254SLiam Girdwood struct snd_sof_pdata *plat_data = sdev->pdata; 28370cd5254SLiam Girdwood int ret; 28470cd5254SLiam Girdwood 28570cd5254SLiam Girdwood ret = snd_sof_load_firmware_raw(sdev); 28670cd5254SLiam Girdwood if (ret < 0) 28770cd5254SLiam Girdwood return ret; 28870cd5254SLiam Girdwood 28970cd5254SLiam Girdwood /* make sure the FW header and file is valid */ 29070cd5254SLiam Girdwood ret = check_header(sdev, plat_data->fw); 29170cd5254SLiam Girdwood if (ret < 0) { 29270cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid FW header\n"); 29370cd5254SLiam Girdwood goto error; 29470cd5254SLiam Girdwood } 29570cd5254SLiam Girdwood 29670cd5254SLiam Girdwood /* prepare the DSP for FW loading */ 29770cd5254SLiam Girdwood ret = snd_sof_dsp_reset(sdev); 29870cd5254SLiam Girdwood if (ret < 0) { 29970cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to reset DSP\n"); 30070cd5254SLiam Girdwood goto error; 30170cd5254SLiam Girdwood } 30270cd5254SLiam Girdwood 30370cd5254SLiam Girdwood /* parse and load firmware modules to DSP */ 30470cd5254SLiam Girdwood ret = load_modules(sdev, plat_data->fw); 30570cd5254SLiam Girdwood if (ret < 0) { 30670cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid FW modules\n"); 30770cd5254SLiam Girdwood goto error; 30870cd5254SLiam Girdwood } 30970cd5254SLiam Girdwood 31070cd5254SLiam Girdwood return 0; 31170cd5254SLiam Girdwood 31270cd5254SLiam Girdwood error: 31370cd5254SLiam Girdwood release_firmware(plat_data->fw); 31470cd5254SLiam Girdwood plat_data->fw = NULL; 31570cd5254SLiam Girdwood return ret; 31670cd5254SLiam Girdwood 31770cd5254SLiam Girdwood } 31870cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 31970cd5254SLiam Girdwood 32070cd5254SLiam Girdwood int snd_sof_load_firmware(struct snd_sof_dev *sdev) 32170cd5254SLiam Girdwood { 32270cd5254SLiam Girdwood dev_dbg(sdev->dev, "loading firmware\n"); 32370cd5254SLiam Girdwood 32470cd5254SLiam Girdwood if (sof_ops(sdev)->load_firmware) 32570cd5254SLiam Girdwood return sof_ops(sdev)->load_firmware(sdev); 32670cd5254SLiam Girdwood return 0; 32770cd5254SLiam Girdwood } 32870cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware); 32970cd5254SLiam Girdwood 33070cd5254SLiam Girdwood int snd_sof_run_firmware(struct snd_sof_dev *sdev) 33170cd5254SLiam Girdwood { 33270cd5254SLiam Girdwood int ret; 33370cd5254SLiam Girdwood int init_core_mask; 33470cd5254SLiam Girdwood 33570cd5254SLiam Girdwood init_waitqueue_head(&sdev->boot_wait); 33670cd5254SLiam Girdwood sdev->boot_complete = false; 33770cd5254SLiam Girdwood 338*5c9714f6SRanjani Sridharan /* create read-only fw_version debugfs to store boot version info */ 33970cd5254SLiam Girdwood if (sdev->first_boot) { 34070cd5254SLiam Girdwood ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 34170cd5254SLiam Girdwood sizeof(sdev->fw_version), 342*5c9714f6SRanjani Sridharan "fw_version", 0444); 34370cd5254SLiam Girdwood /* errors are only due to memory allocation, not debugfs */ 34470cd5254SLiam Girdwood if (ret < 0) { 34570cd5254SLiam Girdwood dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 34670cd5254SLiam Girdwood return ret; 34770cd5254SLiam Girdwood } 34870cd5254SLiam Girdwood } 34970cd5254SLiam Girdwood 35070cd5254SLiam Girdwood /* perform pre fw run operations */ 35170cd5254SLiam Girdwood ret = snd_sof_dsp_pre_fw_run(sdev); 35270cd5254SLiam Girdwood if (ret < 0) { 35370cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed pre fw run op\n"); 35470cd5254SLiam Girdwood return ret; 35570cd5254SLiam Girdwood } 35670cd5254SLiam Girdwood 35770cd5254SLiam Girdwood dev_dbg(sdev->dev, "booting DSP firmware\n"); 35870cd5254SLiam Girdwood 35970cd5254SLiam Girdwood /* boot the firmware on the DSP */ 36070cd5254SLiam Girdwood ret = snd_sof_dsp_run(sdev); 36170cd5254SLiam Girdwood if (ret < 0) { 36270cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to reset DSP\n"); 36370cd5254SLiam Girdwood return ret; 36470cd5254SLiam Girdwood } 36570cd5254SLiam Girdwood 36670cd5254SLiam Girdwood init_core_mask = ret; 36770cd5254SLiam Girdwood 36870cd5254SLiam Girdwood /* now wait for the DSP to boot */ 36970cd5254SLiam Girdwood ret = wait_event_timeout(sdev->boot_wait, sdev->boot_complete, 37070cd5254SLiam Girdwood msecs_to_jiffies(sdev->boot_timeout)); 37170cd5254SLiam Girdwood if (ret == 0) { 37270cd5254SLiam Girdwood dev_err(sdev->dev, "error: firmware boot failure\n"); 373a69270d8SKai Vehmanen /* after this point FW_READY msg should be ignored */ 374a69270d8SKai Vehmanen sdev->boot_complete = true; 37570cd5254SLiam Girdwood snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | 37670cd5254SLiam Girdwood SOF_DBG_TEXT | SOF_DBG_PCI); 37770cd5254SLiam Girdwood return -EIO; 37870cd5254SLiam Girdwood } 37970cd5254SLiam Girdwood 38070cd5254SLiam Girdwood dev_info(sdev->dev, "firmware boot complete\n"); 38170cd5254SLiam Girdwood 38270cd5254SLiam Girdwood /* perform post fw run operations */ 38370cd5254SLiam Girdwood ret = snd_sof_dsp_post_fw_run(sdev); 38470cd5254SLiam Girdwood if (ret < 0) { 38570cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed post fw run op\n"); 38670cd5254SLiam Girdwood return ret; 38770cd5254SLiam Girdwood } 38870cd5254SLiam Girdwood 38970cd5254SLiam Girdwood /* fw boot is complete. Update the active cores mask */ 39070cd5254SLiam Girdwood sdev->enabled_cores_mask = init_core_mask; 39170cd5254SLiam Girdwood 39270cd5254SLiam Girdwood return 0; 39370cd5254SLiam Girdwood } 39470cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_run_firmware); 39570cd5254SLiam Girdwood 39670cd5254SLiam Girdwood void snd_sof_fw_unload(struct snd_sof_dev *sdev) 39770cd5254SLiam Girdwood { 39870cd5254SLiam Girdwood /* TODO: support module unloading at runtime */ 39970cd5254SLiam Girdwood } 40070cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_fw_unload); 401