loader.c (15355B)
1// SPDX-License-Identifier: GPL-2.0-only 2// 3// Copyright(c) 2020 Intel Corporation. All rights reserved. 4// 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 250 16 17#define FW_SIGNATURE "$SST" 18#define FW_SIGNATURE_SIZE 4 19 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 int 179catpt_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 be 439 * 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 else 573 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}