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