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

bpmp-tegra186.c (7417B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (c) 2018, NVIDIA CORPORATION.
      4 */
      5
      6#include <linux/genalloc.h>
      7#include <linux/mailbox_client.h>
      8#include <linux/platform_device.h>
      9
     10#include <soc/tegra/bpmp.h>
     11#include <soc/tegra/bpmp-abi.h>
     12#include <soc/tegra/ivc.h>
     13
     14#include "bpmp-private.h"
     15
     16struct tegra186_bpmp {
     17	struct tegra_bpmp *parent;
     18
     19	struct {
     20		struct gen_pool *pool;
     21		dma_addr_t phys;
     22		void *virt;
     23	} tx, rx;
     24
     25	struct {
     26		struct mbox_client client;
     27		struct mbox_chan *channel;
     28	} mbox;
     29};
     30
     31static inline struct tegra_bpmp *
     32mbox_client_to_bpmp(struct mbox_client *client)
     33{
     34	struct tegra186_bpmp *priv;
     35
     36	priv = container_of(client, struct tegra186_bpmp, mbox.client);
     37
     38	return priv->parent;
     39}
     40
     41static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel)
     42{
     43	void *frame;
     44
     45	frame = tegra_ivc_read_get_next_frame(channel->ivc);
     46	if (IS_ERR(frame)) {
     47		channel->ib = NULL;
     48		return false;
     49	}
     50
     51	channel->ib = frame;
     52
     53	return true;
     54}
     55
     56static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel)
     57{
     58	void *frame;
     59
     60	frame = tegra_ivc_write_get_next_frame(channel->ivc);
     61	if (IS_ERR(frame)) {
     62		channel->ob = NULL;
     63		return false;
     64	}
     65
     66	channel->ob = frame;
     67
     68	return true;
     69}
     70
     71static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel)
     72{
     73	return tegra_ivc_read_advance(channel->ivc);
     74}
     75
     76static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel)
     77{
     78	return tegra_ivc_write_advance(channel->ivc);
     79}
     80
     81static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp)
     82{
     83	struct tegra186_bpmp *priv = bpmp->priv;
     84	int err;
     85
     86	err = mbox_send_message(priv->mbox.channel, NULL);
     87	if (err < 0)
     88		return err;
     89
     90	mbox_client_txdone(priv->mbox.channel, 0);
     91
     92	return 0;
     93}
     94
     95static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
     96{
     97	struct tegra_bpmp *bpmp = data;
     98	struct tegra186_bpmp *priv = bpmp->priv;
     99
    100	if (WARN_ON(priv->mbox.channel == NULL))
    101		return;
    102
    103	tegra186_bpmp_ring_doorbell(bpmp);
    104}
    105
    106static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel,
    107				      struct tegra_bpmp *bpmp,
    108				      unsigned int index)
    109{
    110	struct tegra186_bpmp *priv = bpmp->priv;
    111	size_t message_size, queue_size;
    112	unsigned int offset;
    113	int err;
    114
    115	channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),
    116				    GFP_KERNEL);
    117	if (!channel->ivc)
    118		return -ENOMEM;
    119
    120	message_size = tegra_ivc_align(MSG_MIN_SZ);
    121	queue_size = tegra_ivc_total_queue_size(message_size);
    122	offset = queue_size * index;
    123
    124	err = tegra_ivc_init(channel->ivc, NULL,
    125			     priv->rx.virt + offset, priv->rx.phys + offset,
    126			     priv->tx.virt + offset, priv->tx.phys + offset,
    127			     1, message_size, tegra186_bpmp_ivc_notify,
    128			     bpmp);
    129	if (err < 0) {
    130		dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",
    131			index, err);
    132		return err;
    133	}
    134
    135	init_completion(&channel->completion);
    136	channel->bpmp = bpmp;
    137
    138	return 0;
    139}
    140
    141static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel)
    142{
    143	/* reset the channel state */
    144	tegra_ivc_reset(channel->ivc);
    145
    146	/* sync the channel state with BPMP */
    147	while (tegra_ivc_notified(channel->ivc))
    148		;
    149}
    150
    151static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel)
    152{
    153	tegra_ivc_cleanup(channel->ivc);
    154}
    155
    156static void mbox_handle_rx(struct mbox_client *client, void *data)
    157{
    158	struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);
    159
    160	tegra_bpmp_handle_rx(bpmp);
    161}
    162
    163static int tegra186_bpmp_init(struct tegra_bpmp *bpmp)
    164{
    165	struct tegra186_bpmp *priv;
    166	unsigned int i;
    167	int err;
    168
    169	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
    170	if (!priv)
    171		return -ENOMEM;
    172
    173	bpmp->priv = priv;
    174	priv->parent = bpmp;
    175
    176	priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0);
    177	if (!priv->tx.pool) {
    178		dev_err(bpmp->dev, "TX shmem pool not found\n");
    179		return -EPROBE_DEFER;
    180	}
    181
    182	priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys);
    183	if (!priv->tx.virt) {
    184		dev_err(bpmp->dev, "failed to allocate from TX pool\n");
    185		return -ENOMEM;
    186	}
    187
    188	priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1);
    189	if (!priv->rx.pool) {
    190		dev_err(bpmp->dev, "RX shmem pool not found\n");
    191		err = -EPROBE_DEFER;
    192		goto free_tx;
    193	}
    194
    195	priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys);
    196	if (!priv->rx.virt) {
    197		dev_err(bpmp->dev, "failed to allocate from RX pool\n");
    198		err = -ENOMEM;
    199		goto free_tx;
    200	}
    201
    202	err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp,
    203					 bpmp->soc->channels.cpu_tx.offset);
    204	if (err < 0)
    205		goto free_rx;
    206
    207	err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp,
    208					 bpmp->soc->channels.cpu_rx.offset);
    209	if (err < 0)
    210		goto cleanup_tx_channel;
    211
    212	for (i = 0; i < bpmp->threaded.count; i++) {
    213		unsigned int index = bpmp->soc->channels.thread.offset + i;
    214
    215		err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i],
    216						 bpmp, index);
    217		if (err < 0)
    218			goto cleanup_channels;
    219	}
    220
    221	/* mbox registration */
    222	priv->mbox.client.dev = bpmp->dev;
    223	priv->mbox.client.rx_callback = mbox_handle_rx;
    224	priv->mbox.client.tx_block = false;
    225	priv->mbox.client.knows_txdone = false;
    226
    227	priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0);
    228	if (IS_ERR(priv->mbox.channel)) {
    229		err = PTR_ERR(priv->mbox.channel);
    230		dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err);
    231		goto cleanup_channels;
    232	}
    233
    234	tegra186_bpmp_channel_reset(bpmp->tx_channel);
    235	tegra186_bpmp_channel_reset(bpmp->rx_channel);
    236
    237	for (i = 0; i < bpmp->threaded.count; i++)
    238		tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]);
    239
    240	return 0;
    241
    242cleanup_channels:
    243	for (i = 0; i < bpmp->threaded.count; i++) {
    244		if (!bpmp->threaded_channels[i].bpmp)
    245			continue;
    246
    247		tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
    248	}
    249
    250	tegra186_bpmp_channel_cleanup(bpmp->rx_channel);
    251cleanup_tx_channel:
    252	tegra186_bpmp_channel_cleanup(bpmp->tx_channel);
    253free_rx:
    254	gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096);
    255free_tx:
    256	gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096);
    257
    258	return err;
    259}
    260
    261static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp)
    262{
    263	struct tegra186_bpmp *priv = bpmp->priv;
    264	unsigned int i;
    265
    266	mbox_free_channel(priv->mbox.channel);
    267
    268	for (i = 0; i < bpmp->threaded.count; i++)
    269		tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]);
    270
    271	tegra186_bpmp_channel_cleanup(bpmp->rx_channel);
    272	tegra186_bpmp_channel_cleanup(bpmp->tx_channel);
    273
    274	gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096);
    275	gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096);
    276}
    277
    278static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp)
    279{
    280	unsigned int i;
    281
    282	/* reset message channels */
    283	tegra186_bpmp_channel_reset(bpmp->tx_channel);
    284	tegra186_bpmp_channel_reset(bpmp->rx_channel);
    285
    286	for (i = 0; i < bpmp->threaded.count; i++)
    287		tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]);
    288
    289	return 0;
    290}
    291
    292const struct tegra_bpmp_ops tegra186_bpmp_ops = {
    293	.init = tegra186_bpmp_init,
    294	.deinit = tegra186_bpmp_deinit,
    295	.is_response_ready = tegra186_bpmp_is_message_ready,
    296	.is_request_ready = tegra186_bpmp_is_message_ready,
    297	.ack_response = tegra186_bpmp_ack_message,
    298	.ack_request = tegra186_bpmp_ack_message,
    299	.is_response_channel_free = tegra186_bpmp_is_channel_free,
    300	.is_request_channel_free = tegra186_bpmp_is_channel_free,
    301	.post_response = tegra186_bpmp_post_message,
    302	.post_request = tegra186_bpmp_post_message,
    303	.ring_doorbell = tegra186_bpmp_ring_doorbell,
    304	.resume = tegra186_bpmp_resume,
    305};