162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or
462306a36Sopenharmony_ci// redistributing this file, you may do so under either license.
562306a36Sopenharmony_ci//
662306a36Sopenharmony_ci// Copyright(c) 2021, 2023 Advanced Micro Devices, Inc. All rights reserved.
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
962306a36Sopenharmony_ci//	    Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Hardware interface for generic AMD ACP processor
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/pci.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "../ops.h"
2062306a36Sopenharmony_ci#include "acp.h"
2162306a36Sopenharmony_ci#include "acp-dsp-offset.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define SECURED_FIRMWARE 1
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic bool enable_fw_debug;
2662306a36Sopenharmony_cimodule_param(enable_fw_debug, bool, 0444);
2762306a36Sopenharmony_ciMODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ciconst struct dmi_system_id acp_sof_quirk_table[] = {
3062306a36Sopenharmony_ci	{
3162306a36Sopenharmony_ci		/* Valve Jupiter device */
3262306a36Sopenharmony_ci		.matches = {
3362306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Valve"),
3462306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "Galileo"),
3562306a36Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_FAMILY, "Sephiroth"),
3662306a36Sopenharmony_ci		},
3762306a36Sopenharmony_ci		.driver_data = (void *)SECURED_FIRMWARE,
3862306a36Sopenharmony_ci	},
3962306a36Sopenharmony_ci	{}
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(acp_sof_quirk_table);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int smn_write(struct pci_dev *dev, u32 smn_addr, u32 data)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	pci_write_config_dword(dev, 0x60, smn_addr);
4662306a36Sopenharmony_ci	pci_write_config_dword(dev, 0x64, data);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return 0;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic int smn_read(struct pci_dev *dev, u32 smn_addr)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 data = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	pci_write_config_dword(dev, 0x60, smn_addr);
5662306a36Sopenharmony_ci	pci_read_config_dword(dev, 0x64, &data);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return data;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic void init_dma_descriptor(struct acp_dev_data *adata)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
6462306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
6562306a36Sopenharmony_ci	unsigned int addr;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	addr = desc->sram_pte_offset + sdev->debug_box.offset +
6862306a36Sopenharmony_ci	       offsetof(struct scratch_reg_conf, dma_desc);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_BASE_ADDR, addr);
7162306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DESC_MAX_NUM_DSCR, ACP_MAX_DESC_CNT);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void configure_dma_descriptor(struct acp_dev_data *adata, unsigned short idx,
7562306a36Sopenharmony_ci				     struct dma_descriptor *dscr_info)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
7862306a36Sopenharmony_ci	unsigned int offset;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	offset = ACP_SCRATCH_REG_0 + sdev->debug_box.offset +
8162306a36Sopenharmony_ci		offsetof(struct scratch_reg_conf, dma_desc) +
8262306a36Sopenharmony_ci		idx * sizeof(struct dma_descriptor);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset, dscr_info->src_addr);
8562306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x4, dscr_info->dest_addr);
8662306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, offset + 0x8, dscr_info->tx_cnt.u32_all);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int config_dma_channel(struct acp_dev_data *adata, unsigned int ch,
9062306a36Sopenharmony_ci			      unsigned int idx, unsigned int dscr_count)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
9362306a36Sopenharmony_ci	unsigned int val, status;
9462306a36Sopenharmony_ci	int ret;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32),
9762306a36Sopenharmony_ci			  ACP_DMA_CH_RST | ACP_DMA_CH_GRACEFUL_RST_EN);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_RST_STS, val,
10062306a36Sopenharmony_ci					    val & (1 << ch), ACP_REG_POLL_INTERVAL,
10162306a36Sopenharmony_ci					    ACP_REG_POLL_TIMEOUT_US);
10262306a36Sopenharmony_ci	if (ret < 0) {
10362306a36Sopenharmony_ci		status = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_ERROR_STATUS);
10462306a36Sopenharmony_ci		val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_ERR_STS_0 + ch * sizeof(u32));
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		dev_err(sdev->dev, "ACP_DMA_ERR_STS :0x%x ACP_ERROR_STATUS :0x%x\n", val, status);
10762306a36Sopenharmony_ci		return ret;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, (ACP_DMA_CNTL_0 + ch * sizeof(u32)), 0);
11162306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_CNT_0 + ch * sizeof(u32), dscr_count);
11262306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_DSCR_STRT_IDX_0 + ch * sizeof(u32), idx);
11362306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_PRIO_0 + ch * sizeof(u32), 0);
11462306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32), ACP_DMA_CH_RUN);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return ret;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int acpbus_dma_start(struct acp_dev_data *adata, unsigned int ch,
12062306a36Sopenharmony_ci			    unsigned int dscr_count, struct dma_descriptor *dscr_info)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
12362306a36Sopenharmony_ci	int ret;
12462306a36Sopenharmony_ci	u16 dscr;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!dscr_info || !dscr_count)
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	for (dscr = 0; dscr < dscr_count; dscr++)
13062306a36Sopenharmony_ci		configure_dma_descriptor(adata, dscr, dscr_info++);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = config_dma_channel(adata, ch, 0, dscr_count);
13362306a36Sopenharmony_ci	if (ret < 0)
13462306a36Sopenharmony_ci		dev_err(sdev->dev, "config dma ch failed:%d\n", ret);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return ret;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ciint configure_and_run_dma(struct acp_dev_data *adata, unsigned int src_addr,
14062306a36Sopenharmony_ci			  unsigned int dest_addr, int dsp_data_size)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
14362306a36Sopenharmony_ci	unsigned int desc_count, index;
14462306a36Sopenharmony_ci	int ret;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (desc_count = 0; desc_count < ACP_MAX_DESC && dsp_data_size >= 0;
14762306a36Sopenharmony_ci	     desc_count++, dsp_data_size -= ACP_PAGE_SIZE) {
14862306a36Sopenharmony_ci		adata->dscr_info[desc_count].src_addr = src_addr + desc_count * ACP_PAGE_SIZE;
14962306a36Sopenharmony_ci		adata->dscr_info[desc_count].dest_addr = dest_addr + desc_count * ACP_PAGE_SIZE;
15062306a36Sopenharmony_ci		adata->dscr_info[desc_count].tx_cnt.bits.count = ACP_PAGE_SIZE;
15162306a36Sopenharmony_ci		if (dsp_data_size < ACP_PAGE_SIZE)
15262306a36Sopenharmony_ci			adata->dscr_info[desc_count].tx_cnt.bits.count = dsp_data_size;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	ret = acpbus_dma_start(adata, 0, desc_count, adata->dscr_info);
15662306a36Sopenharmony_ci	if (ret)
15762306a36Sopenharmony_ci		dev_err(sdev->dev, "acpbus_dma_start failed\n");
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* Clear descriptor array */
16062306a36Sopenharmony_ci	for (index = 0; index < desc_count; index++)
16162306a36Sopenharmony_ci		memset(&adata->dscr_info[index], 0x00, sizeof(struct dma_descriptor));
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return ret;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/*
16762306a36Sopenharmony_ci * psp_mbox_ready- function to poll ready bit of psp mbox
16862306a36Sopenharmony_ci * @adata: acp device data
16962306a36Sopenharmony_ci * @ack: bool variable to check ready bit status or psp ack
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int psp_mbox_ready(struct acp_dev_data *adata, bool ack)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
17562306a36Sopenharmony_ci	int ret;
17662306a36Sopenharmony_ci	u32 data;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = read_poll_timeout(smn_read, data, data & MBOX_READY_MASK, MBOX_DELAY_US,
17962306a36Sopenharmony_ci				ACP_PSP_TIMEOUT_US, false, adata->smn_dev, MP0_C2PMSG_114_REG);
18062306a36Sopenharmony_ci	if (!ret)
18162306a36Sopenharmony_ci		return 0;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	dev_err(sdev->dev, "PSP error status %x\n", data & MBOX_STATUS_MASK);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (ack)
18662306a36Sopenharmony_ci		return -ETIMEDOUT;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return -EBUSY;
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/*
19262306a36Sopenharmony_ci * psp_send_cmd - function to send psp command over mbox
19362306a36Sopenharmony_ci * @adata: acp device data
19462306a36Sopenharmony_ci * @cmd: non zero integer value for command type
19562306a36Sopenharmony_ci */
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int psp_send_cmd(struct acp_dev_data *adata, int cmd)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
20062306a36Sopenharmony_ci	int ret;
20162306a36Sopenharmony_ci	u32 data;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (!cmd)
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Get a non-zero Doorbell value from PSP */
20762306a36Sopenharmony_ci	ret = read_poll_timeout(smn_read, data, data, MBOX_DELAY_US, ACP_PSP_TIMEOUT_US, false,
20862306a36Sopenharmony_ci				adata->smn_dev, MP0_C2PMSG_73_REG);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (ret) {
21162306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to get Doorbell from MBOX %x\n", MP0_C2PMSG_73_REG);
21262306a36Sopenharmony_ci		return ret;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Check if PSP is ready for new command */
21662306a36Sopenharmony_ci	ret = psp_mbox_ready(adata, 0);
21762306a36Sopenharmony_ci	if (ret)
21862306a36Sopenharmony_ci		return ret;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	smn_write(adata->smn_dev, MP0_C2PMSG_114_REG, cmd);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* Ring the Doorbell for PSP */
22362306a36Sopenharmony_ci	smn_write(adata->smn_dev, MP0_C2PMSG_73_REG, data);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* Check MBOX ready as PSP ack */
22662306a36Sopenharmony_ci	ret = psp_mbox_ready(adata, 1);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return ret;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ciint configure_and_run_sha_dma(struct acp_dev_data *adata, void *image_addr,
23262306a36Sopenharmony_ci			      unsigned int start_addr, unsigned int dest_addr,
23362306a36Sopenharmony_ci			      unsigned int image_length)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
23662306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
23762306a36Sopenharmony_ci	unsigned int tx_count, fw_qualifier, val;
23862306a36Sopenharmony_ci	int ret;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!image_addr) {
24162306a36Sopenharmony_ci		dev_err(sdev->dev, "SHA DMA image address is NULL\n");
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD);
24662306a36Sopenharmony_ci	if (val & ACP_SHA_RUN) {
24762306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RESET);
24862306a36Sopenharmony_ci		ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD_STS,
24962306a36Sopenharmony_ci						    val, val & ACP_SHA_RESET,
25062306a36Sopenharmony_ci						    ACP_REG_POLL_INTERVAL,
25162306a36Sopenharmony_ci						    ACP_REG_POLL_TIMEOUT_US);
25262306a36Sopenharmony_ci		if (ret < 0) {
25362306a36Sopenharmony_ci			dev_err(sdev->dev, "SHA DMA Failed to Reset\n");
25462306a36Sopenharmony_ci			return ret;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (adata->signed_fw_image)
25962306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_INCLUDE_HDR, ACP_SHA_HEADER);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_STRT_ADDR, start_addr);
26262306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_DESTINATION_ADDR, dest_addr);
26362306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_MSG_LENGTH, image_length);
26462306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SHA_DMA_CMD, ACP_SHA_RUN);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_TRANSFER_BYTE_CNT,
26762306a36Sopenharmony_ci					    tx_count, tx_count == image_length,
26862306a36Sopenharmony_ci					    ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
26962306a36Sopenharmony_ci	if (ret < 0) {
27062306a36Sopenharmony_ci		dev_err(sdev->dev, "SHA DMA Failed to Transfer Length %x\n", tx_count);
27162306a36Sopenharmony_ci		return ret;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* psp_send_cmd only required for renoir platform (rev - 3) */
27562306a36Sopenharmony_ci	if (desc->rev == 3) {
27662306a36Sopenharmony_ci		ret = psp_send_cmd(adata, MBOX_ACP_SHA_DMA_COMMAND);
27762306a36Sopenharmony_ci		if (ret)
27862306a36Sopenharmony_ci			return ret;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SHA_DSP_FW_QUALIFIER,
28262306a36Sopenharmony_ci					    fw_qualifier, fw_qualifier & DSP_FW_RUN_ENABLE,
28362306a36Sopenharmony_ci					    ACP_REG_POLL_INTERVAL, ACP_DMA_COMPLETE_TIMEOUT_US);
28462306a36Sopenharmony_ci	if (ret < 0) {
28562306a36Sopenharmony_ci		dev_err(sdev->dev, "PSP validation failed\n");
28662306a36Sopenharmony_ci		return ret;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	return 0;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ciint acp_dma_status(struct acp_dev_data *adata, unsigned char ch)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct snd_sof_dev *sdev = adata->dev;
29562306a36Sopenharmony_ci	unsigned int val;
29662306a36Sopenharmony_ci	int ret = 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_DMA_CNTL_0 + ch * sizeof(u32));
29962306a36Sopenharmony_ci	if (val & ACP_DMA_CH_RUN) {
30062306a36Sopenharmony_ci		ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_DMA_CH_STS, val, !val,
30162306a36Sopenharmony_ci						    ACP_REG_POLL_INTERVAL,
30262306a36Sopenharmony_ci						    ACP_DMA_COMPLETE_TIMEOUT_US);
30362306a36Sopenharmony_ci		if (ret < 0)
30462306a36Sopenharmony_ci			dev_err(sdev->dev, "DMA_CHANNEL %d status timeout\n", ch);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_civoid memcpy_from_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *dst, size_t bytes)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
31362306a36Sopenharmony_ci	int i, j;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	for (i = 0, j = 0; i < bytes; i = i + 4, j++)
31662306a36Sopenharmony_ci		dst[j] = snd_sof_dsp_read(sdev, ACP_DSP_BAR, reg_offset + i);
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_civoid memcpy_to_scratch(struct snd_sof_dev *sdev, u32 offset, unsigned int *src, size_t bytes)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	unsigned int reg_offset = offset + ACP_SCRATCH_REG_0;
32262306a36Sopenharmony_ci	int i, j;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	for (i = 0, j = 0; i < bytes; i = i + 4, j++)
32562306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, reg_offset + i, src[j]);
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int acp_memory_init(struct snd_sof_dev *sdev)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
33162306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, desc->dsp_intr_base + DSP_SW_INTR_CNTL_OFFSET,
33462306a36Sopenharmony_ci				ACP_DSP_INTR_EN_MASK, ACP_DSP_INTR_EN_MASK);
33562306a36Sopenharmony_ci	init_dma_descriptor(adata);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	return 0;
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic irqreturn_t acp_irq_thread(int irq, void *context)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct snd_sof_dev *sdev = context;
34362306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
34462306a36Sopenharmony_ci	unsigned int count = ACP_HW_SEM_RETRY_COUNT;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset)) {
34762306a36Sopenharmony_ci		/* Wait until acquired HW Semaphore lock or timeout */
34862306a36Sopenharmony_ci		count--;
34962306a36Sopenharmony_ci		if (!count) {
35062306a36Sopenharmony_ci			dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
35162306a36Sopenharmony_ci			return IRQ_NONE;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	sof_ops(sdev)->irq_thread(irq, sdev);
35662306a36Sopenharmony_ci	/* Unlock or Release HW Semaphore */
35762306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->hw_semaphore_offset, 0x0);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return IRQ_HANDLED;
36062306a36Sopenharmony_ci};
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic irqreturn_t acp_irq_handler(int irq, void *dev_id)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct snd_sof_dev *sdev = dev_id;
36562306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
36662306a36Sopenharmony_ci	unsigned int base = desc->dsp_intr_base;
36762306a36Sopenharmony_ci	unsigned int val;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
37062306a36Sopenharmony_ci	if (val & ACP_DSP_TO_HOST_IRQ) {
37162306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET,
37262306a36Sopenharmony_ci				  ACP_DSP_TO_HOST_IRQ);
37362306a36Sopenharmony_ci		return IRQ_WAKE_THREAD;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return IRQ_NONE;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int acp_power_on(struct snd_sof_dev *sdev)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
38262306a36Sopenharmony_ci	unsigned int base = desc->pgfsm_base;
38362306a36Sopenharmony_ci	unsigned int val;
38462306a36Sopenharmony_ci	int ret;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (val == ACP_POWERED_ON)
38962306a36Sopenharmony_ci		return 0;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (val & ACP_PGFSM_STATUS_MASK)
39262306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + PGFSM_CONTROL_OFFSET,
39362306a36Sopenharmony_ci				  ACP_PGFSM_CNTL_POWER_ON_MASK);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, base + PGFSM_STATUS_OFFSET, val,
39662306a36Sopenharmony_ci					    !val, ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
39762306a36Sopenharmony_ci	if (ret < 0)
39862306a36Sopenharmony_ci		dev_err(sdev->dev, "timeout in ACP_PGFSM_STATUS read\n");
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return ret;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int acp_reset(struct snd_sof_dev *sdev)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
40662306a36Sopenharmony_ci	unsigned int val;
40762306a36Sopenharmony_ci	int ret;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_ASSERT_RESET);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val,
41262306a36Sopenharmony_ci					    val & ACP_SOFT_RESET_DONE_MASK,
41362306a36Sopenharmony_ci					    ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
41462306a36Sopenharmony_ci	if (ret < 0) {
41562306a36Sopenharmony_ci		dev_err(sdev->dev, "timeout asserting reset\n");
41662306a36Sopenharmony_ci		return ret;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, ACP_RELEASE_RESET);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	ret = snd_sof_dsp_read_poll_timeout(sdev, ACP_DSP_BAR, ACP_SOFT_RESET, val, !val,
42262306a36Sopenharmony_ci					    ACP_REG_POLL_INTERVAL, ACP_REG_POLL_TIMEOUT_US);
42362306a36Sopenharmony_ci	if (ret < 0)
42462306a36Sopenharmony_ci		dev_err(sdev->dev, "timeout in releasing reset\n");
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	if (desc->acp_clkmux_sel)
42762306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (desc->ext_intr_enb)
43062306a36Sopenharmony_ci		snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return ret;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int acp_init(struct snd_sof_dev *sdev)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	int ret;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	/* power on */
44062306a36Sopenharmony_ci	ret = acp_power_on(sdev);
44162306a36Sopenharmony_ci	if (ret) {
44262306a36Sopenharmony_ci		dev_err(sdev->dev, "ACP power on failed\n");
44362306a36Sopenharmony_ci		return ret;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, 0x01);
44762306a36Sopenharmony_ci	/* Reset */
44862306a36Sopenharmony_ci	return acp_reset(sdev);
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ciint amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	int ret;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	ret = acp_reset(sdev);
45662306a36Sopenharmony_ci	if (ret) {
45762306a36Sopenharmony_ci		dev_err(sdev->dev, "ACP Reset failed\n");
45862306a36Sopenharmony_ci		return ret;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, 0x00);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ciEXPORT_SYMBOL_NS(amd_sof_acp_suspend, SND_SOC_SOF_AMD_COMMON);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ciint amd_sof_acp_resume(struct snd_sof_dev *sdev)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	int ret;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	ret = acp_init(sdev);
47262306a36Sopenharmony_ci	if (ret) {
47362306a36Sopenharmony_ci		dev_err(sdev->dev, "ACP Init failed\n");
47462306a36Sopenharmony_ci		return ret;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci	return acp_memory_init(sdev);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ciEXPORT_SYMBOL_NS(amd_sof_acp_resume, SND_SOC_SOF_AMD_COMMON);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ciint amd_sof_acp_probe(struct snd_sof_dev *sdev)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct pci_dev *pci = to_pci_dev(sdev->dev);
48362306a36Sopenharmony_ci	struct snd_sof_pdata *plat_data = sdev->pdata;
48462306a36Sopenharmony_ci	struct acp_dev_data *adata;
48562306a36Sopenharmony_ci	const struct sof_amd_acp_desc *chip;
48662306a36Sopenharmony_ci	const struct dmi_system_id *dmi_id;
48762306a36Sopenharmony_ci	unsigned int addr;
48862306a36Sopenharmony_ci	int ret;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	chip = get_chip_info(sdev->pdata);
49162306a36Sopenharmony_ci	if (!chip) {
49262306a36Sopenharmony_ci		dev_err(sdev->dev, "no such device supported, chip id:%x\n", pci->device);
49362306a36Sopenharmony_ci		return -EIO;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci	adata = devm_kzalloc(sdev->dev, sizeof(struct acp_dev_data),
49662306a36Sopenharmony_ci			     GFP_KERNEL);
49762306a36Sopenharmony_ci	if (!adata)
49862306a36Sopenharmony_ci		return -ENOMEM;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	adata->dev = sdev;
50162306a36Sopenharmony_ci	adata->dmic_dev = platform_device_register_data(sdev->dev, "dmic-codec",
50262306a36Sopenharmony_ci							PLATFORM_DEVID_NONE, NULL, 0);
50362306a36Sopenharmony_ci	if (IS_ERR(adata->dmic_dev)) {
50462306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to register platform for dmic codec\n");
50562306a36Sopenharmony_ci		return PTR_ERR(adata->dmic_dev);
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci	addr = pci_resource_start(pci, ACP_DSP_BAR);
50862306a36Sopenharmony_ci	sdev->bar[ACP_DSP_BAR] = devm_ioremap(sdev->dev, addr, pci_resource_len(pci, ACP_DSP_BAR));
50962306a36Sopenharmony_ci	if (!sdev->bar[ACP_DSP_BAR]) {
51062306a36Sopenharmony_ci		dev_err(sdev->dev, "ioremap error\n");
51162306a36Sopenharmony_ci		ret = -ENXIO;
51262306a36Sopenharmony_ci		goto unregister_dev;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	pci_set_master(pci);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	sdev->pdata->hw_pdata = adata;
51862306a36Sopenharmony_ci	adata->smn_dev = pci_get_device(PCI_VENDOR_ID_AMD, chip->host_bridge_id, NULL);
51962306a36Sopenharmony_ci	if (!adata->smn_dev) {
52062306a36Sopenharmony_ci		dev_err(sdev->dev, "Failed to get host bridge device\n");
52162306a36Sopenharmony_ci		ret = -ENODEV;
52262306a36Sopenharmony_ci		goto unregister_dev;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	sdev->ipc_irq = pci->irq;
52662306a36Sopenharmony_ci	ret = request_threaded_irq(sdev->ipc_irq, acp_irq_handler, acp_irq_thread,
52762306a36Sopenharmony_ci				   IRQF_SHARED, "AudioDSP", sdev);
52862306a36Sopenharmony_ci	if (ret < 0) {
52962306a36Sopenharmony_ci		dev_err(sdev->dev, "failed to register IRQ %d\n",
53062306a36Sopenharmony_ci			sdev->ipc_irq);
53162306a36Sopenharmony_ci		goto free_smn_dev;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	ret = acp_init(sdev);
53562306a36Sopenharmony_ci	if (ret < 0)
53662306a36Sopenharmony_ci		goto free_ipc_irq;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	sdev->dsp_box.offset = 0;
53962306a36Sopenharmony_ci	sdev->dsp_box.size = BOX_SIZE_512;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	sdev->host_box.offset = sdev->dsp_box.offset + sdev->dsp_box.size;
54262306a36Sopenharmony_ci	sdev->host_box.size = BOX_SIZE_512;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	sdev->debug_box.offset = sdev->host_box.offset + sdev->host_box.size;
54562306a36Sopenharmony_ci	sdev->debug_box.size = BOX_SIZE_1024;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	adata->signed_fw_image = false;
54862306a36Sopenharmony_ci	dmi_id = dmi_first_match(acp_sof_quirk_table);
54962306a36Sopenharmony_ci	if (dmi_id && dmi_id->driver_data) {
55062306a36Sopenharmony_ci		adata->fw_code_bin = devm_kasprintf(sdev->dev, GFP_KERNEL,
55162306a36Sopenharmony_ci						    "%s/sof-%s-code.bin",
55262306a36Sopenharmony_ci						    plat_data->fw_filename_prefix,
55362306a36Sopenharmony_ci						    chip->name);
55462306a36Sopenharmony_ci		if (!adata->fw_code_bin) {
55562306a36Sopenharmony_ci			ret = -ENOMEM;
55662306a36Sopenharmony_ci			goto free_ipc_irq;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		adata->fw_data_bin = devm_kasprintf(sdev->dev, GFP_KERNEL,
56062306a36Sopenharmony_ci						    "%s/sof-%s-data.bin",
56162306a36Sopenharmony_ci						    plat_data->fw_filename_prefix,
56262306a36Sopenharmony_ci						    chip->name);
56362306a36Sopenharmony_ci		if (!adata->fw_data_bin) {
56462306a36Sopenharmony_ci			ret = -ENOMEM;
56562306a36Sopenharmony_ci			goto free_ipc_irq;
56662306a36Sopenharmony_ci		}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		adata->signed_fw_image = dmi_id->driver_data;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	adata->enable_fw_debug = enable_fw_debug;
57262306a36Sopenharmony_ci	acp_memory_init(sdev);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	acp_dsp_stream_init(sdev);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	return 0;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cifree_ipc_irq:
57962306a36Sopenharmony_ci	free_irq(sdev->ipc_irq, sdev);
58062306a36Sopenharmony_cifree_smn_dev:
58162306a36Sopenharmony_ci	pci_dev_put(adata->smn_dev);
58262306a36Sopenharmony_ciunregister_dev:
58362306a36Sopenharmony_ci	platform_device_unregister(adata->dmic_dev);
58462306a36Sopenharmony_ci	return ret;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ciEXPORT_SYMBOL_NS(amd_sof_acp_probe, SND_SOC_SOF_AMD_COMMON);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ciint amd_sof_acp_remove(struct snd_sof_dev *sdev)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (adata->smn_dev)
59362306a36Sopenharmony_ci		pci_dev_put(adata->smn_dev);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (sdev->ipc_irq)
59662306a36Sopenharmony_ci		free_irq(sdev->ipc_irq, sdev);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	if (adata->dmic_dev)
59962306a36Sopenharmony_ci		platform_device_unregister(adata->dmic_dev);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return acp_reset(sdev);
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ciEXPORT_SYMBOL_NS(amd_sof_acp_remove, SND_SOC_SOF_AMD_COMMON);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ciMODULE_DESCRIPTION("AMD ACP sof driver");
60662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
607