diff options
Diffstat (limited to 'drivers/mailbox/omap-mailbox.c')
| -rw-r--r-- | drivers/mailbox/omap-mailbox.c | 803 |
1 files changed, 477 insertions, 326 deletions
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index d79a646b9042..17fe6545875d 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -1,199 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OMAP mailbox driver * * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved. + * Copyright (C) 2013-2021 Texas Instruments Incorporated - https://www.ti.com * * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * + * Suman Anna <s-anna@ti.com> */ #include <linux/interrupt.h> #include <linux/spinlock.h> #include <linux/mutex.h> -#include <linux/delay.h> #include <linux/slab.h> #include <linux/kfifo.h> #include <linux/err.h> -#include <linux/notifier.h> +#include <linux/io.h> #include <linux/module.h> - -#include "omap-mbox.h" - -static struct omap_mbox **mboxes; - -static int mbox_configured; -static DEFINE_MUTEX(mbox_configured_lock); - -static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE; -module_param(mbox_kfifo_size, uint, S_IRUGO); -MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)"); - -/* Mailbox FIFO handle functions */ -static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) -{ - return mbox->ops->fifo_read(mbox); -} -static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) -{ - mbox->ops->fifo_write(mbox, msg); -} -static inline int mbox_fifo_empty(struct omap_mbox *mbox) -{ - return mbox->ops->fifo_empty(mbox); -} -static inline int mbox_fifo_full(struct omap_mbox *mbox) +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/mailbox_controller.h> +#include <linux/mailbox_client.h> + +#include "mailbox.h" + +#define MAILBOX_REVISION 0x000 +#define MAILBOX_MESSAGE(m) (0x040 + 4 * (m)) +#define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m)) +#define MAILBOX_MSGSTATUS(m) (0x0c0 + 4 * (m)) + +#define OMAP2_MAILBOX_IRQSTATUS(u) (0x100 + 8 * (u)) +#define OMAP2_MAILBOX_IRQENABLE(u) (0x104 + 8 * (u)) + +#define OMAP4_MAILBOX_IRQSTATUS(u) (0x104 + 0x10 * (u)) +#define OMAP4_MAILBOX_IRQENABLE(u) (0x108 + 0x10 * (u)) +#define OMAP4_MAILBOX_IRQENABLE_CLR(u) (0x10c + 0x10 * (u)) + +#define MAILBOX_IRQSTATUS(type, u) (type ? OMAP4_MAILBOX_IRQSTATUS(u) : \ + OMAP2_MAILBOX_IRQSTATUS(u)) +#define MAILBOX_IRQENABLE(type, u) (type ? OMAP4_MAILBOX_IRQENABLE(u) : \ + OMAP2_MAILBOX_IRQENABLE(u)) +#define MAILBOX_IRQDISABLE(type, u) (type ? OMAP4_MAILBOX_IRQENABLE_CLR(u) \ + : OMAP2_MAILBOX_IRQENABLE(u)) + +#define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m))) +#define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1)) + +/* Interrupt register configuration types */ +#define MBOX_INTR_CFG_TYPE1 0 +#define MBOX_INTR_CFG_TYPE2 1 + +typedef enum { + IRQ_TX = 1, + IRQ_RX = 2, +} omap_mbox_irq_t; + +struct omap_mbox_fifo { + unsigned long msg; + unsigned long fifo_stat; + unsigned long msg_stat; + unsigned long irqenable; + unsigned long irqstatus; + unsigned long irqdisable; + u32 intr_bit; +}; + +struct omap_mbox_match_data { + u32 intr_type; + bool is_exclusive; +}; + +struct omap_mbox_device { + struct device *dev; + struct mutex cfg_lock; + void __iomem *mbox_base; + u32 *irq_ctx; + u32 num_users; + u32 num_fifos; + u32 intr_type; + const struct omap_mbox_match_data *mbox_data; +}; + +struct omap_mbox { + const char *name; + int irq; + struct omap_mbox_device *parent; + struct omap_mbox_fifo tx_fifo; + struct omap_mbox_fifo rx_fifo; + u32 intr_type; + struct mbox_chan *chan; + bool send_no_irq; +}; + +static inline +unsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs) { - return mbox->ops->fifo_full(mbox); + return __raw_readl(mdev->mbox_base + ofs); } -/* Mailbox IRQ handle functions */ -static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) -{ - if (mbox->ops->ack_irq) - mbox->ops->ack_irq(mbox, irq); -} -static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +static inline +void mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs) { - return mbox->ops->is_irq(mbox, irq); + __raw_writel(val, mdev->mbox_base + ofs); } -/* - * message sender - */ -static int __mbox_poll_for_space(struct omap_mbox *mbox) +/* Mailbox FIFO handle functions */ +static u32 mbox_fifo_read(struct omap_mbox *mbox) { - int ret = 0, i = 1000; - - while (mbox_fifo_full(mbox)) { - if (mbox->ops->type == OMAP_MBOX_TYPE2) - return -1; - if (--i == 0) - return -1; - udelay(1); - } - return ret; + struct omap_mbox_fifo *fifo = &mbox->rx_fifo; + + return mbox_read_reg(mbox->parent, fifo->msg); } -int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) +static void mbox_fifo_write(struct omap_mbox *mbox, u32 msg) { - struct omap_mbox_queue *mq = mbox->txq; - int ret = 0, len; - - 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)); + struct omap_mbox_fifo *fifo = &mbox->tx_fifo; - tasklet_schedule(&mbox->txq->tasklet); - -out: - spin_unlock_bh(&mq->lock); - return ret; + mbox_write_reg(mbox->parent, msg, fifo->msg); } -EXPORT_SYMBOL(omap_mbox_msg_send); -void omap_mbox_save_ctx(struct omap_mbox *mbox) +static int mbox_fifo_empty(struct omap_mbox *mbox) { - if (!mbox->ops->save_ctx) { - dev_err(mbox->dev, "%s:\tno save\n", __func__); - return; - } + struct omap_mbox_fifo *fifo = &mbox->rx_fifo; - mbox->ops->save_ctx(mbox); + return (mbox_read_reg(mbox->parent, fifo->msg_stat) == 0); } -EXPORT_SYMBOL(omap_mbox_save_ctx); -void omap_mbox_restore_ctx(struct omap_mbox *mbox) +static int mbox_fifo_full(struct omap_mbox *mbox) { - if (!mbox->ops->restore_ctx) { - dev_err(mbox->dev, "%s:\tno restore\n", __func__); - return; - } + struct omap_mbox_fifo *fifo = &mbox->tx_fifo; - mbox->ops->restore_ctx(mbox); + return mbox_read_reg(mbox->parent, fifo->fifo_stat); } -EXPORT_SYMBOL(omap_mbox_restore_ctx); -void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +/* Mailbox IRQ handle functions */ +static void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - mbox->ops->enable_irq(mbox, irq); -} -EXPORT_SYMBOL(omap_mbox_enable_irq); + struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? + &mbox->tx_fifo : &mbox->rx_fifo; + u32 bit = fifo->intr_bit; + u32 irqstatus = fifo->irqstatus; -void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) -{ - mbox->ops->disable_irq(mbox, irq); + mbox_write_reg(mbox->parent, bit, irqstatus); + + /* Flush posted write for irq status to avoid spurious interrupts */ + mbox_read_reg(mbox->parent, irqstatus); } -EXPORT_SYMBOL(omap_mbox_disable_irq); -static void mbox_tx_tasklet(unsigned long tx_data) +static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - struct omap_mbox *mbox = (struct omap_mbox *)tx_data; - struct omap_mbox_queue *mq = mbox->txq; - mbox_msg_t msg; - int ret; + struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? + &mbox->tx_fifo : &mbox->rx_fifo; + u32 bit = fifo->intr_bit; + u32 irqenable = fifo->irqenable; + u32 irqstatus = fifo->irqstatus; - while (kfifo_len(&mq->fifo)) { - if (__mbox_poll_for_space(mbox)) { - omap_mbox_enable_irq(mbox, IRQ_TX); - break; - } + u32 enable = mbox_read_reg(mbox->parent, irqenable); + u32 status = mbox_read_reg(mbox->parent, irqstatus); - ret = kfifo_out(&mq->fifo, (unsigned char *)&msg, - sizeof(msg)); - WARN_ON(ret != sizeof(msg)); + return (int)(enable & status & bit); +} - mbox_fifo_write(mbox, msg); - } +static void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + u32 l; + struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? + &mbox->tx_fifo : &mbox->rx_fifo; + u32 bit = fifo->intr_bit; + u32 irqenable = fifo->irqenable; + + l = mbox_read_reg(mbox->parent, irqenable); + l |= bit; + mbox_write_reg(mbox->parent, l, irqenable); } -/* - * Message receiver(workqueue) - */ -static void mbox_rx_work(struct work_struct *work) +static void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - struct omap_mbox_queue *mq = - container_of(work, struct omap_mbox_queue, work); - mbox_msg_t msg; - int len; - - while (kfifo_len(&mq->fifo) >= sizeof(msg)) { - len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); - WARN_ON(len != sizeof(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); - } + struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? + &mbox->tx_fifo : &mbox->rx_fifo; + u32 bit = fifo->intr_bit; + u32 irqdisable = fifo->irqdisable; + + /* + * Read and update the interrupt configuration register for pre-OMAP4. + * OMAP4 and later SoCs have a dedicated interrupt disabling register. + */ + if (!mbox->intr_type) + bit = mbox_read_reg(mbox->parent, irqdisable) & ~bit; + + mbox_write_reg(mbox->parent, bit, irqdisable); } /* @@ -203,35 +199,20 @@ static void __mbox_tx_interrupt(struct omap_mbox *mbox) { omap_mbox_disable_irq(mbox, IRQ_TX); ack_mbox_irq(mbox, IRQ_TX); - tasklet_schedule(&mbox->txq->tasklet); + mbox_chan_txdone(mbox->chan, 0); } static void __mbox_rx_interrupt(struct omap_mbox *mbox) { - struct omap_mbox_queue *mq = mbox->rxq; - mbox_msg_t msg; - int len; + u32 msg; while (!mbox_fifo_empty(mbox)) { - if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { - omap_mbox_disable_irq(mbox, IRQ_RX); - mq->full = true; - goto nomem; - } - msg = mbox_fifo_read(mbox); - - len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); - WARN_ON(len != sizeof(msg)); - - if (mbox->ops->type == OMAP_MBOX_TYPE1) - break; + mbox_chan_received_data(mbox->chan, (void *)(uintptr_t)msg); } - /* no more messages in the fifo. clear IRQ source. */ + /* clear IRQ source. */ ack_mbox_irq(mbox, IRQ_RX); -nomem: - schedule_work(&mbox->rxq->work); } static irqreturn_t mbox_interrupt(int irq, void *p) @@ -247,221 +228,391 @@ static irqreturn_t mbox_interrupt(int irq, void *p) return IRQ_HANDLED; } -static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, - void (*work) (struct work_struct *), - void (*tasklet)(unsigned long)) +static int omap_mbox_startup(struct omap_mbox *mbox) { - struct omap_mbox_queue *mq; - - mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); - if (!mq) - return NULL; + int ret = 0; - spin_lock_init(&mq->lock); + ret = request_threaded_irq(mbox->irq, NULL, mbox_interrupt, + IRQF_SHARED | IRQF_ONESHOT, mbox->name, + mbox); + if (unlikely(ret)) { + pr_err("failed to register mailbox interrupt:%d\n", ret); + return ret; + } - if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL)) - goto error; + if (mbox->send_no_irq) + mbox->chan->txdone_method = TXDONE_BY_ACK; - if (work) - INIT_WORK(&mq->work, work); + omap_mbox_enable_irq(mbox, IRQ_RX); - if (tasklet) - tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox); - return mq; -error: - kfree(mq); - return NULL; + return 0; } -static void mbox_queue_free(struct omap_mbox_queue *q) +static void omap_mbox_fini(struct omap_mbox *mbox) { - kfifo_free(&q->fifo); - kfree(q); + omap_mbox_disable_irq(mbox, IRQ_RX); + free_irq(mbox->irq, mbox); } -static int omap_mbox_startup(struct omap_mbox *mbox) +static int omap_mbox_chan_startup(struct mbox_chan *chan) { + struct omap_mbox *mbox = chan->con_priv; + struct omap_mbox_device *mdev = mbox->parent; int ret = 0; - struct omap_mbox_queue *mq; - - mutex_lock(&mbox_configured_lock); - if (!mbox_configured++) { - if (likely(mbox->ops->startup)) { - ret = mbox->ops->startup(mbox); - if (unlikely(ret)) - goto fail_startup; - } else - goto fail_startup; - } - if (!mbox->use_count++) { - mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); - if (!mq) { - ret = -ENOMEM; - goto fail_alloc_txq; - } - mbox->txq = mq; + mutex_lock(&mdev->cfg_lock); + pm_runtime_get_sync(mdev->dev); + ret = omap_mbox_startup(mbox); + if (ret) + pm_runtime_put_sync(mdev->dev); + mutex_unlock(&mdev->cfg_lock); + return ret; +} - mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); - if (!mq) { - ret = -ENOMEM; - goto fail_alloc_rxq; - } - mbox->rxq = mq; - mq->mbox = mbox; - 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; - } +static void omap_mbox_chan_shutdown(struct mbox_chan *chan) +{ + struct omap_mbox *mbox = chan->con_priv; + struct omap_mbox_device *mdev = mbox->parent; - omap_mbox_enable_irq(mbox, IRQ_RX); - } - mutex_unlock(&mbox_configured_lock); - return 0; + mutex_lock(&mdev->cfg_lock); + omap_mbox_fini(mbox); + pm_runtime_put_sync(mdev->dev); + mutex_unlock(&mdev->cfg_lock); +} -fail_request_irq: - mbox_queue_free(mbox->rxq); -fail_alloc_rxq: - mbox_queue_free(mbox->txq); -fail_alloc_txq: - if (mbox->ops->shutdown) - mbox->ops->shutdown(mbox); - mbox->use_count--; -fail_startup: - mbox_configured--; - mutex_unlock(&mbox_configured_lock); - return ret; +static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg) +{ + if (mbox_fifo_full(mbox)) + return -EBUSY; + + omap_mbox_enable_irq(mbox, IRQ_RX); + mbox_fifo_write(mbox, msg); + omap_mbox_disable_irq(mbox, IRQ_RX); + + /* we must read and ack the interrupt directly from here */ + mbox_fifo_read(mbox); + ack_mbox_irq(mbox, IRQ_RX); + + return 0; } -static void omap_mbox_fini(struct omap_mbox *mbox) +static int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg) { - mutex_lock(&mbox_configured_lock); - - if (!--mbox->use_count) { - omap_mbox_disable_irq(mbox, IRQ_RX); - 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_fifo_full(mbox)) { + /* always enable the interrupt */ + omap_mbox_enable_irq(mbox, IRQ_TX); + return -EBUSY; } - if (likely(mbox->ops->shutdown)) { - if (!--mbox_configured) - mbox->ops->shutdown(mbox); - } + mbox_fifo_write(mbox, msg); - mutex_unlock(&mbox_configured_lock); + /* always enable the interrupt */ + omap_mbox_enable_irq(mbox, IRQ_TX); + return 0; } -struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb) +static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) { - struct omap_mbox *_mbox, *mbox = NULL; - int i, ret; + struct omap_mbox *mbox = chan->con_priv; + int ret; + u32 msg = (u32)(uintptr_t)(data); - if (!mboxes) - return ERR_PTR(-EINVAL); + if (!mbox) + return -EINVAL; - for (i = 0; (_mbox = mboxes[i]); i++) { - if (!strcmp(_mbox->name, name)) { - mbox = _mbox; - break; - } - } + if (mbox->send_no_irq) + ret = omap_mbox_chan_send_noirq(mbox, msg); + else + ret = omap_mbox_chan_send(mbox, msg); - if (!mbox) - return ERR_PTR(-ENOENT); + return ret; +} - if (nb) - blocking_notifier_chain_register(&mbox->notifier, nb); +static const struct mbox_chan_ops omap_mbox_chan_ops = { + .startup = omap_mbox_chan_startup, + .send_data = omap_mbox_chan_send_data, + .shutdown = omap_mbox_chan_shutdown, +}; - ret = omap_mbox_startup(mbox); - if (ret) { - blocking_notifier_chain_unregister(&mbox->notifier, nb); - return ERR_PTR(-ENODEV); +#ifdef CONFIG_PM_SLEEP +static int omap_mbox_suspend(struct device *dev) +{ + struct omap_mbox_device *mdev = dev_get_drvdata(dev); + u32 usr, fifo, reg; + + if (pm_runtime_status_suspended(dev)) + return 0; + + if (mdev->mbox_data->is_exclusive) { + for (fifo = 0; fifo < mdev->num_fifos; fifo++) { + if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) { + dev_err(mdev->dev, "fifo %d has unexpected unread messages\n", + fifo); + return -EBUSY; + } + } + } + + for (usr = 0; usr < mdev->num_users; usr++) { + reg = MAILBOX_IRQENABLE(mdev->intr_type, usr); + mdev->irq_ctx[usr] = mbox_read_reg(mdev, reg); } - return mbox; + return 0; } -EXPORT_SYMBOL(omap_mbox_get); -void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb) +static int omap_mbox_resume(struct device *dev) { - blocking_notifier_chain_unregister(&mbox->notifier, nb); - omap_mbox_fini(mbox); -} -EXPORT_SYMBOL(omap_mbox_put); + struct omap_mbox_device *mdev = dev_get_drvdata(dev); + u32 usr, reg; -static struct class omap_mbox_class = { .name = "mbox", }; + if (pm_runtime_status_suspended(dev)) + return 0; + + for (usr = 0; usr < mdev->num_users; usr++) { + reg = MAILBOX_IRQENABLE(mdev->intr_type, usr); + mbox_write_reg(mdev, mdev->irq_ctx[usr], reg); + } -int omap_mbox_register(struct device *parent, struct omap_mbox **list) + return 0; +} +#endif + +static const struct dev_pm_ops omap_mbox_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_mbox_suspend, omap_mbox_resume) +}; + +static const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1, true }; +static const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2, true }; +static const struct omap_mbox_match_data am654_data = { MBOX_INTR_CFG_TYPE2, false }; + +static const struct of_device_id omap_mailbox_of_match[] = { + { + .compatible = "ti,omap2-mailbox", + .data = &omap2_data, + }, + { + .compatible = "ti,omap3-mailbox", + .data = &omap2_data, + }, + { + .compatible = "ti,omap4-mailbox", + .data = &omap4_data, + }, + { + .compatible = "ti,am654-mailbox", + .data = &am654_data, + }, + { + .compatible = "ti,am64-mailbox", + .data = &am654_data, + }, + { + /* end */ + }, +}; +MODULE_DEVICE_TABLE(of, omap_mailbox_of_match); + +static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller, + const struct of_phandle_args *sp) { - int ret; + phandle phandle = sp->args[0]; + struct device_node *node; + struct omap_mbox_device *mdev; + struct omap_mbox *mbox; int i; - mboxes = list; - if (!mboxes) - return -EINVAL; + mdev = dev_get_drvdata(controller->dev); + if (WARN_ON(!mdev)) + return ERR_PTR(-EINVAL); - for (i = 0; mboxes[i]; i++) { - struct omap_mbox *mbox = mboxes[i]; - mbox->dev = device_create(&omap_mbox_class, - parent, 0, mbox, "%s", mbox->name); - if (IS_ERR(mbox->dev)) { - ret = PTR_ERR(mbox->dev); - goto err_out; - } + node = of_find_node_by_phandle(phandle); + if (!node) { + pr_err("%s: could not find node phandle 0x%x\n", + __func__, phandle); + return ERR_PTR(-ENODEV); + } - BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier); + for (i = 0; i < controller->num_chans; i++) { + mbox = controller->chans[i].con_priv; + if (!strcmp(mbox->name, node->name)) { + of_node_put(node); + return &controller->chans[i]; + } } - return 0; -err_out: - while (i--) - device_unregister(mboxes[i]->dev); - return ret; + of_node_put(node); + return ERR_PTR(-ENOENT); } -EXPORT_SYMBOL(omap_mbox_register); -int omap_mbox_unregister(void) +static int omap_mbox_probe(struct platform_device *pdev) { + int ret; + struct mbox_chan *chnls; + struct omap_mbox *mbox; + struct omap_mbox_device *mdev; + struct omap_mbox_fifo *fifo; + struct device_node *node = pdev->dev.of_node; + struct device_node *child; + struct mbox_controller *controller; + u32 intr_type, info_count; + u32 num_users, num_fifos; + u32 tmp[3]; + u32 l; int i; - if (!mboxes) - return -EINVAL; + if (!node) { + pr_err("%s: only DT-based devices are supported\n", __func__); + return -ENODEV; + } - for (i = 0; mboxes[i]; i++) - device_unregister(mboxes[i]->dev); - mboxes = NULL; - return 0; -} -EXPORT_SYMBOL(omap_mbox_unregister); + if (of_property_read_u32(node, "ti,mbox-num-users", &num_users)) + return -ENODEV; -static int __init omap_mbox_init(void) -{ - int err; + if (of_property_read_u32(node, "ti,mbox-num-fifos", &num_fifos)) + return -ENODEV; - err = class_register(&omap_mbox_class); - if (err) - return err; + info_count = of_get_available_child_count(node); + if (!info_count) { + dev_err(&pdev->dev, "no available mbox devices found\n"); + return -ENODEV; + } - /* 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)); + mdev = devm_kzalloc(&pdev->dev, sizeof(*mdev), GFP_KERNEL); + if (!mdev) + return -ENOMEM; + + mdev->mbox_data = device_get_match_data(&pdev->dev); + if (!mdev->mbox_data) + return -ENODEV; + + intr_type = mdev->mbox_data->intr_type; + + mdev->mbox_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mdev->mbox_base)) + return PTR_ERR(mdev->mbox_base); + + mdev->irq_ctx = devm_kcalloc(&pdev->dev, num_users, sizeof(u32), + GFP_KERNEL); + if (!mdev->irq_ctx) + return -ENOMEM; + + chnls = devm_kcalloc(&pdev->dev, info_count + 1, sizeof(*chnls), + GFP_KERNEL); + if (!chnls) + return -ENOMEM; + + child = NULL; + for (i = 0; i < info_count; i++) { + int tx_id, tx_irq, tx_usr; + int rx_id, rx_usr; + + mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + child = of_get_next_available_child(node, child); + ret = of_property_read_u32_array(child, "ti,mbox-tx", tmp, + ARRAY_SIZE(tmp)); + if (ret) + return ret; + tx_id = tmp[0]; + tx_irq = tmp[1]; + tx_usr = tmp[2]; + + ret = of_property_read_u32_array(child, "ti,mbox-rx", tmp, + ARRAY_SIZE(tmp)); + if (ret) + return ret; + rx_id = tmp[0]; + /* rx_irq = tmp[1]; */ + rx_usr = tmp[2]; + + if (tx_id >= num_fifos || rx_id >= num_fifos || + tx_usr >= num_users || rx_usr >= num_users) + return -EINVAL; + + fifo = &mbox->tx_fifo; + fifo->msg = MAILBOX_MESSAGE(tx_id); + fifo->fifo_stat = MAILBOX_FIFOSTATUS(tx_id); + fifo->intr_bit = MAILBOX_IRQ_NOTFULL(tx_id); + fifo->irqenable = MAILBOX_IRQENABLE(intr_type, tx_usr); + fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, tx_usr); + fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, tx_usr); + + fifo = &mbox->rx_fifo; + fifo->msg = MAILBOX_MESSAGE(rx_id); + fifo->msg_stat = MAILBOX_MSGSTATUS(rx_id); + fifo->intr_bit = MAILBOX_IRQ_NEWMSG(rx_id); + fifo->irqenable = MAILBOX_IRQENABLE(intr_type, rx_usr); + fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, rx_usr); + fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, rx_usr); + + mbox->send_no_irq = of_property_read_bool(child, "ti,mbox-send-noirq"); + mbox->intr_type = intr_type; + + mbox->parent = mdev; + mbox->name = child->name; + mbox->irq = platform_get_irq(pdev, tx_irq); + if (mbox->irq < 0) + return mbox->irq; + mbox->chan = &chnls[i]; + chnls[i].con_priv = mbox; + } + + mutex_init(&mdev->cfg_lock); + mdev->dev = &pdev->dev; + mdev->num_users = num_users; + mdev->num_fifos = num_fifos; + mdev->intr_type = intr_type; + + controller = devm_kzalloc(&pdev->dev, sizeof(*controller), GFP_KERNEL); + if (!controller) + return -ENOMEM; + /* + * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready + * IRQ and is needed to run the Tx state machine + */ + controller->txdone_irq = true; + controller->dev = mdev->dev; + controller->ops = &omap_mbox_chan_ops; + controller->chans = chnls; + controller->num_chans = info_count; + controller->of_xlate = omap_mbox_of_xlate; + ret = devm_mbox_controller_register(mdev->dev, controller); + if (ret) + return ret; + + platform_set_drvdata(pdev, mdev); + devm_pm_runtime_enable(mdev->dev); + + ret = pm_runtime_resume_and_get(mdev->dev); + if (ret < 0) + return ret; + + /* + * just print the raw revision register, the format is not + * uniform across all SoCs + */ + l = mbox_read_reg(mdev, MAILBOX_REVISION); + dev_info(mdev->dev, "omap mailbox rev 0x%x\n", l); + + ret = pm_runtime_put_sync(mdev->dev); + if (ret < 0 && ret != -ENOSYS) + return ret; return 0; } -subsys_initcall(omap_mbox_init); -static void __exit omap_mbox_exit(void) -{ - class_unregister(&omap_mbox_class); -} -module_exit(omap_mbox_exit); +static struct platform_driver omap_mbox_driver = { + .probe = omap_mbox_probe, + .driver = { + .name = "omap-mailbox", + .pm = &omap_mbox_pm_ops, + .of_match_table = omap_mailbox_of_match, + }, +}; +module_platform_driver(omap_mbox_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); |
