linux/drivers/mailbox/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/interrupt.h>
  25#include <linux/spinlock.h>
  26#include <linux/mutex.h>
  27#include <linux/delay.h>
  28#include <linux/slab.h>
  29#include <linux/kfifo.h>
  30#include <linux/err.h>
  31#include <linux/notifier.h>
  32#include <linux/module.h>
  33
  34#include "omap-mbox.h"
  35
  36static struct omap_mbox **mboxes;
  37
  38static int mbox_configured;
  39static DEFINE_MUTEX(mbox_configured_lock);
  40
  41static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
  42module_param(mbox_kfifo_size, uint, S_IRUGO);
  43MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
  44
  45/* Mailbox FIFO handle functions */
  46static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
  47{
  48        return mbox->ops->fifo_read(mbox);
  49}
  50static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
  51{
  52        mbox->ops->fifo_write(mbox, msg);
  53}
  54static inline int mbox_fifo_empty(struct omap_mbox *mbox)
  55{
  56        return mbox->ops->fifo_empty(mbox);
  57}
  58static inline int mbox_fifo_full(struct omap_mbox *mbox)
  59{
  60        return mbox->ops->fifo_full(mbox);
  61}
  62
  63/* Mailbox IRQ handle functions */
  64static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
  65{
  66        if (mbox->ops->ack_irq)
  67                mbox->ops->ack_irq(mbox, irq);
  68}
  69static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
  70{
  71        return mbox->ops->is_irq(mbox, irq);
  72}
  73
  74/*
  75 * message sender
  76 */
  77static int __mbox_poll_for_space(struct omap_mbox *mbox)
  78{
  79        int ret = 0, i = 1000;
  80
  81        while (mbox_fifo_full(mbox)) {
  82                if (mbox->ops->type == OMAP_MBOX_TYPE2)
  83                        return -1;
  84                if (--i == 0)
  85                        return -1;
  86                udelay(1);
  87        }
  88        return ret;
  89}
  90
  91int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
  92{
  93        struct omap_mbox_queue *mq = mbox->txq;
  94        int ret = 0, len;
  95
  96        spin_lock_bh(&mq->lock);
  97
  98        if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
  99                ret = -ENOMEM;
 100                goto out;
 101        }
 102
 103        if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
 104                mbox_fifo_write(mbox, msg);
 105                goto out;
 106        }
 107
 108        len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
 109        WARN_ON(len != sizeof(msg));
 110
 111        tasklet_schedule(&mbox->txq->tasklet);
 112
 113out:
 114        spin_unlock_bh(&mq->lock);
 115        return ret;
 116}
 117EXPORT_SYMBOL(omap_mbox_msg_send);
 118
 119void omap_mbox_save_ctx(struct omap_mbox *mbox)
 120{
 121        if (!mbox->ops->save_ctx) {
 122                dev_err(mbox->dev, "%s:\tno save\n", __func__);
 123                return;
 124        }
 125
 126        mbox->ops->save_ctx(mbox);
 127}
 128EXPORT_SYMBOL(omap_mbox_save_ctx);
 129
 130void omap_mbox_restore_ctx(struct omap_mbox *mbox)
 131{
 132        if (!mbox->ops->restore_ctx) {
 133                dev_err(mbox->dev, "%s:\tno restore\n", __func__);
 134                return;
 135        }
 136
 137        mbox->ops->restore_ctx(mbox);
 138}
 139EXPORT_SYMBOL(omap_mbox_restore_ctx);
 140
 141void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
 142{
 143        mbox->ops->enable_irq(mbox, irq);
 144}
 145EXPORT_SYMBOL(omap_mbox_enable_irq);
 146
 147void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
 148{
 149        mbox->ops->disable_irq(mbox, irq);
 150}
 151EXPORT_SYMBOL(omap_mbox_disable_irq);
 152
 153static void mbox_tx_tasklet(unsigned long tx_data)
 154{
 155        struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
 156        struct omap_mbox_queue *mq = mbox->txq;
 157        mbox_msg_t msg;
 158        int ret;
 159
 160        while (kfifo_len(&mq->fifo)) {
 161                if (__mbox_poll_for_space(mbox)) {
 162                        omap_mbox_enable_irq(mbox, IRQ_TX);
 163                        break;
 164                }
 165
 166                ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
 167                                                                sizeof(msg));
 168                WARN_ON(ret != sizeof(msg));
 169
 170                mbox_fifo_write(mbox, msg);
 171        }
 172}
 173
 174/*
 175 * Message receiver(workqueue)
 176 */
 177static void mbox_rx_work(struct work_struct *work)
 178{
 179        struct omap_mbox_queue *mq =
 180                        container_of(work, struct omap_mbox_queue, work);
 181        mbox_msg_t msg;
 182        int len;
 183
 184        while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
 185                len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
 186                WARN_ON(len != sizeof(msg));
 187
 188                blocking_notifier_call_chain(&mq->mbox->notifier, len,
 189                                                                (void *)msg);
 190                spin_lock_irq(&mq->lock);
 191                if (mq->full) {
 192                        mq->full = false;
 193                        omap_mbox_enable_irq(mq->mbox, IRQ_RX);
 194                }
 195                spin_unlock_irq(&mq->lock);
 196        }
 197}
 198
 199/*
 200 * Mailbox interrupt handler
 201 */
 202static void __mbox_tx_interrupt(struct omap_mbox *mbox)
 203{
 204        omap_mbox_disable_irq(mbox, IRQ_TX);
 205        ack_mbox_irq(mbox, IRQ_TX);
 206        tasklet_schedule(&mbox->txq->tasklet);
 207}
 208
 209static void __mbox_rx_interrupt(struct omap_mbox *mbox)
 210{
 211        struct omap_mbox_queue *mq = mbox->rxq;
 212        mbox_msg_t msg;
 213        int len;
 214
 215        while (!mbox_fifo_empty(mbox)) {
 216                if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
 217                        omap_mbox_disable_irq(mbox, IRQ_RX);
 218                        mq->full = true;
 219                        goto nomem;
 220                }
 221
 222                msg = mbox_fifo_read(mbox);
 223
 224                len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
 225                WARN_ON(len != sizeof(msg));
 226
 227                if (mbox->ops->type == OMAP_MBOX_TYPE1)
 228                        break;
 229        }
 230
 231        /* no more messages in the fifo. clear IRQ source. */
 232        ack_mbox_irq(mbox, IRQ_RX);
 233nomem:
 234        schedule_work(&mbox->rxq->work);
 235}
 236
 237static irqreturn_t mbox_interrupt(int irq, void *p)
 238{
 239        struct omap_mbox *mbox = p;
 240
 241        if (is_mbox_irq(mbox, IRQ_TX))
 242                __mbox_tx_interrupt(mbox);
 243
 244        if (is_mbox_irq(mbox, IRQ_RX))
 245                __mbox_rx_interrupt(mbox);
 246
 247        return IRQ_HANDLED;
 248}
 249
 250static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
 251                                        void (*work) (struct work_struct *),
 252                                        void (*tasklet)(unsigned long))
 253{
 254        struct omap_mbox_queue *mq;
 255
 256        mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
 257        if (!mq)
 258                return NULL;
 259
 260        spin_lock_init(&mq->lock);
 261
 262        if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
 263                goto error;
 264
 265        if (work)
 266                INIT_WORK(&mq->work, work);
 267
 268        if (tasklet)
 269                tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox);
 270        return mq;
 271error:
 272        kfree(mq);
 273        return NULL;
 274}
 275
 276static void mbox_queue_free(struct omap_mbox_queue *q)
 277{
 278        kfifo_free(&q->fifo);
 279        kfree(q);
 280}
 281
 282static int omap_mbox_startup(struct omap_mbox *mbox)
 283{
 284        int ret = 0;
 285        struct omap_mbox_queue *mq;
 286
 287        mutex_lock(&mbox_configured_lock);
 288        if (!mbox_configured++) {
 289                if (likely(mbox->ops->startup)) {
 290                        ret = mbox->ops->startup(mbox);
 291                        if (unlikely(ret))
 292                                goto fail_startup;
 293                } else
 294                        goto fail_startup;
 295        }
 296
 297        if (!mbox->use_count++) {
 298                mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
 299                if (!mq) {
 300                        ret = -ENOMEM;
 301                        goto fail_alloc_txq;
 302                }
 303                mbox->txq = mq;
 304
 305                mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
 306                if (!mq) {
 307                        ret = -ENOMEM;
 308                        goto fail_alloc_rxq;
 309                }
 310                mbox->rxq = mq;
 311                mq->mbox = mbox;
 312                ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
 313                                                        mbox->name, mbox);
 314                if (unlikely(ret)) {
 315                        pr_err("failed to register mailbox interrupt:%d\n",
 316                                                                        ret);
 317                        goto fail_request_irq;
 318                }
 319
 320                omap_mbox_enable_irq(mbox, IRQ_RX);
 321        }
 322        mutex_unlock(&mbox_configured_lock);
 323        return 0;
 324
 325fail_request_irq:
 326        mbox_queue_free(mbox->rxq);
 327fail_alloc_rxq:
 328        mbox_queue_free(mbox->txq);
 329fail_alloc_txq:
 330        if (mbox->ops->shutdown)
 331                mbox->ops->shutdown(mbox);
 332        mbox->use_count--;
 333fail_startup:
 334        mbox_configured--;
 335        mutex_unlock(&mbox_configured_lock);
 336        return ret;
 337}
 338
 339static void omap_mbox_fini(struct omap_mbox *mbox)
 340{
 341        mutex_lock(&mbox_configured_lock);
 342
 343        if (!--mbox->use_count) {
 344                omap_mbox_disable_irq(mbox, IRQ_RX);
 345                free_irq(mbox->irq, mbox);
 346                tasklet_kill(&mbox->txq->tasklet);
 347                flush_work(&mbox->rxq->work);
 348                mbox_queue_free(mbox->txq);
 349                mbox_queue_free(mbox->rxq);
 350        }
 351
 352        if (likely(mbox->ops->shutdown)) {
 353                if (!--mbox_configured)
 354                        mbox->ops->shutdown(mbox);
 355        }
 356
 357        mutex_unlock(&mbox_configured_lock);
 358}
 359
 360struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
 361{
 362        struct omap_mbox *_mbox, *mbox = NULL;
 363        int i, ret;
 364
 365        if (!mboxes)
 366                return ERR_PTR(-EINVAL);
 367
 368        for (i = 0; (_mbox = mboxes[i]); i++) {
 369                if (!strcmp(_mbox->name, name)) {
 370                        mbox = _mbox;
 371                        break;
 372                }
 373        }
 374
 375        if (!mbox)
 376                return ERR_PTR(-ENOENT);
 377
 378        if (nb)
 379                blocking_notifier_chain_register(&mbox->notifier, nb);
 380
 381        ret = omap_mbox_startup(mbox);
 382        if (ret) {
 383                blocking_notifier_chain_unregister(&mbox->notifier, nb);
 384                return ERR_PTR(-ENODEV);
 385        }
 386
 387        return mbox;
 388}
 389EXPORT_SYMBOL(omap_mbox_get);
 390
 391void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb)
 392{
 393        blocking_notifier_chain_unregister(&mbox->notifier, nb);
 394        omap_mbox_fini(mbox);
 395}
 396EXPORT_SYMBOL(omap_mbox_put);
 397
 398static struct class omap_mbox_class = { .name = "mbox", };
 399
 400int omap_mbox_register(struct device *parent, struct omap_mbox **list)
 401{
 402        int ret;
 403        int i;
 404
 405        mboxes = list;
 406        if (!mboxes)
 407                return -EINVAL;
 408
 409        for (i = 0; mboxes[i]; i++) {
 410                struct omap_mbox *mbox = mboxes[i];
 411                mbox->dev = device_create(&omap_mbox_class,
 412                                parent, 0, mbox, "%s", mbox->name);
 413                if (IS_ERR(mbox->dev)) {
 414                        ret = PTR_ERR(mbox->dev);
 415                        goto err_out;
 416                }
 417
 418                BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
 419        }
 420        return 0;
 421
 422err_out:
 423        while (i--)
 424                device_unregister(mboxes[i]->dev);
 425        return ret;
 426}
 427EXPORT_SYMBOL(omap_mbox_register);
 428
 429int omap_mbox_unregister(void)
 430{
 431        int i;
 432
 433        if (!mboxes)
 434                return -EINVAL;
 435
 436        for (i = 0; mboxes[i]; i++)
 437                device_unregister(mboxes[i]->dev);
 438        mboxes = NULL;
 439        return 0;
 440}
 441EXPORT_SYMBOL(omap_mbox_unregister);
 442
 443static int __init omap_mbox_init(void)
 444{
 445        int err;
 446
 447        err = class_register(&omap_mbox_class);
 448        if (err)
 449                return err;
 450
 451        /* kfifo size sanity check: alignment and minimal size */
 452        mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
 453        mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
 454                                                        sizeof(mbox_msg_t));
 455
 456        return 0;
 457}
 458subsys_initcall(omap_mbox_init);
 459
 460static void __exit omap_mbox_exit(void)
 461{
 462        class_unregister(&omap_mbox_class);
 463}
 464module_exit(omap_mbox_exit);
 465
 466MODULE_LICENSE("GPL v2");
 467MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
 468MODULE_AUTHOR("Toshihiro Kobayashi");
 469MODULE_AUTHOR("Hiroshi DOYU");
 470
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.