linux/drivers/firmware/tegra/bpmp-tegra186.c
<<
>>
Prefs
   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 -ENOMEM;
 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 = -ENOMEM;
 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};
 306