diff options
Diffstat (limited to 'arch/arm/plat-omap/mailbox.c')
-rw-r--r-- | arch/arm/plat-omap/mailbox.c | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index d2fafb892f7..459b319a9fa 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c @@ -28,12 +28,12 @@ #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; } |