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