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

ipc.c (5579B)


      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) 2018 Intel Corporation. All rights reserved.
      7//
      8// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
      9//
     10// Generic IPC layer that can work over MMIO and SPI/I2C. PHY layer provided
     11// by platform driver code.
     12//
     13
     14#include <linux/mutex.h>
     15#include <linux/types.h>
     16
     17#include "sof-priv.h"
     18#include "sof-audio.h"
     19#include "ops.h"
     20
     21/**
     22 * sof_ipc_send_msg - generic function to prepare and send one IPC message
     23 * @sdev:		pointer to SOF core device struct
     24 * @msg_data:		pointer to a message to send
     25 * @msg_bytes:		number of bytes in the message
     26 * @reply_bytes:	number of bytes available for the reply.
     27 *			The buffer for the reply data is not passed to this
     28 *			function, the available size is an information for the
     29 *			reply handling functions.
     30 *
     31 * On success the function returns 0, otherwise negative error number.
     32 *
     33 * Note: higher level sdev->ipc->tx_mutex must be held to make sure that
     34 *	 transfers are synchronized.
     35 */
     36int sof_ipc_send_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes,
     37		     size_t reply_bytes)
     38{
     39	struct snd_sof_ipc *ipc = sdev->ipc;
     40	struct snd_sof_ipc_msg *msg;
     41	int ret;
     42
     43	if (ipc->disable_ipc_tx || sdev->fw_state != SOF_FW_BOOT_COMPLETE)
     44		return -ENODEV;
     45
     46	/*
     47	 * The spin-lock is needed to protect message objects against other
     48	 * atomic contexts.
     49	 */
     50	spin_lock_irq(&sdev->ipc_lock);
     51
     52	/* initialise the message */
     53	msg = &ipc->msg;
     54
     55	/* attach message data */
     56	msg->msg_data = msg_data;
     57	msg->msg_size = msg_bytes;
     58
     59	msg->reply_size = reply_bytes;
     60	msg->reply_error = 0;
     61
     62	sdev->msg = msg;
     63
     64	ret = snd_sof_dsp_send_msg(sdev, msg);
     65	/* Next reply that we receive will be related to this message */
     66	if (!ret)
     67		msg->ipc_complete = false;
     68
     69	spin_unlock_irq(&sdev->ipc_lock);
     70
     71	return ret;
     72}
     73
     74/* send IPC message from host to DSP */
     75int sof_ipc_tx_message(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
     76		       void *reply_data, size_t reply_bytes)
     77{
     78	if (msg_bytes > ipc->max_payload_size ||
     79	    reply_bytes > ipc->max_payload_size)
     80		return -ENOBUFS;
     81
     82	return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
     83				reply_bytes, false);
     84}
     85EXPORT_SYMBOL(sof_ipc_tx_message);
     86
     87/*
     88 * send IPC message from host to DSP without modifying the DSP state.
     89 * This will be used for IPC's that can be handled by the DSP
     90 * even in a low-power D0 substate.
     91 */
     92int sof_ipc_tx_message_no_pm(struct snd_sof_ipc *ipc, void *msg_data, size_t msg_bytes,
     93			     void *reply_data, size_t reply_bytes)
     94{
     95	if (msg_bytes > ipc->max_payload_size ||
     96	    reply_bytes > ipc->max_payload_size)
     97		return -ENOBUFS;
     98
     99	return ipc->ops->tx_msg(ipc->sdev, msg_data, msg_bytes, reply_data,
    100				reply_bytes, true);
    101}
    102EXPORT_SYMBOL(sof_ipc_tx_message_no_pm);
    103
    104/* Generic helper function to retrieve the reply */
    105void snd_sof_ipc_get_reply(struct snd_sof_dev *sdev)
    106{
    107	/*
    108	 * Sometimes, there is unexpected reply ipc arriving. The reply
    109	 * ipc belongs to none of the ipcs sent from driver.
    110	 * In this case, the driver must ignore the ipc.
    111	 */
    112	if (!sdev->msg) {
    113		dev_warn(sdev->dev, "unexpected ipc interrupt raised!\n");
    114		return;
    115	}
    116
    117	sdev->msg->reply_error = sdev->ipc->ops->get_reply(sdev);
    118}
    119EXPORT_SYMBOL(snd_sof_ipc_get_reply);
    120
    121/* handle reply message from DSP */
    122void snd_sof_ipc_reply(struct snd_sof_dev *sdev, u32 msg_id)
    123{
    124	struct snd_sof_ipc_msg *msg = &sdev->ipc->msg;
    125
    126	if (msg->ipc_complete) {
    127		dev_dbg(sdev->dev,
    128			"no reply expected, received 0x%x, will be ignored",
    129			msg_id);
    130		return;
    131	}
    132
    133	/* wake up and return the error if we have waiters on this message ? */
    134	msg->ipc_complete = true;
    135	wake_up(&msg->waitq);
    136}
    137EXPORT_SYMBOL(snd_sof_ipc_reply);
    138
    139struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev)
    140{
    141	struct snd_sof_ipc *ipc;
    142	struct snd_sof_ipc_msg *msg;
    143	const struct sof_ipc_ops *ops;
    144
    145	ipc = devm_kzalloc(sdev->dev, sizeof(*ipc), GFP_KERNEL);
    146	if (!ipc)
    147		return NULL;
    148
    149	mutex_init(&ipc->tx_mutex);
    150	ipc->sdev = sdev;
    151	msg = &ipc->msg;
    152
    153	/* indicate that we aren't sending a message ATM */
    154	msg->ipc_complete = true;
    155
    156	init_waitqueue_head(&msg->waitq);
    157
    158	/*
    159	 * Use IPC3 ops as it is the only available version now. With the addition of new IPC
    160	 * versions, this will need to be modified to use the selected version at runtime.
    161	 */
    162	ipc->ops = &ipc3_ops;
    163	ops = ipc->ops;
    164
    165	/* check for mandatory ops */
    166	if (!ops->tx_msg || !ops->rx_msg || !ops->set_get_data || !ops->get_reply) {
    167		dev_err(sdev->dev, "Missing IPC message handling ops\n");
    168		return NULL;
    169	}
    170
    171	if (!ops->fw_loader || !ops->fw_loader->validate ||
    172	    !ops->fw_loader->parse_ext_manifest) {
    173		dev_err(sdev->dev, "Missing IPC firmware loading ops\n");
    174		return NULL;
    175	}
    176
    177	if (!ops->pcm) {
    178		dev_err(sdev->dev, "Missing IPC PCM ops\n");
    179		return NULL;
    180	}
    181
    182	if (!ops->tplg || !ops->tplg->widget || !ops->tplg->control) {
    183		dev_err(sdev->dev, "Missing IPC topology ops\n");
    184		return NULL;
    185	}
    186
    187	if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend ||
    188				!ops->fw_tracing->resume)) {
    189		dev_err(sdev->dev, "Missing firmware tracing ops\n");
    190		return NULL;
    191	}
    192
    193	return ipc;
    194}
    195EXPORT_SYMBOL(snd_sof_ipc_init);
    196
    197void snd_sof_ipc_free(struct snd_sof_dev *sdev)
    198{
    199	struct snd_sof_ipc *ipc = sdev->ipc;
    200
    201	if (!ipc)
    202		return;
    203
    204	/* disable sending of ipc's */
    205	mutex_lock(&ipc->tx_mutex);
    206	ipc->disable_ipc_tx = true;
    207	mutex_unlock(&ipc->tx_mutex);
    208}
    209EXPORT_SYMBOL(snd_sof_ipc_free);