#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/device.h>
+#include <linux/blkdev.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <asm/io.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)
{
/*
* message sender
*/
-int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg)
+static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void *arg)
{
- int ret;
- int i = 1000;
- static DEFINE_MUTEX(msg_send_lock);
+ int ret = 0, i = 1000;
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 (mbox->ops->type == OMAP_MBOX_TYPE2)
+ return -1;
if (--i == 0)
return -1;
+ udelay(1);
}
-
- mutex_lock(&msg_send_lock);
-
if (mbox->msg_sender_cb && arg) {
ret = mbox->msg_sender_cb(arg);
if (ret)
mbox_seq_toggle(mbox, &msg);
mbox_fifo_write(mbox, msg);
out:
- mutex_unlock(&msg_send_lock);
+ return ret;
+}
- return 0;
+int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg)
+{
+ struct request *rq;
+ int ret = 0;
+
+ rq = blk_get_request(mbox->txq, WRITE, GFP_ATOMIC);
+ if (unlikely(!rq)) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ rq->data = (void *)msg;
+ blk_insert_request(mbox->txq, rq, 0, arg);
+ fail:
+ return ret;
}
EXPORT_SYMBOL(omap_mbox_msg_send);
static void mbox_msg_receiver(struct work_struct *work)
{
struct omap_mbox *mbox =
- container_of(work, struct omap_mbox, msg_receive);
- struct omap_mbq *mbq = mbox->mbq;
+ container_of(work, struct omap_mbox, msg_receive);
+ struct request_queue *q = mbox->rxq;
+ struct request *rq;
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 (mbox->msg_receive_cb == NULL) {
+ sysfs_notify(&mbox->dev.kobj, NULL, "mbox");
+ return;
+ }
- if (unlikely(mbox_seq_test(mbox, msg))) {
- printk(KERN_ERR
- "mbox: illegal seq bit! ignoring this command. "
- "(%08x)\n", msg);
- continue;
- }
+ while (1) {
+ rq = elv_next_request(q);
+ if (!rq)
+ break;
+
+ msg = (mbox_msg_t)rq->data;
+
+ blkdev_dequeue_request(rq);
+ end_that_request_last(rq, 0);
- if (likely(mbox->msg_receive_cb))
+ if (mbox->msg_receive_cb)
mbox->msg_receive_cb(msg);
}
}
/*
* Mailbox interrupt handler
*/
-static irqreturn_t mbox_interrupt(int irq, void *p)
+static void mbox_txq_fn(request_queue_t *q)
{
- mbox_msg_t msg;
- struct omap_mbox *mbox = (struct omap_mbox *)p;
+ struct omap_mbox *mbox = q->queuedata;
+ int ret;
+ struct request *rq;
- 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);
+ while ((rq = elv_next_request(q)) != NULL) {
+ ret = __mbox_msg_send(mbox, (mbox_msg_t)rq->data, rq->special);
+ if (ret) {
+ blk_stop_queue(q);
+ enable_mbox_irq(mbox, IRQ_TX);
+ return;
+ }
+ blkdev_dequeue_request(rq);
+ end_that_request_last(rq, 0);
}
+}
- if (!is_mbox_irq(mbox, IRQ_RX))
- return IRQ_HANDLED;
+static void mbox_rxq_fn(request_queue_t *q)
+{
+ struct request *rq;
+ struct omap_mbox *mbox = q->queuedata;
+ mbox_msg_t msg;
while (!mbox_fifo_empty(mbox)) {
+ rq = blk_get_request(q, WRITE, GFP_ATOMIC);
+ if (unlikely(!rq))
+ goto nomem;
+
msg = mbox_fifo_read(mbox);
- if (mbq_add(mbox->mbq, msg)) { /* mbq full */
- disable_mbox_irq(mbox, IRQ_RX);
- goto flush_queue;
+ rq->data = (void *)msg;
+
+ if (unlikely(mbox_seq_test(mbox, msg))) {
+ pr_info("mbox: Illegal seq bit!(%08x)\n", msg);
+ if (mbox->err_notify)
+ mbox->err_notify();
}
+
+ blk_insert_request(mbox->rxq, rq, 0, NULL);
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);
+ enable_mbox_irq(mbox, IRQ_RX);
+ nomem:
+ queue_work(mbox->workq, &mbox->msg_receive);
+}
+
+static irqreturn_t mbox_interrupt(int irq, void *p)
+{
+ struct omap_mbox *mbox = (struct omap_mbox *)p;
+
+ if (is_mbox_irq(mbox, IRQ_TX)) {
+ disable_mbox_irq(mbox, IRQ_TX);
+ ack_mbox_irq(mbox, IRQ_TX);
+ blk_start_queue(mbox->txq);
+ }
+
+ if (is_mbox_irq(mbox, IRQ_RX)) {
+ disable_mbox_irq(mbox, IRQ_RX);
+ blk_start_queue(mbox->rxq);
+ }
return IRQ_HANDLED;
}
/*
* sysfs files
*/
-static ssize_t mbox_attr_write(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static ssize_t
+omap_mbox_write(struct device *dev, struct device_attribute *attr,
+ const char * buf, size_t count)
{
int ret;
- mbox_msg_t msg;
+ mbox_msg_t *p = (mbox_msg_t *)buf;
struct omap_mbox *mbox = dev_get_drvdata(dev);
- msg = (mbox_msg_t) simple_strtoul(buf, NULL, 16);
-
- ret = omap_mbox_msg_send(mbox, msg, NULL);
- if (ret)
- return -1;
+ for (; count >= sizeof(mbox_msg_t); count -= sizeof(mbox_msg_t)) {
+ ret = omap_mbox_msg_send(mbox, be32_to_cpu(*p), NULL);
+ if (ret)
+ return -EAGAIN;
+ p++;
+ }
- return count;
+ return (size_t)((char *)p - buf);
}
-static ssize_t mbox_attr_read(struct device *dev, struct device_attribute *attr,
- char *buf)
+static ssize_t
+omap_mbox_read(struct device *dev, struct device_attribute *attr,
+ char * buf)
{
+ struct request *rq;
+ mbox_msg_t *p = (mbox_msg_t *)buf;
struct omap_mbox *mbox = dev_get_drvdata(dev);
+ struct request_queue *q = mbox->rxq;
+
+ while ((rq = elv_next_request(q)) != NULL) {
+ *p = (mbox_msg_t)rq->data;
+ blkdev_dequeue_request(rq);
+ end_that_request_last(rq, 0);
+
+ if (unlikely(mbox_seq_test(mbox, *p))) {
+ pr_info("mbox: Illegal seq bit!(%08x) ignored\n", *p);
+ continue;
+ }
+ p++;
+ }
+
+ pr_debug("%02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
- return sprintf(buf, mbox->name);
+ return (size_t)((char *)p - buf);
}
-static DEVICE_ATTR(mbox, S_IALLUGO, mbox_attr_read, mbox_attr_write);
+static DEVICE_ATTR(mbox, S_IRUGO | S_IWUSR, omap_mbox_read, omap_mbox_write);
static ssize_t mbox_show(struct class *class, char *buf)
{
static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL);
static struct class omap_mbox_class = {
- .name = "mbox",
+ .name = "omap_mbox",
};
static int omap_mbox_init(struct omap_mbox *mbox)
{
int ret;
+ request_queue_t *q;
if (likely(mbox->ops->startup)) {
ret = mbox->ops->startup(mbox);
ret = device_register(&mbox->dev);
if (unlikely(ret))
- return ret;
+ goto fail_device_reg;
ret = device_create_file(&mbox->dev, &dev_attr_mbox);
if (unlikely(ret)) {
printk(KERN_ERR
"device_create_file failed: %d\n", ret);
- goto fail1;
+ goto fail_create_mbox;
}
spin_lock_init(&mbox->lock);
INIT_WORK(&mbox->msg_receive, mbox_msg_receiver);
- init_waitqueue_head(&mbox->tx_waitq);
-
- ret = mbq_init(&mbox->mbq);
- if (unlikely(ret))
- goto fail2;
ret = request_irq(mbox->irq, mbox_interrupt, IRQF_DISABLED,
mbox->name, mbox);
if (unlikely(ret)) {
printk(KERN_ERR
"failed to register mailbox interrupt:%d\n", ret);
- goto fail3;
+ goto fail_request_irq;
}
- disable_mbox_irq(mbox, IRQ_RX);
enable_mbox_irq(mbox, IRQ_RX);
+ q = blk_init_queue(mbox_txq_fn, &mbox->lock);
+ if (!q)
+ goto fail_blkinit_txq;
+ mbox->txq = q;
+ q->queuedata = mbox;
+
+ q = blk_init_queue(mbox_rxq_fn, &mbox->lock);
+ if (!q)
+ goto fail_blkinit_rxq;
+ mbox->rxq = q;
+ q->queuedata = mbox;
+
return 0;
- fail3:
- kfree(mbox->mbq);
- fail2:
- class_remove_file(&omap_mbox_class, &class_attr_mbox);
- fail1:
- class_unregister(&omap_mbox_class);
+ fail_blkinit_rxq:
+ blk_cleanup_queue(mbox->txq);
+ fail_blkinit_txq:
+ free_irq(mbox->irq, mbox);
+ fail_request_irq:
+ device_remove_file(&mbox->dev, &dev_attr_mbox);
+ fail_create_mbox:
+ device_unregister(&mbox->dev);
+ fail_device_reg:
if (unlikely(mbox->ops->shutdown))
mbox->ops->shutdown(mbox);
return ret;
}
-static void omap_mbox_shutdown(struct omap_mbox *mbox)
+static void omap_mbox_fini(struct omap_mbox *mbox)
{
+ blk_cleanup_queue(mbox->txq);
+ blk_cleanup_queue(mbox->rxq);
+
+ flush_workqueue(mbox->workq);
+ destroy_workqueue(mbox->workq);
+
free_irq(mbox->irq, mbox);
- kfree(mbox->mbq);
- class_remove_file(&omap_mbox_class, &class_attr_mbox);
+ device_remove_file(&mbox->dev, &dev_attr_mbox);
class_unregister(&omap_mbox_class);
if (unlikely(mbox->ops->shutdown))
mbox->ops->shutdown(mbox);
}
+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;
+ char queue_name[MBOX_NAME_LEN];
+ int ret;
+
+ read_lock(&mboxes_lock);
+ mbox = *(find_mboxes(name));
+ if (mbox == NULL) {
+ read_unlock(&mboxes_lock);
+ return ERR_PTR(-ENOENT);
+ }
+
+ strcpy(queue_name, "mailbox/");
+ strcat(queue_name, mbox->name);
+ mbox->workq = create_singlethread_workqueue(queue_name);
+
+ read_unlock(&mboxes_lock);
+
+ ret = omap_mbox_init(mbox);
+ if (ret)
+ return ERR_PTR(-ENODEV);
+
+ return mbox;
+}
+EXPORT_SYMBOL(omap_mbox_get);
+
+void omap_mbox_put(struct omap_mbox *mbox)
+{
+ omap_mbox_fini(mbox);
+}
+EXPORT_SYMBOL(omap_mbox_put);
+
int omap_mbox_register(struct omap_mbox *mbox)
{
int ret = 0;
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)
*tmp = mbox->next;
mbox->next = NULL;
write_unlock(&mboxes_lock);
-
- omap_mbox_shutdown(mbox);
-
return 0;
}
tmp = &(*tmp)->next;
#include <linux/wait.h>
#include <linux/workqueue.h>
+#include <linux/blkdev.h>
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)
struct omap_mbox_ops {
omap_mbox_type_t type;
- int (*startup)(struct omap_mbox *mbox);
- void (*shutdown)(struct omap_mbox *mbox);
+ 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);
+ 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);
+ 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;
+ char *name;
+ spinlock_t lock;
+ unsigned int irq;
- wait_queue_head_t tx_waitq;
+ struct workqueue_struct *workq;
+ struct work_struct msg_receive;
- struct work_struct msg_receive;
- void (*msg_receive_cb)(mbox_msg_t);
- struct omap_mbq *mbq;
+ request_queue_t *txq, *rxq;
- int (*msg_sender_cb)(void*);
+ void (*msg_receive_cb)(mbox_msg_t);
+ int (*msg_sender_cb)(void*);
- mbox_msg_t seq_snd, seq_rcv;
+ struct omap_mbox_ops *ops;
- struct device dev;
+ mbox_msg_t seq_snd, seq_rcv;
- void *priv;
+ struct device dev;
- struct omap_mbox *next;
+ struct omap_mbox *next;
+ void *priv;
+
+ void (*err_notify)(void);
};
-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);
+int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg, void *);
+void omap_mbox_init_seq(struct omap_mbox *);
+
+struct omap_mbox *omap_mbox_get(const char *);
+void omap_mbox_put(struct omap_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);
+int omap_mbox_register(struct omap_mbox *);
+int omap_mbox_unregister(struct omap_mbox *);
#endif /* MAILBOX_H */