238 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2018, NVIDIA CORPORATION.4 */5 6#include <linux/interrupt.h>7#include <linux/irq.h>8#include <linux/io.h>9#include <linux/of.h>10#include <linux/platform_device.h>11 12#include <soc/tegra/bpmp.h>13 14#include "bpmp-private.h"15 16#define TRIGGER_OFFSET 0x00017#define RESULT_OFFSET(id) (0xc00 + id * 4)18#define TRIGGER_ID_SHIFT 1619#define TRIGGER_CMD_GET 420 21#define STA_OFFSET 022#define SET_OFFSET 423#define CLR_OFFSET 824 25#define CH_MASK(ch) (0x3 << ((ch) * 2))26#define SL_SIGL(ch) (0x0 << ((ch) * 2))27#define SL_QUED(ch) (0x1 << ((ch) * 2))28#define MA_FREE(ch) (0x2 << ((ch) * 2))29#define MA_ACKD(ch) (0x3 << ((ch) * 2))30 31struct tegra210_bpmp {32 void __iomem *atomics;33 void __iomem *arb_sema;34 struct irq_data *tx_irq_data;35};36 37static u32 bpmp_channel_status(struct tegra_bpmp *bpmp, unsigned int index)38{39 struct tegra210_bpmp *priv = bpmp->priv;40 41 return __raw_readl(priv->arb_sema + STA_OFFSET) & CH_MASK(index);42}43 44static bool tegra210_bpmp_is_response_ready(struct tegra_bpmp_channel *channel)45{46 unsigned int index = channel->index;47 48 return bpmp_channel_status(channel->bpmp, index) == MA_ACKD(index);49}50 51static bool tegra210_bpmp_is_request_ready(struct tegra_bpmp_channel *channel)52{53 unsigned int index = channel->index;54 55 return bpmp_channel_status(channel->bpmp, index) == SL_SIGL(index);56}57 58static bool59tegra210_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel)60{61 unsigned int index = channel->index;62 63 return bpmp_channel_status(channel->bpmp, index) == MA_FREE(index);64}65 66static bool67tegra210_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel)68{69 unsigned int index = channel->index;70 71 return bpmp_channel_status(channel->bpmp, index) == SL_QUED(index);72}73 74static int tegra210_bpmp_post_request(struct tegra_bpmp_channel *channel)75{76 struct tegra210_bpmp *priv = channel->bpmp->priv;77 78 __raw_writel(CH_MASK(channel->index), priv->arb_sema + CLR_OFFSET);79 80 return 0;81}82 83static int tegra210_bpmp_post_response(struct tegra_bpmp_channel *channel)84{85 struct tegra210_bpmp *priv = channel->bpmp->priv;86 87 __raw_writel(MA_ACKD(channel->index), priv->arb_sema + SET_OFFSET);88 89 return 0;90}91 92static int tegra210_bpmp_ack_response(struct tegra_bpmp_channel *channel)93{94 struct tegra210_bpmp *priv = channel->bpmp->priv;95 96 __raw_writel(MA_ACKD(channel->index) ^ MA_FREE(channel->index),97 priv->arb_sema + CLR_OFFSET);98 99 return 0;100}101 102static int tegra210_bpmp_ack_request(struct tegra_bpmp_channel *channel)103{104 struct tegra210_bpmp *priv = channel->bpmp->priv;105 106 __raw_writel(SL_QUED(channel->index), priv->arb_sema + SET_OFFSET);107 108 return 0;109}110 111static int tegra210_bpmp_ring_doorbell(struct tegra_bpmp *bpmp)112{113 struct tegra210_bpmp *priv = bpmp->priv;114 struct irq_data *irq_data = priv->tx_irq_data;115 116 /*117 * Tegra Legacy Interrupt Controller (LIC) is used to notify BPMP of118 * available messages119 */120 if (irq_data->chip->irq_retrigger)121 return irq_data->chip->irq_retrigger(irq_data);122 123 return -EINVAL;124}125 126static irqreturn_t rx_irq(int irq, void *data)127{128 struct tegra_bpmp *bpmp = data;129 130 tegra_bpmp_handle_rx(bpmp);131 132 return IRQ_HANDLED;133}134 135static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel,136 struct tegra_bpmp *bpmp,137 unsigned int index)138{139 struct tegra210_bpmp *priv = bpmp->priv;140 void __iomem *p;141 u32 address;142 143 /* Retrieve channel base address from BPMP */144 writel(index << TRIGGER_ID_SHIFT | TRIGGER_CMD_GET,145 priv->atomics + TRIGGER_OFFSET);146 address = readl(priv->atomics + RESULT_OFFSET(index));147 148 p = devm_ioremap(bpmp->dev, address, 0x80);149 if (!p)150 return -ENOMEM;151 152 iosys_map_set_vaddr_iomem(&channel->ib, p);153 iosys_map_set_vaddr_iomem(&channel->ob, p);154 155 channel->index = index;156 init_completion(&channel->completion);157 channel->bpmp = bpmp;158 159 return 0;160}161 162static int tegra210_bpmp_init(struct tegra_bpmp *bpmp)163{164 struct platform_device *pdev = to_platform_device(bpmp->dev);165 struct tegra210_bpmp *priv;166 unsigned int i;167 int err;168 169 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);170 if (!priv)171 return -ENOMEM;172 173 bpmp->priv = priv;174 175 priv->atomics = devm_platform_ioremap_resource(pdev, 0);176 if (IS_ERR(priv->atomics))177 return PTR_ERR(priv->atomics);178 179 priv->arb_sema = devm_platform_ioremap_resource(pdev, 1);180 if (IS_ERR(priv->arb_sema))181 return PTR_ERR(priv->arb_sema);182 183 err = tegra210_bpmp_channel_init(bpmp->tx_channel, bpmp,184 bpmp->soc->channels.cpu_tx.offset);185 if (err < 0)186 return err;187 188 err = tegra210_bpmp_channel_init(bpmp->rx_channel, bpmp,189 bpmp->soc->channels.cpu_rx.offset);190 if (err < 0)191 return err;192 193 for (i = 0; i < bpmp->threaded.count; i++) {194 unsigned int index = bpmp->soc->channels.thread.offset + i;195 196 err = tegra210_bpmp_channel_init(&bpmp->threaded_channels[i],197 bpmp, index);198 if (err < 0)199 return err;200 }201 202 err = platform_get_irq_byname(pdev, "tx");203 if (err < 0)204 return err;205 206 priv->tx_irq_data = irq_get_irq_data(err);207 if (!priv->tx_irq_data) {208 dev_err(&pdev->dev, "failed to get IRQ data for TX IRQ\n");209 return -ENOENT;210 }211 212 err = platform_get_irq_byname(pdev, "rx");213 if (err < 0)214 return err;215 216 err = devm_request_irq(&pdev->dev, err, rx_irq,217 IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp);218 if (err < 0) {219 dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);220 return err;221 }222 223 return 0;224}225 226const struct tegra_bpmp_ops tegra210_bpmp_ops = {227 .init = tegra210_bpmp_init,228 .is_response_ready = tegra210_bpmp_is_response_ready,229 .is_request_ready = tegra210_bpmp_is_request_ready,230 .ack_response = tegra210_bpmp_ack_response,231 .ack_request = tegra210_bpmp_ack_request,232 .is_response_channel_free = tegra210_bpmp_is_response_channel_free,233 .is_request_channel_free = tegra210_bpmp_is_request_channel_free,234 .post_response = tegra210_bpmp_post_response,235 .post_request = tegra210_bpmp_post_request,236 .ring_doorbell = tegra210_bpmp_ring_doorbell,237};238