cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sst_loader.c (12097B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  sst_dsp.c - Intel SST Driver for audio engine
      4 *
      5 *  Copyright (C) 2008-14	Intel Corp
      6 *  Authors:	Vinod Koul <vinod.koul@intel.com>
      7 *		Harsha Priya <priya.harsha@intel.com>
      8 *		Dharageswari R <dharageswari.r@intel.com>
      9 *		KP Jeeja <jeeja.kp@intel.com>
     10 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     11 *
     12 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     13 *
     14 *  This file contains all dsp controlling functions like firmware download,
     15 * setting/resetting dsp cores, etc
     16 */
     17#include <linux/pci.h>
     18#include <linux/delay.h>
     19#include <linux/fs.h>
     20#include <linux/sched.h>
     21#include <linux/firmware.h>
     22#include <linux/dmaengine.h>
     23#include <linux/pm_runtime.h>
     24#include <linux/pm_qos.h>
     25#include <sound/core.h>
     26#include <sound/pcm.h>
     27#include <sound/soc.h>
     28#include <sound/compress_driver.h>
     29#include <asm/platform_sst_audio.h>
     30#include "../sst-mfld-platform.h"
     31#include "sst.h"
     32
     33void memcpy32_toio(void __iomem *dst, const void *src, int count)
     34{
     35	/* __iowrite32_copy uses 32-bit count values so divide by 4 for
     36	 * right count in words
     37	 */
     38	__iowrite32_copy(dst, src, count / 4);
     39}
     40
     41void memcpy32_fromio(void *dst, const void __iomem *src, int count)
     42{
     43	/* __ioread32_copy uses 32-bit count values so divide by 4 for
     44	 * right count in words
     45	 */
     46	__ioread32_copy(dst, src, count / 4);
     47}
     48
     49/**
     50 * intel_sst_reset_dsp_mrfld - Resetting SST DSP
     51 * @sst_drv_ctx: intel_sst_drv context pointer
     52 *
     53 * This resets DSP in case of MRFLD platfroms
     54 */
     55int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
     56{
     57	union config_status_reg_mrfld csr;
     58
     59	dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
     60	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
     61
     62	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
     63
     64	csr.full |= 0x7;
     65	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
     66	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
     67
     68	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
     69
     70	csr.full &= ~(0x1);
     71	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
     72
     73	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
     74	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
     75	return 0;
     76}
     77
     78/**
     79 * sst_start_mrfld - Start the SST DSP processor
     80 * @sst_drv_ctx: intel_sst_drv context pointer
     81 *
     82 * This starts the DSP in MERRIFIELD platfroms
     83 */
     84int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
     85{
     86	union config_status_reg_mrfld csr;
     87
     88	dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
     89	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
     90	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
     91
     92	csr.full |= 0x7;
     93	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
     94
     95	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
     96	dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
     97
     98	csr.part.xt_snoop = 1;
     99	csr.full &= ~(0x5);
    100	sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
    101
    102	csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
    103	dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
    104			csr.full);
    105	return 0;
    106}
    107
    108static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
    109		struct fw_module_header **module, u32 *num_modules)
    110{
    111	struct sst_fw_header *header;
    112	const void *sst_fw_in_mem = ctx->fw_in_mem;
    113
    114	dev_dbg(ctx->dev, "Enter\n");
    115
    116	/* Read the header information from the data pointer */
    117	header = (struct sst_fw_header *)sst_fw_in_mem;
    118	dev_dbg(ctx->dev,
    119		"header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
    120		header->signature, header->file_size, header->modules,
    121		header->file_format, sizeof(*header));
    122
    123	/* verify FW */
    124	if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
    125		(size != header->file_size + sizeof(*header))) {
    126		/* Invalid FW signature */
    127		dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
    128		return -EINVAL;
    129	}
    130	*num_modules = header->modules;
    131	*module = (void *)sst_fw_in_mem + sizeof(*header);
    132
    133	return 0;
    134}
    135
    136/*
    137 * sst_fill_memcpy_list - Fill the memcpy list
    138 *
    139 * @memcpy_list: List to be filled
    140 * @destn: Destination addr to be filled in the list
    141 * @src: Source addr to be filled in the list
    142 * @size: Size to be filled in the list
    143 *
    144 * Adds the node to the list after required fields
    145 * are populated in the node
    146 */
    147static int sst_fill_memcpy_list(struct list_head *memcpy_list,
    148			void *destn, const void *src, u32 size, bool is_io)
    149{
    150	struct sst_memcpy_list *listnode;
    151
    152	listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
    153	if (listnode == NULL)
    154		return -ENOMEM;
    155	listnode->dstn = destn;
    156	listnode->src = src;
    157	listnode->size = size;
    158	listnode->is_io = is_io;
    159	list_add_tail(&listnode->memcpylist, memcpy_list);
    160
    161	return 0;
    162}
    163
    164/**
    165 * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
    166 *
    167 * @sst_drv_ctx		: driver context
    168 * @module		: FW module header
    169 * @memcpy_list	: Pointer to the list to be populated
    170 * Create the memcpy list as the number of block to be copied
    171 * returns error or 0 if module sizes are proper
    172 */
    173static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
    174		struct fw_module_header *module, struct list_head *memcpy_list)
    175{
    176	struct fw_block_info *block;
    177	u32 count;
    178	int ret_val = 0;
    179	void __iomem *ram_iomem;
    180
    181	dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
    182			module->signature, module->mod_size,
    183			module->blocks, module->type);
    184	dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
    185
    186	block = (void *)module + sizeof(*module);
    187
    188	for (count = 0; count < module->blocks; count++) {
    189		if (block->size <= 0) {
    190			dev_err(sst_drv_ctx->dev, "block size invalid\n");
    191			return -EINVAL;
    192		}
    193		switch (block->type) {
    194		case SST_IRAM:
    195			ram_iomem = sst_drv_ctx->iram;
    196			break;
    197		case SST_DRAM:
    198			ram_iomem = sst_drv_ctx->dram;
    199			break;
    200		case SST_DDR:
    201			ram_iomem = sst_drv_ctx->ddr;
    202			break;
    203		case SST_CUSTOM_INFO:
    204			block = (void *)block + sizeof(*block) + block->size;
    205			continue;
    206		default:
    207			dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
    208					block->type, count);
    209			return -EINVAL;
    210		}
    211
    212		ret_val = sst_fill_memcpy_list(memcpy_list,
    213				ram_iomem + block->ram_offset,
    214				(void *)block + sizeof(*block), block->size, 1);
    215		if (ret_val)
    216			return ret_val;
    217
    218		block = (void *)block + sizeof(*block) + block->size;
    219	}
    220	return 0;
    221}
    222
    223/**
    224 * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
    225 *
    226 * @ctx			: pointer to drv context
    227 * @size		: size of the firmware
    228 * @fw_list		: pointer to list_head to be populated
    229 * This function parses the FW image and saves the parsed image in the list
    230 * for memcpy
    231 */
    232static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
    233				struct list_head *fw_list)
    234{
    235	struct fw_module_header *module;
    236	u32 count, num_modules;
    237	int ret_val;
    238
    239	ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
    240	if (ret_val)
    241		return ret_val;
    242
    243	for (count = 0; count < num_modules; count++) {
    244		ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
    245		if (ret_val)
    246			return ret_val;
    247		module = (void *)module + sizeof(*module) + module->mod_size;
    248	}
    249
    250	return 0;
    251}
    252
    253/**
    254 * sst_do_memcpy - function initiates the memcpy
    255 *
    256 * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
    257 *
    258 * Triggers the memcpy
    259 */
    260static void sst_do_memcpy(struct list_head *memcpy_list)
    261{
    262	struct sst_memcpy_list *listnode;
    263
    264	list_for_each_entry(listnode, memcpy_list, memcpylist) {
    265		if (listnode->is_io)
    266			memcpy32_toio((void __iomem *)listnode->dstn,
    267					listnode->src, listnode->size);
    268		else
    269			memcpy(listnode->dstn, listnode->src, listnode->size);
    270	}
    271}
    272
    273void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
    274{
    275	struct sst_memcpy_list *listnode, *tmplistnode;
    276
    277	/* Free the list */
    278	list_for_each_entry_safe(listnode, tmplistnode,
    279				 &sst_drv_ctx->memcpy_list, memcpylist) {
    280		list_del(&listnode->memcpylist);
    281		kfree(listnode);
    282	}
    283}
    284
    285static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
    286		const struct firmware *fw)
    287{
    288	int retval = 0;
    289
    290	sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
    291	if (!sst->fw_in_mem) {
    292		retval = -ENOMEM;
    293		goto end_release;
    294	}
    295	dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
    296	dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
    297	memcpy(sst->fw_in_mem, fw->data, fw->size);
    298	retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
    299	if (retval) {
    300		dev_err(sst->dev, "Failed to parse fw\n");
    301		kfree(sst->fw_in_mem);
    302		sst->fw_in_mem = NULL;
    303	}
    304
    305end_release:
    306	release_firmware(fw);
    307	return retval;
    308
    309}
    310
    311void sst_firmware_load_cb(const struct firmware *fw, void *context)
    312{
    313	struct intel_sst_drv *ctx = context;
    314
    315	dev_dbg(ctx->dev, "Enter\n");
    316
    317	if (fw == NULL) {
    318		dev_err(ctx->dev, "request fw failed\n");
    319		return;
    320	}
    321
    322	mutex_lock(&ctx->sst_lock);
    323
    324	if (ctx->sst_state != SST_RESET ||
    325			ctx->fw_in_mem != NULL) {
    326		release_firmware(fw);
    327		mutex_unlock(&ctx->sst_lock);
    328		return;
    329	}
    330
    331	dev_dbg(ctx->dev, "Request Fw completed\n");
    332	sst_cache_and_parse_fw(ctx, fw);
    333	mutex_unlock(&ctx->sst_lock);
    334}
    335
    336/*
    337 * sst_request_fw - requests audio fw from kernel and saves a copy
    338 *
    339 * This function requests the SST FW from the kernel, parses it and
    340 * saves a copy in the driver context
    341 */
    342static int sst_request_fw(struct intel_sst_drv *sst)
    343{
    344	int retval = 0;
    345	const struct firmware *fw;
    346
    347	retval = request_firmware(&fw, sst->firmware_name, sst->dev);
    348	if (retval) {
    349		dev_err(sst->dev, "request fw failed %d\n", retval);
    350		return retval;
    351	}
    352	if (fw == NULL) {
    353		dev_err(sst->dev, "fw is returning as null\n");
    354		return -EINVAL;
    355	}
    356	mutex_lock(&sst->sst_lock);
    357	retval = sst_cache_and_parse_fw(sst, fw);
    358	mutex_unlock(&sst->sst_lock);
    359
    360	return retval;
    361}
    362
    363/*
    364 * Writing the DDR physical base to DCCM offset
    365 * so that FW can use it to setup TLB
    366 */
    367static void sst_dccm_config_write(void __iomem *dram_base,
    368		unsigned int ddr_base)
    369{
    370	void __iomem *addr;
    371	u32 bss_reset = 0;
    372
    373	addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
    374	memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
    375	bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
    376	addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
    377	memcpy32_toio(addr, &bss_reset, sizeof(u32));
    378
    379}
    380
    381void sst_post_download_mrfld(struct intel_sst_drv *ctx)
    382{
    383	sst_dccm_config_write(ctx->dram, ctx->ddr_base);
    384	dev_dbg(ctx->dev, "config written to DCCM\n");
    385}
    386
    387/**
    388 * sst_load_fw - function to load FW into DSP
    389 * @sst_drv_ctx: intel_sst_drv context pointer
    390 *
    391 * Transfers the FW to DSP using dma/memcpy
    392 */
    393int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
    394{
    395	int ret_val = 0;
    396	struct sst_block *block;
    397
    398	dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
    399
    400	if (sst_drv_ctx->sst_state !=  SST_RESET)
    401		return -EAGAIN;
    402
    403	if (!sst_drv_ctx->fw_in_mem) {
    404		dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
    405		ret_val = sst_request_fw(sst_drv_ctx);
    406		if (ret_val)
    407			return ret_val;
    408	}
    409
    410	block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
    411	if (block == NULL)
    412		return -ENOMEM;
    413
    414	/* Prevent C-states beyond C6 */
    415	cpu_latency_qos_update_request(sst_drv_ctx->qos, 0);
    416
    417	sst_drv_ctx->sst_state = SST_FW_LOADING;
    418
    419	ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
    420	if (ret_val)
    421		goto restore;
    422
    423	sst_do_memcpy(&sst_drv_ctx->memcpy_list);
    424
    425	/* Write the DRAM/DCCM config before enabling FW */
    426	if (sst_drv_ctx->ops->post_download)
    427		sst_drv_ctx->ops->post_download(sst_drv_ctx);
    428
    429	/* bring sst out of reset */
    430	ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
    431	if (ret_val)
    432		goto restore;
    433
    434	ret_val = sst_wait_timeout(sst_drv_ctx, block);
    435	if (ret_val) {
    436		dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
    437		/* FW download failed due to timeout */
    438		ret_val = -EBUSY;
    439
    440	}
    441
    442
    443restore:
    444	/* Re-enable Deeper C-states beyond C6 */
    445	cpu_latency_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
    446	sst_free_block(sst_drv_ctx, block);
    447	dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
    448
    449	if (sst_drv_ctx->ops->restore_dsp_context)
    450		sst_drv_ctx->ops->restore_dsp_context();
    451	sst_drv_ctx->sst_state = SST_FW_RUNNING;
    452	return ret_val;
    453}
    454