18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * cnl-sst.c - DSP library functions for CNL platform
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016-17, Intel Corporation.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Guneshwor Singh <guneshwor.o.singh@intel.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Modified from:
108c2ecf20Sopenharmony_ci *	HDA DSP library functions for SKL platform
118c2ecf20Sopenharmony_ci *	Copyright (C) 2014-15, Intel Corporation.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci#include <linux/firmware.h>
218c2ecf20Sopenharmony_ci#include <linux/device.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "../common/sst-dsp.h"
248c2ecf20Sopenharmony_ci#include "../common/sst-dsp-priv.h"
258c2ecf20Sopenharmony_ci#include "../common/sst-ipc.h"
268c2ecf20Sopenharmony_ci#include "cnl-sst-dsp.h"
278c2ecf20Sopenharmony_ci#include "skl.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define CNL_FW_ROM_INIT		0x1
308c2ecf20Sopenharmony_ci#define CNL_FW_INIT		0x5
318c2ecf20Sopenharmony_ci#define CNL_IPC_PURGE		0x01004000
328c2ecf20Sopenharmony_ci#define CNL_INIT_TIMEOUT	300
338c2ecf20Sopenharmony_ci#define CNL_BASEFW_TIMEOUT	3000
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define CNL_ADSP_SRAM0_BASE	0x80000
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Firmware status window */
388c2ecf20Sopenharmony_ci#define CNL_ADSP_FW_STATUS	CNL_ADSP_SRAM0_BASE
398c2ecf20Sopenharmony_ci#define CNL_ADSP_ERROR_CODE	(CNL_ADSP_FW_STATUS + 0x4)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define CNL_INSTANCE_ID		0
428c2ecf20Sopenharmony_ci#define CNL_BASE_FW_MODULE_ID	0
438c2ecf20Sopenharmony_ci#define CNL_ADSP_FW_HDR_OFFSET	0x2000
448c2ecf20Sopenharmony_ci#define CNL_ROM_CTRL_DMA_ID	0x9
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int cnl_prepare_fw(struct sst_dsp *ctx, const void *fwdata, u32 fwsize)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	int ret, stream_tag;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
528c2ecf20Sopenharmony_ci	if (stream_tag <= 0) {
538c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "dma prepare failed: 0%#x\n", stream_tag);
548c2ecf20Sopenharmony_ci		return stream_tag;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	ctx->dsp_ops.stream_tag = stream_tag;
588c2ecf20Sopenharmony_ci	memcpy(ctx->dmab.area, fwdata, fwsize);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK);
618c2ecf20Sopenharmony_ci	if (ret < 0) {
628c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "dsp core0 power up failed\n");
638c2ecf20Sopenharmony_ci		ret = -EIO;
648c2ecf20Sopenharmony_ci		goto base_fw_load_failed;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* purge FW request */
688c2ecf20Sopenharmony_ci	sst_dsp_shim_write(ctx, CNL_ADSP_REG_HIPCIDR,
698c2ecf20Sopenharmony_ci			   CNL_ADSP_REG_HIPCIDR_BUSY | (CNL_IPC_PURGE |
708c2ecf20Sopenharmony_ci			   ((stream_tag - 1) << CNL_ROM_CTRL_DMA_ID)));
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
738c2ecf20Sopenharmony_ci	if (ret < 0) {
748c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret);
758c2ecf20Sopenharmony_ci		ret = -EIO;
768c2ecf20Sopenharmony_ci		goto base_fw_load_failed;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	ret = sst_dsp_register_poll(ctx, CNL_ADSP_REG_HIPCIDA,
808c2ecf20Sopenharmony_ci				    CNL_ADSP_REG_HIPCIDA_DONE,
818c2ecf20Sopenharmony_ci				    CNL_ADSP_REG_HIPCIDA_DONE,
828c2ecf20Sopenharmony_ci				    BXT_INIT_TIMEOUT, "HIPCIDA Done");
838c2ecf20Sopenharmony_ci	if (ret < 0) {
848c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "timeout for purge request: %d\n", ret);
858c2ecf20Sopenharmony_ci		goto base_fw_load_failed;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* enable interrupt */
898c2ecf20Sopenharmony_ci	cnl_ipc_int_enable(ctx);
908c2ecf20Sopenharmony_ci	cnl_ipc_op_int_enable(ctx);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	ret = sst_dsp_register_poll(ctx, CNL_ADSP_FW_STATUS, CNL_FW_STS_MASK,
938c2ecf20Sopenharmony_ci				    CNL_FW_ROM_INIT, CNL_INIT_TIMEOUT,
948c2ecf20Sopenharmony_ci				    "rom load");
958c2ecf20Sopenharmony_ci	if (ret < 0) {
968c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "rom init timeout, ret: %d\n", ret);
978c2ecf20Sopenharmony_ci		goto base_fw_load_failed;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return 0;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cibase_fw_load_failed:
1038c2ecf20Sopenharmony_ci	ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
1048c2ecf20Sopenharmony_ci	cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return ret;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int ret;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	ctx->dsp_ops.trigger(ctx->dev, true, ctx->dsp_ops.stream_tag);
1148c2ecf20Sopenharmony_ci	ret = sst_dsp_register_poll(ctx, CNL_ADSP_FW_STATUS, CNL_FW_STS_MASK,
1158c2ecf20Sopenharmony_ci				    CNL_FW_INIT, CNL_BASEFW_TIMEOUT,
1168c2ecf20Sopenharmony_ci				    "firmware boot");
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ctx->dsp_ops.trigger(ctx->dev, false, ctx->dsp_ops.stream_tag);
1198c2ecf20Sopenharmony_ci	ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, ctx->dsp_ops.stream_tag);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return ret;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int cnl_load_base_firmware(struct sst_dsp *ctx)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct firmware stripped_fw;
1278c2ecf20Sopenharmony_ci	struct skl_dev *cnl = ctx->thread_context;
1288c2ecf20Sopenharmony_ci	int ret, i;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (!ctx->fw) {
1318c2ecf20Sopenharmony_ci		ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
1328c2ecf20Sopenharmony_ci		if (ret < 0) {
1338c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "request firmware failed: %d\n", ret);
1348c2ecf20Sopenharmony_ci			goto cnl_load_base_firmware_failed;
1358c2ecf20Sopenharmony_ci		}
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* parse uuids if first boot */
1398c2ecf20Sopenharmony_ci	if (cnl->is_first_boot) {
1408c2ecf20Sopenharmony_ci		ret = snd_skl_parse_uuids(ctx, ctx->fw,
1418c2ecf20Sopenharmony_ci					  CNL_ADSP_FW_HDR_OFFSET, 0);
1428c2ecf20Sopenharmony_ci		if (ret < 0)
1438c2ecf20Sopenharmony_ci			goto cnl_load_base_firmware_failed;
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	stripped_fw.data = ctx->fw->data;
1478c2ecf20Sopenharmony_ci	stripped_fw.size = ctx->fw->size;
1488c2ecf20Sopenharmony_ci	skl_dsp_strip_extended_manifest(&stripped_fw);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) {
1518c2ecf20Sopenharmony_ci		ret = cnl_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
1528c2ecf20Sopenharmony_ci		if (!ret)
1538c2ecf20Sopenharmony_ci			break;
1548c2ecf20Sopenharmony_ci		dev_dbg(ctx->dev, "prepare firmware failed: %d\n", ret);
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (ret < 0)
1588c2ecf20Sopenharmony_ci		goto cnl_load_base_firmware_failed;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	ret = sst_transfer_fw_host_dma(ctx);
1618c2ecf20Sopenharmony_ci	if (ret < 0) {
1628c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "transfer firmware failed: %d\n", ret);
1638c2ecf20Sopenharmony_ci		cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
1648c2ecf20Sopenharmony_ci		goto cnl_load_base_firmware_failed;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	ret = wait_event_timeout(cnl->boot_wait, cnl->boot_complete,
1688c2ecf20Sopenharmony_ci				 msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
1698c2ecf20Sopenharmony_ci	if (ret == 0) {
1708c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "FW ready timed-out\n");
1718c2ecf20Sopenharmony_ci		cnl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
1728c2ecf20Sopenharmony_ci		ret = -EIO;
1738c2ecf20Sopenharmony_ci		goto cnl_load_base_firmware_failed;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	cnl->fw_loaded = true;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	return 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cicnl_load_base_firmware_failed:
1818c2ecf20Sopenharmony_ci	dev_err(ctx->dev, "firmware load failed: %d\n", ret);
1828c2ecf20Sopenharmony_ci	release_firmware(ctx->fw);
1838c2ecf20Sopenharmony_ci	ctx->fw = NULL;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	return ret;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int cnl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct skl_dev *cnl = ctx->thread_context;
1918c2ecf20Sopenharmony_ci	unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
1928c2ecf20Sopenharmony_ci	struct skl_ipc_dxstate_info dx;
1938c2ecf20Sopenharmony_ci	int ret;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (!cnl->fw_loaded) {
1968c2ecf20Sopenharmony_ci		cnl->boot_complete = false;
1978c2ecf20Sopenharmony_ci		ret = cnl_load_base_firmware(ctx);
1988c2ecf20Sopenharmony_ci		if (ret < 0) {
1998c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "fw reload failed: %d\n", ret);
2008c2ecf20Sopenharmony_ci			return ret;
2018c2ecf20Sopenharmony_ci		}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		cnl->cores.state[core_id] = SKL_DSP_RUNNING;
2048c2ecf20Sopenharmony_ci		return ret;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = cnl_dsp_enable_core(ctx, core_mask);
2088c2ecf20Sopenharmony_ci	if (ret < 0) {
2098c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "enable dsp core %d failed: %d\n",
2108c2ecf20Sopenharmony_ci			core_id, ret);
2118c2ecf20Sopenharmony_ci		goto err;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (core_id == SKL_DSP_CORE0_ID) {
2158c2ecf20Sopenharmony_ci		/* enable interrupt */
2168c2ecf20Sopenharmony_ci		cnl_ipc_int_enable(ctx);
2178c2ecf20Sopenharmony_ci		cnl_ipc_op_int_enable(ctx);
2188c2ecf20Sopenharmony_ci		cnl->boot_complete = false;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		ret = wait_event_timeout(cnl->boot_wait, cnl->boot_complete,
2218c2ecf20Sopenharmony_ci					 msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
2228c2ecf20Sopenharmony_ci		if (ret == 0) {
2238c2ecf20Sopenharmony_ci			dev_err(ctx->dev,
2248c2ecf20Sopenharmony_ci				"dsp boot timeout, status=%#x error=%#x\n",
2258c2ecf20Sopenharmony_ci				sst_dsp_shim_read(ctx, CNL_ADSP_FW_STATUS),
2268c2ecf20Sopenharmony_ci				sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE));
2278c2ecf20Sopenharmony_ci			ret = -ETIMEDOUT;
2288c2ecf20Sopenharmony_ci			goto err;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci	} else {
2318c2ecf20Sopenharmony_ci		dx.core_mask = core_mask;
2328c2ecf20Sopenharmony_ci		dx.dx_mask = core_mask;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		ret = skl_ipc_set_dx(&cnl->ipc, CNL_INSTANCE_ID,
2358c2ecf20Sopenharmony_ci				     CNL_BASE_FW_MODULE_ID, &dx);
2368c2ecf20Sopenharmony_ci		if (ret < 0) {
2378c2ecf20Sopenharmony_ci			dev_err(ctx->dev, "set_dx failed, core: %d ret: %d\n",
2388c2ecf20Sopenharmony_ci				core_id, ret);
2398c2ecf20Sopenharmony_ci			goto err;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci	cnl->cores.state[core_id] = SKL_DSP_RUNNING;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	return 0;
2458c2ecf20Sopenharmony_cierr:
2468c2ecf20Sopenharmony_ci	cnl_dsp_disable_core(ctx, core_mask);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return ret;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int cnl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct skl_dev *cnl = ctx->thread_context;
2548c2ecf20Sopenharmony_ci	unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
2558c2ecf20Sopenharmony_ci	struct skl_ipc_dxstate_info dx;
2568c2ecf20Sopenharmony_ci	int ret;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	dx.core_mask = core_mask;
2598c2ecf20Sopenharmony_ci	dx.dx_mask = SKL_IPC_D3_MASK;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	ret = skl_ipc_set_dx(&cnl->ipc, CNL_INSTANCE_ID,
2628c2ecf20Sopenharmony_ci			     CNL_BASE_FW_MODULE_ID, &dx);
2638c2ecf20Sopenharmony_ci	if (ret < 0) {
2648c2ecf20Sopenharmony_ci		dev_err(ctx->dev,
2658c2ecf20Sopenharmony_ci			"dsp core %d to d3 failed; continue reset\n",
2668c2ecf20Sopenharmony_ci			core_id);
2678c2ecf20Sopenharmony_ci		cnl->fw_loaded = false;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* disable interrupts if core 0 */
2718c2ecf20Sopenharmony_ci	if (core_id == SKL_DSP_CORE0_ID) {
2728c2ecf20Sopenharmony_ci		skl_ipc_op_int_disable(ctx);
2738c2ecf20Sopenharmony_ci		skl_ipc_int_disable(ctx);
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	ret = cnl_dsp_disable_core(ctx, core_mask);
2778c2ecf20Sopenharmony_ci	if (ret < 0) {
2788c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "disable dsp core %d failed: %d\n",
2798c2ecf20Sopenharmony_ci			core_id, ret);
2808c2ecf20Sopenharmony_ci		return ret;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	cnl->cores.state[core_id] = SKL_DSP_RESET;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return ret;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic unsigned int cnl_get_errno(struct sst_dsp *ctx)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	return sst_dsp_shim_read(ctx, CNL_ADSP_ERROR_CODE);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic const struct skl_dsp_fw_ops cnl_fw_ops = {
2948c2ecf20Sopenharmony_ci	.set_state_D0 = cnl_set_dsp_D0,
2958c2ecf20Sopenharmony_ci	.set_state_D3 = cnl_set_dsp_D3,
2968c2ecf20Sopenharmony_ci	.load_fw = cnl_load_base_firmware,
2978c2ecf20Sopenharmony_ci	.get_fw_errcode = cnl_get_errno,
2988c2ecf20Sopenharmony_ci};
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic struct sst_ops cnl_ops = {
3018c2ecf20Sopenharmony_ci	.irq_handler = cnl_dsp_sst_interrupt,
3028c2ecf20Sopenharmony_ci	.write = sst_shim32_write,
3038c2ecf20Sopenharmony_ci	.read = sst_shim32_read,
3048c2ecf20Sopenharmony_ci	.free = cnl_dsp_free,
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci#define CNL_IPC_GLB_NOTIFY_RSP_SHIFT	29
3088c2ecf20Sopenharmony_ci#define CNL_IPC_GLB_NOTIFY_RSP_MASK	0x1
3098c2ecf20Sopenharmony_ci#define CNL_IPC_GLB_NOTIFY_RSP_TYPE(x)	(((x) >> CNL_IPC_GLB_NOTIFY_RSP_SHIFT) \
3108c2ecf20Sopenharmony_ci					& CNL_IPC_GLB_NOTIFY_RSP_MASK)
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic irqreturn_t cnl_dsp_irq_thread_handler(int irq, void *context)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct sst_dsp *dsp = context;
3158c2ecf20Sopenharmony_ci	struct skl_dev *cnl = dsp->thread_context;
3168c2ecf20Sopenharmony_ci	struct sst_generic_ipc *ipc = &cnl->ipc;
3178c2ecf20Sopenharmony_ci	struct skl_ipc_header header = {0};
3188c2ecf20Sopenharmony_ci	u32 hipcida, hipctdr, hipctdd;
3198c2ecf20Sopenharmony_ci	int ipc_irq = 0;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* here we handle ipc interrupts only */
3228c2ecf20Sopenharmony_ci	if (!(dsp->intr_status & CNL_ADSPIS_IPC))
3238c2ecf20Sopenharmony_ci		return IRQ_NONE;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	hipcida = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCIDA);
3268c2ecf20Sopenharmony_ci	hipctdr = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCTDR);
3278c2ecf20Sopenharmony_ci	hipctdd = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCTDD);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* reply message from dsp */
3308c2ecf20Sopenharmony_ci	if (hipcida & CNL_ADSP_REG_HIPCIDA_DONE) {
3318c2ecf20Sopenharmony_ci		sst_dsp_shim_update_bits(dsp, CNL_ADSP_REG_HIPCCTL,
3328c2ecf20Sopenharmony_ci			CNL_ADSP_REG_HIPCCTL_DONE, 0);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		/* clear done bit - tell dsp operation is complete */
3358c2ecf20Sopenharmony_ci		sst_dsp_shim_update_bits_forced(dsp, CNL_ADSP_REG_HIPCIDA,
3368c2ecf20Sopenharmony_ci			CNL_ADSP_REG_HIPCIDA_DONE, CNL_ADSP_REG_HIPCIDA_DONE);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		ipc_irq = 1;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		/* unmask done interrupt */
3418c2ecf20Sopenharmony_ci		sst_dsp_shim_update_bits(dsp, CNL_ADSP_REG_HIPCCTL,
3428c2ecf20Sopenharmony_ci			CNL_ADSP_REG_HIPCCTL_DONE, CNL_ADSP_REG_HIPCCTL_DONE);
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* new message from dsp */
3468c2ecf20Sopenharmony_ci	if (hipctdr & CNL_ADSP_REG_HIPCTDR_BUSY) {
3478c2ecf20Sopenharmony_ci		header.primary = hipctdr;
3488c2ecf20Sopenharmony_ci		header.extension = hipctdd;
3498c2ecf20Sopenharmony_ci		dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x",
3508c2ecf20Sopenharmony_ci						header.primary);
3518c2ecf20Sopenharmony_ci		dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x",
3528c2ecf20Sopenharmony_ci						header.extension);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		if (CNL_IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
3558c2ecf20Sopenharmony_ci			/* Handle Immediate reply from DSP Core */
3568c2ecf20Sopenharmony_ci			skl_ipc_process_reply(ipc, header);
3578c2ecf20Sopenharmony_ci		} else {
3588c2ecf20Sopenharmony_ci			dev_dbg(dsp->dev, "IPC irq: Notification from firmware\n");
3598c2ecf20Sopenharmony_ci			skl_ipc_process_notification(ipc, header);
3608c2ecf20Sopenharmony_ci		}
3618c2ecf20Sopenharmony_ci		/* clear busy interrupt */
3628c2ecf20Sopenharmony_ci		sst_dsp_shim_update_bits_forced(dsp, CNL_ADSP_REG_HIPCTDR,
3638c2ecf20Sopenharmony_ci			CNL_ADSP_REG_HIPCTDR_BUSY, CNL_ADSP_REG_HIPCTDR_BUSY);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		/* set done bit to ack dsp */
3668c2ecf20Sopenharmony_ci		sst_dsp_shim_update_bits_forced(dsp, CNL_ADSP_REG_HIPCTDA,
3678c2ecf20Sopenharmony_ci			CNL_ADSP_REG_HIPCTDA_DONE, CNL_ADSP_REG_HIPCTDA_DONE);
3688c2ecf20Sopenharmony_ci		ipc_irq = 1;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	if (ipc_irq == 0)
3728c2ecf20Sopenharmony_ci		return IRQ_NONE;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	cnl_ipc_int_enable(dsp);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* continue to send any remaining messages */
3778c2ecf20Sopenharmony_ci	schedule_work(&ipc->kwork);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic struct sst_dsp_device cnl_dev = {
3838c2ecf20Sopenharmony_ci	.thread = cnl_dsp_irq_thread_handler,
3848c2ecf20Sopenharmony_ci	.ops = &cnl_ops,
3858c2ecf20Sopenharmony_ci};
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void cnl_ipc_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct skl_ipc_header *header = (struct skl_ipc_header *)(&msg->tx.header);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (msg->tx.size)
3928c2ecf20Sopenharmony_ci		sst_dsp_outbox_write(ipc->dsp, msg->tx.data, msg->tx.size);
3938c2ecf20Sopenharmony_ci	sst_dsp_shim_write_unlocked(ipc->dsp, CNL_ADSP_REG_HIPCIDD,
3948c2ecf20Sopenharmony_ci				    header->extension);
3958c2ecf20Sopenharmony_ci	sst_dsp_shim_write_unlocked(ipc->dsp, CNL_ADSP_REG_HIPCIDR,
3968c2ecf20Sopenharmony_ci				header->primary | CNL_ADSP_REG_HIPCIDR_BUSY);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic bool cnl_ipc_is_dsp_busy(struct sst_dsp *dsp)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	u32 hipcidr;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	hipcidr = sst_dsp_shim_read_unlocked(dsp, CNL_ADSP_REG_HIPCIDR);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return (hipcidr & CNL_ADSP_REG_HIPCIDR_BUSY);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int cnl_ipc_init(struct device *dev, struct skl_dev *cnl)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct sst_generic_ipc *ipc;
4118c2ecf20Sopenharmony_ci	int err;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ipc = &cnl->ipc;
4148c2ecf20Sopenharmony_ci	ipc->dsp = cnl->dsp;
4158c2ecf20Sopenharmony_ci	ipc->dev = dev;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	ipc->tx_data_max_size = CNL_ADSP_W1_SZ;
4188c2ecf20Sopenharmony_ci	ipc->rx_data_max_size = CNL_ADSP_W0_UP_SZ;
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	err = sst_ipc_init(ipc);
4218c2ecf20Sopenharmony_ci	if (err)
4228c2ecf20Sopenharmony_ci		return err;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/*
4258c2ecf20Sopenharmony_ci	 * overriding tx_msg and is_dsp_busy since
4268c2ecf20Sopenharmony_ci	 * ipc registers are different for cnl
4278c2ecf20Sopenharmony_ci	 */
4288c2ecf20Sopenharmony_ci	ipc->ops.tx_msg = cnl_ipc_tx_msg;
4298c2ecf20Sopenharmony_ci	ipc->ops.tx_data_copy = skl_ipc_tx_data_copy;
4308c2ecf20Sopenharmony_ci	ipc->ops.is_dsp_busy = cnl_ipc_is_dsp_busy;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ciint cnl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
4368c2ecf20Sopenharmony_ci		     const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
4378c2ecf20Sopenharmony_ci		     struct skl_dev **dsp)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct skl_dev *cnl;
4408c2ecf20Sopenharmony_ci	struct sst_dsp *sst;
4418c2ecf20Sopenharmony_ci	int ret;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &cnl_dev);
4448c2ecf20Sopenharmony_ci	if (ret < 0) {
4458c2ecf20Sopenharmony_ci		dev_err(dev, "%s: no device\n", __func__);
4468c2ecf20Sopenharmony_ci		return ret;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	cnl = *dsp;
4508c2ecf20Sopenharmony_ci	sst = cnl->dsp;
4518c2ecf20Sopenharmony_ci	sst->fw_ops = cnl_fw_ops;
4528c2ecf20Sopenharmony_ci	sst->addr.lpe = mmio_base;
4538c2ecf20Sopenharmony_ci	sst->addr.shim = mmio_base;
4548c2ecf20Sopenharmony_ci	sst->addr.sram0_base = CNL_ADSP_SRAM0_BASE;
4558c2ecf20Sopenharmony_ci	sst->addr.sram1_base = CNL_ADSP_SRAM1_BASE;
4568c2ecf20Sopenharmony_ci	sst->addr.w0_stat_sz = CNL_ADSP_W0_STAT_SZ;
4578c2ecf20Sopenharmony_ci	sst->addr.w0_up_sz = CNL_ADSP_W0_UP_SZ;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	sst_dsp_mailbox_init(sst, (CNL_ADSP_SRAM0_BASE + CNL_ADSP_W0_STAT_SZ),
4608c2ecf20Sopenharmony_ci			     CNL_ADSP_W0_UP_SZ, CNL_ADSP_SRAM1_BASE,
4618c2ecf20Sopenharmony_ci			     CNL_ADSP_W1_SZ);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	ret = cnl_ipc_init(dev, cnl);
4648c2ecf20Sopenharmony_ci	if (ret) {
4658c2ecf20Sopenharmony_ci		skl_dsp_free(sst);
4668c2ecf20Sopenharmony_ci		return ret;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	cnl->boot_complete = false;
4708c2ecf20Sopenharmony_ci	init_waitqueue_head(&cnl->boot_wait);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	return skl_dsp_acquire_irq(sst);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cnl_sst_dsp_init);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ciint cnl_sst_init_fw(struct device *dev, struct skl_dev *skl)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	int ret;
4798c2ecf20Sopenharmony_ci	struct sst_dsp *sst = skl->dsp;
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	ret = skl->dsp->fw_ops.load_fw(sst);
4828c2ecf20Sopenharmony_ci	if (ret < 0) {
4838c2ecf20Sopenharmony_ci		dev_err(dev, "load base fw failed: %d", ret);
4848c2ecf20Sopenharmony_ci		return ret;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	skl_dsp_init_core_state(sst);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	skl->is_first_boot = false;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	return 0;
4928c2ecf20Sopenharmony_ci}
4938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cnl_sst_init_fw);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_civoid cnl_sst_dsp_cleanup(struct device *dev, struct skl_dev *skl)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	if (skl->dsp->fw)
4988c2ecf20Sopenharmony_ci		release_firmware(skl->dsp->fw);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	skl_freeup_uuid_list(skl);
5018c2ecf20Sopenharmony_ci	cnl_ipc_free(&skl->ipc);
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	skl->dsp->ops->free(skl->dsp);
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cnl_sst_dsp_cleanup);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
5088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Cannonlake IPC driver");
509