linux/arch/arm/plat-omap/mailbox.c
<<
>>
Prefs
   1/*
   2 * OMAP mailbox driver
   3 *
   4 * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved.
   5 *
   6 * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License
  10 * version 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20 * 02110-1301 USA
  21 *
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/interrupt.h>
  26#include <linux/device.h>
  27#include <linux/delay.h>
  28
  29#include <mach/mailbox.h>
  30
  31static int enable_seq_bit;
  32module_param(enable_seq_bit, bool, 0);
  33MODULE_PARM_DESC(enable_seq_bit, "Enable sequence bit checking.");
  34
  35static struct omap_mbox *mboxes;
  36static DEFINE_RWLOCK(mboxes_lock);
  37
  38/*
  39 * Mailbox sequence bit API
  40 */
  41
  42/* seq_rcv should be initialized with any value other than
  43 * 0 and 1 << 31, to allow either value for the first
  44 * message.  */
  45static inline void mbox_seq_init(struct omap_mbox *mbox)
  46{
  47        if (!enable_seq_bit)
  48                return;
  49
  50        /* any value other than 0 and 1 << 31 */
  51        mbox->seq_rcv = 0xffffffff;
  52}
  53
  54static inline void mbox_seq_toggle(struct omap_mbox *mbox, mbox_msg_t * msg)
  55{
  56        if (!enable_seq_bit)
  57                return;
  58
  59        /* add seq_snd to msg */
  60        *msg = (*msg & 0x7fffffff) | mbox->seq_snd;
  61        /* flip seq_snd */
  62        mbox->seq_snd ^= 1 << 31;
  63}
  64
  65static inline int mbox_seq_test(struct omap_mbox *mbox, mbox_msg_t msg)
  66{
  67        mbox_msg_t seq;
  68
  69        if (!enable_seq_bit)
  70                return 0;
  71
  72        seq = msg & (1 << 31);
  73        if (seq == mbox->seq_rcv)
  74                return -1;
  75        mbox->seq_rcv = seq;
  76        return 0;
  77}
  78
  79/* Mailbox FIFO handle functions */
  80static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
  81{
  82        return mbox->ops->fifo_read(mbox);
  83}
  84static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
  85{
  86        mbox->ops->fifo_write(mbox, msg);
  87}
  88static inline int mbox_fifo_empty(struct omap_mbox *mbox)
  89{
  90        return mbox->ops->fifo_empty(mbox);
  91}
  92static inline int mbox_fifo_full(struct omap_mbox *mbox)
  93{
  94        return mbox->ops->fifo_full(mbox);
  95}
  96
  97/* Mailbox IRQ handle functions */
  98static inline void enable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
  99{
 100        mbox->ops->enable_irq(mbox, irq);
 101}
 102static inline void disable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
 103{
 104        mbox->ops->disable_irq(mbox, irq);
 105}
 106static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
 107{
 108        if (mbox->ops->ack_irq)
 109                mbox->ops->ack_irq(mbox, irq);
 110}
 111static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
 112{
 113        return mbox->ops->is_irq(mbox, irq);
 114}
 115
 116/* Mailbox Sequence Bit function */
 117void omap_mbox_init_seq(struct omap_mbox *mbox)
 118{
 119        mbox_seq_init(mbox);
 120}
 121EXPORT_SYMBOL(omap_mbox_init_seq);
 122
 123/*
 124 * message sender
 125 */
 126static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void *arg)
 127{
 128        int ret = 0, i = 1000;
 129
 130        while (mbox_fifo_full(mbox)) {
 131                if (mbox->ops->type == OMAP_MBOX_TYPE2)
 132                        return -1;
 133                if (--i == 0)
 134                        return -1;
 135                udelay(1);
 136        }
 137
 138        if (arg && mbox->txq->callback) {
 139                ret = mbox->txq->callback(arg);
 140                if (ret)
 141                        goto out;
 142        }
 143
 144        mbox_seq_toggle(mbox, &msg);
 145        mbox_fifo_write(mbox, msg);
 146 out:
 147        return ret;
 148}
 149
 150struct omap_msg_tx_data {
 151        mbox_msg_t      msg;
 152        void            *arg;
 153};
 154
 155static void omap_msg_tx_end_io(struct request *rq, int error)
 156{
 157        kfree(rq->special);
 158        __blk_put_request(rq->q, rq);
 159}
 160
 161int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg)
 162{
 163        struct omap_msg_tx_data *tx_data;
 164        struct request *rq;
 165        struct request_queue *q = mbox->txq->queue;
 166
 167        tx_data = kmalloc(sizeof(*tx_data), GFP_ATOMIC);
 168        if (unlikely(!tx_data))
 169                return -ENOMEM;
 170
 171        rq = blk_get_request(q, WRITE, GFP_ATOMIC);
 172        if (unlikely(!rq)) {
 173                kfree(tx_data);
 174                return -ENOMEM;
 175        }
 176
 177        tx_data->msg = msg;
 178        tx_data->arg = arg;
 179        rq->end_io = omap_msg_tx_end_io;
 180        blk_insert_request(q, rq, 0, tx_data);
 181
 182        schedule_work(&mbox->txq->work);
 183        return 0;
 184}
 185EXPORT_SYMBOL(omap_mbox_msg_send);
 186
 187static void mbox_tx_work(struct work_struct *work)
 188{
 189        int ret;
 190        struct request *rq;
 191        struct omap_mbox_queue *mq = container_of(work,
 192                                struct omap_mbox_queue, work);
 193        struct omap_mbox *mbox = mq->queue->queuedata;
 194        struct request_queue *q = mbox->txq->queue;
 195
 196        while (1) {
 197                struct omap_msg_tx_data *tx_data;
 198
 199                spin_lock(q->queue_lock);
 200                rq = blk_fetch_request(q);
 201                spin_unlock(q->queue_lock);
 202
 203                if (!rq)
 204                        break;
 205
 206                tx_data = rq->special;
 207
 208                ret = __mbox_msg_send(mbox, tx_data->msg, tx_data->arg);
 209                if (ret) {
 210                        enable_mbox_irq(mbox, IRQ_TX);
 211                        spin_lock(q->queue_lock);
 212                        blk_requeue_request(q, rq);
 213                        spin_unlock(q->queue_lock);
 214                        return;
 215                }
 216
 217                spin_lock(q->queue_lock);
 218                __blk_end_request_all(rq, 0);
 219                spin_unlock(q->queue_lock);
 220        }
 221}
 222
 223/*
 224 * Message receiver(workqueue)
 225 */
 226static void mbox_rx_work(struct work_struct *work)
 227{
 228        struct omap_mbox_queue *mq =
 229                        container_of(work, struct omap_mbox_queue, work);
 230        struct omap_mbox *mbox = mq->queue->queuedata;
 231        struct request_queue *q = mbox->rxq->queue;
 232        struct request *rq;
 233        mbox_msg_t msg;
 234        unsigned long flags;
 235
 236        if (mbox->rxq->callback == NULL) {
 237                sysfs_notify(&mbox->dev->kobj, NULL, "mbox");
 238                return;
 239        }
 240
 241        while (1) {
 242                spin_lock_irqsave(q->queue_lock, flags);
 243                rq = blk_fetch_request(q);
 244                spin_unlock_irqrestore(q->queue_lock, flags);
 245                if (!rq)
 246                        break;
 247
 248                msg = (mbox_msg_t)rq->special;
 249                blk_end_request_all(rq, 0);
 250                mbox->rxq->callback((void *)msg);
 251        }
 252}
 253
 254/*
 255 * Mailbox interrupt handler
 256 */
 257static void mbox_txq_fn(struct request_queue * q)
 258{
 259}
 260
 261static void mbox_rxq_fn(struct request_queue * q)
 262{
 263}
 264
 265static void __mbox_tx_interrupt(struct omap_mbox *mbox)
 266{
 267        disable_mbox_irq(mbox, IRQ_TX);
 268        ack_mbox_irq(mbox, IRQ_TX);
 269        schedule_work(&mbox->txq->work);
 270}
 271
 272static void __mbox_rx_interrupt(struct omap_mbox *mbox)
 273{
 274        struct request *rq;
 275        mbox_msg_t msg;
 276        struct request_queue *q = mbox->rxq->queue;
 277
 278        disable_mbox_irq(mbox, IRQ_RX);
 279
 280        while (!mbox_fifo_empty(mbox)) {
 281                rq = blk_get_request(q, WRITE, GFP_ATOMIC);
 282                if (unlikely(!rq))
 283                        goto nomem;
 284
 285                msg = mbox_fifo_read(mbox);
 286
 287                if (unlikely(mbox_seq_test(mbox, msg))) {
 288                        pr_info("mbox: Illegal seq bit!(%08x)\n", msg);
 289                        if (mbox->err_notify)
 290                                mbox->err_notify();
 291                }
 292
 293                blk_insert_request(q, rq, 0, (void *)msg);
 294                if (mbox->ops->type == OMAP_MBOX_TYPE1)
 295                        break;
 296        }
 297
 298        /* no more messages in the fifo. clear IRQ source. */
 299        ack_mbox_irq(mbox, IRQ_RX);
 300        enable_mbox_irq(mbox, IRQ_RX);
 301nomem:
 302        schedule_work(&mbox->rxq->work);
 303}
 304
 305static irqreturn_t mbox_interrupt(int irq, void *p)
 306{
 307        struct omap_mbox *mbox = p;
 308
 309        if (is_mbox_irq(mbox, IRQ_TX))
 310                __mbox_tx_interrupt(mbox);
 311
 312        if (is_mbox_irq(mbox, IRQ_RX))
 313                __mbox_rx_interrupt(mbox);
 314
 315        return IRQ_HANDLED;
 316}
 317
 318/*
 319 * sysfs files
 320 */
 321static ssize_t
 322omap_mbox_write(struct device *dev, struct device_attribute *attr,
 323                const char * buf, size_t count)
 324{
 325        int ret;
 326        mbox_msg_t *p = (mbox_msg_t *)buf;
 327        struct omap_mbox *mbox = dev_get_drvdata(dev);
 328
 329        for (; count >= sizeof(mbox_msg_t); count -= sizeof(mbox_msg_t)) {
 330                ret = omap_mbox_msg_send(mbox, be32_to_cpu(*p), NULL);
 331                if (ret)
 332                        return -EAGAIN;
 333                p++;
 334        }
 335
 336        return (size_t)((char *)p - buf);
 337}
 338
 339static ssize_t
 340omap_mbox_read(struct device *dev, struct device_attribute *attr, char *buf)
 341{
 342        unsigned long flags;
 343        struct request *rq;
 344        mbox_msg_t *p = (mbox_msg_t *) buf;
 345        struct omap_mbox *mbox = dev_get_drvdata(dev);
 346        struct request_queue *q = mbox->rxq->queue;
 347
 348        while (1) {
 349                spin_lock_irqsave(q->queue_lock, flags);
 350                rq = blk_fetch_request(q);
 351                spin_unlock_irqrestore(q->queue_lock, flags);
 352
 353                if (!rq)
 354                        break;
 355
 356                *p = (mbox_msg_t)rq->special;
 357
 358                blk_end_request_all(rq, 0);
 359
 360                if (unlikely(mbox_seq_test(mbox, *p))) {
 361                        pr_info("mbox: Illegal seq bit!(%08x) ignored\n", *p);
 362                        continue;
 363                }
 364                p++;
 365        }
 366
 367        pr_debug("%02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
 368
 369        return (size_t) ((char *)p - buf);
 370}
 371
 372static DEVICE_ATTR(mbox, S_IRUGO | S_IWUSR, omap_mbox_read, omap_mbox_write);
 373
 374static ssize_t mbox_show(struct class *class, char *buf)
 375{
 376        return sprintf(buf, "mbox");
 377}
 378
 379static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL);
 380
 381static struct class omap_mbox_class = {
 382        .name = "omap-mailbox",
 383};
 384
 385static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
 386                                        request_fn_proc * proc,
 387                                        void (*work) (struct work_struct *))
 388{
 389        struct request_queue *q;
 390        struct omap_mbox_queue *mq;
 391
 392        mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
 393        if (!mq)
 394                return NULL;
 395
 396        spin_lock_init(&mq->lock);
 397
 398        q = blk_init_queue(proc, &mq->lock);
 399        if (!q)
 400                goto error;
 401        q->queuedata = mbox;
 402        mq->queue = q;
 403
 404        INIT_WORK(&mq->work, work);
 405
 406        return mq;
 407error:
 408        kfree(mq);
 409        return NULL;
 410}
 411
 412static void mbox_queue_free(struct omap_mbox_queue *q)
 413{
 414        blk_cleanup_queue(q->queue);
 415        kfree(q);
 416}
 417
 418static int omap_mbox_init(struct omap_mbox *mbox)
 419{
 420        int ret;
 421        struct omap_mbox_queue *mq;
 422
 423        if (likely(mbox->ops->startup)) {
 424                ret = mbox->ops->startup(mbox);
 425                if (unlikely(ret))
 426                        return ret;
 427        }
 428
 429        ret = request_irq(mbox->irq, mbox_interrupt, IRQF_DISABLED,
 430                                mbox->name, mbox);
 431        if (unlikely(ret)) {
 432                printk(KERN_ERR
 433                        "failed to register mailbox interrupt:%d\n", ret);
 434                goto fail_request_irq;
 435        }
 436
 437        mq = mbox_queue_alloc(mbox, mbox_txq_fn, mbox_tx_work);
 438        if (!mq) {
 439                ret = -ENOMEM;
 440                goto fail_alloc_txq;
 441        }
 442        mbox->txq = mq;
 443
 444        mq = mbox_queue_alloc(mbox, mbox_rxq_fn, mbox_rx_work);
 445        if (!mq) {
 446                ret = -ENOMEM;
 447                goto fail_alloc_rxq;
 448        }
 449        mbox->rxq = mq;
 450
 451        return 0;
 452
 453 fail_alloc_rxq:
 454        mbox_queue_free(mbox->txq);
 455 fail_alloc_txq:
 456        free_irq(mbox->irq, mbox);
 457 fail_request_irq:
 458        if (unlikely(mbox->ops->shutdown))
 459                mbox->ops->shutdown(mbox);
 460
 461        return ret;
 462}
 463
 464static void omap_mbox_fini(struct omap_mbox *mbox)
 465{
 466        mbox_queue_free(mbox->txq);
 467        mbox_queue_free(mbox->rxq);
 468
 469        free_irq(mbox->irq, mbox);
 470
 471        if (unlikely(mbox->ops->shutdown))
 472                mbox->ops->shutdown(mbox);
 473}
 474
 475static struct omap_mbox **find_mboxes(const char *name)
 476{
 477        struct omap_mbox **p;
 478
 479        for (p = &mboxes; *p; p = &(*p)->next) {
 480                if (strcmp((*p)->name, name) == 0)
 481                        break;
 482        }
 483
 484        return p;
 485}
 486
 487struct omap_mbox *omap_mbox_get(const char *name)
 488{
 489        struct omap_mbox *mbox;
 490        int ret;
 491
 492        read_lock(&mboxes_lock);
 493        mbox = *(find_mboxes(name));
 494        if (mbox == NULL) {
 495                read_unlock(&mboxes_lock);
 496                return ERR_PTR(-ENOENT);
 497        }
 498
 499        read_unlock(&mboxes_lock);
 500
 501        ret = omap_mbox_init(mbox);
 502        if (ret)
 503                return ERR_PTR(-ENODEV);
 504
 505        return mbox;
 506}
 507EXPORT_SYMBOL(omap_mbox_get);
 508
 509void omap_mbox_put(struct omap_mbox *mbox)
 510{
 511        omap_mbox_fini(mbox);
 512}
 513EXPORT_SYMBOL(omap_mbox_put);
 514
 515int omap_mbox_register(struct device *parent, struct omap_mbox *mbox)
 516{
 517        int ret = 0;
 518        struct omap_mbox **tmp;
 519
 520        if (!mbox)
 521                return -EINVAL;
 522        if (mbox->next)
 523                return -EBUSY;
 524
 525        mbox->dev = device_create(&omap_mbox_class,
 526                                  parent, 0, mbox, "%s", mbox->name);
 527        if (IS_ERR(mbox->dev))
 528                return PTR_ERR(mbox->dev);
 529
 530        ret = device_create_file(mbox->dev, &dev_attr_mbox);
 531        if (ret)
 532                goto err_sysfs;
 533
 534        write_lock(&mboxes_lock);
 535        tmp = find_mboxes(mbox->name);
 536        if (*tmp) {
 537                ret = -EBUSY;
 538                write_unlock(&mboxes_lock);
 539                goto err_find;
 540        }
 541        *tmp = mbox;
 542        write_unlock(&mboxes_lock);
 543
 544        return 0;
 545
 546err_find:
 547        device_remove_file(mbox->dev, &dev_attr_mbox);
 548err_sysfs:
 549        device_unregister(mbox->dev);
 550        return ret;
 551}
 552EXPORT_SYMBOL(omap_mbox_register);
 553
 554int omap_mbox_unregister(struct omap_mbox *mbox)
 555{
 556        struct omap_mbox **tmp;
 557
 558        write_lock(&mboxes_lock);
 559        tmp = &mboxes;
 560        while (*tmp) {
 561                if (mbox == *tmp) {
 562                        *tmp = mbox->next;
 563                        mbox->next = NULL;
 564                        write_unlock(&mboxes_lock);
 565                        device_remove_file(mbox->dev, &dev_attr_mbox);
 566                        device_unregister(mbox->dev);
 567                        return 0;
 568                }
 569                tmp = &(*tmp)->next;
 570        }
 571        write_unlock(&mboxes_lock);
 572
 573        return -EINVAL;
 574}
 575EXPORT_SYMBOL(omap_mbox_unregister);
 576
 577static int __init omap_mbox_class_init(void)
 578{
 579        int ret = class_register(&omap_mbox_class);
 580        if (!ret)
 581                ret = class_create_file(&omap_mbox_class, &class_attr_mbox);
 582
 583        return ret;
 584}
 585
 586static void __exit omap_mbox_class_exit(void)
 587{
 588        class_remove_file(&omap_mbox_class, &class_attr_mbox);
 589        class_unregister(&omap_mbox_class);
 590}
 591
 592subsys_initcall(omap_mbox_class_init);
 593module_exit(omap_mbox_class_exit);
 594
 595MODULE_LICENSE("GPL v2");
 596MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
 597MODULE_AUTHOR("Toshihiro Kobayashi and Hiroshi DOYU");
 598