linux/sound/soc/intel/catpt/loader.c
<<
>>
Prefs
   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 (!catpt_resource_overlapping(&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}
 672