From a5734973f0c04bc26e2813a643bf8f7e22bac26f Mon Sep 17 00:00:00 2001 From: Hiroshi DOYU Date: Thu, 7 Dec 2006 15:43:59 -0800 Subject: [PATCH] ARM: OMAP: Add mailbox support for IVA This patch adds a generic mailbox interface considering IVA (Image Video Accelerator) use on 2420, but this patch itself doesn't contain any IVA driver. Signed-off-by: Hiroshi DOYU Signed-off-by: Juha Yrjola Signed-off-by: Tony Lindgren --- arch/arm/mach-omap1/mailbox.c | 206 ++++++++++++++++++++ arch/arm/mach-omap2/mailbox.c | 310 ++++++++++++++++++++++++++++++ arch/arm/plat-omap/mailbox.c | 352 +++++++++++++++++++++++++++++++++++ arch/arm/plat-omap/mailbox.h | 193 +++++++++++++++++++ include/asm-arm/arch-omap/mailbox.h | 68 +++++++ 5 files changed, 1129 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-omap1/mailbox.c b/arch/arm/mach-omap1/mailbox.c new file mode 100644 index 0000000..877b838 --- /dev/null +++ b/arch/arm/mach-omap1/mailbox.c @@ -0,0 +1,206 @@ +/* + * Mailbox reservation modules for DSP + * + * Copyright (C) 2006 Nokia Corporation + * Written by: Hiroshi DOYU + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_ARM2DSP1 0x00 +#define MAILBOX_ARM2DSP1b 0x04 +#define MAILBOX_DSP2ARM1 0x08 +#define MAILBOX_DSP2ARM1b 0x0c +#define MAILBOX_DSP2ARM2 0x10 +#define MAILBOX_DSP2ARM2b 0x14 +#define MAILBOX_ARM2DSP1_Flag 0x18 +#define MAILBOX_DSP2ARM1_Flag 0x1c +#define MAILBOX_DSP2ARM2_Flag 0x20 + +unsigned long mbox_base; + +struct omap_mbox1_fifo { + unsigned long cmd; + unsigned long data; + unsigned long flag; +}; + +struct omap_mbox1_priv { + struct omap_mbox1_fifo tx_fifo; + struct omap_mbox1_fifo rx_fifo; +}; + +static inline int mbox_read_reg(unsigned int reg) +{ + return __raw_readw(mbox_base + reg); +} + +static inline void mbox_write_reg(unsigned int val, unsigned int reg) +{ + __raw_writew(val, mbox_base + reg); +} + +/* msg */ +static inline mbox_msg_t omap1_mbox_fifo_read(struct omap_mbox *mbox) +{ + struct omap_mbox1_fifo *fifo = + &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo; + mbox_msg_t msg; + + msg = mbox_read_reg(fifo->data); + msg |= ((mbox_msg_t) mbox_read_reg(fifo->cmd)) << 16; + + return msg; +} + +static inline void +omap1_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) +{ + struct omap_mbox1_fifo *fifo = + &((struct omap_mbox1_priv *)mbox->priv)->tx_fifo; + + mbox_write_reg(msg & 0xffff, fifo->data); + mbox_write_reg(msg >> 16, fifo->cmd); +} + +static inline int omap1_mbox_fifo_empty(struct omap_mbox *mbox) +{ + return 0; +} + +static inline int omap1_mbox_fifo_full(struct omap_mbox *mbox) +{ + struct omap_mbox1_fifo *fifo = + &((struct omap_mbox1_priv *)mbox->priv)->rx_fifo; + + return (mbox_read_reg(fifo->flag)); +} + +/* irq */ +static inline void +omap1_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + if (irq == IRQ_RX) + enable_irq(mbox->irq); +} + +static inline void +omap1_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + if (irq == IRQ_RX) + disable_irq(mbox->irq); +} + +static inline int +omap1_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + if (irq == IRQ_TX) + return 0; + return 1; +} + +struct omap_mbox_ops omap1_mbox_ops = { + .type = OMAP_MBOX_TYPE1, + .fifo_read = omap1_mbox_fifo_read, + .fifo_write = omap1_mbox_fifo_write, + .fifo_empty = omap1_mbox_fifo_empty, + .fifo_full = omap1_mbox_fifo_full, + .enable_irq = omap1_mbox_enable_irq, + .disable_irq = omap1_mbox_disable_irq, + .is_irq = omap1_mbox_is_irq, +}; + +/* FIXME: the following struct should be created automatically by the user id */ + +/* DSP */ +static struct omap_mbox1_priv omap1_mbox_dsp_priv = { + .tx_fifo = { + .cmd = MAILBOX_ARM2DSP1b, + .data = MAILBOX_ARM2DSP1, + .flag = MAILBOX_ARM2DSP1_Flag, + }, + .rx_fifo = { + .cmd = MAILBOX_DSP2ARM1b, + .data = MAILBOX_DSP2ARM1, + .flag = MAILBOX_DSP2ARM1_Flag, + }, +}; + +struct omap_mbox mbox_dsp_info = { + .name = "dsp", + .ops = &omap1_mbox_ops, + .priv = &omap1_mbox_dsp_priv, +}; +EXPORT_SYMBOL(mbox_dsp_info); + +static int __init omap1_mbox_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = 0; + + if (pdev->num_resources != 2) { + dev_err(&pdev->dev, "invalid number of resources: %d\n", + pdev->num_resources); + return -ENODEV; + } + + /* MBOX base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid mem resource\n"); + return -ENODEV; + } + mbox_base = res->start; + + /* DSP IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid irq resource\n"); + return -ENODEV; + } + mbox_dsp_info.irq = res->start; + + ret = omap_mbox_register(&mbox_dsp_info); + + return ret; +} + +static int omap1_mbox_remove(struct platform_device *pdev) +{ + omap_mbox_unregister(&mbox_dsp_info); + + return 0; +} + +static struct platform_driver omap1_mbox_driver = { + .probe = omap1_mbox_probe, + .remove = omap1_mbox_remove, + .driver = { + .name = "mailbox", + }, +}; + +static int __init omap1_mbox_init(void) +{ + return platform_driver_register(&omap1_mbox_driver); +} + +static void __exit omap1_mbox_exit(void) +{ + platform_driver_unregister(&omap1_mbox_driver); +} + +module_init(omap1_mbox_init); +module_exit(omap1_mbox_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/mach-omap2/mailbox.c b/arch/arm/mach-omap2/mailbox.c new file mode 100644 index 0000000..e541e95 --- /dev/null +++ b/arch/arm/mach-omap2/mailbox.c @@ -0,0 +1,310 @@ +/* + * Mailbox reservation modules for OMAP2 + * + * Copyright (C) 2006 Nokia Corporation + * Written by: Hiroshi DOYU + * and Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAILBOX_REVISION 0x00 +#define MAILBOX_SYSCONFIG 0x10 +#define MAILBOX_SYSSTATUS 0x14 +#define MAILBOX_MESSAGE_0 0x40 +#define MAILBOX_MESSAGE_1 0x44 +#define MAILBOX_MESSAGE_2 0x48 +#define MAILBOX_MESSAGE_3 0x4c +#define MAILBOX_MESSAGE_4 0x50 +#define MAILBOX_MESSAGE_5 0x54 +#define MAILBOX_FIFOSTATUS_0 0x80 +#define MAILBOX_FIFOSTATUS_1 0x84 +#define MAILBOX_FIFOSTATUS_2 0x88 +#define MAILBOX_FIFOSTATUS_3 0x8c +#define MAILBOX_FIFOSTATUS_4 0x90 +#define MAILBOX_FIFOSTATUS_5 0x94 +#define MAILBOX_MSGSTATUS_0 0xc0 +#define MAILBOX_MSGSTATUS_1 0xc4 +#define MAILBOX_MSGSTATUS_2 0xc8 +#define MAILBOX_MSGSTATUS_3 0xcc +#define MAILBOX_MSGSTATUS_4 0xd0 +#define MAILBOX_MSGSTATUS_5 0xd4 +#define MAILBOX_IRQSTATUS_0 0x100 +#define MAILBOX_IRQENABLE_0 0x104 +#define MAILBOX_IRQSTATUS_1 0x108 +#define MAILBOX_IRQENABLE_1 0x10c +#define MAILBOX_IRQSTATUS_2 0x110 +#define MAILBOX_IRQENABLE_2 0x114 +#define MAILBOX_IRQSTATUS_3 0x118 +#define MAILBOX_IRQENABLE_3 0x11c + +unsigned long mbox_base; + +#define MAILBOX_IRQ_NOTFULL(n) (1 << (2 * (n) + 1)) +#define MAILBOX_IRQ_NEWMSG(n) (1 << (2 * (n))) + +struct omap_mbox2_fifo { + unsigned long msg; + unsigned long fifo_stat; + unsigned long msg_stat; +}; + +struct omap_mbox2_priv { + struct omap_mbox2_fifo tx_fifo; + struct omap_mbox2_fifo rx_fifo; + unsigned long irqenable; + unsigned long irqstatus; + u32 newmsg_bit; + u32 notfull_bit; +}; + +struct clk *mbox_ick_handle; + +static inline unsigned int mbox_read_reg(unsigned int reg) +{ + return __raw_readl(mbox_base + reg); +} + +static inline void mbox_write_reg(unsigned int val, unsigned int reg) +{ + __raw_writel(val, mbox_base + reg); +} + +/* Mailbox H/W preparations */ +static inline int omap2_mbox_startup(struct omap_mbox *mbox) +{ + unsigned int l; + + mbox_ick_handle = clk_get(NULL, "mailboxes_ick"); + if (IS_ERR(mbox_ick_handle)) { + printk("Could not get mailboxes_ick\n"); + return -ENODEV; + } + clk_enable(mbox_ick_handle); + + /* set smart-idle & autoidle */ + l = mbox_read_reg(MAILBOX_SYSCONFIG); + l |= 0x00000011; + mbox_write_reg(l, MAILBOX_SYSCONFIG); + + return 0; +} + +static inline void omap2_mbox_shutdown(struct omap_mbox *mbox) +{ + clk_disable(mbox_ick_handle); + clk_put(mbox_ick_handle); +} + +/* Mailbox FIFO handle functions */ +static inline mbox_msg_t omap2_mbox_fifo_read(struct omap_mbox *mbox) +{ + struct omap_mbox2_fifo *fifo = &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo; + return (mbox_msg_t) mbox_read_reg(fifo->msg); +} + +static inline void omap2_mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) +{ + struct omap_mbox2_fifo *fifo = &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo; + mbox_write_reg(msg, fifo->msg); +} + +static inline int omap2_mbox_fifo_empty(struct omap_mbox *mbox) +{ + struct omap_mbox2_fifo *fifo = &((struct omap_mbox2_priv *)mbox->priv)->rx_fifo; + return (mbox_read_reg(fifo->msg_stat) == 0); +} + +static inline int omap2_mbox_fifo_full(struct omap_mbox *mbox) +{ + struct omap_mbox2_fifo *fifo = &((struct omap_mbox2_priv *)mbox->priv)->tx_fifo; + return (mbox_read_reg(fifo->fifo_stat)); +} + +/* Mailbox IRQ handle functions */ +static inline void omap2_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)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); +} + +static inline void omap2_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)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); +} + +static inline void omap2_mbox_ack_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)mbox->priv; + u32 bit = (irq == IRQ_TX) ? p->notfull_bit : p->newmsg_bit; + + mbox_write_reg(bit, p->irqstatus); +} + +static inline int omap2_mbox_is_irq(struct omap_mbox *mbox, omap_mbox_type_t irq) +{ + struct omap_mbox2_priv *p = (struct omap_mbox2_priv *)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 (enable & status & bit); +} + +struct omap_mbox_ops omap2_mbox_ops = { + .type = OMAP_MBOX_TYPE2, + .startup = omap2_mbox_startup, + .shutdown = omap2_mbox_shutdown, + .fifo_read = omap2_mbox_fifo_read, + .fifo_write = omap2_mbox_fifo_write, + .fifo_empty = omap2_mbox_fifo_empty, + .fifo_full = omap2_mbox_fifo_full, + .enable_irq = omap2_mbox_enable_irq, + .disable_irq = omap2_mbox_disable_irq, + .ack_irq = omap2_mbox_ack_irq, + .is_irq = omap2_mbox_is_irq, +}; + +/* + * MAILBOX 0: ARM -> DSP, + * MAILBOX 1: ARM <- DSP. + * MAILBOX 2: ARM -> IVA, + * MAILBOX 3: ARM <- IVA. + */ + +/* FIXME: the following structs should be filled automatically by the user id */ + +/* DSP */ +static struct omap_mbox2_priv omap2_mbox_dsp_priv = { + .tx_fifo = { + .msg = MAILBOX_MESSAGE_0, + .fifo_stat = MAILBOX_FIFOSTATUS_0, + }, + .rx_fifo = { + .msg = MAILBOX_MESSAGE_1, + .msg_stat = MAILBOX_MSGSTATUS_1, + }, + .irqenable = MAILBOX_IRQENABLE_0, + .irqstatus = MAILBOX_IRQSTATUS_0, + .notfull_bit = MAILBOX_IRQ_NOTFULL(0), + .newmsg_bit = MAILBOX_IRQ_NEWMSG(1), +}; + +struct omap_mbox mbox_dsp_info = { + .name = "dsp", + .ops = &omap2_mbox_ops, + .priv = &omap2_mbox_dsp_priv, +}; +EXPORT_SYMBOL(mbox_dsp_info); + +/* IVA */ +static struct omap_mbox2_priv omap2_mbox_iva_priv = { + .tx_fifo = { + .msg = MAILBOX_MESSAGE_2, + .fifo_stat = MAILBOX_FIFOSTATUS_2, + }, + .rx_fifo = { + .msg = MAILBOX_MESSAGE_3, + .msg_stat = MAILBOX_MSGSTATUS_3, + }, + .irqenable = MAILBOX_IRQENABLE_3, + .irqstatus = MAILBOX_IRQSTATUS_3, + .notfull_bit = MAILBOX_IRQ_NOTFULL(2), + .newmsg_bit = MAILBOX_IRQ_NEWMSG(3), +}; + +static struct omap_mbox mbox_iva_info = { + .name = "iva", + .ops = &omap2_mbox_ops, + .priv = &omap2_mbox_iva_priv, +}; + +static int __init omap2_mbox_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = 0; + + if (pdev->num_resources != 3) { + dev_err(&pdev->dev, "invalid number of resources: %d\n", + pdev->num_resources); + return -ENODEV; + } + + /* MBOX base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid mem resource\n"); + return -ENODEV; + } + mbox_base = res->start; + + /* DSP IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid irq resource\n"); + return -ENODEV; + } + mbox_dsp_info.irq = res->start; + + ret = omap_mbox_register(&mbox_dsp_info); + + /* IVA IRQ */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid irq resource\n"); + return -ENODEV; + } + mbox_iva_info.irq = res->start; + + ret = omap_mbox_register(&mbox_iva_info); + + return ret; +} + +static int omap2_mbox_remove(struct platform_device *pdev) +{ + omap_mbox_unregister(&mbox_dsp_info); + return 0; +} + +static struct platform_driver omap2_mbox_driver = { + .probe = omap2_mbox_probe, + .remove = omap2_mbox_remove, + .driver = { + .name = "mailbox", + }, +}; + +int __init omap2_mbox_init(void) +{ + return platform_driver_register(&omap2_mbox_driver); +} + +static void __exit omap2_mbox_exit(void) +{ + platform_driver_unregister(&omap2_mbox_driver); +} + +module_init(omap2_mbox_init); +module_exit(omap2_mbox_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c new file mode 100644 index 0000000..723c92f --- /dev/null +++ b/arch/arm/plat-omap/mailbox.c @@ -0,0 +1,352 @@ +/* + * OMAP mailbox driver + * + * Copyright (C) 2006 Nokia Corporation. All rights reserved. + * + * Contact: Toshihiro Kobayashi + * Restructured by Hiroshi DOYU + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mailbox.h" + +static struct omap_mbox *mboxes; +static DEFINE_RWLOCK(mboxes_lock); + +static struct omap_mbox **find_mboxes(const char *name) +{ + struct omap_mbox **p; + + for (p = &mboxes; *p; p = &(*p)->next) { + if (strcmp((*p)->name, name) == 0) + break; + } + + return p; +} + +struct omap_mbox *omap_mbox_get(const char *name) +{ + struct omap_mbox *mbox; + + read_lock(&mboxes_lock); + mbox = *(find_mboxes(name)); + read_unlock(&mboxes_lock); + + return mbox; +} +EXPORT_SYMBOL(omap_mbox_get); + +/* Mailbox Sequence Bit function */ +void omap_mbox_init_seq(struct omap_mbox *mbox) +{ + mbox_seq_init(mbox); +} +EXPORT_SYMBOL(omap_mbox_init_seq); + +/* + * message sender + */ +int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg) +{ + int ret; + int i = 1000; + static DEFINE_MUTEX(msg_send_lock); + + while (mbox_fifo_full(mbox)) { + if (mbox->ops->type == OMAP_MBOX_TYPE2) { + enable_mbox_irq(mbox, IRQ_TX); + wait_event_interruptible(mbox->tx_waitq, + !mbox_fifo_full(mbox)); + } else + udelay(1); + + if (--i == 0) + return -1; + } + + + mutex_lock(&msg_send_lock); + + if (mbox->msg_sender_cb && arg) { + ret = mbox->msg_sender_cb(arg); + if (ret) + goto out; + } + + mbox_seq_toggle(mbox, &msg); + mbox_fifo_write(mbox, msg); + out: + mutex_unlock(&msg_send_lock); + + return 0; +} +EXPORT_SYMBOL(omap_mbox_msg_send); + +/* + * Message receiver(workqueue) + */ +static void mbox_msg_receiver(void *p) +{ + struct omap_mbox *mbox = (struct omap_mbox *)p; + struct omap_mbq *mbq = mbox->mbq; + mbox_msg_t msg; + int was_full; + + while (!mbq_empty(mbq)) { + was_full = mbq_full(mbq); + msg = mbq_get(mbq); + if (was_full) /* now we have a room in the mbq. */ + enable_mbox_irq(mbox, IRQ_RX); + + if (unlikely(mbox_seq_test(mbox, msg))) { + printk(KERN_ERR + "mbox: illegal seq bit! ignoring this command. " + "(%08x)\n", msg); + continue; + } + + if (likely(mbox->msg_receive_cb)) + mbox->msg_receive_cb(msg); + } +} + +/* + * Mailbox interrupt handler + */ +static irqreturn_t mbox_interrupt(int irq, void *p) +{ + mbox_msg_t msg; + struct omap_mbox *mbox = (struct omap_mbox *)p; + + if (is_mbox_irq(mbox, IRQ_TX)) { + disable_mbox_irq(mbox, IRQ_TX); + /* + * NOTE: this doesn't seeem to work as explained in the manual. + * IRQSTATUS:NOTFULL can't be cleared even we write 1 to that bit. + * It is always set when it's not full, regardless of IRQENABLE setting. + */ + ack_mbox_irq(mbox, IRQ_TX); + wake_up_interruptible_all(&mbox->tx_waitq); + } + + if (!is_mbox_irq(mbox, IRQ_RX)) + return IRQ_HANDLED; + + while (!mbox_fifo_empty(mbox)) { + msg = mbox_fifo_read(mbox); + if (mbq_add(mbox->mbq, msg)) { /* mbq full */ + disable_mbox_irq(mbox, IRQ_RX); + goto flush_queue; + } + if (mbox->ops->type == OMAP_MBOX_TYPE1) + break; + } + + /* no more messages in the fifo. clear IRQ source. */ + ack_mbox_irq(mbox, IRQ_RX); + flush_queue: + schedule_work(&mbox->msg_receive); + + return IRQ_HANDLED; +} + +/* + * sysfs files + */ +static ssize_t mbox_attr_write(struct class_device *dev, const char *buf, + size_t count) +{ + int ret; + mbox_msg_t msg; + struct omap_mbox *mbox = class_get_devdata(dev); + + msg = (mbox_msg_t) simple_strtoul(buf, NULL, 16); + + ret = omap_mbox_msg_send(mbox, msg, NULL); + if (ret) + return -1; + + return count; +} + +static ssize_t mbox_attr_read(struct class_device *dev, char *buf) +{ + struct omap_mbox *mbox = class_get_devdata(dev); + + return sprintf(buf, mbox->name); +} + +static CLASS_DEVICE_ATTR(mbox, S_IALLUGO, mbox_attr_read, mbox_attr_write); + +static ssize_t mbox_show(struct class *class, char *buf) +{ + return sprintf(buf, "mbox"); +} + +static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL); + +static struct class omap_mbox_class = { + .name = "mbox", +}; + +static int omap_mbox_init(struct omap_mbox *mbox) +{ + int ret; + + if (likely(mbox->ops->startup)) { + ret = mbox->ops->startup(mbox); + if (unlikely(ret)) + return ret; + } + + mbox->class_dev.class = &omap_mbox_class; + strlcpy(mbox->class_dev.class_id, mbox->name, KOBJ_NAME_LEN); + class_set_devdata(&mbox->class_dev, mbox); + + ret = class_device_register(&mbox->class_dev); + if (unlikely(ret)) + return ret; + + ret = class_device_create_file(&mbox->class_dev, &class_device_attr_mbox); + if (unlikely(ret)) { + printk(KERN_ERR + "class_device_create_file failed: %d\n", ret); + goto fail1; + } + + spin_lock_init(&mbox->lock); + INIT_WORK(&mbox->msg_receive, mbox_msg_receiver, mbox); + init_waitqueue_head(&mbox->tx_waitq); + + ret = mbq_init(&mbox->mbq); + if (unlikely(ret)) + goto fail2; + + ret = request_irq(mbox->irq, mbox_interrupt, SA_INTERRUPT, + mbox->name, mbox); + if (unlikely(ret)) { + printk(KERN_ERR + "failed to register mailbox interrupt:%d\n", ret); + goto fail3; + } + disable_mbox_irq(mbox, IRQ_RX); + enable_mbox_irq(mbox, IRQ_RX); + + return 0; + + fail3: + kfree(mbox->mbq); + fail2: + class_remove_file(&omap_mbox_class, &class_attr_mbox); + fail1: + class_unregister(&omap_mbox_class); + if (unlikely(mbox->ops->shutdown)) + mbox->ops->shutdown(mbox); + + return ret; +} + +static void omap_mbox_shutdown(struct omap_mbox *mbox) +{ + free_irq(mbox->irq, mbox); + kfree(mbox->mbq); + class_remove_file(&omap_mbox_class, &class_attr_mbox); + class_unregister(&omap_mbox_class); + + if (unlikely(mbox->ops->shutdown)) + mbox->ops->shutdown(mbox); +} + +int omap_mbox_register(struct omap_mbox *mbox) +{ + int ret = 0; + struct omap_mbox **tmp; + + if (!mbox) + return -EINVAL; + if (mbox->next) + return -EBUSY; + + ret = omap_mbox_init(mbox); + if (ret) + return ret; + + write_lock(&mboxes_lock); + tmp = find_mboxes(mbox->name); + if (*tmp) + ret = -EBUSY; + else + *tmp = mbox; + write_unlock(&mboxes_lock); + + return ret; +} +EXPORT_SYMBOL(omap_mbox_register); + +int omap_mbox_unregister(struct omap_mbox *mbox) +{ + struct omap_mbox **tmp; + + write_lock(&mboxes_lock); + tmp = &mboxes; + while (*tmp) { + if (mbox == *tmp) { + *tmp = mbox->next; + mbox->next = NULL; + write_unlock(&mboxes_lock); + + omap_mbox_shutdown(mbox); + + return 0; + } + tmp = &(*tmp)->next; + } + write_unlock(&mboxes_lock); + + return -EINVAL; +} +EXPORT_SYMBOL(omap_mbox_unregister); + +static int __init omap_mbox_class_init(void) +{ + int ret = class_register(&omap_mbox_class); + if (!ret) + ret = class_create_file(&omap_mbox_class, &class_attr_mbox); + + return ret; +} + +static void __exit omap_mbox_class_exit(void) +{ + class_remove_file(&omap_mbox_class, &class_attr_mbox); + class_unregister(&omap_mbox_class); +} + +subsys_initcall(omap_mbox_class_init); +module_exit(omap_mbox_class_exit); + +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/mailbox.h b/arch/arm/plat-omap/mailbox.h new file mode 100644 index 0000000..c3f8f2c --- /dev/null +++ b/arch/arm/plat-omap/mailbox.h @@ -0,0 +1,193 @@ +/* + * Mailbox internal functions + * + * Copyright (C) 2006 Nokia Corporation + * Written by: Hiroshi DOYU + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#ifndef __ARCH_ARM_PLAT_MAILBOX_H +#define __ARCH_ARM_PLAT_MAILBOX_H + +/* + * Mailbox queue handling API + */ + +#define MBQ_DEPTH 16 +struct omap_mbq { + rwlock_t lock; + mbox_msg_t msg[MBQ_DEPTH]; + mbox_msg_t *rp, *wp; + int cnt; +}; + +static inline int mbq_init(struct omap_mbq **addr) +{ + struct omap_mbq *m = kmalloc(sizeof(struct omap_mbq), GFP_KERNEL); + if (!m) + return -ENOMEM; + + rwlock_init(&m->lock); + + write_lock_irq(&m->lock); + m->rp = m->wp = &m->msg[0]; + m->cnt = 0; + write_unlock_irq(&m->lock); + + *addr = m; + + return 0; +} + +static inline int mbq_empty(struct omap_mbq *mbq) +{ + int ret; + + read_lock_irq(&mbq->lock); + ret = (mbq->cnt == 0); + read_unlock_irq(&mbq->lock); + + return ret; +} + +static inline int mbq_full(struct omap_mbq *mbq) +{ + int ret; + + read_lock_irq(&mbq->lock); + ret = (mbq->cnt == MBQ_DEPTH); + read_unlock_irq(&mbq->lock); + + return ret; +} + +static inline int mbq_add(struct omap_mbq *mbq, mbox_msg_t msg) +{ + int ret = 0; + + write_lock_irq(&mbq->lock); + + *mbq->wp = msg; + if (++mbq->wp == &mbq->msg[MBQ_DEPTH]) + mbq->wp = &mbq->msg[0]; + + if (++mbq->cnt == MBQ_DEPTH) /* full */ + ret = -1; + + write_unlock_irq(&mbq->lock); + + return ret; +} + +static inline mbox_msg_t mbq_get(struct omap_mbq *mbq) +{ + mbox_msg_t msg; + + write_lock_irq(&mbq->lock); + + msg = *mbq->rp; + + if (++mbq->rp == &mbq->msg[MBQ_DEPTH]) + mbq->rp = &mbq->msg[0]; + mbq->cnt--; + + write_unlock_irq(&mbq->lock); + + return msg; +} + +static inline void mbq_exit(struct omap_mbq **addr) +{ + if (*addr) + kfree(*addr); +} + +/* + * Mailbox sequence bit API + */ +#if defined(CONFIG_ARCH_OMAP1) +# define MBOX_USE_SEQ_BIT +#elif defined(CONFIG_ARCH_OMAP2) +# define MBOX_USE_SEQ_BIT +#endif + +#ifdef MBOX_USE_SEQ_BIT +/* seq_rcv should be initialized with any value other than + * 0 and 1 << 31, to allow either value for the first + * message. */ +static inline void mbox_seq_init(struct omap_mbox *mbox) +{ + /* any value other than 0 and 1 << 31 */ + mbox->seq_rcv = 0xffffffff; +} + +static inline void mbox_seq_toggle(struct omap_mbox *mbox, mbox_msg_t * msg) +{ + /* add seq_snd to msg */ + *msg = (*msg & 0x7fffffff) | mbox->seq_snd; + /* flip seq_snd */ + mbox->seq_snd ^= 1 << 31; +} + +static inline int mbox_seq_test(struct omap_mbox *mbox, mbox_msg_t msg) +{ + mbox_msg_t seq = msg & (1 << 31); + if (seq == mbox->seq_rcv) + return -1; + mbox->seq_rcv = seq; + return 0; +} +#else +static inline void mbox_seq_init(struct omap_mbox *mbox) +{ +} +static inline void mbox_seq_toggle(struct omap_mbox *mbox, mbox_msg_t * msg) +{ +} +static inline int mbox_seq_test(struct omap_mbox *mbox, mbox_msg_t msg) +{ + return 0; +} +#endif + +/* 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) +{ + return mbox->ops->fifo_full(mbox); +} + +/* Mailbox IRQ handle functions */ +static inline void enable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + mbox->ops->enable_irq(mbox, irq); +} +static inline void disable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) +{ + mbox->ops->disable_irq(mbox, irq); +} +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) +{ + return mbox->ops->is_irq(mbox, irq); +} + +#endif /* __ARCH_ARM_PLAT_MAILBOX_H */ diff --git a/include/asm-arm/arch-omap/mailbox.h b/include/asm-arm/arch-omap/mailbox.h new file mode 100644 index 0000000..66669f0 --- /dev/null +++ b/include/asm-arm/arch-omap/mailbox.h @@ -0,0 +1,68 @@ +/* mailbox.h */ + +#ifndef MAILBOX_H +#define MAILBOX_H + +#include +#include + +typedef u32 mbox_msg_t; +typedef void (mbox_receiver_t)(mbox_msg_t msg); +struct omap_mbox; +struct omap_mbq; + +typedef int __bitwise omap_mbox_irq_t; +#define IRQ_TX ((__force omap_mbox_irq_t) 1) +#define IRQ_RX ((__force omap_mbox_irq_t) 2) + +typedef int __bitwise omap_mbox_type_t; +#define OMAP_MBOX_TYPE1 ((__force omap_mbox_type_t) 1) +#define OMAP_MBOX_TYPE2 ((__force omap_mbox_type_t) 2) + +struct omap_mbox_ops { + omap_mbox_type_t type; + int (*startup)(struct omap_mbox *mbox); + void (*shutdown)(struct omap_mbox *mbox); + /* fifo */ + mbox_msg_t (*fifo_read)(struct omap_mbox *mbox); + void (*fifo_write)(struct omap_mbox *mbox, mbox_msg_t msg); + int (*fifo_empty)(struct omap_mbox *mbox); + int (*fifo_full)(struct omap_mbox *mbox); + /* irq */ + void (*enable_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); + void (*disable_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); + void (*ack_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); + int (*is_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); +}; + +struct omap_mbox { + char *name; + spinlock_t lock; + unsigned int irq; + struct omap_mbox_ops *ops; + + wait_queue_head_t tx_waitq; + + struct work_struct msg_receive; + void (*msg_receive_cb)(mbox_msg_t); + struct omap_mbq *mbq; + + int (*msg_sender_cb)(void*); + + mbox_msg_t seq_snd, seq_rcv; + + struct class_device class_dev; + + void *priv; + + struct omap_mbox *next; +}; + +int omap_mbox_msg_send(struct omap_mbox *mbox_h, mbox_msg_t msg, void* arg); +void omap_mbox_init_seq(struct omap_mbox *mbox); + +struct omap_mbox *omap_mbox_get(const char *name); +int omap_mbox_register(struct omap_mbox *mbox); +int omap_mbox_unregister(struct omap_mbox *mbox); + +#endif /* MAILBOX_H */ -- 1.4.4.2