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

dsp.c (7695B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2//
      3// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
      4//
      5// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
      6//          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
      7//
      8
      9#include <sound/hdaudio_ext.h>
     10#include "avs.h"
     11#include "registers.h"
     12#include "trace.h"
     13
     14#define AVS_ADSPCS_INTERVAL_US		500
     15#define AVS_ADSPCS_TIMEOUT_US		50000
     16
     17int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
     18{
     19	u32 value, mask, reg;
     20	int ret;
     21
     22	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
     23	trace_avs_dsp_core_op(value, core_mask, "power", power);
     24
     25	mask = AVS_ADSPCS_SPA_MASK(core_mask);
     26	value = power ? mask : 0;
     27
     28	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
     29
     30	mask = AVS_ADSPCS_CPA_MASK(core_mask);
     31	value = power ? mask : 0;
     32
     33	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
     34				       reg, (reg & mask) == value,
     35				       AVS_ADSPCS_INTERVAL_US,
     36				       AVS_ADSPCS_TIMEOUT_US);
     37	if (ret)
     38		dev_err(adev->dev, "core_mask %d power %s failed: %d\n",
     39			core_mask, power ? "on" : "off", ret);
     40
     41	return ret;
     42}
     43
     44int avs_dsp_core_reset(struct avs_dev *adev, u32 core_mask, bool reset)
     45{
     46	u32 value, mask, reg;
     47	int ret;
     48
     49	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
     50	trace_avs_dsp_core_op(value, core_mask, "reset", reset);
     51
     52	mask = AVS_ADSPCS_CRST_MASK(core_mask);
     53	value = reset ? mask : 0;
     54
     55	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
     56
     57	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
     58				       reg, (reg & mask) == value,
     59				       AVS_ADSPCS_INTERVAL_US,
     60				       AVS_ADSPCS_TIMEOUT_US);
     61	if (ret)
     62		dev_err(adev->dev, "core_mask %d %s reset failed: %d\n",
     63			core_mask, reset ? "enter" : "exit", ret);
     64
     65	return ret;
     66}
     67
     68int avs_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
     69{
     70	u32 value, mask, reg;
     71	int ret;
     72
     73	value = snd_hdac_adsp_readl(adev, AVS_ADSP_REG_ADSPCS);
     74	trace_avs_dsp_core_op(value, core_mask, "stall", stall);
     75
     76	mask = AVS_ADSPCS_CSTALL_MASK(core_mask);
     77	value = stall ? mask : 0;
     78
     79	snd_hdac_adsp_updatel(adev, AVS_ADSP_REG_ADSPCS, mask, value);
     80
     81	ret = snd_hdac_adsp_readl_poll(adev, AVS_ADSP_REG_ADSPCS,
     82				       reg, (reg & mask) == value,
     83				       AVS_ADSPCS_INTERVAL_US,
     84				       AVS_ADSPCS_TIMEOUT_US);
     85	if (ret)
     86		dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
     87			core_mask, stall ? "" : "un", ret);
     88
     89	return ret;
     90}
     91
     92int avs_dsp_core_enable(struct avs_dev *adev, u32 core_mask)
     93{
     94	int ret;
     95
     96	ret = avs_dsp_op(adev, power, core_mask, true);
     97	if (ret)
     98		return ret;
     99
    100	ret = avs_dsp_op(adev, reset, core_mask, false);
    101	if (ret)
    102		return ret;
    103
    104	return avs_dsp_op(adev, stall, core_mask, false);
    105}
    106
    107int avs_dsp_core_disable(struct avs_dev *adev, u32 core_mask)
    108{
    109	/* No error checks to allow for complete DSP shutdown. */
    110	avs_dsp_op(adev, stall, core_mask, true);
    111	avs_dsp_op(adev, reset, core_mask, true);
    112
    113	return avs_dsp_op(adev, power, core_mask, false);
    114}
    115
    116static int avs_dsp_enable(struct avs_dev *adev, u32 core_mask)
    117{
    118	u32 mask;
    119	int ret;
    120
    121	ret = avs_dsp_core_enable(adev, core_mask);
    122	if (ret < 0)
    123		return ret;
    124
    125	mask = core_mask & ~AVS_MAIN_CORE_MASK;
    126	if (!mask)
    127		/*
    128		 * without main core, fw is dead anyway
    129		 * so setting D0 for it is futile.
    130		 */
    131		return 0;
    132
    133	ret = avs_ipc_set_dx(adev, mask, true);
    134	return AVS_IPC_RET(ret);
    135}
    136
    137static int avs_dsp_disable(struct avs_dev *adev, u32 core_mask)
    138{
    139	int ret;
    140
    141	ret = avs_ipc_set_dx(adev, core_mask, false);
    142	if (ret)
    143		return AVS_IPC_RET(ret);
    144
    145	return avs_dsp_core_disable(adev, core_mask);
    146}
    147
    148static int avs_dsp_get_core(struct avs_dev *adev, u32 core_id)
    149{
    150	u32 mask;
    151	int ret;
    152
    153	mask = BIT_MASK(core_id);
    154	if (mask == AVS_MAIN_CORE_MASK)
    155		/* nothing to do for main core */
    156		return 0;
    157	if (core_id >= adev->hw_cfg.dsp_cores) {
    158		ret = -EINVAL;
    159		goto err;
    160	}
    161
    162	adev->core_refs[core_id]++;
    163	if (adev->core_refs[core_id] == 1) {
    164		/*
    165		 * No cores other than main-core can be running for DSP
    166		 * to achieve d0ix. Conscious SET_D0IX IPC failure is permitted,
    167		 * simply d0ix power state will no longer be attempted.
    168		 */
    169		ret = avs_dsp_disable_d0ix(adev);
    170		if (ret && ret != -AVS_EIPC)
    171			goto err_disable_d0ix;
    172
    173		ret = avs_dsp_enable(adev, mask);
    174		if (ret)
    175			goto err_enable_dsp;
    176	}
    177
    178	return 0;
    179
    180err_enable_dsp:
    181	avs_dsp_enable_d0ix(adev);
    182err_disable_d0ix:
    183	adev->core_refs[core_id]--;
    184err:
    185	dev_err(adev->dev, "get core %d failed: %d\n", core_id, ret);
    186	return ret;
    187}
    188
    189static int avs_dsp_put_core(struct avs_dev *adev, u32 core_id)
    190{
    191	u32 mask;
    192	int ret;
    193
    194	mask = BIT_MASK(core_id);
    195	if (mask == AVS_MAIN_CORE_MASK)
    196		/* nothing to do for main core */
    197		return 0;
    198	if (core_id >= adev->hw_cfg.dsp_cores) {
    199		ret = -EINVAL;
    200		goto err;
    201	}
    202
    203	adev->core_refs[core_id]--;
    204	if (!adev->core_refs[core_id]) {
    205		ret = avs_dsp_disable(adev, mask);
    206		if (ret)
    207			goto err;
    208
    209		/* Match disable_d0ix in avs_dsp_get_core(). */
    210		avs_dsp_enable_d0ix(adev);
    211	}
    212
    213	return 0;
    214err:
    215	dev_err(adev->dev, "put core %d failed: %d\n", core_id, ret);
    216	return ret;
    217}
    218
    219int avs_dsp_init_module(struct avs_dev *adev, u16 module_id, u8 ppl_instance_id,
    220			u8 core_id, u8 domain, void *param, u32 param_size,
    221			u16 *instance_id)
    222{
    223	struct avs_module_entry mentry;
    224	bool was_loaded = false;
    225	int ret, id;
    226
    227	id = avs_module_id_alloc(adev, module_id);
    228	if (id < 0)
    229		return id;
    230
    231	ret = avs_get_module_id_entry(adev, module_id, &mentry);
    232	if (ret)
    233		goto err_mod_entry;
    234
    235	ret = avs_dsp_get_core(adev, core_id);
    236	if (ret)
    237		goto err_mod_entry;
    238
    239	/* Load code into memory if this is the first instance. */
    240	if (!id && !avs_module_entry_is_loaded(&mentry)) {
    241		ret = avs_dsp_op(adev, transfer_mods, true, &mentry, 1);
    242		if (ret) {
    243			dev_err(adev->dev, "load modules failed: %d\n", ret);
    244			goto err_mod_entry;
    245		}
    246		was_loaded = true;
    247	}
    248
    249	ret = avs_ipc_init_instance(adev, module_id, id, ppl_instance_id,
    250				    core_id, domain, param, param_size);
    251	if (ret) {
    252		ret = AVS_IPC_RET(ret);
    253		goto err_ipc;
    254	}
    255
    256	*instance_id = id;
    257	return 0;
    258
    259err_ipc:
    260	if (was_loaded)
    261		avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
    262	avs_dsp_put_core(adev, core_id);
    263err_mod_entry:
    264	avs_module_id_free(adev, module_id, id);
    265	return ret;
    266}
    267
    268void avs_dsp_delete_module(struct avs_dev *adev, u16 module_id, u16 instance_id,
    269			   u8 ppl_instance_id, u8 core_id)
    270{
    271	struct avs_module_entry mentry;
    272	int ret;
    273
    274	/* Modules not owned by any pipeline need to be freed explicitly. */
    275	if (ppl_instance_id == INVALID_PIPELINE_ID)
    276		avs_ipc_delete_instance(adev, module_id, instance_id);
    277
    278	avs_module_id_free(adev, module_id, instance_id);
    279
    280	ret = avs_get_module_id_entry(adev, module_id, &mentry);
    281	/* Unload occupied memory if this was the last instance. */
    282	if (!ret && mentry.type.load_type == AVS_MODULE_LOAD_TYPE_LOADABLE) {
    283		if (avs_is_module_ida_empty(adev, module_id)) {
    284			ret = avs_dsp_op(adev, transfer_mods, false, &mentry, 1);
    285			if (ret)
    286				dev_err(adev->dev, "unload modules failed: %d\n", ret);
    287		}
    288	}
    289
    290	avs_dsp_put_core(adev, core_id);
    291}
    292
    293int avs_dsp_create_pipeline(struct avs_dev *adev, u16 req_size, u8 priority,
    294			    bool lp, u16 attributes, u8 *instance_id)
    295{
    296	struct avs_fw_cfg *fw_cfg = &adev->fw_cfg;
    297	int ret, id;
    298
    299	id = ida_alloc_max(&adev->ppl_ida, fw_cfg->max_ppl_count - 1, GFP_KERNEL);
    300	if (id < 0)
    301		return id;
    302
    303	ret = avs_ipc_create_pipeline(adev, req_size, priority, id, lp, attributes);
    304	if (ret) {
    305		ida_free(&adev->ppl_ida, id);
    306		return AVS_IPC_RET(ret);
    307	}
    308
    309	*instance_id = id;
    310	return 0;
    311}
    312
    313int avs_dsp_delete_pipeline(struct avs_dev *adev, u8 instance_id)
    314{
    315	int ret;
    316
    317	ret = avs_ipc_delete_pipeline(adev, instance_id);
    318	if (ret)
    319		ret = AVS_IPC_RET(ret);
    320
    321	ida_free(&adev->ppl_ida, instance_id);
    322	return ret;
    323}