linux/drivers/mailbox/hi6220-mailbox.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Hisilicon's Hi6220 mailbox driver
   4 *
   5 * Copyright (c) 2015 HiSilicon Limited.
   6 * Copyright (c) 2015 Linaro Limited.
   7 *
   8 * Author: Leo Yan <leo.yan@linaro.org>
   9 */
  10
  11#include <linux/device.h>
  12#include <linux/err.h>
  13#include <linux/interrupt.h>
  14#include <linux/io.h>
  15#include <linux/kfifo.h>
  16#include <linux/mailbox_controller.h>
  17#include <linux/module.h>
  18#include <linux/platform_device.h>
  19#include <linux/slab.h>
  20
  21#define MBOX_CHAN_MAX                   32
  22
  23#define MBOX_TX                         0x1
  24
  25/* Mailbox message length: 8 words */
  26#define MBOX_MSG_LEN                    8
  27
  28/* Mailbox Registers */
  29#define MBOX_OFF(m)                     (0x40 * (m))
  30#define MBOX_MODE_REG(m)                (MBOX_OFF(m) + 0x0)
  31#define MBOX_DATA_REG(m)                (MBOX_OFF(m) + 0x4)
  32
  33#define MBOX_STATE_MASK                 (0xF << 4)
  34#define MBOX_STATE_IDLE                 (0x1 << 4)
  35#define MBOX_STATE_TX                   (0x2 << 4)
  36#define MBOX_STATE_RX                   (0x4 << 4)
  37#define MBOX_STATE_ACK                  (0x8 << 4)
  38#define MBOX_ACK_CONFIG_MASK            (0x1 << 0)
  39#define MBOX_ACK_AUTOMATIC              (0x1 << 0)
  40#define MBOX_ACK_IRQ                    (0x0 << 0)
  41
  42/* IPC registers */
  43#define ACK_INT_RAW_REG(i)              ((i) + 0x400)
  44#define ACK_INT_MSK_REG(i)              ((i) + 0x404)
  45#define ACK_INT_STAT_REG(i)             ((i) + 0x408)
  46#define ACK_INT_CLR_REG(i)              ((i) + 0x40c)
  47#define ACK_INT_ENA_REG(i)              ((i) + 0x500)
  48#define ACK_INT_DIS_REG(i)              ((i) + 0x504)
  49#define DST_INT_RAW_REG(i)              ((i) + 0x420)
  50
  51
  52struct hi6220_mbox_chan {
  53
  54        /*
  55         * Description for channel's hardware info:
  56         *  - direction: tx or rx
  57         *  - dst irq: peer core's irq number
  58         *  - ack irq: local irq number
  59         *  - slot number
  60         */
  61        unsigned int dir, dst_irq, ack_irq;
  62        unsigned int slot;
  63
  64        struct hi6220_mbox *parent;
  65};
  66
  67struct hi6220_mbox {
  68        struct device *dev;
  69
  70        int irq;
  71
  72        /* flag of enabling tx's irq mode */
  73        bool tx_irq_mode;
  74
  75        /* region for ipc event */
  76        void __iomem *ipc;
  77
  78        /* region for mailbox */
  79        void __iomem *base;
  80
  81        unsigned int chan_num;
  82        struct hi6220_mbox_chan *mchan;
  83
  84        void *irq_map_chan[MBOX_CHAN_MAX];
  85        struct mbox_chan *chan;
  86        struct mbox_controller controller;
  87};
  88
  89static void mbox_set_state(struct hi6220_mbox *mbox,
  90                           unsigned int slot, u32 val)
  91{
  92        u32 status;
  93
  94        status = readl(mbox->base + MBOX_MODE_REG(slot));
  95        status = (status & ~MBOX_STATE_MASK) | val;
  96        writel(status, mbox->base + MBOX_MODE_REG(slot));
  97}
  98
  99static void mbox_set_mode(struct hi6220_mbox *mbox,
 100                          unsigned int slot, u32 val)
 101{
 102        u32 mode;
 103
 104        mode = readl(mbox->base + MBOX_MODE_REG(slot));
 105        mode = (mode & ~MBOX_ACK_CONFIG_MASK) | val;
 106        writel(mode, mbox->base + MBOX_MODE_REG(slot));
 107}
 108
 109static bool hi6220_mbox_last_tx_done(struct mbox_chan *chan)
 110{
 111        struct hi6220_mbox_chan *mchan = chan->con_priv;
 112        struct hi6220_mbox *mbox = mchan->parent;
 113        u32 state;
 114
 115        /* Only set idle state for polling mode */
 116        BUG_ON(mbox->tx_irq_mode);
 117
 118        state = readl(mbox->base + MBOX_MODE_REG(mchan->slot));
 119        return ((state & MBOX_STATE_MASK) == MBOX_STATE_IDLE);
 120}
 121
 122static int hi6220_mbox_send_data(struct mbox_chan *chan, void *msg)
 123{
 124        struct hi6220_mbox_chan *mchan = chan->con_priv;
 125        struct hi6220_mbox *mbox = mchan->parent;
 126        unsigned int slot = mchan->slot;
 127        u32 *buf = msg;
 128        int i;
 129
 130        /* indicate as a TX channel */
 131        mchan->dir = MBOX_TX;
 132
 133        mbox_set_state(mbox, slot, MBOX_STATE_TX);
 134
 135        if (mbox->tx_irq_mode)
 136                mbox_set_mode(mbox, slot, MBOX_ACK_IRQ);
 137        else
 138                mbox_set_mode(mbox, slot, MBOX_ACK_AUTOMATIC);
 139
 140        for (i = 0; i < MBOX_MSG_LEN; i++)
 141                writel(buf[i], mbox->base + MBOX_DATA_REG(slot) + i * 4);
 142
 143        /* trigger remote request */
 144        writel(BIT(mchan->dst_irq), DST_INT_RAW_REG(mbox->ipc));
 145        return 0;
 146}
 147
 148static irqreturn_t hi6220_mbox_interrupt(int irq, void *p)
 149{
 150        struct hi6220_mbox *mbox = p;
 151        struct hi6220_mbox_chan *mchan;
 152        struct mbox_chan *chan;
 153        unsigned int state, intr_bit, i;
 154        u32 msg[MBOX_MSG_LEN];
 155
 156        state = readl(ACK_INT_STAT_REG(mbox->ipc));
 157        if (!state) {
 158                dev_warn(mbox->dev, "%s: spurious interrupt\n",
 159                         __func__);
 160                return IRQ_HANDLED;
 161        }
 162
 163        while (state) {
 164                intr_bit = __ffs(state);
 165                state &= (state - 1);
 166
 167                chan = mbox->irq_map_chan[intr_bit];
 168                if (!chan) {
 169                        dev_warn(mbox->dev, "%s: unexpected irq vector %d\n",
 170                                 __func__, intr_bit);
 171                        continue;
 172                }
 173
 174                mchan = chan->con_priv;
 175                if (mchan->dir == MBOX_TX)
 176                        mbox_chan_txdone(chan, 0);
 177                else {
 178                        for (i = 0; i < MBOX_MSG_LEN; i++)
 179                                msg[i] = readl(mbox->base +
 180                                        MBOX_DATA_REG(mchan->slot) + i * 4);
 181
 182                        mbox_chan_received_data(chan, (void *)msg);
 183                }
 184
 185                /* clear IRQ source */
 186                writel(BIT(mchan->ack_irq), ACK_INT_CLR_REG(mbox->ipc));
 187                mbox_set_state(mbox, mchan->slot, MBOX_STATE_IDLE);
 188        }
 189
 190        return IRQ_HANDLED;
 191}
 192
 193static int hi6220_mbox_startup(struct mbox_chan *chan)
 194{
 195        struct hi6220_mbox_chan *mchan = chan->con_priv;
 196        struct hi6220_mbox *mbox = mchan->parent;
 197
 198        mchan->dir = 0;
 199
 200        /* enable interrupt */
 201        writel(BIT(mchan->ack_irq), ACK_INT_ENA_REG(mbox->ipc));
 202        return 0;
 203}
 204
 205static void hi6220_mbox_shutdown(struct mbox_chan *chan)
 206{
 207        struct hi6220_mbox_chan *mchan = chan->con_priv;
 208        struct hi6220_mbox *mbox = mchan->parent;
 209
 210        /* disable interrupt */
 211        writel(BIT(mchan->ack_irq), ACK_INT_DIS_REG(mbox->ipc));
 212        mbox->irq_map_chan[mchan->ack_irq] = NULL;
 213}
 214
 215static const struct mbox_chan_ops hi6220_mbox_ops = {
 216        .send_data    = hi6220_mbox_send_data,
 217        .startup      = hi6220_mbox_startup,
 218        .shutdown     = hi6220_mbox_shutdown,
 219        .last_tx_done = hi6220_mbox_last_tx_done,
 220};
 221
 222static struct mbox_chan *hi6220_mbox_xlate(struct mbox_controller *controller,
 223                                           const struct of_phandle_args *spec)
 224{
 225        struct hi6220_mbox *mbox = dev_get_drvdata(controller->dev);
 226        struct hi6220_mbox_chan *mchan;
 227        struct mbox_chan *chan;
 228        unsigned int i = spec->args[0];
 229        unsigned int dst_irq = spec->args[1];
 230        unsigned int ack_irq = spec->args[2];
 231
 232        /* Bounds checking */
 233        if (i >= mbox->chan_num || dst_irq >= mbox->chan_num ||
 234            ack_irq >= mbox->chan_num) {
 235                dev_err(mbox->dev,
 236                        "Invalid channel idx %d dst_irq %d ack_irq %d\n",
 237                        i, dst_irq, ack_irq);
 238                return ERR_PTR(-EINVAL);
 239        }
 240
 241        /* Is requested channel free? */
 242        chan = &mbox->chan[i];
 243        if (mbox->irq_map_chan[ack_irq] == (void *)chan) {
 244                dev_err(mbox->dev, "Channel in use\n");
 245                return ERR_PTR(-EBUSY);
 246        }
 247
 248        mchan = chan->con_priv;
 249        mchan->dst_irq = dst_irq;
 250        mchan->ack_irq = ack_irq;
 251
 252        mbox->irq_map_chan[ack_irq] = (void *)chan;
 253        return chan;
 254}
 255
 256static const struct of_device_id hi6220_mbox_of_match[] = {
 257        { .compatible = "hisilicon,hi6220-mbox", },
 258        {},
 259};
 260MODULE_DEVICE_TABLE(of, hi6220_mbox_of_match);
 261
 262static int hi6220_mbox_probe(struct platform_device *pdev)
 263{
 264        struct device_node *node = pdev->dev.of_node;
 265        struct device *dev = &pdev->dev;
 266        struct hi6220_mbox *mbox;
 267        struct resource *res;
 268        int i, err;
 269
 270        mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
 271        if (!mbox)
 272                return -ENOMEM;
 273
 274        mbox->dev = dev;
 275        mbox->chan_num = MBOX_CHAN_MAX;
 276        mbox->mchan = devm_kcalloc(dev,
 277                mbox->chan_num, sizeof(*mbox->mchan), GFP_KERNEL);
 278        if (!mbox->mchan)
 279                return -ENOMEM;
 280
 281        mbox->chan = devm_kcalloc(dev,
 282                mbox->chan_num, sizeof(*mbox->chan), GFP_KERNEL);
 283        if (!mbox->chan)
 284                return -ENOMEM;
 285
 286        mbox->irq = platform_get_irq(pdev, 0);
 287        if (mbox->irq < 0)
 288                return mbox->irq;
 289
 290        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 291        mbox->ipc = devm_ioremap_resource(dev, res);
 292        if (IS_ERR(mbox->ipc)) {
 293                dev_err(dev, "ioremap ipc failed\n");
 294                return PTR_ERR(mbox->ipc);
 295        }
 296
 297        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 298        mbox->base = devm_ioremap_resource(dev, res);
 299        if (IS_ERR(mbox->base)) {
 300                dev_err(dev, "ioremap buffer failed\n");
 301                return PTR_ERR(mbox->base);
 302        }
 303
 304        err = devm_request_irq(dev, mbox->irq, hi6220_mbox_interrupt, 0,
 305                        dev_name(dev), mbox);
 306        if (err) {
 307                dev_err(dev, "Failed to register a mailbox IRQ handler: %d\n",
 308                        err);
 309                return -ENODEV;
 310        }
 311
 312        mbox->controller.dev = dev;
 313        mbox->controller.chans = &mbox->chan[0];
 314        mbox->controller.num_chans = mbox->chan_num;
 315        mbox->controller.ops = &hi6220_mbox_ops;
 316        mbox->controller.of_xlate = hi6220_mbox_xlate;
 317
 318        for (i = 0; i < mbox->chan_num; i++) {
 319                mbox->chan[i].con_priv = &mbox->mchan[i];
 320                mbox->irq_map_chan[i] = NULL;
 321
 322                mbox->mchan[i].parent = mbox;
 323                mbox->mchan[i].slot   = i;
 324        }
 325
 326        /* mask and clear all interrupt vectors */
 327        writel(0x0,  ACK_INT_MSK_REG(mbox->ipc));
 328        writel(~0x0, ACK_INT_CLR_REG(mbox->ipc));
 329
 330        /* use interrupt for tx's ack */
 331        if (of_find_property(node, "hi6220,mbox-tx-noirq", NULL))
 332                mbox->tx_irq_mode = false;
 333        else
 334                mbox->tx_irq_mode = true;
 335
 336        if (mbox->tx_irq_mode)
 337                mbox->controller.txdone_irq = true;
 338        else {
 339                mbox->controller.txdone_poll = true;
 340                mbox->controller.txpoll_period = 5;
 341        }
 342
 343        err = devm_mbox_controller_register(dev, &mbox->controller);
 344        if (err) {
 345                dev_err(dev, "Failed to register mailbox %d\n", err);
 346                return err;
 347        }
 348
 349        platform_set_drvdata(pdev, mbox);
 350        dev_info(dev, "Mailbox enabled\n");
 351        return 0;
 352}
 353
 354static struct platform_driver hi6220_mbox_driver = {
 355        .driver = {
 356                .name = "hi6220-mbox",
 357                .of_match_table = hi6220_mbox_of_match,
 358        },
 359        .probe  = hi6220_mbox_probe,
 360};
 361
 362static int __init hi6220_mbox_init(void)
 363{
 364        return platform_driver_register(&hi6220_mbox_driver);
 365}
 366core_initcall(hi6220_mbox_init);
 367
 368static void __exit hi6220_mbox_exit(void)
 369{
 370        platform_driver_unregister(&hi6220_mbox_driver);
 371}
 372module_exit(hi6220_mbox_exit);
 373
 374MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
 375MODULE_DESCRIPTION("Hi6220 mailbox driver");
 376MODULE_LICENSE("GPL v2");
 377