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