Commit 4b35036f authored by Hiroshi DOYU's avatar Hiroshi DOYU Committed by Juha Yrjola

ARM: OMAP: mailbox restructure

Mailbox is restructured as below:

- OMAP1 and OMAP2 parts split out to mach-omap[1-2] respectively.
- struct mbx isolation
- Introduced device model
- Added class interface

Moved omap[1,2] specific parts in mach-omap[1,2]/
Signed-off-by: default avatarHiroshi DOYU <Hiroshi.DOYU@nokia.com>
Signed-off-by: default avatarJuha Yrjola <juha.yrjola@solidboot.com>
parent 09928167
...@@ -34,12 +34,13 @@ ...@@ -34,12 +34,13 @@
#include "dsp_mbcmd.h" #include "dsp_mbcmd.h"
#include "dsp.h" #include "dsp.h"
#include "ipbuf.h" #include "ipbuf.h"
#include "dsp_common.h"
MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>"); MODULE_AUTHOR("Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>");
MODULE_DESCRIPTION("OMAP DSP driver module"); MODULE_DESCRIPTION("OMAP DSP driver module");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
struct mbox *mbox_dsp; struct omap_mbox *mbox_dsp;
static struct sync_seq *mbseq; static struct sync_seq *mbseq;
static u16 mbseq_expect_tmp; static u16 mbseq_expect_tmp;
static u16 *mbseq_expect = &mbseq_expect_tmp; static u16 *mbseq_expect = &mbseq_expect_tmp;
...@@ -204,7 +205,7 @@ int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg, ...@@ -204,7 +205,7 @@ int __dsp_mbcmd_send_exarg(struct mbcmd *mb, struct mb_exarg *arg,
mblog_add(mb, DIR_A2D); mblog_add(mb, DIR_A2D);
ret = mbox_send(mbox_dsp, *(mbox_msg_t *)mb); ret = omap_mbox_msg_send(mbox_dsp, *(mbox_msg_t *)mb);
out: out:
mutex_unlock(&mbsend_lock); mutex_unlock(&mbsend_lock);
...@@ -261,7 +262,7 @@ static int mbsync_hold_mem_active; ...@@ -261,7 +262,7 @@ static int mbsync_hold_mem_active;
void dsp_mbox_start(void) void dsp_mbox_start(void)
{ {
mbox_init_seq(mbox_dsp); omap_mbox_init_seq(mbox_dsp);
mbseq_expect_tmp = 0; mbseq_expect_tmp = 0;
} }
...@@ -301,34 +302,20 @@ int dsp_mbox_config(void *p) ...@@ -301,34 +302,20 @@ int dsp_mbox_config(void *p)
static int __init dsp_mbox_init(void) static int __init dsp_mbox_init(void)
{ {
int i; mbox_dsp->mbox = omap_mbox_get("dsp");
int ret; if (IS_ERR(mbox_dsp)) {
printk(KERN_ERR "failed to get mailbox handler for DSP.\n");
for (i = 0; i < MBOX_CMD_MAX; i++) { return -ENODEV;
if (cmdinfo[i] != NULL) {
ret = register_mbox_receiver(mbox_dsp, i, mbcmd_receiver);
if (ret)
goto fail;
}
} }
return 0; mbox_dsp->mbox->msg_receive_cb = mbcmd_receiver;
fail: return 0;
for (i--; i; i--)
unregister_mbox_receiver(mbox_dsp, i, mbcmd_receiver);
return ret;
} }
static void dsp_mbox_exit(void) static void dsp_mbox_exit(void)
{ {
int i; mbox_dsp->mbox->msg_receive_cb = NULL;
for (i = 0; i < MBOX_CMD_MAX; i++) {
if (cmdinfo[i] != NULL)
unregister_mbox_receiver(mbox_dsp, i, mbcmd_receiver);
}
if (mbsync_hold_mem_active) { if (mbsync_hold_mem_active) {
dsp_mem_disable((void *)daram_base); dsp_mem_disable((void *)daram_base);
......
...@@ -21,6 +21,36 @@ ...@@ -21,6 +21,36 @@
* *
*/ */
/*
* mailbox command: 0x00 - 0x7f
* when a driver wants to use mailbox, it must reserve mailbox commands here.
*/
#define MBOX_CMD_DSP_WDSND 0x10
#define MBOX_CMD_DSP_WDREQ 0x11
#define MBOX_CMD_DSP_BKSND 0x20
#define MBOX_CMD_DSP_BKREQ 0x21
#define MBOX_CMD_DSP_BKYLD 0x23
#define MBOX_CMD_DSP_BKSNDP 0x24
#define MBOX_CMD_DSP_BKREQP 0x25
#define MBOX_CMD_DSP_TCTL 0x30
#define MBOX_CMD_DSP_TCTLDATA 0x31
#define MBOX_CMD_DSP_POLL 0x32
#define MBOX_CMD_DSP_WDT 0x50
#define MBOX_CMD_DSP_RUNLEVEL 0x51
#define MBOX_CMD_DSP_PM 0x52
#define MBOX_CMD_DSP_SUSPEND 0x53
#define MBOX_CMD_DSP_KFUNC 0x54
#define MBOX_CMD_DSP_TCFG 0x60
#define MBOX_CMD_DSP_TADD 0x62
#define MBOX_CMD_DSP_TDEL 0x63
#define MBOX_CMD_DSP_TSTOP 0x65
#define MBOX_CMD_DSP_DSPCFG 0x70
#define MBOX_CMD_DSP_REGRW 0x72
#define MBOX_CMD_DSP_GETVAR 0x74
#define MBOX_CMD_DSP_SETVAR 0x75
#define MBOX_CMD_DSP_ERR 0x78
#define MBOX_CMD_DSP_DBG 0x79
/* /*
* DSP mailbox protocol definitions * DSP mailbox protocol definitions
*/ */
......
...@@ -44,7 +44,6 @@ ...@@ -44,7 +44,6 @@
#include <asm/arch/dsp_common.h> #include <asm/arch/dsp_common.h>
#include "uaccess_dsp.h" #include "uaccess_dsp.h"
#include "dsp_mbcmd.h" #include "dsp_mbcmd.h"
#include "../mailbox_hw.h"
#include "dsp.h" #include "dsp.h"
#include "ioctl.h" #include "ioctl.h"
#include "ipbuf.h" #include "ipbuf.h"
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Copyright (C) 2006 Nokia Corporation. All rights reserved. * Copyright (C) 2006 Nokia Corporation. All rights reserved.
* *
* Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com> * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
* Restructured by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -25,550 +26,313 @@ ...@@ -25,550 +26,313 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#ifdef CONFIG_ARCH_OMAP2
#include <linux/clk.h>
#endif
#include <asm/io.h> #include <asm/io.h>
#ifdef CONFIG_ARCH_OMAP1
#include <asm/delay.h> #include <asm/delay.h>
#endif
#include <asm/arch/mailbox.h> #include <asm/arch/mailbox.h>
#include "mailbox_hw.h" #include "mailbox.h"
#if defined(CONFIG_ARCH_OMAP1)
#define read_mbox(m, msgp) \
do { \
*(msgp) = omap_readw((m)->data_r); \
*(msgp) |= ((mbox_msg_t)omap_readw((m)->cmd_r)) << 16; \
} while (0)
#define write_mbox(m, msg) \
do { \
omap_writew((msg) & 0xffff, (m)->data_w); \
omap_writew((msg) >> 16, (m)->cmd_w); \
} while (0)
#define enable_newmsg_irq(m) enable_irq((m)->irq)
#define disable_newmsg_irq(m) disable_irq((m)->irq)
#define mbox_is_notfull(m) (omap_readw((m)->flag_w) == 0)
#elif defined(CONFIG_ARCH_OMAP2)
#define omap_bit_setl(b,r) \
do { omap_writel(omap_readl(r) | (b), (r)); } while(0)
#define omap_bit_clrl(b,r) \
do { omap_writel(omap_readl(r) & ~(b), (r)); } while(0)
#define read_mbox(m, msgp) \
do { *(msgp) = omap_readl((m)->message_r); } while (0)
#define write_mbox(m, msg) omap_writel(msg, (m)->message_w)
#define enable_newmsg_irq(m) omap_bit_setl((m)->newmsg_bit, (m)->irqenable)
#define disable_newmsg_irq(m) omap_bit_clrl((m)->newmsg_bit, (m)->irqenable)
#define enable_notfull_irq(m) omap_bit_setl((m)->notfull_bit, (m)->irqenable)
#define disable_notfull_irq(m) omap_bit_clrl((m)->notfull_bit, (m)->irqenable)
#define clear_newmsg_irq(m) omap_writel((m)->newmsg_bit, (m)->irqstatus)
#define clear_notfull_irq(m) omap_writel((m)->notfull_bit, (m)->irqstatus)
#define notfull_irq_enabled(m) (omap_readl((m)->irqenable) & (m)->notfull_bit)
#define has_newmsg_irq(m) (omap_readl((m)->irqstatus) & (m)->newmsg_bit)
#define has_notfull_irq(m) (omap_readl((m)->irqstatus) & (m)->notfull_bit)
#define mbox_nomsg(m) (omap_readl((m)->msgstatus_r) == 0)
#define mbox_is_notfull(m) (omap_readl((m)->fifostatus_w) == 0)
#endif /* CONFIG_ARCH_OMAP2 */
static void do_mbox(void *p);
#define MBQ_DEPTH 16
struct mbq {
mbox_msg_t msg[MBQ_DEPTH];
int rp, wp, full;
};
#define mbq_inc(p) do { if (++(p) == MBQ_DEPTH) (p) = 0; } while(0) static struct omap_mbox *mboxes;
static DEFINE_RWLOCK(mboxes_lock);
#if defined(CONFIG_ARCH_OMAP1)
# define MBOX_USE_SEQ_BIT /* XXX */
#elif defined(CONFIG_ARCH_OMAP2)
# undef MBOX_USE_SEQ_BIT
#endif
struct mbox {
char *name;
unsigned int irq;
char irq_devid_newmsg;
#ifdef CONFIG_ARCH_OMAP2
char irq_devid_notfull;
#endif
#ifdef MBOX_USE_SEQ_BIT
mbox_msg_t seq_snd;
mbox_msg_t seq_rcv;
/* seq_rcv should be initialized with any value other than
* 0 and 1 << 31, to allow either value for the first
* message. */
#endif
mbox_receiver_t *receiver_map[MBOX_CMD_MAX];
struct work_struct work;
struct mbq mbq;
#ifdef CONFIG_ARCH_OMAP2
wait_queue_head_t full_wait_q;
#endif
#if defined(CONFIG_ARCH_OMAP1)
void *cmd_w;
void *data_w;
void *flag_w;
void *cmd_r;
void *data_r;
#elif defined(CONFIG_ARCH_OMAP2)
void *irqenable;
void *irqstatus;
void *message_w;
void *message_r;
void *fifostatus_w;
void *msgstatus_r;
u32 notfull_bit;
u32 newmsg_bit;
#endif
};
#if defined(CONFIG_ARCH_OMAP1) static struct omap_mbox **find_mboxes(const char *name)
{
struct omap_mbox **p;
#if defined(CONFIG_ARCH_OMAP15XX) for (p = &mboxes; *p; p = &(*p)->next) {
#define INT_DSP_MAILBOX1 INT_1510_DSP_MAILBOX1 if (strcmp((*p)->name, name) == 0)
#elif defined(CONFIG_ARCH_OMAP16XX) break;
#define INT_DSP_MAILBOX1 INT_1610_DSP_MAILBOX1 }
#endif
static struct mbox mbox_dsp = { return p;
.name = "DSP", }
.irq = INT_DSP_MAILBOX1,
.work = __WORK_INITIALIZER(mbox_dsp.work, do_mbox, &mbox_dsp),
.cmd_w = (void *)MAILBOX_ARM2DSP1b, struct omap_mbox *omap_mbox_get(const char *name)
.data_w = (void *)MAILBOX_ARM2DSP1, {
.flag_w = (void *)MAILBOX_ARM2DSP1_Flag, struct omap_mbox *mbox;
.cmd_r = (void *)MAILBOX_DSP2ARM1b,
.data_r = (void *)MAILBOX_DSP2ARM1,
};
#elif defined(CONFIG_ARCH_OMAP2) 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);
/* /*
* MAILBOX 0: ARM -> DSP, * message sender
* MAILBOX 1: ARM <- DSP.
* MAILBOX 2: ARM -> IVA,
* MAILBOX 3: ARM <- IVA.
*/ */
static struct mbox mbox_dsp = { int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg)
.name = "DSP", {
.irq = INT_24XX_MAIL_U0_MPU, int ret;
.work = __WORK_INITIALIZER(mbox_dsp.work, do_mbox, &mbox_dsp), int i = 1000;
.full_wait_q = __WAIT_QUEUE_HEAD_INITIALIZER(mbox_dsp.full_wait_q), static DEFINE_MUTEX(msg_send_lock);
.irqenable = (void *)MAILBOX_IRQENABLE_0, while (mbox_fifo_full(mbox)) {
.irqstatus = (void *)MAILBOX_IRQSTATUS_0, if (mbox->ops->type == OMAP_MBOX_TYPE2) {
.message_w = (void *)MAILBOX_MESSAGE_0, enable_mbox_irq(mbox, IRQ_TX);
.message_r = (void *)MAILBOX_MESSAGE_1, wait_event_interruptible(mbox->tx_waitq,
.fifostatus_w = (void *)MAILBOX_FIFOSTATUS_0, !mbox_fifo_full(mbox));
.msgstatus_r = (void *)MAILBOX_MSGSTATUS_1, } else
.notfull_bit = MAILBOX_IRQ_NOTFULL(0), udelay(1);
.newmsg_bit = MAILBOX_IRQ_NEWMSG(1),
}; if (--i == 0)
static struct mbox mbox_iva = { return -1;
.name = "IVA", }
.irq = INT_24XX_MAIL_U3_MPU,
.work = __WORK_INITIALIZER(mbox_iva.work, do_mbox, &mbox_iva),
.full_wait_q = __WAIT_QUEUE_HEAD_INITIALIZER(mbox_iva.full_wait_q),
.irqenable = (void *)MAILBOX_IRQENABLE_3,
.irqstatus = (void *)MAILBOX_IRQSTATUS_3,
.message_w = (void *)MAILBOX_MESSAGE_2,
.message_r = (void *)MAILBOX_MESSAGE_3,
.fifostatus_w = (void *)MAILBOX_FIFOSTATUS_2,
.msgstatus_r = (void *)MAILBOX_MSGSTATUS_3,
.notfull_bit = MAILBOX_IRQ_NOTFULL(2),
.newmsg_bit = MAILBOX_IRQ_NEWMSG(3),
};
#endif /* CONFIG_ARCH_OMAP2 */
static struct mbox *mboxes[] = {
&mbox_dsp,
#ifdef CONFIG_ARCH_OMAP2
&mbox_iva,
#endif
};
struct mbox *mbox_get(const char *id) mutex_lock(&msg_send_lock);
{
int i;
for (i = 0; i < ARRAY_SIZE(mboxes); i++) { if (mbox->msg_sender_cb && arg) {
if (!strcmp(id, mboxes[i]->name)) ret = mbox->msg_sender_cb(arg);
return mboxes[i]; if (ret)
goto out;
} }
return ERR_PTR(-ENOENT); mbox_seq_toggle(mbox, &msg);
mbox_fifo_write(mbox, msg);
out:
mutex_unlock(&msg_send_lock);
return 0;
} }
EXPORT_SYMBOL(omap_mbox_msg_send);
#if defined(CONFIG_ARCH_OMAP1) /*
static __inline__ int mbsync_irq_save(struct mbox *mbox, unsigned long *flags, * Message receiver(workqueue)
int try_cnt) */
static void mbox_msg_receiver(void *p)
{ {
int cnt; struct omap_mbox *mbox = (struct omap_mbox *)p;
struct omap_mbq *mbq = mbox->mbq;
local_irq_save(*flags); mbox_msg_t msg;
if (mbox_is_notfull(mbox))
return 0;
/*
* mailbox is busy. wait for some usecs...
*/
local_irq_restore(*flags);
for (cnt = 0; cnt < try_cnt; cnt++) {
udelay(1);
local_irq_save(*flags);
if (mbox_is_notfull(mbox)) /* success! */
return 0;
local_irq_restore(*flags);
}
/* fail! */ while (!mbq_empty(mbq)) {
return -1; msg = mbq_get(mbq);
} enable_mbox_irq(mbox, IRQ_RX);
#elif defined(CONFIG_ARCH_OMAP2)
static __inline__ int mbsync_irq_save(struct mbox *mbox, unsigned long *flags)
{
long current_state;
DECLARE_WAITQUEUE(wait, current);
do { if (unlikely(mbox_seq_test(mbox, msg))) {
local_irq_save(*flags); printk(KERN_ERR
if (mbox_is_notfull(mbox)) "mbox: illegal seq bit! ignoring this command. "
return 0; "(%08x)\n", msg);
continue;
}
/* if (likely(mbox->msg_receive_cb))
* mailbox is busy. mbox->msg_receive_cb(msg);
*/ }
local_irq_restore(*flags);
enable_notfull_irq(mbox);
/* wait until the FIFO becomes not-full */
add_wait_queue(&mbox->full_wait_q, &wait);
current_state = current->state;
set_current_state(TASK_INTERRUPTIBLE);
if (!mbox_is_notfull(mbox)) /* last check */
schedule();
set_current_state(current_state);
remove_wait_queue(&mbox->full_wait_q, &wait);
if (signal_pending(current))
return -1;
} while (1);
} }
#endif
/* /*
* message dispatcher API * Mailbox interrupt handler
*/ */
int mbox_send(struct mbox *mbox, mbox_msg_t msg) static irqreturn_t mbox_interrupt(int irq, void *p, struct pt_regs *regs)
{ {
unsigned long flags; mbox_msg_t msg;
struct omap_mbox *mbox = (struct omap_mbox *)p;
#if defined(CONFIG_ARCH_OMAP1) if (is_mbox_irq(mbox, IRQ_TX)) {
/* disable_mbox_irq(mbox, IRQ_TX);
* DSP mailbox interrupt latency must be less than 1ms. /*
*/ * NOTE: this doesn't seeem to work as explained in the manual.
if (mbsync_irq_save(mbox, &flags, 1000) < 0) { * IRQSTATUS:NOTFULL can't be cleared even we write 1 to that bit.
printk(KERN_ERR * It is always set when it's not full, regardless of IRQENABLE setting.
"mailbox(%s) is busy. message 0x%08x is aborting.\n", */
mbox->name, msg); ack_mbox_irq(mbox, IRQ_TX);
return -1; wake_up_interruptible_all(&mbox->tx_waitq);
} }
#elif defined(CONFIG_ARCH_OMAP2)
if (mbsync_irq_save(mbox, &flags) < 0)
return -1;
#endif
#ifdef MBOX_USE_SEQ_BIT if (!is_mbox_irq(mbox, IRQ_RX))
/* add seq_snd to msg */ return IRQ_HANDLED;
msg = (msg & 0x7fffffff) | mbox->seq_snd;
/* flip seq_snd */
mbox->seq_snd ^= 1 << 31;
#endif
write_mbox(mbox, msg); 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;
}
local_irq_restore(flags); /* no more messages in the fifo. clear IRQ source. */
return 0; ack_mbox_irq(mbox, IRQ_RX);
flush_queue:
schedule_work(&mbox->msg_receive);
return IRQ_HANDLED;
} }
/* /*
* register / unregister API * sysfs files
*/ */
int register_mbox_receiver(struct mbox *mbox, unsigned char cmd, static ssize_t mbox_attr_write(struct class_device *dev, const char *buf,
mbox_receiver_t *rcv) size_t count)
{ {
if (cmd >= MBOX_CMD_MAX) { int ret;
printk(KERN_ERR "register_mbox_receiver(): " mbox_msg_t msg;
"bad cmd (0x%x)\n", cmd); struct omap_mbox *mbox = class_get_devdata(dev);
return -EINVAL;
}
if (mbox->receiver_map[cmd] != NULL) {
printk(KERN_ERR "register_mbox_receiver(): cmd 0x%x is "
"already reserved.\n", cmd);
return -EINVAL;
}
mbox->receiver_map[cmd] = rcv; msg = (mbox_msg_t) simple_strtoul(buf, NULL, 16);
return 0;
}
int unregister_mbox_receiver(struct mbox *mbox, unsigned char cmd, ret = omap_mbox_msg_send(mbox, msg, NULL);
mbox_receiver_t *rcv) if (ret)
{ return -1;
if (cmd >= MBOX_CMD_MAX) {
printk(KERN_ERR "unregister_mbox_receiver(): "
"bad cmd (0x%x)\n", cmd);
return -EINVAL;
}
if (mbox->receiver_map[cmd] != rcv) {
printk(KERN_ERR "unregister_mbox_receiver(): cmd 0x%x and "
"receiver function mismatch!\n", cmd);
return -EINVAL;
}
mbox->receiver_map[cmd] = NULL; return count;
return 0;
} }
/* static ssize_t mbox_attr_read(struct class_device *dev, char *buf)
* IRQ disable / enable API
*/
void disable_mbox_irq(struct mbox *mbox)
{ {
disable_irq(mbox->irq); struct omap_mbox *mbox = class_get_devdata(dev);
return sprintf(buf, mbox->name);
} }
void enable_mbox_irq(struct mbox *mbox) static CLASS_DEVICE_ATTR(mbox, S_IALLUGO, mbox_attr_read, mbox_attr_write);
static ssize_t mbox_show(struct class *class, char *buf)
{ {
enable_irq(mbox->irq); return sprintf(buf, "mbox");
} }
/* static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL);
* init_seq API
*/
void mbox_init_seq(struct mbox *mbox)
{
#ifdef MBOX_USE_SEQ_BIT
/* backward compatibility */
mbox->seq_snd = 0x80000000;
/* any value other than 0 and 1 << 31 */ static struct class omap_mbox_class = {
mbox->seq_rcv = 0xffffffff; .name = "mbox",
#endif /* MBOX_USE_SEQ_BIT */ };
}
/* static int omap_mbox_init(struct omap_mbox *mbox)
* receiver workqueue
*/
static void do_mbox(void *p)
{ {
int empty = 0; int ret;
struct mbox *mbox = (struct mbox *)p;
struct mbq *mbq = &mbox->mbq;
mbox_receiver_t *receiver;
mbox_msg_t msg;
#ifdef MBOX_USE_SEQ_BIT
mbox_msg_t seq;
#endif
disable_newmsg_irq(mbox); if (likely(mbox->ops->startup)) {
if ((mbq->rp == mbq->wp) && !mbq->full) ret = mbox->ops->startup(mbox);
empty = 1; if (unlikely(ret))
enable_newmsg_irq(mbox); return ret;
}
while (!empty) { mbox->class_dev.class = &omap_mbox_class;
msg = mbq->msg[mbq->rp]; strlcpy(mbox->class_dev.class_id, mbox->name, KOBJ_NAME_LEN);
#ifdef MBOX_USE_SEQ_BIT class_set_devdata(&mbox->class_dev, mbox);
seq = msg & (1 << 31);
if (seq == mbox->seq_rcv) { ret = class_device_register(&mbox->class_dev);
printk(KERN_ERR if (unlikely(ret))
"mbox: illegal seq bit! ignoring this command. " return ret;
"(%08x)\n", msg);
goto inc;
}
mbox->seq_rcv = seq;
#endif
/* call receiver function */ class_device_create_file(&mbox->class_dev, &class_device_attr_mbox);
if ((receiver = mbox->receiver_map[(msg >> 24) & 0x7f]) == NULL)
printk(KERN_ERR
"mbox: unknown message (%08x) received from "
"%s.\n", msg, mbox->name);
else
receiver(msg);
#ifdef MBOX_USE_SEQ_BIT
inc:
#endif
disable_newmsg_irq(mbox);
mbq_inc(mbq->rp);
if (mbq->rp == mbq->wp)
empty = 1;
/* if mbq has been full, now we have a room. */
if (mbq->full) {
mbq->full = 0;
enable_newmsg_irq(mbox);
}
enable_newmsg_irq(mbox);
}
}
/* ret = request_irq(mbox->irq, mbox_interrupt, SA_INTERRUPT,
* interrupt handler mbox->name, mbox);
*/ if (unlikely(ret)) {
static irqreturn_t mbox_int_newmsg(int irq, void *p, struct pt_regs *regs) printk(KERN_ERR
{ "failed to register mailbox interrupt:%d\n", ret);
struct mbox *mbox = container_of(p, struct mbox, irq_devid_newmsg); goto fail1;
struct mbq *mbq = &mbox->mbq; }
mbox_msg_t *msg; enable_mbox_irq(mbox, IRQ_RX);
#ifdef CONFIG_ARCH_OMAP2
/*
* mailbox IRQ can be muxed.
* if it is not a newmsg interrupt, do nothing.
*/
if (!has_newmsg_irq(mbox))
return IRQ_NONE;
#endif
do {
#ifdef CONFIG_ARCH_OMAP2
if (mbox_nomsg(mbox)) {
/* no more messages in the fifo. clear IRQ source. */
clear_newmsg_irq(mbox);
break;
}
#endif
msg = &mbq->msg[mbq->wp]; spin_lock_init(&mbox->lock);
read_mbox(mbox, msg); INIT_WORK(&mbox->msg_receive, mbox_msg_receiver, mbox);
init_waitqueue_head(&mbox->tx_waitq);
mbq_inc(mbq->wp); ret = mbq_init(&mbox->mbq);
if (mbq->wp == mbq->rp) { /* mbq is full */ if (unlikely(ret))
mbq->full = 1; goto fail2;
disable_newmsg_irq(mbox);
break;
}
#if defined(CONFIG_ARCH_OMAP1)
} while (0); /* do it once */
#elif defined(CONFIG_ARCH_OMAP2)
} while (1);
#endif
schedule_work(&mbox->work); return 0;
return IRQ_HANDLED; fail2:
free_irq(mbox->irq, mbox);
class_remove_file(&omap_mbox_class, &class_attr_mbox);
class_unregister(&omap_mbox_class);
fail1:
if (unlikely(mbox->ops->shutdown))
mbox->ops->shutdown(mbox);
return ret;
} }
#ifdef CONFIG_ARCH_OMAP2 static void omap_mbox_shutdown(struct omap_mbox *mbox)
static irqreturn_t mbox_int_notfull(int irq, void *p, struct pt_regs *regs)
{ {
struct mbox *mbox = container_of(p, struct mbox, irq_devid_notfull); free_irq(mbox->irq, mbox);
class_remove_file(&omap_mbox_class, &class_attr_mbox);
/* class_unregister(&omap_mbox_class);
* mailbox IRQ can be muxed.
* if it is not a notfull interrupt, we do nothing. if (unlikely(mbox->ops->shutdown))
*/ mbox->ops->shutdown(mbox);
#if 0
if (!has_notfull_irq(mbox))
#else
if (!(has_notfull_irq(mbox) && notfull_irq_enabled(mbox)))
#endif
return IRQ_NONE;
disable_notfull_irq(mbox);
#if 0 /*
* 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.
*/
clear_notfull_irq(mbox);
#endif
wake_up_interruptible_all(&mbox->full_wait_q);
return IRQ_HANDLED;
} }
#endif /* CONFIG_ARCH_OMAP2 */
static int __init mbox_request_irq(struct mbox *mbox, const char *devname) int omap_mbox_register(struct omap_mbox *mbox)
{ {
int ret; int ret = 0;
struct omap_mbox **tmp;
#ifdef CONFIG_ARCH_OMAP2 if (!mbox)
enable_newmsg_irq(mbox); return -EINVAL;
#endif if (mbox->next)
return -EBUSY;
ret = request_irq(mbox->irq, mbox_int_newmsg, SA_INTERRUPT | SA_SHIRQ, ret = omap_mbox_init(mbox);
devname, &mbox->irq_devid_newmsg); if (ret)
if (ret) {
printk(KERN_ERR
"failed to register DSP mailbox newmsg interrupt: "
"%d\n", ret);
return ret; return ret;
}
#ifdef CONFIG_ARCH_OMAP2 write_lock(&mboxes_lock);
ret = request_irq(mbox->irq, mbox_int_notfull, SA_INTERRUPT | SA_SHIRQ, tmp = find_mboxes(mbox->name);
devname, &mbox->irq_devid_notfull); if (*tmp)
if (ret) { ret = -EBUSY;
printk(KERN_ERR else
"failed to register DSP mailbox notfull interrupt: " *tmp = mbox;
"%d\n", ret); write_unlock(&mboxes_lock);
return ret;
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;
} }
#endif write_unlock(&mboxes_lock);
return 0; return -EINVAL;
} }
EXPORT_SYMBOL(omap_mbox_unregister);
static int __init omap_mailbox_init(void) static int __init omap_mbox_class_init(void)
{ {
int ret; int ret = class_register(&omap_mbox_class);
#ifdef CONFIG_ARCH_OMAP2 if (!ret)
struct clk *mbox_ick_handle; ret = class_create_file(&omap_mbox_class, &class_attr_mbox);
#endif
printk(KERN_INFO "Initializing OMAP Mailboxes\n");
#ifdef CONFIG_ARCH_OMAP2
/*
* FIXME: mbox_ick will never unsed
*/
mbox_ick_handle = clk_get(NULL, "mailboxes_ick");
if (IS_ERR(mbox_ick_handle)) {
printk("Could not get mailboxes_ick\n");
return -ENODEV;
} else
clk_enable(mbox_ick_handle);
#endif
if ((ret = mbox_request_irq(&mbox_dsp, "mbox_dsp")) != 0)
return ret;
#ifdef CONFIG_ARCH_OMAP2
if ((ret = mbox_request_irq(&mbox_iva, "mbox_iva")) != 0)
return ret;
#endif
return 0; return ret;
} }
arch_initcall(omap_mailbox_init); static void __exit omap_mbox_class_exit(void)
{
class_remove_file(&omap_mbox_class, &class_attr_mbox);
class_unregister(&omap_mbox_class);
}
EXPORT_SYMBOL(mbox_get); subsys_initcall(omap_mbox_class_init);
EXPORT_SYMBOL(mbox_send); module_exit(omap_mbox_class_exit);
EXPORT_SYMBOL(register_mbox_receiver);
EXPORT_SYMBOL(unregister_mbox_receiver);
EXPORT_SYMBOL(disable_mbox_irq);
EXPORT_SYMBOL(enable_mbox_irq);
EXPORT_SYMBOL(mbox_init_seq);
/*
* Mailbox internal functions
*
* Copyright (C) 2006 Nokia Corporation
* Written by: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
*
* 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;
};
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];
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->rp == mbq->wp);
read_unlock_irq(&mbq->lock);
return ret;
}
static inline int mbq_full(struct omap_mbq *mbq)
{
int ret;
mbox_msg_t *p;
read_lock_irq(&mbq->lock);
p = mbq->wp;
if (++p == &mbq->msg[MBQ_DEPTH])
p = &mbq->msg[0];
ret = (p == mbq->rp);
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->wp == mbq->rp) /* 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];
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 */
/*
* Header for OMAP mailbox driver
*
* Copyright (C) 2006 Nokia Corporation. All rights reserved.
*
* Contact: Toshihiro Kobayashi <toshihiro.kobayashi@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
*
*/
#include <asm/hardware.h>
#if defined(CONFIG_ARCH_OMAP1)
#define MAILBOX_BASE (0xfffcf000)
#define MAILBOX_ARM2DSP1 (MAILBOX_BASE + 0x00)
#define MAILBOX_ARM2DSP1b (MAILBOX_BASE + 0x04)
#define MAILBOX_DSP2ARM1 (MAILBOX_BASE + 0x08)
#define MAILBOX_DSP2ARM1b (MAILBOX_BASE + 0x0c)
#define MAILBOX_DSP2ARM2 (MAILBOX_BASE + 0x10)
#define MAILBOX_DSP2ARM2b (MAILBOX_BASE + 0x14)
#define MAILBOX_ARM2DSP1_Flag (MAILBOX_BASE + 0x18)
#define MAILBOX_DSP2ARM1_Flag (MAILBOX_BASE + 0x1c)
#define MAILBOX_DSP2ARM2_Flag (MAILBOX_BASE + 0x20)
#elif defined(CONFIG_ARCH_OMAP2)
/*
* Mailbox: L4 peripheral -- use omap_readX(), omap_writeX()
*/
#define OMAP24XX_MAILBOX_BASE (L4_24XX_BASE + 0x94000)
#define MAILBOX_REVISION (OMAP24XX_MAILBOX_BASE + 0x00)
#define MAILBOX_SYSCONFIG (OMAP24XX_MAILBOX_BASE + 0x10)
#define MAILBOX_SYSSTATUS (OMAP24XX_MAILBOX_BASE + 0x14)
#define MAILBOX_MESSAGE_0 (OMAP24XX_MAILBOX_BASE + 0x40)
#define MAILBOX_MESSAGE_1 (OMAP24XX_MAILBOX_BASE + 0x44)
#define MAILBOX_MESSAGE_2 (OMAP24XX_MAILBOX_BASE + 0x48)
#define MAILBOX_MESSAGE_3 (OMAP24XX_MAILBOX_BASE + 0x4c)
#define MAILBOX_MESSAGE_4 (OMAP24XX_MAILBOX_BASE + 0x50)
#define MAILBOX_MESSAGE_5 (OMAP24XX_MAILBOX_BASE + 0x54)
#define MAILBOX_FIFOSTATUS_0 (OMAP24XX_MAILBOX_BASE + 0x80)
#define MAILBOX_FIFOSTATUS_1 (OMAP24XX_MAILBOX_BASE + 0x84)
#define MAILBOX_FIFOSTATUS_2 (OMAP24XX_MAILBOX_BASE + 0x88)
#define MAILBOX_FIFOSTATUS_3 (OMAP24XX_MAILBOX_BASE + 0x8c)
#define MAILBOX_FIFOSTATUS_4 (OMAP24XX_MAILBOX_BASE + 0x90)
#define MAILBOX_FIFOSTATUS_5 (OMAP24XX_MAILBOX_BASE + 0x94)
#define MAILBOX_MSGSTATUS_0 (OMAP24XX_MAILBOX_BASE + 0xc0)
#define MAILBOX_MSGSTATUS_1 (OMAP24XX_MAILBOX_BASE + 0xc4)
#define MAILBOX_MSGSTATUS_2 (OMAP24XX_MAILBOX_BASE + 0xc8)
#define MAILBOX_MSGSTATUS_3 (OMAP24XX_MAILBOX_BASE + 0xcc)
#define MAILBOX_MSGSTATUS_4 (OMAP24XX_MAILBOX_BASE + 0xd0)
#define MAILBOX_MSGSTATUS_5 (OMAP24XX_MAILBOX_BASE + 0xd4)
#define MAILBOX_IRQSTATUS_0 (OMAP24XX_MAILBOX_BASE + 0x100)
#define MAILBOX_IRQENABLE_0 (OMAP24XX_MAILBOX_BASE + 0x104)
#define MAILBOX_IRQSTATUS_1 (OMAP24XX_MAILBOX_BASE + 0x108)
#define MAILBOX_IRQENABLE_1 (OMAP24XX_MAILBOX_BASE + 0x10c)
#define MAILBOX_IRQSTATUS_2 (OMAP24XX_MAILBOX_BASE + 0x110)
#define MAILBOX_IRQENABLE_2 (OMAP24XX_MAILBOX_BASE + 0x114)
#define MAILBOX_IRQSTATUS_3 (OMAP24XX_MAILBOX_BASE + 0x118)
#define MAILBOX_IRQENABLE_3 (OMAP24XX_MAILBOX_BASE + 0x11c)
#define MAILBOX_IRQ_NOTFULL(n) (1<<(2*(n)+1))
#define MAILBOX_IRQ_NEWMSG(n) (1<<(2*(n)))
#endif /* CONFIG_ARCH_OMAP2 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment