linux/drivers/mailbox/mailbox-altera.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright Altera Corporation (C) 2013-2014. All rights reserved
   4 */
   5
   6#include <linux/device.h>
   7#include <linux/interrupt.h>
   8#include <linux/io.h>
   9#include <linux/kernel.h>
  10#include <linux/mailbox_controller.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/platform_device.h>
  14
  15#define DRIVER_NAME     "altera-mailbox"
  16
  17#define MAILBOX_CMD_REG                 0x00
  18#define MAILBOX_PTR_REG                 0x04
  19#define MAILBOX_STS_REG                 0x08
  20#define MAILBOX_INTMASK_REG             0x0C
  21
  22#define INT_PENDING_MSK                 0x1
  23#define INT_SPACE_MSK                   0x2
  24
  25#define STS_PENDING_MSK                 0x1
  26#define STS_FULL_MSK                    0x2
  27#define STS_FULL_OFT                    0x1
  28
  29#define MBOX_PENDING(status)    (((status) & STS_PENDING_MSK))
  30#define MBOX_FULL(status)       (((status) & STS_FULL_MSK) >> STS_FULL_OFT)
  31
  32enum altera_mbox_msg {
  33        MBOX_CMD = 0,
  34        MBOX_PTR,
  35};
  36
  37#define MBOX_POLLING_MS         5       /* polling interval 5ms */
  38
  39struct altera_mbox {
  40        bool is_sender;         /* 1-sender, 0-receiver */
  41        bool intr_mode;
  42        int irq;
  43        void __iomem *mbox_base;
  44        struct device *dev;
  45        struct mbox_controller controller;
  46
  47        /* If the controller supports only RX polling mode */
  48        struct timer_list rxpoll_timer;
  49        struct mbox_chan *chan;
  50};
  51
  52static struct altera_mbox *mbox_chan_to_altera_mbox(struct mbox_chan *chan)
  53{
  54        if (!chan || !chan->con_priv)
  55                return NULL;
  56
  57        return (struct altera_mbox *)chan->con_priv;
  58}
  59
  60static inline int altera_mbox_full(struct altera_mbox *mbox)
  61{
  62        u32 status;
  63
  64        status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG);
  65        return MBOX_FULL(status);
  66}
  67
  68static inline int altera_mbox_pending(struct altera_mbox *mbox)
  69{
  70        u32 status;
  71
  72        status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG);
  73        return MBOX_PENDING(status);
  74}
  75
  76static void altera_mbox_rx_intmask(struct altera_mbox *mbox, bool enable)
  77{
  78        u32 mask;
  79
  80        mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG);
  81        if (enable)
  82                mask |= INT_PENDING_MSK;
  83        else
  84                mask &= ~INT_PENDING_MSK;
  85        writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG);
  86}
  87
  88static void altera_mbox_tx_intmask(struct altera_mbox *mbox, bool enable)
  89{
  90        u32 mask;
  91
  92        mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG);
  93        if (enable)
  94                mask |= INT_SPACE_MSK;
  95        else
  96                mask &= ~INT_SPACE_MSK;
  97        writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG);
  98}
  99
 100static bool altera_mbox_is_sender(struct altera_mbox *mbox)
 101{
 102        u32 reg;
 103        /* Write a magic number to PTR register and read back this register.
 104         * This register is read-write if it is a sender.
 105         */
 106        #define MBOX_MAGIC      0xA5A5AA55
 107        writel_relaxed(MBOX_MAGIC, mbox->mbox_base + MAILBOX_PTR_REG);
 108        reg = readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG);
 109        if (reg == MBOX_MAGIC) {
 110                /* Clear to 0 */
 111                writel_relaxed(0, mbox->mbox_base + MAILBOX_PTR_REG);
 112                return true;
 113        }
 114        return false;
 115}
 116
 117static void altera_mbox_rx_data(struct mbox_chan *chan)
 118{
 119        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 120        u32 data[2];
 121
 122        if (altera_mbox_pending(mbox)) {
 123                data[MBOX_PTR] =
 124                        readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG);
 125                data[MBOX_CMD] =
 126                        readl_relaxed(mbox->mbox_base + MAILBOX_CMD_REG);
 127                mbox_chan_received_data(chan, (void *)data);
 128        }
 129}
 130
 131static void altera_mbox_poll_rx(struct timer_list *t)
 132{
 133        struct altera_mbox *mbox = from_timer(mbox, t, rxpoll_timer);
 134
 135        altera_mbox_rx_data(mbox->chan);
 136
 137        mod_timer(&mbox->rxpoll_timer,
 138                  jiffies + msecs_to_jiffies(MBOX_POLLING_MS));
 139}
 140
 141static irqreturn_t altera_mbox_tx_interrupt(int irq, void *p)
 142{
 143        struct mbox_chan *chan = (struct mbox_chan *)p;
 144        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 145
 146        altera_mbox_tx_intmask(mbox, false);
 147        mbox_chan_txdone(chan, 0);
 148
 149        return IRQ_HANDLED;
 150}
 151
 152static irqreturn_t altera_mbox_rx_interrupt(int irq, void *p)
 153{
 154        struct mbox_chan *chan = (struct mbox_chan *)p;
 155
 156        altera_mbox_rx_data(chan);
 157        return IRQ_HANDLED;
 158}
 159
 160static int altera_mbox_startup_sender(struct mbox_chan *chan)
 161{
 162        int ret;
 163        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 164
 165        if (mbox->intr_mode) {
 166                ret = request_irq(mbox->irq, altera_mbox_tx_interrupt, 0,
 167                                  DRIVER_NAME, chan);
 168                if (unlikely(ret)) {
 169                        dev_err(mbox->dev,
 170                                "failed to register mailbox interrupt:%d\n",
 171                                ret);
 172                        return ret;
 173                }
 174        }
 175
 176        return 0;
 177}
 178
 179static int altera_mbox_startup_receiver(struct mbox_chan *chan)
 180{
 181        int ret;
 182        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 183
 184        if (mbox->intr_mode) {
 185                ret = request_irq(mbox->irq, altera_mbox_rx_interrupt, 0,
 186                                  DRIVER_NAME, chan);
 187                if (unlikely(ret)) {
 188                        mbox->intr_mode = false;
 189                        goto polling; /* use polling if failed */
 190                }
 191
 192                altera_mbox_rx_intmask(mbox, true);
 193                return 0;
 194        }
 195
 196polling:
 197        /* Setup polling timer */
 198        mbox->chan = chan;
 199        timer_setup(&mbox->rxpoll_timer, altera_mbox_poll_rx, 0);
 200        mod_timer(&mbox->rxpoll_timer,
 201                  jiffies + msecs_to_jiffies(MBOX_POLLING_MS));
 202
 203        return 0;
 204}
 205
 206static int altera_mbox_send_data(struct mbox_chan *chan, void *data)
 207{
 208        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 209        u32 *udata = (u32 *)data;
 210
 211        if (!mbox || !data)
 212                return -EINVAL;
 213        if (!mbox->is_sender) {
 214                dev_warn(mbox->dev,
 215                         "failed to send. This is receiver mailbox.\n");
 216                return -EINVAL;
 217        }
 218
 219        if (altera_mbox_full(mbox))
 220                return -EBUSY;
 221
 222        /* Enable interrupt before send */
 223        if (mbox->intr_mode)
 224                altera_mbox_tx_intmask(mbox, true);
 225
 226        /* Pointer register must write before command register */
 227        writel_relaxed(udata[MBOX_PTR], mbox->mbox_base + MAILBOX_PTR_REG);
 228        writel_relaxed(udata[MBOX_CMD], mbox->mbox_base + MAILBOX_CMD_REG);
 229
 230        return 0;
 231}
 232
 233static bool altera_mbox_last_tx_done(struct mbox_chan *chan)
 234{
 235        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 236
 237        /* Return false if mailbox is full */
 238        return altera_mbox_full(mbox) ? false : true;
 239}
 240
 241static bool altera_mbox_peek_data(struct mbox_chan *chan)
 242{
 243        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 244
 245        return altera_mbox_pending(mbox) ? true : false;
 246}
 247
 248static int altera_mbox_startup(struct mbox_chan *chan)
 249{
 250        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 251        int ret = 0;
 252
 253        if (!mbox)
 254                return -EINVAL;
 255
 256        if (mbox->is_sender)
 257                ret = altera_mbox_startup_sender(chan);
 258        else
 259                ret = altera_mbox_startup_receiver(chan);
 260
 261        return ret;
 262}
 263
 264static void altera_mbox_shutdown(struct mbox_chan *chan)
 265{
 266        struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
 267
 268        if (mbox->intr_mode) {
 269                /* Unmask all interrupt masks */
 270                writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG);
 271                free_irq(mbox->irq, chan);
 272        } else if (!mbox->is_sender) {
 273                del_timer_sync(&mbox->rxpoll_timer);
 274        }
 275}
 276
 277static const struct mbox_chan_ops altera_mbox_ops = {
 278        .send_data = altera_mbox_send_data,
 279        .startup = altera_mbox_startup,
 280        .shutdown = altera_mbox_shutdown,
 281        .last_tx_done = altera_mbox_last_tx_done,
 282        .peek_data = altera_mbox_peek_data,
 283};
 284
 285static int altera_mbox_probe(struct platform_device *pdev)
 286{
 287        struct altera_mbox *mbox;
 288        struct resource *regs;
 289        struct mbox_chan *chans;
 290        int ret;
 291
 292        mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox),
 293                            GFP_KERNEL);
 294        if (!mbox)
 295                return -ENOMEM;
 296
 297        /* Allocated one channel */
 298        chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL);
 299        if (!chans)
 300                return -ENOMEM;
 301
 302        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 303
 304        mbox->mbox_base = devm_ioremap_resource(&pdev->dev, regs);
 305        if (IS_ERR(mbox->mbox_base))
 306                return PTR_ERR(mbox->mbox_base);
 307
 308        /* Check is it a sender or receiver? */
 309        mbox->is_sender = altera_mbox_is_sender(mbox);
 310
 311        mbox->irq = platform_get_irq(pdev, 0);
 312        if (mbox->irq >= 0)
 313                mbox->intr_mode = true;
 314
 315        mbox->dev = &pdev->dev;
 316
 317        /* Hardware supports only one channel. */
 318        chans[0].con_priv = mbox;
 319        mbox->controller.dev = mbox->dev;
 320        mbox->controller.num_chans = 1;
 321        mbox->controller.chans = chans;
 322        mbox->controller.ops = &altera_mbox_ops;
 323
 324        if (mbox->is_sender) {
 325                if (mbox->intr_mode) {
 326                        mbox->controller.txdone_irq = true;
 327                } else {
 328                        mbox->controller.txdone_poll = true;
 329                        mbox->controller.txpoll_period = MBOX_POLLING_MS;
 330                }
 331        }
 332
 333        ret = devm_mbox_controller_register(&pdev->dev, &mbox->controller);
 334        if (ret) {
 335                dev_err(&pdev->dev, "Register mailbox failed\n");
 336                goto err;
 337        }
 338
 339        platform_set_drvdata(pdev, mbox);
 340err:
 341        return ret;
 342}
 343
 344static const struct of_device_id altera_mbox_match[] = {
 345        { .compatible = "altr,mailbox-1.0" },
 346        { /* Sentinel */ }
 347};
 348
 349MODULE_DEVICE_TABLE(of, altera_mbox_match);
 350
 351static struct platform_driver altera_mbox_driver = {
 352        .probe  = altera_mbox_probe,
 353        .driver = {
 354                .name   = DRIVER_NAME,
 355                .of_match_table = altera_mbox_match,
 356        },
 357};
 358
 359module_platform_driver(altera_mbox_driver);
 360
 361MODULE_LICENSE("GPL v2");
 362MODULE_DESCRIPTION("Altera mailbox specific functions");
 363MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
 364MODULE_ALIAS("platform:altera-mailbox");
 365