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