1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only 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> 15*a80cf198SKarol Trzcinski #include <sound/sof/ext_manifest.h> 1670cd5254SLiam Girdwood #include "ops.h" 1770cd5254SLiam Girdwood 1870cd5254SLiam Girdwood static int get_ext_windows(struct snd_sof_dev *sdev, 190730c092SKarol Trzcinski const struct sof_ipc_ext_data_hdr *ext_hdr) 2070cd5254SLiam Girdwood { 210730c092SKarol Trzcinski const struct sof_ipc_window *w = 2270cd5254SLiam Girdwood container_of(ext_hdr, struct sof_ipc_window, ext_hdr); 2370cd5254SLiam Girdwood 2470cd5254SLiam Girdwood if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) 2570cd5254SLiam Girdwood return -EINVAL; 2670cd5254SLiam Girdwood 2770cd5254SLiam Girdwood /* keep a local copy of the data */ 28d8e25a10SMark Brown sdev->info_window = kmemdup(w, struct_size(w, window, w->num_windows), 29d8e25a10SMark Brown GFP_KERNEL); 3070cd5254SLiam Girdwood if (!sdev->info_window) 3170cd5254SLiam Girdwood return -ENOMEM; 3270cd5254SLiam Girdwood 3370cd5254SLiam Girdwood return 0; 3470cd5254SLiam Girdwood } 3570cd5254SLiam Girdwood 3659283959SKarol Trzcinski static int get_cc_info(struct snd_sof_dev *sdev, 370730c092SKarol Trzcinski const struct sof_ipc_ext_data_hdr *ext_hdr) 3859283959SKarol Trzcinski { 3959283959SKarol Trzcinski int ret; 4059283959SKarol Trzcinski 410730c092SKarol Trzcinski const struct sof_ipc_cc_version *cc = 4259283959SKarol Trzcinski container_of(ext_hdr, struct sof_ipc_cc_version, ext_hdr); 4359283959SKarol Trzcinski 4459283959SKarol Trzcinski dev_dbg(sdev->dev, "Firmware info: used compiler %s %d:%d:%d%s used optimization flags %s\n", 4559283959SKarol Trzcinski cc->name, cc->major, cc->minor, cc->micro, cc->desc, 4659283959SKarol Trzcinski cc->optim); 4759283959SKarol Trzcinski 4859283959SKarol Trzcinski /* create read-only cc_version debugfs to store compiler version info */ 4959283959SKarol Trzcinski /* use local copy of the cc_version to prevent data corruption */ 5059283959SKarol Trzcinski if (sdev->first_boot) { 5159283959SKarol Trzcinski sdev->cc_version = devm_kmalloc(sdev->dev, cc->ext_hdr.hdr.size, 5259283959SKarol Trzcinski GFP_KERNEL); 5359283959SKarol Trzcinski 5459283959SKarol Trzcinski if (!sdev->cc_version) 5559283959SKarol Trzcinski return -ENOMEM; 5659283959SKarol Trzcinski 5759283959SKarol Trzcinski memcpy(sdev->cc_version, cc, cc->ext_hdr.hdr.size); 5859283959SKarol Trzcinski ret = snd_sof_debugfs_buf_item(sdev, sdev->cc_version, 5959283959SKarol Trzcinski cc->ext_hdr.hdr.size, 6059283959SKarol Trzcinski "cc_version", 0444); 6159283959SKarol Trzcinski 6259283959SKarol Trzcinski /* errors are only due to memory allocation, not debugfs */ 6359283959SKarol Trzcinski if (ret < 0) { 6459283959SKarol Trzcinski dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 6559283959SKarol Trzcinski return ret; 6659283959SKarol Trzcinski } 6759283959SKarol Trzcinski } 6859283959SKarol Trzcinski 6959283959SKarol Trzcinski return 0; 7059283959SKarol Trzcinski } 7159283959SKarol Trzcinski 7270cd5254SLiam Girdwood /* parse the extended FW boot data structures from FW boot message */ 7370cd5254SLiam Girdwood int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) 7470cd5254SLiam Girdwood { 7570cd5254SLiam Girdwood struct sof_ipc_ext_data_hdr *ext_hdr; 7670cd5254SLiam Girdwood void *ext_data; 7770cd5254SLiam Girdwood int ret = 0; 7870cd5254SLiam Girdwood 7970cd5254SLiam Girdwood ext_data = kzalloc(PAGE_SIZE, GFP_KERNEL); 8070cd5254SLiam Girdwood if (!ext_data) 8170cd5254SLiam Girdwood return -ENOMEM; 8270cd5254SLiam Girdwood 8370cd5254SLiam Girdwood /* get first header */ 8470cd5254SLiam Girdwood snd_sof_dsp_block_read(sdev, bar, offset, ext_data, 8570cd5254SLiam Girdwood sizeof(*ext_hdr)); 8670cd5254SLiam Girdwood ext_hdr = ext_data; 8770cd5254SLiam Girdwood 8870cd5254SLiam Girdwood while (ext_hdr->hdr.cmd == SOF_IPC_FW_READY) { 8970cd5254SLiam Girdwood /* read in ext structure */ 906bb03c21SKarol Trzcinski snd_sof_dsp_block_read(sdev, bar, offset + sizeof(*ext_hdr), 9170cd5254SLiam Girdwood (void *)((u8 *)ext_data + sizeof(*ext_hdr)), 9270cd5254SLiam Girdwood ext_hdr->hdr.size - sizeof(*ext_hdr)); 9370cd5254SLiam Girdwood 9470cd5254SLiam Girdwood dev_dbg(sdev->dev, "found ext header type %d size 0x%x\n", 9570cd5254SLiam Girdwood ext_hdr->type, ext_hdr->hdr.size); 9670cd5254SLiam Girdwood 9770cd5254SLiam Girdwood /* process structure data */ 9870cd5254SLiam Girdwood switch (ext_hdr->type) { 9970cd5254SLiam Girdwood case SOF_IPC_EXT_WINDOW: 10070cd5254SLiam Girdwood ret = get_ext_windows(sdev, ext_hdr); 10170cd5254SLiam Girdwood break; 10259283959SKarol Trzcinski case SOF_IPC_EXT_CC_INFO: 10359283959SKarol Trzcinski ret = get_cc_info(sdev, ext_hdr); 10459283959SKarol Trzcinski break; 10570cd5254SLiam Girdwood default: 1068edc9566SKarol Trzcinski dev_warn(sdev->dev, "warning: unknown ext header type %d size 0x%x\n", 1078edc9566SKarol Trzcinski ext_hdr->type, ext_hdr->hdr.size); 1086bb03c21SKarol Trzcinski ret = 0; 10970cd5254SLiam Girdwood break; 11070cd5254SLiam Girdwood } 11170cd5254SLiam Girdwood 11270cd5254SLiam Girdwood if (ret < 0) { 11370cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to parse ext data type %d\n", 11470cd5254SLiam Girdwood ext_hdr->type); 11570cd5254SLiam Girdwood break; 11670cd5254SLiam Girdwood } 11770cd5254SLiam Girdwood 11870cd5254SLiam Girdwood /* move to next header */ 11970cd5254SLiam Girdwood offset += ext_hdr->hdr.size; 12070cd5254SLiam Girdwood snd_sof_dsp_block_read(sdev, bar, offset, ext_data, 12170cd5254SLiam Girdwood sizeof(*ext_hdr)); 12270cd5254SLiam Girdwood ext_hdr = ext_data; 12370cd5254SLiam Girdwood } 12470cd5254SLiam Girdwood 12570cd5254SLiam Girdwood kfree(ext_data); 12670cd5254SLiam Girdwood return ret; 12770cd5254SLiam Girdwood } 12870cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_fw_parse_ext_data); 12970cd5254SLiam Girdwood 130*a80cf198SKarol Trzcinski static ssize_t snd_sof_ext_man_size(const struct firmware *fw) 131*a80cf198SKarol Trzcinski { 132*a80cf198SKarol Trzcinski const struct sof_ext_man_header *head; 133*a80cf198SKarol Trzcinski 134*a80cf198SKarol Trzcinski head = (struct sof_ext_man_header *)fw->data; 135*a80cf198SKarol Trzcinski 136*a80cf198SKarol Trzcinski /* 137*a80cf198SKarol Trzcinski * assert fw size is big enough to contain extended manifest header, 138*a80cf198SKarol Trzcinski * it prevents from reading unallocated memory from `head` in following 139*a80cf198SKarol Trzcinski * step. 140*a80cf198SKarol Trzcinski */ 141*a80cf198SKarol Trzcinski if (fw->size < sizeof(*head)) 142*a80cf198SKarol Trzcinski return -EINVAL; 143*a80cf198SKarol Trzcinski 144*a80cf198SKarol Trzcinski /* 145*a80cf198SKarol Trzcinski * When fw points to extended manifest, 146*a80cf198SKarol Trzcinski * then first u32 must be equal SOF_EXT_MAN_MAGIC_NUMBER. 147*a80cf198SKarol Trzcinski */ 148*a80cf198SKarol Trzcinski if (head->magic == SOF_EXT_MAN_MAGIC_NUMBER) 149*a80cf198SKarol Trzcinski return head->full_size; 150*a80cf198SKarol Trzcinski 151*a80cf198SKarol Trzcinski /* otherwise given fw don't have an extended manifest */ 152*a80cf198SKarol Trzcinski return 0; 153*a80cf198SKarol Trzcinski } 154*a80cf198SKarol Trzcinski 155*a80cf198SKarol Trzcinski /* parse extended FW manifest data structures */ 156*a80cf198SKarol Trzcinski static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev, 157*a80cf198SKarol Trzcinski const struct firmware *fw) 158*a80cf198SKarol Trzcinski { 159*a80cf198SKarol Trzcinski const struct sof_ext_man_elem_header *elem_hdr; 160*a80cf198SKarol Trzcinski const struct sof_ext_man_header *head; 161*a80cf198SKarol Trzcinski ssize_t ext_man_size; 162*a80cf198SKarol Trzcinski ssize_t remaining; 163*a80cf198SKarol Trzcinski uintptr_t iptr; 164*a80cf198SKarol Trzcinski int ret = 0; 165*a80cf198SKarol Trzcinski 166*a80cf198SKarol Trzcinski head = (struct sof_ext_man_header *)fw->data; 167*a80cf198SKarol Trzcinski remaining = head->full_size - head->header_size; 168*a80cf198SKarol Trzcinski ext_man_size = snd_sof_ext_man_size(fw); 169*a80cf198SKarol Trzcinski 170*a80cf198SKarol Trzcinski /* Assert firmware starts with extended manifest */ 171*a80cf198SKarol Trzcinski if (ext_man_size <= 0) 172*a80cf198SKarol Trzcinski return ext_man_size; 173*a80cf198SKarol Trzcinski 174*a80cf198SKarol Trzcinski /* incompatible version */ 175*a80cf198SKarol Trzcinski if (SOF_EXT_MAN_VERSION_INCOMPATIBLE(SOF_EXT_MAN_VERSION, 176*a80cf198SKarol Trzcinski head->header_version)) { 177*a80cf198SKarol Trzcinski dev_err(sdev->dev, "error: extended manifest version 0x%X differ from used 0x%X\n", 178*a80cf198SKarol Trzcinski head->header_version, SOF_EXT_MAN_VERSION); 179*a80cf198SKarol Trzcinski return -EINVAL; 180*a80cf198SKarol Trzcinski } 181*a80cf198SKarol Trzcinski 182*a80cf198SKarol Trzcinski /* get first extended manifest element header */ 183*a80cf198SKarol Trzcinski iptr = (uintptr_t)fw->data + head->header_size; 184*a80cf198SKarol Trzcinski 185*a80cf198SKarol Trzcinski while (remaining > sizeof(*elem_hdr)) { 186*a80cf198SKarol Trzcinski elem_hdr = (struct sof_ext_man_elem_header *)iptr; 187*a80cf198SKarol Trzcinski 188*a80cf198SKarol Trzcinski dev_dbg(sdev->dev, "found sof_ext_man header type %d size 0x%X\n", 189*a80cf198SKarol Trzcinski elem_hdr->type, elem_hdr->size); 190*a80cf198SKarol Trzcinski 191*a80cf198SKarol Trzcinski if (elem_hdr->size < sizeof(*elem_hdr) || 192*a80cf198SKarol Trzcinski elem_hdr->size > remaining) { 193*a80cf198SKarol Trzcinski dev_err(sdev->dev, "error: invalid sof_ext_man header size, type %d size 0x%X\n", 194*a80cf198SKarol Trzcinski elem_hdr->type, elem_hdr->size); 195*a80cf198SKarol Trzcinski return -EINVAL; 196*a80cf198SKarol Trzcinski } 197*a80cf198SKarol Trzcinski 198*a80cf198SKarol Trzcinski /* process structure data */ 199*a80cf198SKarol Trzcinski switch (elem_hdr->type) { 200*a80cf198SKarol Trzcinski default: 201*a80cf198SKarol Trzcinski dev_warn(sdev->dev, "warning: unknown sof_ext_man header type %d size 0x%X\n", 202*a80cf198SKarol Trzcinski elem_hdr->type, elem_hdr->size); 203*a80cf198SKarol Trzcinski break; 204*a80cf198SKarol Trzcinski } 205*a80cf198SKarol Trzcinski 206*a80cf198SKarol Trzcinski if (ret < 0) { 207*a80cf198SKarol Trzcinski dev_err(sdev->dev, "error: failed to parse sof_ext_man header type %d size 0x%X\n", 208*a80cf198SKarol Trzcinski elem_hdr->type, elem_hdr->size); 209*a80cf198SKarol Trzcinski return ret; 210*a80cf198SKarol Trzcinski } 211*a80cf198SKarol Trzcinski 212*a80cf198SKarol Trzcinski remaining -= elem_hdr->size; 213*a80cf198SKarol Trzcinski iptr += elem_hdr->size; 214*a80cf198SKarol Trzcinski } 215*a80cf198SKarol Trzcinski 216*a80cf198SKarol Trzcinski if (remaining) { 217*a80cf198SKarol Trzcinski dev_err(sdev->dev, "error: sof_ext_man header is inconsistent\n"); 218*a80cf198SKarol Trzcinski return -EINVAL; 219*a80cf198SKarol Trzcinski } 220*a80cf198SKarol Trzcinski 221*a80cf198SKarol Trzcinski return ext_man_size; 222*a80cf198SKarol Trzcinski } 223*a80cf198SKarol Trzcinski 22483ee7ab1SDaniel Baluta /* 22583ee7ab1SDaniel Baluta * IPC Firmware ready. 22683ee7ab1SDaniel Baluta */ 22783ee7ab1SDaniel Baluta static void sof_get_windows(struct snd_sof_dev *sdev) 22883ee7ab1SDaniel Baluta { 22983ee7ab1SDaniel Baluta struct sof_ipc_window_elem *elem; 23083ee7ab1SDaniel Baluta u32 outbox_offset = 0; 23183ee7ab1SDaniel Baluta u32 stream_offset = 0; 23283ee7ab1SDaniel Baluta u32 inbox_offset = 0; 23383ee7ab1SDaniel Baluta u32 outbox_size = 0; 23483ee7ab1SDaniel Baluta u32 stream_size = 0; 23583ee7ab1SDaniel Baluta u32 inbox_size = 0; 23683ee7ab1SDaniel Baluta int window_offset; 23783ee7ab1SDaniel Baluta int bar; 23883ee7ab1SDaniel Baluta int i; 23983ee7ab1SDaniel Baluta 24083ee7ab1SDaniel Baluta if (!sdev->info_window) { 24183ee7ab1SDaniel Baluta dev_err(sdev->dev, "error: have no window info\n"); 24283ee7ab1SDaniel Baluta return; 24383ee7ab1SDaniel Baluta } 24483ee7ab1SDaniel Baluta 24583ee7ab1SDaniel Baluta bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM); 24683ee7ab1SDaniel Baluta if (bar < 0) { 24783ee7ab1SDaniel Baluta dev_err(sdev->dev, "error: have no bar mapping\n"); 24883ee7ab1SDaniel Baluta return; 24983ee7ab1SDaniel Baluta } 25083ee7ab1SDaniel Baluta 25183ee7ab1SDaniel Baluta for (i = 0; i < sdev->info_window->num_windows; i++) { 25283ee7ab1SDaniel Baluta elem = &sdev->info_window->window[i]; 25383ee7ab1SDaniel Baluta 25483ee7ab1SDaniel Baluta window_offset = snd_sof_dsp_get_window_offset(sdev, elem->id); 25583ee7ab1SDaniel Baluta if (window_offset < 0) { 25683ee7ab1SDaniel Baluta dev_warn(sdev->dev, "warn: no offset for window %d\n", 25783ee7ab1SDaniel Baluta elem->id); 25883ee7ab1SDaniel Baluta continue; 25983ee7ab1SDaniel Baluta } 26083ee7ab1SDaniel Baluta 26183ee7ab1SDaniel Baluta switch (elem->type) { 26283ee7ab1SDaniel Baluta case SOF_IPC_REGION_UPBOX: 26383ee7ab1SDaniel Baluta inbox_offset = window_offset + elem->offset; 26483ee7ab1SDaniel Baluta inbox_size = elem->size; 26583ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 26683ee7ab1SDaniel Baluta sdev->bar[bar] + 26783ee7ab1SDaniel Baluta inbox_offset, 26883ee7ab1SDaniel Baluta elem->size, "inbox", 26983ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 27083ee7ab1SDaniel Baluta break; 27183ee7ab1SDaniel Baluta case SOF_IPC_REGION_DOWNBOX: 27283ee7ab1SDaniel Baluta outbox_offset = window_offset + elem->offset; 27383ee7ab1SDaniel Baluta outbox_size = elem->size; 27483ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 27583ee7ab1SDaniel Baluta sdev->bar[bar] + 27683ee7ab1SDaniel Baluta outbox_offset, 27783ee7ab1SDaniel Baluta elem->size, "outbox", 27883ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 27983ee7ab1SDaniel Baluta break; 28083ee7ab1SDaniel Baluta case SOF_IPC_REGION_TRACE: 28183ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 28283ee7ab1SDaniel Baluta sdev->bar[bar] + 28383ee7ab1SDaniel Baluta window_offset + 28483ee7ab1SDaniel Baluta elem->offset, 28583ee7ab1SDaniel Baluta elem->size, "etrace", 28683ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 28783ee7ab1SDaniel Baluta break; 28883ee7ab1SDaniel Baluta case SOF_IPC_REGION_DEBUG: 28983ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 29083ee7ab1SDaniel Baluta sdev->bar[bar] + 29183ee7ab1SDaniel Baluta window_offset + 29283ee7ab1SDaniel Baluta elem->offset, 29383ee7ab1SDaniel Baluta elem->size, "debug", 29483ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 29583ee7ab1SDaniel Baluta break; 29683ee7ab1SDaniel Baluta case SOF_IPC_REGION_STREAM: 29783ee7ab1SDaniel Baluta stream_offset = window_offset + elem->offset; 29883ee7ab1SDaniel Baluta stream_size = elem->size; 29983ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 30083ee7ab1SDaniel Baluta sdev->bar[bar] + 30183ee7ab1SDaniel Baluta stream_offset, 30283ee7ab1SDaniel Baluta elem->size, "stream", 30383ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 30483ee7ab1SDaniel Baluta break; 30583ee7ab1SDaniel Baluta case SOF_IPC_REGION_REGS: 30683ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 30783ee7ab1SDaniel Baluta sdev->bar[bar] + 30883ee7ab1SDaniel Baluta window_offset + 30983ee7ab1SDaniel Baluta elem->offset, 31083ee7ab1SDaniel Baluta elem->size, "regs", 31183ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 31283ee7ab1SDaniel Baluta break; 31383ee7ab1SDaniel Baluta case SOF_IPC_REGION_EXCEPTION: 31483ee7ab1SDaniel Baluta sdev->dsp_oops_offset = window_offset + elem->offset; 31583ee7ab1SDaniel Baluta snd_sof_debugfs_io_item(sdev, 31683ee7ab1SDaniel Baluta sdev->bar[bar] + 31783ee7ab1SDaniel Baluta window_offset + 31883ee7ab1SDaniel Baluta elem->offset, 31983ee7ab1SDaniel Baluta elem->size, "exception", 32083ee7ab1SDaniel Baluta SOF_DEBUGFS_ACCESS_D0_ONLY); 32183ee7ab1SDaniel Baluta break; 32283ee7ab1SDaniel Baluta default: 32383ee7ab1SDaniel Baluta dev_err(sdev->dev, "error: get illegal window info\n"); 32483ee7ab1SDaniel Baluta return; 32583ee7ab1SDaniel Baluta } 32683ee7ab1SDaniel Baluta } 32783ee7ab1SDaniel Baluta 32883ee7ab1SDaniel Baluta if (outbox_size == 0 || inbox_size == 0) { 32983ee7ab1SDaniel Baluta dev_err(sdev->dev, "error: get illegal mailbox window\n"); 33083ee7ab1SDaniel Baluta return; 33183ee7ab1SDaniel Baluta } 33283ee7ab1SDaniel Baluta 33383ee7ab1SDaniel Baluta snd_sof_dsp_mailbox_init(sdev, inbox_offset, inbox_size, 33483ee7ab1SDaniel Baluta outbox_offset, outbox_size); 33583ee7ab1SDaniel Baluta sdev->stream_box.offset = stream_offset; 33683ee7ab1SDaniel Baluta sdev->stream_box.size = stream_size; 33783ee7ab1SDaniel Baluta 33883ee7ab1SDaniel Baluta dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", 33983ee7ab1SDaniel Baluta inbox_offset, inbox_size); 34083ee7ab1SDaniel Baluta dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", 34183ee7ab1SDaniel Baluta outbox_offset, outbox_size); 34283ee7ab1SDaniel Baluta dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", 34383ee7ab1SDaniel Baluta stream_offset, stream_size); 34483ee7ab1SDaniel Baluta } 34583ee7ab1SDaniel Baluta 34683ee7ab1SDaniel Baluta /* check for ABI compatibility and create memory windows on first boot */ 34783ee7ab1SDaniel Baluta int sof_fw_ready(struct snd_sof_dev *sdev, u32 msg_id) 34883ee7ab1SDaniel Baluta { 34983ee7ab1SDaniel Baluta struct sof_ipc_fw_ready *fw_ready = &sdev->fw_ready; 35083ee7ab1SDaniel Baluta int offset; 35183ee7ab1SDaniel Baluta int bar; 35283ee7ab1SDaniel Baluta int ret; 35383ee7ab1SDaniel Baluta 35483ee7ab1SDaniel Baluta /* mailbox must be on 4k boundary */ 35583ee7ab1SDaniel Baluta offset = snd_sof_dsp_get_mailbox_offset(sdev); 35683ee7ab1SDaniel Baluta if (offset < 0) { 35783ee7ab1SDaniel Baluta dev_err(sdev->dev, "error: have no mailbox offset\n"); 35883ee7ab1SDaniel Baluta return offset; 35983ee7ab1SDaniel Baluta } 36083ee7ab1SDaniel Baluta 36183ee7ab1SDaniel Baluta bar = snd_sof_dsp_get_bar_index(sdev, SOF_FW_BLK_TYPE_SRAM); 36283ee7ab1SDaniel Baluta if (bar < 0) { 36383ee7ab1SDaniel Baluta dev_err(sdev->dev, "error: have no bar mapping\n"); 36483ee7ab1SDaniel Baluta return -EINVAL; 36583ee7ab1SDaniel Baluta } 36683ee7ab1SDaniel Baluta 36783ee7ab1SDaniel Baluta dev_dbg(sdev->dev, "ipc: DSP is ready 0x%8.8x offset 0x%x\n", 36883ee7ab1SDaniel Baluta msg_id, offset); 36983ee7ab1SDaniel Baluta 37083ee7ab1SDaniel Baluta /* no need to re-check version/ABI for subsequent boots */ 37183ee7ab1SDaniel Baluta if (!sdev->first_boot) 37283ee7ab1SDaniel Baluta return 0; 37383ee7ab1SDaniel Baluta 37483ee7ab1SDaniel Baluta /* copy data from the DSP FW ready offset */ 37583ee7ab1SDaniel Baluta sof_block_read(sdev, bar, offset, fw_ready, sizeof(*fw_ready)); 37683ee7ab1SDaniel Baluta 37783ee7ab1SDaniel Baluta /* make sure ABI version is compatible */ 37883ee7ab1SDaniel Baluta ret = snd_sof_ipc_valid(sdev); 37983ee7ab1SDaniel Baluta if (ret < 0) 38083ee7ab1SDaniel Baluta return ret; 38183ee7ab1SDaniel Baluta 38283ee7ab1SDaniel Baluta /* now check for extended data */ 38383ee7ab1SDaniel Baluta snd_sof_fw_parse_ext_data(sdev, bar, offset + 38483ee7ab1SDaniel Baluta sizeof(struct sof_ipc_fw_ready)); 38583ee7ab1SDaniel Baluta 38683ee7ab1SDaniel Baluta sof_get_windows(sdev); 38783ee7ab1SDaniel Baluta 38883ee7ab1SDaniel Baluta return 0; 38983ee7ab1SDaniel Baluta } 39083ee7ab1SDaniel Baluta EXPORT_SYMBOL(sof_fw_ready); 39183ee7ab1SDaniel Baluta 39270cd5254SLiam Girdwood /* generic module parser for mmaped DSPs */ 39370cd5254SLiam Girdwood int snd_sof_parse_module_memcpy(struct snd_sof_dev *sdev, 39470cd5254SLiam Girdwood struct snd_sof_mod_hdr *module) 39570cd5254SLiam Girdwood { 39670cd5254SLiam Girdwood struct snd_sof_blk_hdr *block; 3977198879eSDaniel Baluta int count, bar; 39870cd5254SLiam Girdwood u32 offset; 39970cd5254SLiam Girdwood size_t remaining; 40070cd5254SLiam Girdwood 40170cd5254SLiam Girdwood dev_dbg(sdev->dev, "new module size 0x%x blocks 0x%x type 0x%x\n", 40270cd5254SLiam Girdwood module->size, module->num_blocks, module->type); 40370cd5254SLiam Girdwood 40470cd5254SLiam Girdwood block = (struct snd_sof_blk_hdr *)((u8 *)module + sizeof(*module)); 40570cd5254SLiam Girdwood 40670cd5254SLiam Girdwood /* module->size doesn't include header size */ 40770cd5254SLiam Girdwood remaining = module->size; 40870cd5254SLiam Girdwood for (count = 0; count < module->num_blocks; count++) { 40970cd5254SLiam Girdwood /* check for wrap */ 41070cd5254SLiam Girdwood if (remaining < sizeof(*block)) { 41170cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 41270cd5254SLiam Girdwood return -EINVAL; 41370cd5254SLiam Girdwood } 41470cd5254SLiam Girdwood 41570cd5254SLiam Girdwood /* minus header size of block */ 41670cd5254SLiam Girdwood remaining -= sizeof(*block); 41770cd5254SLiam Girdwood 41870cd5254SLiam Girdwood if (block->size == 0) { 41970cd5254SLiam Girdwood dev_warn(sdev->dev, 42070cd5254SLiam Girdwood "warning: block %d size zero\n", count); 42170cd5254SLiam Girdwood dev_warn(sdev->dev, " type 0x%x offset 0x%x\n", 42270cd5254SLiam Girdwood block->type, block->offset); 42370cd5254SLiam Girdwood continue; 42470cd5254SLiam Girdwood } 42570cd5254SLiam Girdwood 42670cd5254SLiam Girdwood switch (block->type) { 42770cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_RSRVD0: 428441c58cfSDaniel Baluta case SOF_FW_BLK_TYPE_ROM...SOF_FW_BLK_TYPE_RSRVD14: 42970cd5254SLiam Girdwood continue; /* not handled atm */ 43070cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_IRAM: 43170cd5254SLiam Girdwood case SOF_FW_BLK_TYPE_DRAM: 432441c58cfSDaniel Baluta case SOF_FW_BLK_TYPE_SRAM: 43370cd5254SLiam Girdwood offset = block->offset; 4347198879eSDaniel Baluta bar = snd_sof_dsp_get_bar_index(sdev, block->type); 4357198879eSDaniel Baluta if (bar < 0) { 4367198879eSDaniel Baluta dev_err(sdev->dev, 4377198879eSDaniel Baluta "error: no BAR mapping for block type 0x%x\n", 4387198879eSDaniel Baluta block->type); 4397198879eSDaniel Baluta return bar; 4407198879eSDaniel Baluta } 44170cd5254SLiam Girdwood break; 44270cd5254SLiam Girdwood default: 44370cd5254SLiam Girdwood dev_err(sdev->dev, "error: bad type 0x%x for block 0x%x\n", 44470cd5254SLiam Girdwood block->type, count); 44570cd5254SLiam Girdwood return -EINVAL; 44670cd5254SLiam Girdwood } 44770cd5254SLiam Girdwood 44870cd5254SLiam Girdwood dev_dbg(sdev->dev, 44970cd5254SLiam Girdwood "block %d type 0x%x size 0x%x ==> offset 0x%x\n", 45070cd5254SLiam Girdwood count, block->type, block->size, offset); 45170cd5254SLiam Girdwood 45270cd5254SLiam Girdwood /* checking block->size to avoid unaligned access */ 45370cd5254SLiam Girdwood if (block->size % sizeof(u32)) { 45470cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid block size 0x%x\n", 45570cd5254SLiam Girdwood block->size); 45670cd5254SLiam Girdwood return -EINVAL; 45770cd5254SLiam Girdwood } 4587198879eSDaniel Baluta snd_sof_dsp_block_write(sdev, bar, offset, 45970cd5254SLiam Girdwood block + 1, block->size); 46070cd5254SLiam Girdwood 46170cd5254SLiam Girdwood if (remaining < block->size) { 46270cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 46370cd5254SLiam Girdwood return -EINVAL; 46470cd5254SLiam Girdwood } 46570cd5254SLiam Girdwood 46670cd5254SLiam Girdwood /* minus body size of block */ 46770cd5254SLiam Girdwood remaining -= block->size; 46870cd5254SLiam Girdwood /* next block */ 46970cd5254SLiam Girdwood block = (struct snd_sof_blk_hdr *)((u8 *)block + sizeof(*block) 47070cd5254SLiam Girdwood + block->size); 47170cd5254SLiam Girdwood } 47270cd5254SLiam Girdwood 47370cd5254SLiam Girdwood return 0; 47470cd5254SLiam Girdwood } 47570cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_parse_module_memcpy); 47670cd5254SLiam Girdwood 47792be17a5SKarol Trzcinski static int check_header(struct snd_sof_dev *sdev, const struct firmware *fw, 47892be17a5SKarol Trzcinski size_t fw_offset) 47970cd5254SLiam Girdwood { 48070cd5254SLiam Girdwood struct snd_sof_fw_header *header; 48192be17a5SKarol Trzcinski size_t fw_size = fw->size - fw_offset; 48292be17a5SKarol Trzcinski 483523773b9SKarol Trzcinski if (fw->size <= fw_offset) { 48492be17a5SKarol Trzcinski dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 48592be17a5SKarol Trzcinski return -EINVAL; 48692be17a5SKarol Trzcinski } 48770cd5254SLiam Girdwood 48870cd5254SLiam Girdwood /* Read the header information from the data pointer */ 48992be17a5SKarol Trzcinski header = (struct snd_sof_fw_header *)(fw->data + fw_offset); 49070cd5254SLiam Girdwood 49170cd5254SLiam Girdwood /* verify FW sig */ 49270cd5254SLiam Girdwood if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) { 49370cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid firmware signature\n"); 49470cd5254SLiam Girdwood return -EINVAL; 49570cd5254SLiam Girdwood } 49670cd5254SLiam Girdwood 49770cd5254SLiam Girdwood /* check size is valid */ 49892be17a5SKarol Trzcinski if (fw_size != header->file_size + sizeof(*header)) { 49970cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid filesize mismatch got 0x%zx expected 0x%zx\n", 50092be17a5SKarol Trzcinski fw_size, header->file_size + sizeof(*header)); 50170cd5254SLiam Girdwood return -EINVAL; 50270cd5254SLiam Girdwood } 50370cd5254SLiam Girdwood 50470cd5254SLiam Girdwood dev_dbg(sdev->dev, "header size=0x%x modules=0x%x abi=0x%x size=%zu\n", 50570cd5254SLiam Girdwood header->file_size, header->num_modules, 50670cd5254SLiam Girdwood header->abi, sizeof(*header)); 50770cd5254SLiam Girdwood 50870cd5254SLiam Girdwood return 0; 50970cd5254SLiam Girdwood } 51070cd5254SLiam Girdwood 51192be17a5SKarol Trzcinski static int load_modules(struct snd_sof_dev *sdev, const struct firmware *fw, 51292be17a5SKarol Trzcinski size_t fw_offset) 51370cd5254SLiam Girdwood { 51470cd5254SLiam Girdwood struct snd_sof_fw_header *header; 51570cd5254SLiam Girdwood struct snd_sof_mod_hdr *module; 51670cd5254SLiam Girdwood int (*load_module)(struct snd_sof_dev *sof_dev, 51770cd5254SLiam Girdwood struct snd_sof_mod_hdr *hdr); 51870cd5254SLiam Girdwood int ret, count; 51970cd5254SLiam Girdwood size_t remaining; 52070cd5254SLiam Girdwood 52192be17a5SKarol Trzcinski header = (struct snd_sof_fw_header *)(fw->data + fw_offset); 52270cd5254SLiam Girdwood load_module = sof_ops(sdev)->load_module; 52370cd5254SLiam Girdwood if (!load_module) 52470cd5254SLiam Girdwood return -EINVAL; 52570cd5254SLiam Girdwood 52670cd5254SLiam Girdwood /* parse each module */ 52792be17a5SKarol Trzcinski module = (struct snd_sof_mod_hdr *)(fw->data + fw_offset + 52892be17a5SKarol Trzcinski sizeof(*header)); 52992be17a5SKarol Trzcinski remaining = fw->size - sizeof(*header) - fw_offset; 53070cd5254SLiam Girdwood /* check for wrap */ 53170cd5254SLiam Girdwood if (remaining > fw->size) { 53270cd5254SLiam Girdwood dev_err(sdev->dev, "error: fw size smaller than header size\n"); 53370cd5254SLiam Girdwood return -EINVAL; 53470cd5254SLiam Girdwood } 53570cd5254SLiam Girdwood 53670cd5254SLiam Girdwood for (count = 0; count < header->num_modules; count++) { 53770cd5254SLiam Girdwood /* check for wrap */ 53870cd5254SLiam Girdwood if (remaining < sizeof(*module)) { 53970cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 54070cd5254SLiam Girdwood return -EINVAL; 54170cd5254SLiam Girdwood } 54270cd5254SLiam Girdwood 54370cd5254SLiam Girdwood /* minus header size of module */ 54470cd5254SLiam Girdwood remaining -= sizeof(*module); 54570cd5254SLiam Girdwood 54670cd5254SLiam Girdwood /* module */ 54770cd5254SLiam Girdwood ret = load_module(sdev, module); 54870cd5254SLiam Girdwood if (ret < 0) { 54970cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid module %d\n", count); 55070cd5254SLiam Girdwood return ret; 55170cd5254SLiam Girdwood } 55270cd5254SLiam Girdwood 55370cd5254SLiam Girdwood if (remaining < module->size) { 55470cd5254SLiam Girdwood dev_err(sdev->dev, "error: not enough data remaining\n"); 55570cd5254SLiam Girdwood return -EINVAL; 55670cd5254SLiam Girdwood } 55770cd5254SLiam Girdwood 55870cd5254SLiam Girdwood /* minus body size of module */ 55970cd5254SLiam Girdwood remaining -= module->size; 56070cd5254SLiam Girdwood module = (struct snd_sof_mod_hdr *)((u8 *)module 56170cd5254SLiam Girdwood + sizeof(*module) + module->size); 56270cd5254SLiam Girdwood } 56370cd5254SLiam Girdwood 56470cd5254SLiam Girdwood return 0; 56570cd5254SLiam Girdwood } 56670cd5254SLiam Girdwood 56770cd5254SLiam Girdwood int snd_sof_load_firmware_raw(struct snd_sof_dev *sdev) 56870cd5254SLiam Girdwood { 56970cd5254SLiam Girdwood struct snd_sof_pdata *plat_data = sdev->pdata; 57070cd5254SLiam Girdwood const char *fw_filename; 571*a80cf198SKarol Trzcinski ssize_t ext_man_size; 57270cd5254SLiam Girdwood int ret; 57370cd5254SLiam Girdwood 57470cd5254SLiam Girdwood /* Don't request firmware again if firmware is already requested */ 57570cd5254SLiam Girdwood if (plat_data->fw) 57670cd5254SLiam Girdwood return 0; 57770cd5254SLiam Girdwood 57870cd5254SLiam Girdwood fw_filename = kasprintf(GFP_KERNEL, "%s/%s", 57970cd5254SLiam Girdwood plat_data->fw_filename_prefix, 58070cd5254SLiam Girdwood plat_data->fw_filename); 58170cd5254SLiam Girdwood if (!fw_filename) 58270cd5254SLiam Girdwood return -ENOMEM; 58370cd5254SLiam Girdwood 58470cd5254SLiam Girdwood ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); 58570cd5254SLiam Girdwood 58670cd5254SLiam Girdwood if (ret < 0) { 58770cd5254SLiam Girdwood dev_err(sdev->dev, "error: request firmware %s failed err: %d\n", 58870cd5254SLiam Girdwood fw_filename, ret); 589*a80cf198SKarol Trzcinski goto err; 590490a625bSPierre-Louis Bossart } else { 591490a625bSPierre-Louis Bossart dev_dbg(sdev->dev, "request_firmware %s successful\n", 592490a625bSPierre-Louis Bossart fw_filename); 59370cd5254SLiam Girdwood } 59470cd5254SLiam Girdwood 595*a80cf198SKarol Trzcinski /* check for extended manifest */ 596*a80cf198SKarol Trzcinski ext_man_size = snd_sof_fw_ext_man_parse(sdev, plat_data->fw); 597*a80cf198SKarol Trzcinski if (ext_man_size > 0) { 598*a80cf198SKarol Trzcinski /* when no error occurred, drop extended manifest */ 599*a80cf198SKarol Trzcinski plat_data->fw_offset = ext_man_size; 600*a80cf198SKarol Trzcinski } else if (!ext_man_size) { 601*a80cf198SKarol Trzcinski /* No extended manifest, so nothing to skip during FW load */ 602*a80cf198SKarol Trzcinski dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); 603*a80cf198SKarol Trzcinski } else { 604*a80cf198SKarol Trzcinski ret = ext_man_size; 605*a80cf198SKarol Trzcinski dev_err(sdev->dev, "error: firmware %s contains unsupported or invalid extended manifest: %d\n", 606*a80cf198SKarol Trzcinski fw_filename, ret); 607*a80cf198SKarol Trzcinski } 608*a80cf198SKarol Trzcinski 609*a80cf198SKarol Trzcinski err: 61070cd5254SLiam Girdwood kfree(fw_filename); 61170cd5254SLiam Girdwood 61270cd5254SLiam Girdwood return ret; 61370cd5254SLiam Girdwood } 61470cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_raw); 61570cd5254SLiam Girdwood 61670cd5254SLiam Girdwood int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 61770cd5254SLiam Girdwood { 61870cd5254SLiam Girdwood struct snd_sof_pdata *plat_data = sdev->pdata; 61970cd5254SLiam Girdwood int ret; 62070cd5254SLiam Girdwood 62170cd5254SLiam Girdwood ret = snd_sof_load_firmware_raw(sdev); 62270cd5254SLiam Girdwood if (ret < 0) 62370cd5254SLiam Girdwood return ret; 62470cd5254SLiam Girdwood 62570cd5254SLiam Girdwood /* make sure the FW header and file is valid */ 62692be17a5SKarol Trzcinski ret = check_header(sdev, plat_data->fw, plat_data->fw_offset); 62770cd5254SLiam Girdwood if (ret < 0) { 62870cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid FW header\n"); 62970cd5254SLiam Girdwood goto error; 63070cd5254SLiam Girdwood } 63170cd5254SLiam Girdwood 63270cd5254SLiam Girdwood /* prepare the DSP for FW loading */ 63370cd5254SLiam Girdwood ret = snd_sof_dsp_reset(sdev); 63470cd5254SLiam Girdwood if (ret < 0) { 63570cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to reset DSP\n"); 63670cd5254SLiam Girdwood goto error; 63770cd5254SLiam Girdwood } 63870cd5254SLiam Girdwood 63970cd5254SLiam Girdwood /* parse and load firmware modules to DSP */ 64092be17a5SKarol Trzcinski ret = load_modules(sdev, plat_data->fw, plat_data->fw_offset); 64170cd5254SLiam Girdwood if (ret < 0) { 64270cd5254SLiam Girdwood dev_err(sdev->dev, "error: invalid FW modules\n"); 64370cd5254SLiam Girdwood goto error; 64470cd5254SLiam Girdwood } 64570cd5254SLiam Girdwood 64670cd5254SLiam Girdwood return 0; 64770cd5254SLiam Girdwood 64870cd5254SLiam Girdwood error: 64970cd5254SLiam Girdwood release_firmware(plat_data->fw); 65070cd5254SLiam Girdwood plat_data->fw = NULL; 65170cd5254SLiam Girdwood return ret; 65270cd5254SLiam Girdwood 65370cd5254SLiam Girdwood } 65470cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware_memcpy); 65570cd5254SLiam Girdwood 65670cd5254SLiam Girdwood int snd_sof_load_firmware(struct snd_sof_dev *sdev) 65770cd5254SLiam Girdwood { 65870cd5254SLiam Girdwood dev_dbg(sdev->dev, "loading firmware\n"); 65970cd5254SLiam Girdwood 66070cd5254SLiam Girdwood if (sof_ops(sdev)->load_firmware) 66170cd5254SLiam Girdwood return sof_ops(sdev)->load_firmware(sdev); 66270cd5254SLiam Girdwood return 0; 66370cd5254SLiam Girdwood } 66470cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_load_firmware); 66570cd5254SLiam Girdwood 66670cd5254SLiam Girdwood int snd_sof_run_firmware(struct snd_sof_dev *sdev) 66770cd5254SLiam Girdwood { 66870cd5254SLiam Girdwood int ret; 66970cd5254SLiam Girdwood int init_core_mask; 67070cd5254SLiam Girdwood 67170cd5254SLiam Girdwood init_waitqueue_head(&sdev->boot_wait); 67270cd5254SLiam Girdwood 6735c9714f6SRanjani Sridharan /* create read-only fw_version debugfs to store boot version info */ 67470cd5254SLiam Girdwood if (sdev->first_boot) { 67570cd5254SLiam Girdwood ret = snd_sof_debugfs_buf_item(sdev, &sdev->fw_version, 67670cd5254SLiam Girdwood sizeof(sdev->fw_version), 6775c9714f6SRanjani Sridharan "fw_version", 0444); 67870cd5254SLiam Girdwood /* errors are only due to memory allocation, not debugfs */ 67970cd5254SLiam Girdwood if (ret < 0) { 68070cd5254SLiam Girdwood dev_err(sdev->dev, "error: snd_sof_debugfs_buf_item failed\n"); 68170cd5254SLiam Girdwood return ret; 68270cd5254SLiam Girdwood } 68370cd5254SLiam Girdwood } 68470cd5254SLiam Girdwood 68570cd5254SLiam Girdwood /* perform pre fw run operations */ 68670cd5254SLiam Girdwood ret = snd_sof_dsp_pre_fw_run(sdev); 68770cd5254SLiam Girdwood if (ret < 0) { 68870cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed pre fw run op\n"); 68970cd5254SLiam Girdwood return ret; 69070cd5254SLiam Girdwood } 69170cd5254SLiam Girdwood 69270cd5254SLiam Girdwood dev_dbg(sdev->dev, "booting DSP firmware\n"); 69370cd5254SLiam Girdwood 69470cd5254SLiam Girdwood /* boot the firmware on the DSP */ 69570cd5254SLiam Girdwood ret = snd_sof_dsp_run(sdev); 69670cd5254SLiam Girdwood if (ret < 0) { 69770cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed to reset DSP\n"); 69870cd5254SLiam Girdwood return ret; 69970cd5254SLiam Girdwood } 70070cd5254SLiam Girdwood 70170cd5254SLiam Girdwood init_core_mask = ret; 70270cd5254SLiam Girdwood 7036ca5cecbSRanjani Sridharan /* 7046ca5cecbSRanjani Sridharan * now wait for the DSP to boot. There are 3 possible outcomes: 7056ca5cecbSRanjani Sridharan * 1. Boot wait times out indicating FW boot failure. 7066ca5cecbSRanjani Sridharan * 2. FW boots successfully and fw_ready op succeeds. 7076ca5cecbSRanjani Sridharan * 3. FW boots but fw_ready op fails. 7086ca5cecbSRanjani Sridharan */ 7096ca5cecbSRanjani Sridharan ret = wait_event_timeout(sdev->boot_wait, 7106ca5cecbSRanjani Sridharan sdev->fw_state > SOF_FW_BOOT_IN_PROGRESS, 71170cd5254SLiam Girdwood msecs_to_jiffies(sdev->boot_timeout)); 71270cd5254SLiam Girdwood if (ret == 0) { 71370cd5254SLiam Girdwood dev_err(sdev->dev, "error: firmware boot failure\n"); 71470cd5254SLiam Girdwood snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX | 71570cd5254SLiam Girdwood SOF_DBG_TEXT | SOF_DBG_PCI); 7166ca5cecbSRanjani Sridharan sdev->fw_state = SOF_FW_BOOT_FAILED; 71770cd5254SLiam Girdwood return -EIO; 71870cd5254SLiam Girdwood } 71970cd5254SLiam Girdwood 7206ca5cecbSRanjani Sridharan if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) 721904f353dSHans de Goede dev_dbg(sdev->dev, "firmware boot complete\n"); 7226ca5cecbSRanjani Sridharan else 7236ca5cecbSRanjani Sridharan return -EIO; /* FW boots but fw_ready op failed */ 72470cd5254SLiam Girdwood 72570cd5254SLiam Girdwood /* perform post fw run operations */ 72670cd5254SLiam Girdwood ret = snd_sof_dsp_post_fw_run(sdev); 72770cd5254SLiam Girdwood if (ret < 0) { 72870cd5254SLiam Girdwood dev_err(sdev->dev, "error: failed post fw run op\n"); 72970cd5254SLiam Girdwood return ret; 73070cd5254SLiam Girdwood } 73170cd5254SLiam Girdwood 73270cd5254SLiam Girdwood /* fw boot is complete. Update the active cores mask */ 73370cd5254SLiam Girdwood sdev->enabled_cores_mask = init_core_mask; 73470cd5254SLiam Girdwood 73570cd5254SLiam Girdwood return 0; 73670cd5254SLiam Girdwood } 73770cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_run_firmware); 73870cd5254SLiam Girdwood 73970cd5254SLiam Girdwood void snd_sof_fw_unload(struct snd_sof_dev *sdev) 74070cd5254SLiam Girdwood { 74170cd5254SLiam Girdwood /* TODO: support module unloading at runtime */ 74270cd5254SLiam Girdwood } 74370cd5254SLiam Girdwood EXPORT_SYMBOL(snd_sof_fw_unload); 744