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