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

acp-ipc.c (5877B)


      1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
      2//
      3// This file is provided under a dual BSD/GPLv2 license. When using or
      4// redistributing this file, you may do so under either license.
      5//
      6// Copyright(c) 2021 Advanced Micro Devices, Inc.
      7//
      8// Authors: Balakishore Pati <Balakishore.pati@amd.com>
      9//	    Ajit Kumar Pandey <AjitKumar.Pandey@amd.com>
     10
     11/* ACP-specific SOF IPC code */
     12
     13#include <linux/module.h>
     14#include "../ops.h"
     15#include "acp.h"
     16#include "acp-dsp-offset.h"
     17
     18void acp_mailbox_write(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
     19{
     20	memcpy_to_scratch(sdev, offset, message, bytes);
     21}
     22EXPORT_SYMBOL_NS(acp_mailbox_write, SND_SOC_SOF_AMD_COMMON);
     23
     24void acp_mailbox_read(struct snd_sof_dev *sdev, u32 offset, void *message, size_t bytes)
     25{
     26	memcpy_from_scratch(sdev, offset, message, bytes);
     27}
     28EXPORT_SYMBOL_NS(acp_mailbox_read, SND_SOC_SOF_AMD_COMMON);
     29
     30static void acpbus_trigger_host_to_dsp_swintr(struct acp_dev_data *adata)
     31{
     32	struct snd_sof_dev *sdev = adata->dev;
     33	u32 swintr_trigger;
     34
     35	swintr_trigger = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG);
     36	swintr_trigger |= 0x01;
     37	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SW_INTR_TRIG, swintr_trigger);
     38}
     39
     40static void acp_ipc_host_msg_set(struct snd_sof_dev *sdev)
     41{
     42	unsigned int host_msg = offsetof(struct scratch_ipc_conf, sof_host_msg_write);
     43
     44	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + host_msg, 1);
     45}
     46
     47static void acp_dsp_ipc_host_done(struct snd_sof_dev *sdev)
     48{
     49	unsigned int dsp_msg = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
     50
     51	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg, 0);
     52}
     53
     54static void acp_dsp_ipc_dsp_done(struct snd_sof_dev *sdev)
     55{
     56	unsigned int dsp_ack = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
     57
     58	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack, 0);
     59}
     60
     61int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
     62{
     63	struct acp_dev_data *adata = sdev->pdata->hw_pdata;
     64	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
     65	unsigned int count = ACP_HW_SEM_RETRY_COUNT;
     66
     67	while (snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0)) {
     68		/* Wait until acquired HW Semaphore Lock or timeout*/
     69		count--;
     70		if (!count) {
     71			dev_err(sdev->dev, "%s: Failed to acquire HW lock\n", __func__);
     72			return -EINVAL;
     73		}
     74	}
     75
     76	acp_mailbox_write(sdev, offset, msg->msg_data, msg->msg_size);
     77	acp_ipc_host_msg_set(sdev);
     78
     79	/* Trigger host to dsp interrupt for the msg */
     80	acpbus_trigger_host_to_dsp_swintr(adata);
     81
     82	/* Unlock or Release HW Semaphore */
     83	snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_AXI2DAGB_SEM_0, 0x0);
     84
     85	return 0;
     86}
     87EXPORT_SYMBOL_NS(acp_sof_ipc_send_msg, SND_SOC_SOF_AMD_COMMON);
     88
     89static void acp_dsp_ipc_get_reply(struct snd_sof_dev *sdev)
     90{
     91	struct snd_sof_ipc_msg *msg = sdev->msg;
     92	struct sof_ipc_reply reply;
     93	struct sof_ipc_cmd_hdr *hdr;
     94	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_in_box);
     95	int ret = 0;
     96
     97       /*
     98	* Sometimes, there is unexpected reply ipc arriving. The reply
     99	* ipc belongs to none of the ipcs sent from driver.
    100	* In this case, the driver must ignore the ipc.
    101	*/
    102	if (!msg) {
    103		dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
    104		return;
    105	}
    106	hdr = msg->msg_data;
    107	if (hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE) ||
    108	    hdr->cmd == (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE)) {
    109		/*
    110		 * memory windows are powered off before sending IPC reply,
    111		 * so we can't read the mailbox for CTX_SAVE and PM_GATE
    112		 * replies.
    113		 */
    114		reply.error = 0;
    115		reply.hdr.cmd = SOF_IPC_GLB_REPLY;
    116		reply.hdr.size = sizeof(reply);
    117		memcpy(msg->reply_data, &reply, sizeof(reply));
    118		goto out;
    119	}
    120	/* get IPC reply from DSP in the mailbox */
    121	acp_mailbox_read(sdev, offset, &reply, sizeof(reply));
    122	if (reply.error < 0) {
    123		memcpy(msg->reply_data, &reply, sizeof(reply));
    124		ret = reply.error;
    125	} else {
    126		/* reply correct size ? */
    127		if (reply.hdr.size != msg->reply_size &&
    128		    !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) {
    129			dev_err(sdev->dev, "reply expected %zu got %u bytes\n",
    130				msg->reply_size, reply.hdr.size);
    131			ret = -EINVAL;
    132		}
    133		/* read the message */
    134		if (msg->reply_size > 0)
    135			acp_mailbox_read(sdev, offset, msg->reply_data, msg->reply_size);
    136	}
    137out:
    138	msg->reply_error = ret;
    139}
    140
    141irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
    142{
    143	struct snd_sof_dev *sdev = context;
    144	unsigned int dsp_msg_write = offsetof(struct scratch_ipc_conf, sof_dsp_msg_write);
    145	unsigned int dsp_ack_write = offsetof(struct scratch_ipc_conf, sof_dsp_ack_write);
    146	bool ipc_irq = false;
    147	int dsp_msg, dsp_ack;
    148
    149	dsp_msg = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_msg_write);
    150	if (dsp_msg) {
    151		snd_sof_ipc_msgs_rx(sdev);
    152		acp_dsp_ipc_host_done(sdev);
    153		ipc_irq = true;
    154	}
    155
    156	dsp_ack = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SCRATCH_REG_0 + dsp_ack_write);
    157	if (dsp_ack) {
    158		spin_lock_irq(&sdev->ipc_lock);
    159		/* handle immediate reply from DSP core */
    160		acp_dsp_ipc_get_reply(sdev);
    161		snd_sof_ipc_reply(sdev, 0);
    162		/* set the done bit */
    163		acp_dsp_ipc_dsp_done(sdev);
    164		spin_unlock_irq(&sdev->ipc_lock);
    165		ipc_irq = true;
    166	}
    167
    168	if (!ipc_irq)
    169		dev_dbg_ratelimited(sdev->dev, "nothing to do in IPC IRQ thread\n");
    170
    171	return IRQ_HANDLED;
    172}
    173EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
    174
    175int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
    176			 void *p, size_t sz)
    177{
    178	unsigned int offset = offsetof(struct scratch_ipc_conf, sof_out_box);
    179
    180	if (!substream || !sdev->stream_box.size)
    181		acp_mailbox_read(sdev, offset, p, sz);
    182
    183	return 0;
    184}
    185EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
    186
    187int acp_sof_ipc_get_mailbox_offset(struct snd_sof_dev *sdev)
    188{
    189	return ACP_SCRATCH_MEMORY_ADDRESS;
    190}
    191EXPORT_SYMBOL_NS(acp_sof_ipc_get_mailbox_offset, SND_SOC_SOF_AMD_COMMON);
    192
    193MODULE_DESCRIPTION("AMD ACP sof-ipc driver");