Merge branch 'x86-olpc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / arch / arm / plat-omap / mailbox.c
index d2fafb8..459b319 100644 (file)
 #include <linux/slab.h>
 #include <linux/kfifo.h>
 #include <linux/err.h>
+#include <linux/notifier.h>
 
 #include <plat/mailbox.h>
 
 static struct workqueue_struct *mboxd;
 static struct omap_mbox **mboxes;
-static bool rq_full;
 
 static int mbox_configured;
 static DEFINE_MUTEX(mbox_configured_lock);
@@ -93,20 +93,25 @@ int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
        struct omap_mbox_queue *mq = mbox->txq;
        int ret = 0, len;
 
-       spin_lock(&mq->lock);
+       spin_lock_bh(&mq->lock);
 
        if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
                ret = -ENOMEM;
                goto out;
        }
 
+       if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
+               mbox_fifo_write(mbox, msg);
+               goto out;
+       }
+
        len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
        WARN_ON(len != sizeof(msg));
 
        tasklet_schedule(&mbox->txq->tasklet);
 
 out:
-       spin_unlock(&mq->lock);
+       spin_unlock_bh(&mq->lock);
        return ret;
 }
 EXPORT_SYMBOL(omap_mbox_msg_send);
@@ -146,8 +151,14 @@ static void mbox_rx_work(struct work_struct *work)
                len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
                WARN_ON(len != sizeof(msg));
 
-               if (mq->callback)
-                       mq->callback((void *)msg);
+               blocking_notifier_call_chain(&mq->mbox->notifier, len,
+                                                               (void *)msg);
+               spin_lock_irq(&mq->lock);
+               if (mq->full) {
+                       mq->full = false;
+                       omap_mbox_enable_irq(mq->mbox, IRQ_RX);
+               }
+               spin_unlock_irq(&mq->lock);
        }
 }
 
@@ -170,7 +181,7 @@ static void __mbox_rx_interrupt(struct omap_mbox *mbox)
        while (!mbox_fifo_empty(mbox)) {
                if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
                        omap_mbox_disable_irq(mbox, IRQ_RX);
-                       rq_full = true;
+                       mq->full = true;
                        goto nomem;
                }
 
@@ -239,73 +250,77 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
        int ret = 0;
        struct omap_mbox_queue *mq;
 
-       if (mbox->ops->startup) {
-               mutex_lock(&mbox_configured_lock);
-               if (!mbox_configured)
+       mutex_lock(&mbox_configured_lock);
+       if (!mbox_configured++) {
+               if (likely(mbox->ops->startup)) {
                        ret = mbox->ops->startup(mbox);
-
-               if (ret) {
-                       mutex_unlock(&mbox_configured_lock);
-                       return ret;
-               }
-               mbox_configured++;
-               mutex_unlock(&mbox_configured_lock);
-       }
-
-       ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
-                               mbox->name, mbox);
-       if (ret) {
-               printk(KERN_ERR
-                       "failed to register mailbox interrupt:%d\n", ret);
-               goto fail_request_irq;
+                       if (unlikely(ret))
+                               goto fail_startup;
+               } else
+                       goto fail_startup;
        }
 
-       mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
-       if (!mq) {
-               ret = -ENOMEM;
-               goto fail_alloc_txq;
-       }
-       mbox->txq = mq;
+       if (!mbox->use_count++) {
+               ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
+                                                       mbox->name, mbox);
+               if (unlikely(ret)) {
+                       pr_err("failed to register mailbox interrupt:%d\n",
+                                                                       ret);
+                       goto fail_request_irq;
+               }
+               mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
+               if (!mq) {
+                       ret = -ENOMEM;
+                       goto fail_alloc_txq;
+               }
+               mbox->txq = mq;
 
-       mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
-       if (!mq) {
-               ret = -ENOMEM;
-               goto fail_alloc_rxq;
+               mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
+               if (!mq) {
+                       ret = -ENOMEM;
+                       goto fail_alloc_rxq;
+               }
+               mbox->rxq = mq;
+               mq->mbox = mbox;
        }
-       mbox->rxq = mq;
-
+       mutex_unlock(&mbox_configured_lock);
        return 0;
 
- fail_alloc_rxq:
+fail_alloc_rxq:
        mbox_queue_free(mbox->txq);
- fail_alloc_txq:
+fail_alloc_txq:
        free_irq(mbox->irq, mbox);
- fail_request_irq:
+fail_request_irq:
        if (mbox->ops->shutdown)
                mbox->ops->shutdown(mbox);
-
+       mbox->use_count--;
+fail_startup:
+       mbox_configured--;
+       mutex_unlock(&mbox_configured_lock);
        return ret;
 }
 
 static void omap_mbox_fini(struct omap_mbox *mbox)
 {
-       free_irq(mbox->irq, mbox);
-       tasklet_kill(&mbox->txq->tasklet);
-       flush_work(&mbox->rxq->work);
-       mbox_queue_free(mbox->txq);
-       mbox_queue_free(mbox->rxq);
+       mutex_lock(&mbox_configured_lock);
+
+       if (!--mbox->use_count) {
+               free_irq(mbox->irq, mbox);
+               tasklet_kill(&mbox->txq->tasklet);
+               flush_work(&mbox->rxq->work);
+               mbox_queue_free(mbox->txq);
+               mbox_queue_free(mbox->rxq);
+       }
 
-       if (mbox->ops->shutdown) {
-               mutex_lock(&mbox_configured_lock);
-               if (mbox_configured > 0)
-                       mbox_configured--;
-               if (!mbox_configured)
+       if (likely(mbox->ops->shutdown)) {
+               if (!--mbox_configured)
                        mbox->ops->shutdown(mbox);
-               mutex_unlock(&mbox_configured_lock);
        }
+
+       mutex_unlock(&mbox_configured_lock);
 }
 
-struct omap_mbox *omap_mbox_get(const char *name)
+struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
 {
        struct omap_mbox *mbox;
        int ret;
@@ -324,12 +339,16 @@ struct omap_mbox *omap_mbox_get(const char *name)
        if (ret)
                return ERR_PTR(-ENODEV);
 
+       if (nb)
+               blocking_notifier_chain_register(&mbox->notifier, nb);
+
        return mbox;
 }
 EXPORT_SYMBOL(omap_mbox_get);
 
-void omap_mbox_put(struct omap_mbox *mbox)
+void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb)
 {
+       blocking_notifier_chain_unregister(&mbox->notifier, nb);
        omap_mbox_fini(mbox);
 }
 EXPORT_SYMBOL(omap_mbox_put);
@@ -353,6 +372,8 @@ int omap_mbox_register(struct device *parent, struct omap_mbox **list)
                        ret = PTR_ERR(mbox->dev);
                        goto err_out;
                }
+
+               BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
        }
        return 0;
 
@@ -391,7 +412,8 @@ static int __init omap_mbox_init(void)
 
        /* kfifo size sanity check: alignment and minimal size */
        mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
-       mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(mbox_msg_t));
+       mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
+                                                       sizeof(mbox_msg_t));
 
        return 0;
 }