linux/drivers/mailbox/sprd-mailbox.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Spreadtrum mailbox driver
   4 *
   5 * Copyright (c) 2020 Spreadtrum Communications Inc.
   6 */
   7
   8#include <linux/delay.h>
   9#include <linux/err.h>
  10#include <linux/interrupt.h>
  11#include <linux/io.h>
  12#include <linux/mailbox_controller.h>
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/clk.h>
  16
  17#define SPRD_MBOX_ID            0x0
  18#define SPRD_MBOX_MSG_LOW       0x4
  19#define SPRD_MBOX_MSG_HIGH      0x8
  20#define SPRD_MBOX_TRIGGER       0xc
  21#define SPRD_MBOX_FIFO_RST      0x10
  22#define SPRD_MBOX_FIFO_STS      0x14
  23#define SPRD_MBOX_IRQ_STS       0x18
  24#define SPRD_MBOX_IRQ_MSK       0x1c
  25#define SPRD_MBOX_LOCK          0x20
  26#define SPRD_MBOX_FIFO_DEPTH    0x24
  27
  28/* Bit and mask definiation for inbox's SPRD_MBOX_FIFO_STS register */
  29#define SPRD_INBOX_FIFO_DELIVER_MASK            GENMASK(23, 16)
  30#define SPRD_INBOX_FIFO_OVERLOW_MASK            GENMASK(15, 8)
  31#define SPRD_INBOX_FIFO_DELIVER_SHIFT           16
  32#define SPRD_INBOX_FIFO_BUSY_MASK               GENMASK(7, 0)
  33
  34/* Bit and mask definiation for SPRD_MBOX_IRQ_STS register */
  35#define SPRD_MBOX_IRQ_CLR                       BIT(0)
  36
  37/* Bit and mask definiation for outbox's SPRD_MBOX_FIFO_STS register */
  38#define SPRD_OUTBOX_FIFO_FULL                   BIT(0)
  39#define SPRD_OUTBOX_FIFO_WR_SHIFT               16
  40#define SPRD_OUTBOX_FIFO_RD_SHIFT               24
  41#define SPRD_OUTBOX_FIFO_POS_MASK               GENMASK(7, 0)
  42
  43/* Bit and mask definiation for inbox's SPRD_MBOX_IRQ_MSK register */
  44#define SPRD_INBOX_FIFO_BLOCK_IRQ               BIT(0)
  45#define SPRD_INBOX_FIFO_OVERFLOW_IRQ            BIT(1)
  46#define SPRD_INBOX_FIFO_DELIVER_IRQ             BIT(2)
  47#define SPRD_INBOX_FIFO_IRQ_MASK                GENMASK(2, 0)
  48
  49/* Bit and mask definiation for outbox's SPRD_MBOX_IRQ_MSK register */
  50#define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ          BIT(0)
  51#define SPRD_OUTBOX_FIFO_IRQ_MASK               GENMASK(4, 0)
  52
  53#define SPRD_MBOX_CHAN_MAX                      8
  54
  55struct sprd_mbox_priv {
  56        struct mbox_controller  mbox;
  57        struct device           *dev;
  58        void __iomem            *inbox_base;
  59        void __iomem            *outbox_base;
  60        struct clk              *clk;
  61        u32                     outbox_fifo_depth;
  62
  63        struct mbox_chan        chan[SPRD_MBOX_CHAN_MAX];
  64};
  65
  66static struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox)
  67{
  68        return container_of(mbox, struct sprd_mbox_priv, mbox);
  69}
  70
  71static u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts)
  72{
  73        u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) &
  74                SPRD_OUTBOX_FIFO_POS_MASK;
  75        u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) &
  76                SPRD_OUTBOX_FIFO_POS_MASK;
  77        u32 fifo_len;
  78
  79        /*
  80         * If the read pointer is equal with write pointer, which means the fifo
  81         * is full or empty.
  82         */
  83        if (wr_pos == rd_pos) {
  84                if (fifo_sts & SPRD_OUTBOX_FIFO_FULL)
  85                        fifo_len = priv->outbox_fifo_depth;
  86                else
  87                        fifo_len = 0;
  88        } else if (wr_pos > rd_pos) {
  89                fifo_len = wr_pos - rd_pos;
  90        } else {
  91                fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos;
  92        }
  93
  94        return fifo_len;
  95}
  96
  97static irqreturn_t sprd_mbox_outbox_isr(int irq, void *data)
  98{
  99        struct sprd_mbox_priv *priv = data;
 100        struct mbox_chan *chan;
 101        u32 fifo_sts, fifo_len, msg[2];
 102        int i, id;
 103
 104        fifo_sts = readl(priv->outbox_base + SPRD_MBOX_FIFO_STS);
 105
 106        fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts);
 107        if (!fifo_len) {
 108                dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n");
 109                return IRQ_NONE;
 110        }
 111
 112        for (i = 0; i < fifo_len; i++) {
 113                msg[0] = readl(priv->outbox_base + SPRD_MBOX_MSG_LOW);
 114                msg[1] = readl(priv->outbox_base + SPRD_MBOX_MSG_HIGH);
 115                id = readl(priv->outbox_base + SPRD_MBOX_ID);
 116
 117                chan = &priv->chan[id];
 118                mbox_chan_received_data(chan, (void *)msg);
 119
 120                /* Trigger to update outbox FIFO pointer */
 121                writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER);
 122        }
 123
 124        /* Clear irq status after reading all message. */
 125        writel(SPRD_MBOX_IRQ_CLR, priv->outbox_base + SPRD_MBOX_IRQ_STS);
 126
 127        return IRQ_HANDLED;
 128}
 129
 130static irqreturn_t sprd_mbox_inbox_isr(int irq, void *data)
 131{
 132        struct sprd_mbox_priv *priv = data;
 133        struct mbox_chan *chan;
 134        u32 fifo_sts, send_sts, busy, id;
 135
 136        fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS);
 137
 138        /* Get the inbox data delivery status */
 139        send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >>
 140                SPRD_INBOX_FIFO_DELIVER_SHIFT;
 141        if (!send_sts) {
 142                dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n");
 143                return IRQ_NONE;
 144        }
 145
 146        while (send_sts) {
 147                id = __ffs(send_sts);
 148                send_sts &= (send_sts - 1);
 149
 150                chan = &priv->chan[id];
 151
 152                /*
 153                 * Check if the message was fetched by remote traget, if yes,
 154                 * that means the transmission has been completed.
 155                 */
 156                busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK;
 157                if (!(busy & BIT(id)))
 158                        mbox_chan_txdone(chan, 0);
 159        }
 160
 161        /* Clear FIFO delivery and overflow status */
 162        writel(fifo_sts &
 163               (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK),
 164               priv->inbox_base + SPRD_MBOX_FIFO_RST);
 165
 166        /* Clear irq status */
 167        writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS);
 168
 169        return IRQ_HANDLED;
 170}
 171
 172static int sprd_mbox_send_data(struct mbox_chan *chan, void *msg)
 173{
 174        struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);
 175        unsigned long id = (unsigned long)chan->con_priv;
 176        u32 *data = msg;
 177
 178        /* Write data into inbox FIFO, and only support 8 bytes every time */
 179        writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW);
 180        writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH);
 181
 182        /* Set target core id */
 183        writel(id, priv->inbox_base + SPRD_MBOX_ID);
 184
 185        /* Trigger remote request */
 186        writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER);
 187
 188        return 0;
 189}
 190
 191static int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout)
 192{
 193        struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);
 194        unsigned long id = (unsigned long)chan->con_priv;
 195        u32 busy;
 196
 197        timeout = jiffies + msecs_to_jiffies(timeout);
 198
 199        while (time_before(jiffies, timeout)) {
 200                busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) &
 201                        SPRD_INBOX_FIFO_BUSY_MASK;
 202                if (!(busy & BIT(id))) {
 203                        mbox_chan_txdone(chan, 0);
 204                        return 0;
 205                }
 206
 207                udelay(1);
 208        }
 209
 210        return -ETIME;
 211}
 212
 213static int sprd_mbox_startup(struct mbox_chan *chan)
 214{
 215        struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);
 216        u32 val;
 217
 218        /* Select outbox FIFO mode and reset the outbox FIFO status */
 219        writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST);
 220
 221        /* Enable inbox FIFO overflow and delivery interrupt */
 222        val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK);
 223        val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ);
 224        writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK);
 225
 226        /* Enable outbox FIFO not empty interrupt */
 227        val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK);
 228        val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ;
 229        writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK);
 230
 231        return 0;
 232}
 233
 234static void sprd_mbox_shutdown(struct mbox_chan *chan)
 235{
 236        struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox);
 237
 238        /* Disable inbox & outbox interrupt */
 239        writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK);
 240        writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK);
 241}
 242
 243static const struct mbox_chan_ops sprd_mbox_ops = {
 244        .send_data      = sprd_mbox_send_data,
 245        .flush          = sprd_mbox_flush,
 246        .startup        = sprd_mbox_startup,
 247        .shutdown       = sprd_mbox_shutdown,
 248};
 249
 250static void sprd_mbox_disable(void *data)
 251{
 252        struct sprd_mbox_priv *priv = data;
 253
 254        clk_disable_unprepare(priv->clk);
 255}
 256
 257static int sprd_mbox_probe(struct platform_device *pdev)
 258{
 259        struct device *dev = &pdev->dev;
 260        struct sprd_mbox_priv *priv;
 261        int ret, inbox_irq, outbox_irq;
 262        unsigned long id;
 263
 264        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 265        if (!priv)
 266                return -ENOMEM;
 267
 268        priv->dev = dev;
 269
 270        /*
 271         * The Spreadtrum mailbox uses an inbox to send messages to the target
 272         * core, and uses an outbox to receive messages from other cores.
 273         *
 274         * Thus the mailbox controller supplies 2 different register addresses
 275         * and IRQ numbers for inbox and outbox.
 276         */
 277        priv->inbox_base = devm_platform_ioremap_resource(pdev, 0);
 278        if (IS_ERR(priv->inbox_base))
 279                return PTR_ERR(priv->inbox_base);
 280
 281        priv->outbox_base = devm_platform_ioremap_resource(pdev, 1);
 282        if (IS_ERR(priv->outbox_base))
 283                return PTR_ERR(priv->outbox_base);
 284
 285        priv->clk = devm_clk_get(dev, "enable");
 286        if (IS_ERR(priv->clk)) {
 287                dev_err(dev, "failed to get mailbox clock\n");
 288                return PTR_ERR(priv->clk);
 289        }
 290
 291        ret = clk_prepare_enable(priv->clk);
 292        if (ret)
 293                return ret;
 294
 295        ret = devm_add_action_or_reset(dev, sprd_mbox_disable, priv);
 296        if (ret) {
 297                dev_err(dev, "failed to add mailbox disable action\n");
 298                return ret;
 299        }
 300
 301        inbox_irq = platform_get_irq(pdev, 0);
 302        if (inbox_irq < 0)
 303                return inbox_irq;
 304
 305        ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr,
 306                               IRQF_NO_SUSPEND, dev_name(dev), priv);
 307        if (ret) {
 308                dev_err(dev, "failed to request inbox IRQ: %d\n", ret);
 309                return ret;
 310        }
 311
 312        outbox_irq = platform_get_irq(pdev, 1);
 313        if (outbox_irq < 0)
 314                return outbox_irq;
 315
 316        ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr,
 317                               IRQF_NO_SUSPEND, dev_name(dev), priv);
 318        if (ret) {
 319                dev_err(dev, "failed to request outbox IRQ: %d\n", ret);
 320                return ret;
 321        }
 322
 323        /* Get the default outbox FIFO depth */
 324        priv->outbox_fifo_depth =
 325                readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1;
 326        priv->mbox.dev = dev;
 327        priv->mbox.chans = &priv->chan[0];
 328        priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX;
 329        priv->mbox.ops = &sprd_mbox_ops;
 330        priv->mbox.txdone_irq = true;
 331
 332        for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++)
 333                priv->chan[id].con_priv = (void *)id;
 334
 335        ret = devm_mbox_controller_register(dev, &priv->mbox);
 336        if (ret) {
 337                dev_err(dev, "failed to register mailbox: %d\n", ret);
 338                return ret;
 339        }
 340
 341        return 0;
 342}
 343
 344static const struct of_device_id sprd_mbox_of_match[] = {
 345        { .compatible = "sprd,sc9860-mailbox", },
 346        { },
 347};
 348MODULE_DEVICE_TABLE(of, sprd_mbox_of_match);
 349
 350static struct platform_driver sprd_mbox_driver = {
 351        .driver = {
 352                .name = "sprd-mailbox",
 353                .of_match_table = sprd_mbox_of_match,
 354        },
 355        .probe  = sprd_mbox_probe,
 356};
 357module_platform_driver(sprd_mbox_driver);
 358
 359MODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>");
 360MODULE_DESCRIPTION("Spreadtrum mailbox driver");
 361MODULE_LICENSE("GPL v2");
 362