diff options
author | Suman Anna <s-anna@ti.com> | 2014-06-25 04:43:41 +0400 |
---|---|---|
committer | Tony Lindgren <tony@atomide.com> | 2014-07-29 12:57:25 +0400 |
commit | 5040f534385a300dee4f05af2484cdbf9ecef8a6 (patch) | |
tree | c0b9e9a84e1700f951986ada8ad6014c74898a75 /drivers/mailbox/omap-mailbox.c | |
parent | ef45eae6e9f6af297c0cd0bfb98c85f3f51e96be (diff) | |
download | linux-5040f534385a300dee4f05af2484cdbf9ecef8a6.tar.xz |
mailbox/omap: consolidate OMAP mailbox driver
There is no need for a separate common OMAP mailbox module
now that the OMAP1 mailbox driver has been removed. So,
consolidate the two individual OMAP mailbox modules into a
single driver. This streamlines the driver for converting
to mailbox framework.
The following are the main changes:
- collapse mailbox-omap2.c into omap-mailbox.c
- remove omap_mbox_ops and replace the ops calls with
the equivalent functionality.
- simplify the sub-mailbox startup/shutdown functionality,
the one-time operations are moved into probe, and the
pm_runtime_get_sync and pm_runtime_put_sync can be invoked
without using a configuration counter.
- move all definitions from private omap_mbox.h into the
source code, and eliminate this internal header.
- rename some variables that used the omap2_mbox prefix with
a generic omap_mbox prefix.
Signed-off-by: Suman Anna <s-anna@ti.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Diffstat (limited to 'drivers/mailbox/omap-mailbox.c')
-rw-r--r-- | drivers/mailbox/omap-mailbox.c | 331 |
1 files changed, 282 insertions, 49 deletions
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c index eab72276ffab..66b02ab00b19 100644 --- a/drivers/mailbox/omap-mailbox.c +++ b/drivers/mailbox/omap-mailbox.c @@ -2,8 +2,10 @@ * OMAP mailbox driver * * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved. + * Copyright (C) 2013-2014 Texas Instruments Inc. * * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> + * Suman Anna <s-anna@ti.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,45 +31,145 @@ #include <linux/err.h> #include <linux/notifier.h> #include <linux/module.h> - -#include "omap-mbox.h" - +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/platform_data/mailbox-omap.h> +#include <linux/omap-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)) + +#define MBOX_REG_SIZE 0x120 + +#define OMAP4_MBOX_REG_SIZE 0x130 + +#define MBOX_NR_REGS (MBOX_REG_SIZE / sizeof(u32)) +#define OMAP4_MBOX_NR_REGS (OMAP4_MBOX_REG_SIZE / sizeof(u32)) + +struct omap_mbox_fifo { + unsigned long msg; + unsigned long fifo_stat; + unsigned long msg_stat; +}; + +struct omap_mbox_priv { + struct omap_mbox_fifo tx_fifo; + struct omap_mbox_fifo rx_fifo; + unsigned long irqenable; + unsigned long irqstatus; + u32 newmsg_bit; + u32 notfull_bit; + u32 ctx[OMAP4_MBOX_NR_REGS]; + unsigned long irqdisable; + u32 intr_type; +}; + +struct omap_mbox_queue { + spinlock_t lock; + struct kfifo fifo; + struct work_struct work; + struct tasklet_struct tasklet; + struct omap_mbox *mbox; + bool full; +}; + +struct omap_mbox { + const char *name; + int irq; + struct omap_mbox_queue *txq, *rxq; + struct device *dev; + void *priv; + int use_count; + struct blocking_notifier_head notifier; +}; + +static void __iomem *mbox_base; 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)"); +static inline unsigned int mbox_read_reg(size_t ofs) +{ + return __raw_readl(mbox_base + ofs); +} + +static inline void mbox_write_reg(u32 val, size_t ofs) +{ + __raw_writel(val, mbox_base + ofs); +} + /* Mailbox FIFO handle functions */ -static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) +static mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) { - return mbox->ops->fifo_read(mbox); + struct omap_mbox_fifo *fifo = + &((struct omap_mbox_priv *)mbox->priv)->rx_fifo; + return (mbox_msg_t) mbox_read_reg(fifo->msg); } -static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) + +static void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) { - mbox->ops->fifo_write(mbox, msg); + struct omap_mbox_fifo *fifo = + &((struct omap_mbox_priv *)mbox->priv)->tx_fifo; + mbox_write_reg(msg, fifo->msg); } -static inline int mbox_fifo_empty(struct omap_mbox *mbox) + +static int mbox_fifo_empty(struct omap_mbox *mbox) { - return mbox->ops->fifo_empty(mbox); + struct omap_mbox_fifo *fifo = + &((struct omap_mbox_priv *)mbox->priv)->rx_fifo; + return (mbox_read_reg(fifo->msg_stat) == 0); } -static inline int mbox_fifo_full(struct omap_mbox *mbox) + +static int mbox_fifo_full(struct omap_mbox *mbox) { - return mbox->ops->fifo_full(mbox); + struct omap_mbox_fifo *fifo = + &((struct omap_mbox_priv *)mbox->priv)->tx_fifo; + return mbox_read_reg(fifo->fifo_stat); } /* Mailbox IRQ handle functions */ -static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +static void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - if (mbox->ops->ack_irq) - mbox->ops->ack_irq(mbox, irq); + struct omap_mbox_priv *p = mbox->priv; + u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + mbox_write_reg(bit, p->irqstatus); + + /* Flush posted write for irq status to avoid spurious interrupts */ + mbox_read_reg(p->irqstatus); } -static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) + +static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - return mbox->ops->is_irq(mbox, irq); + struct omap_mbox_priv *p = mbox->priv; + u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + u32 enable = mbox_read_reg(p->irqenable); + u32 status = mbox_read_reg(p->irqstatus); + + return (int)(enable & status & bit); } /* @@ -103,35 +205,66 @@ EXPORT_SYMBOL(omap_mbox_msg_send); void omap_mbox_save_ctx(struct omap_mbox *mbox) { - if (!mbox->ops->save_ctx) { - dev_err(mbox->dev, "%s:\tno save\n", __func__); - return; + int i; + struct omap_mbox_priv *p = mbox->priv; + int nr_regs; + + if (p->intr_type) + nr_regs = OMAP4_MBOX_NR_REGS; + else + nr_regs = MBOX_NR_REGS; + for (i = 0; i < nr_regs; i++) { + p->ctx[i] = mbox_read_reg(i * sizeof(u32)); + + dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__, + i, p->ctx[i]); } - - mbox->ops->save_ctx(mbox); } EXPORT_SYMBOL(omap_mbox_save_ctx); void omap_mbox_restore_ctx(struct omap_mbox *mbox) { - if (!mbox->ops->restore_ctx) { - dev_err(mbox->dev, "%s:\tno restore\n", __func__); - return; + int i; + struct omap_mbox_priv *p = mbox->priv; + int nr_regs; + + if (p->intr_type) + nr_regs = OMAP4_MBOX_NR_REGS; + else + nr_regs = MBOX_NR_REGS; + for (i = 0; i < nr_regs; i++) { + mbox_write_reg(p->ctx[i], i * sizeof(u32)); + + dev_dbg(mbox->dev, "%s: [%02x] %08x\n", __func__, + i, p->ctx[i]); } - - mbox->ops->restore_ctx(mbox); } EXPORT_SYMBOL(omap_mbox_restore_ctx); void omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - mbox->ops->enable_irq(mbox, irq); + struct omap_mbox_priv *p = mbox->priv; + u32 l, bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + l = mbox_read_reg(p->irqenable); + l |= bit; + mbox_write_reg(l, p->irqenable); } EXPORT_SYMBOL(omap_mbox_enable_irq); void omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) { - mbox->ops->disable_irq(mbox, irq); + struct omap_mbox_priv *p = mbox->priv; + u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + /* + * Read and update the interrupt configuration register for pre-OMAP4. + * OMAP4 and later SoCs have a dedicated interrupt disabling register. + */ + if (!p->intr_type) + bit = mbox_read_reg(p->irqdisable) & ~bit; + + mbox_write_reg(bit, p->irqdisable); } EXPORT_SYMBOL(omap_mbox_disable_irq); @@ -267,14 +400,9 @@ static int omap_mbox_startup(struct omap_mbox *mbox) 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; - } + ret = pm_runtime_get_sync(mbox->dev->parent); + if (unlikely(ret < 0)) + goto fail_startup; if (!mbox->use_count++) { mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); @@ -309,11 +437,9 @@ fail_request_irq: fail_alloc_rxq: mbox_queue_free(mbox->txq); fail_alloc_txq: - if (mbox->ops->shutdown) - mbox->ops->shutdown(mbox); + pm_runtime_put_sync(mbox->dev->parent); mbox->use_count--; fail_startup: - mbox_configured--; mutex_unlock(&mbox_configured_lock); return ret; } @@ -331,10 +457,7 @@ static void omap_mbox_fini(struct omap_mbox *mbox) mbox_queue_free(mbox->rxq); } - if (likely(mbox->ops->shutdown)) { - if (!--mbox_configured) - mbox->ops->shutdown(mbox); - } + pm_runtime_put_sync(mbox->dev->parent); mutex_unlock(&mbox_configured_lock); } @@ -379,7 +502,7 @@ EXPORT_SYMBOL(omap_mbox_put); static struct class omap_mbox_class = { .name = "mbox", }; -int omap_mbox_register(struct device *parent, struct omap_mbox **list) +static int omap_mbox_register(struct device *parent, struct omap_mbox **list) { int ret; int i; @@ -406,9 +529,8 @@ err_out: device_unregister(mboxes[i]->dev); return ret; } -EXPORT_SYMBOL(omap_mbox_register); -int omap_mbox_unregister(void) +static int omap_mbox_unregister(void) { int i; @@ -420,7 +542,117 @@ int omap_mbox_unregister(void) mboxes = NULL; return 0; } -EXPORT_SYMBOL(omap_mbox_unregister); + +static int omap_mbox_probe(struct platform_device *pdev) +{ + struct resource *mem; + int ret; + struct omap_mbox **list, *mbox, *mboxblk; + struct omap_mbox_priv *priv, *privblk; + struct omap_mbox_pdata *pdata = pdev->dev.platform_data; + struct omap_mbox_dev_info *info; + u32 intr_type; + u32 l; + int i; + + if (!pdata || !pdata->info_cnt || !pdata->info) { + pr_err("%s: platform not supported\n", __func__); + return -ENODEV; + } + + /* allocate one extra for marking end of list */ + list = devm_kzalloc(&pdev->dev, (pdata->info_cnt + 1) * sizeof(*list), + GFP_KERNEL); + if (!list) + return -ENOMEM; + + mboxblk = devm_kzalloc(&pdev->dev, pdata->info_cnt * sizeof(*mbox), + GFP_KERNEL); + if (!mboxblk) + return -ENOMEM; + + privblk = devm_kzalloc(&pdev->dev, pdata->info_cnt * sizeof(*priv), + GFP_KERNEL); + if (!privblk) + return -ENOMEM; + + info = pdata->info; + intr_type = pdata->intr_type; + mbox = mboxblk; + priv = privblk; + for (i = 0; i < pdata->info_cnt; i++, info++, priv++) { + priv->tx_fifo.msg = MAILBOX_MESSAGE(info->tx_id); + priv->tx_fifo.fifo_stat = MAILBOX_FIFOSTATUS(info->tx_id); + priv->rx_fifo.msg = MAILBOX_MESSAGE(info->rx_id); + priv->rx_fifo.msg_stat = MAILBOX_MSGSTATUS(info->rx_id); + priv->notfull_bit = MAILBOX_IRQ_NOTFULL(info->tx_id); + priv->newmsg_bit = MAILBOX_IRQ_NEWMSG(info->rx_id); + priv->irqenable = MAILBOX_IRQENABLE(intr_type, info->usr_id); + priv->irqstatus = MAILBOX_IRQSTATUS(intr_type, info->usr_id); + priv->irqdisable = MAILBOX_IRQDISABLE(intr_type, info->usr_id); + priv->intr_type = intr_type; + + mbox->priv = priv; + mbox->name = info->name; + mbox->irq = platform_get_irq(pdev, info->irq_id); + if (mbox->irq < 0) + return mbox->irq; + list[i] = mbox++; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mbox_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(mbox_base)) + return PTR_ERR(mbox_base); + + ret = omap_mbox_register(&pdev->dev, list); + if (ret) + return ret; + + platform_set_drvdata(pdev, list); + pm_runtime_enable(&pdev->dev); + + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + pm_runtime_put_noidle(&pdev->dev); + goto unregister; + } + + /* + * just print the raw revision register, the format is not + * uniform across all SoCs + */ + l = mbox_read_reg(MAILBOX_REVISION); + dev_info(&pdev->dev, "omap mailbox rev 0x%x\n", l); + + ret = pm_runtime_put_sync(&pdev->dev); + if (ret < 0) + goto unregister; + + return 0; + +unregister: + pm_runtime_disable(&pdev->dev); + omap_mbox_unregister(); + return ret; +} + +static int omap_mbox_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + omap_mbox_unregister(); + + return 0; +} + +static struct platform_driver omap_mbox_driver = { + .probe = omap_mbox_probe, + .remove = omap_mbox_remove, + .driver = { + .name = "omap-mailbox", + .owner = THIS_MODULE, + }, +}; static int __init omap_mbox_init(void) { @@ -435,12 +667,13 @@ static int __init omap_mbox_init(void) mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(mbox_msg_t)); - return 0; + return platform_driver_register(&omap_mbox_driver); } subsys_initcall(omap_mbox_init); static void __exit omap_mbox_exit(void) { + platform_driver_unregister(&omap_mbox_driver); class_unregister(&omap_mbox_class); } module_exit(omap_mbox_exit); |