]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/mailbox.c
ARM: OMAP: Add mailbox support for IVA
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / mailbox.c
1 /*
2  * OMAP mailbox driver
3  *
4  * Copyright (C) 2006 Nokia Corporation. All rights reserved.
5  *
6  * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com>
7  *              Restructured by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * version 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 #include <linux/init.h>
26 #include <linux/module.h>
27 #include <linux/sched.h>
28 #include <linux/interrupt.h>
29 #include <linux/device.h>
30 #include <linux/err.h>
31 #include <linux/delay.h>
32 #include <asm/io.h>
33 #include <asm/arch/mailbox.h>
34 #include "mailbox.h"
35
36 static struct omap_mbox *mboxes;
37 static DEFINE_RWLOCK(mboxes_lock);
38
39 static struct omap_mbox **find_mboxes(const char *name)
40 {
41         struct omap_mbox **p;
42
43         for (p = &mboxes; *p; p = &(*p)->next) {
44                 if (strcmp((*p)->name, name) == 0)
45                         break;
46         }
47
48         return p;
49 }
50
51 struct omap_mbox *omap_mbox_get(const char *name)
52 {
53         struct omap_mbox *mbox;
54
55         read_lock(&mboxes_lock);
56         mbox = *(find_mboxes(name));
57         read_unlock(&mboxes_lock);
58
59         return mbox;
60 }
61 EXPORT_SYMBOL(omap_mbox_get);
62
63 /* Mailbox Sequence Bit function */
64 void omap_mbox_init_seq(struct omap_mbox *mbox)
65 {
66         mbox_seq_init(mbox);
67 }
68 EXPORT_SYMBOL(omap_mbox_init_seq);
69
70 /*
71  * message sender
72  */
73 int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg)
74 {
75         int ret;
76         int i = 1000;
77         static DEFINE_MUTEX(msg_send_lock);
78
79         while (mbox_fifo_full(mbox)) {
80                 if (mbox->ops->type == OMAP_MBOX_TYPE2) {
81                         enable_mbox_irq(mbox, IRQ_TX);
82                         wait_event_interruptible(mbox->tx_waitq,
83                                                  !mbox_fifo_full(mbox));
84                 } else
85                         udelay(1);
86
87                 if (--i == 0)
88                         return -1;
89         }
90
91
92         mutex_lock(&msg_send_lock);
93
94         if (mbox->msg_sender_cb && arg) {
95                 ret = mbox->msg_sender_cb(arg);
96                 if (ret)
97                         goto out;
98         }
99
100         mbox_seq_toggle(mbox, &msg);
101         mbox_fifo_write(mbox, msg);
102  out:
103         mutex_unlock(&msg_send_lock);
104
105         return 0;
106 }
107 EXPORT_SYMBOL(omap_mbox_msg_send);
108
109 /*
110  * Message receiver(workqueue)
111  */
112 static void mbox_msg_receiver(void *p)
113 {
114         struct omap_mbox *mbox = (struct omap_mbox *)p;
115         struct omap_mbq *mbq = mbox->mbq;
116         mbox_msg_t msg;
117         int was_full;
118
119         while (!mbq_empty(mbq)) {
120                 was_full = mbq_full(mbq);
121                 msg = mbq_get(mbq);
122                 if (was_full)   /* now we have a room in the mbq. */
123                         enable_mbox_irq(mbox, IRQ_RX);
124
125                 if (unlikely(mbox_seq_test(mbox, msg))) {
126                         printk(KERN_ERR
127                                "mbox: illegal seq bit! ignoring this command. "
128                                "(%08x)\n", msg);
129                         continue;
130                 }
131
132                 if (likely(mbox->msg_receive_cb))
133                         mbox->msg_receive_cb(msg);
134         }
135 }
136
137 /*
138  * Mailbox interrupt handler
139  */
140 static irqreturn_t mbox_interrupt(int irq, void *p)
141 {
142         mbox_msg_t msg;
143         struct omap_mbox *mbox = (struct omap_mbox *)p;
144
145         if (is_mbox_irq(mbox, IRQ_TX)) {
146                 disable_mbox_irq(mbox, IRQ_TX);
147                 /*
148                  * NOTE: this doesn't seeem to work as explained in the manual.
149                  * IRQSTATUS:NOTFULL can't be cleared even we write 1 to that bit.
150                  * It is always set when it's not full, regardless of IRQENABLE setting.
151                  */
152                 ack_mbox_irq(mbox, IRQ_TX);
153                 wake_up_interruptible_all(&mbox->tx_waitq);
154         }
155
156         if (!is_mbox_irq(mbox, IRQ_RX))
157                 return IRQ_HANDLED;
158
159         while (!mbox_fifo_empty(mbox)) {
160                 msg = mbox_fifo_read(mbox);
161                 if (mbq_add(mbox->mbq, msg)) {  /* mbq full */
162                         disable_mbox_irq(mbox, IRQ_RX);
163                         goto flush_queue;
164                 }
165                 if (mbox->ops->type == OMAP_MBOX_TYPE1)
166                         break;
167         }
168
169         /* no more messages in the fifo. clear IRQ source. */
170         ack_mbox_irq(mbox, IRQ_RX);
171  flush_queue:
172         schedule_work(&mbox->msg_receive);
173
174         return IRQ_HANDLED;
175 }
176
177 /*
178  * sysfs files
179  */
180 static ssize_t mbox_attr_write(struct class_device *dev, const char *buf,
181                               size_t count)
182 {
183         int ret;
184         mbox_msg_t msg;
185         struct omap_mbox *mbox = class_get_devdata(dev);
186
187         msg = (mbox_msg_t) simple_strtoul(buf, NULL, 16);
188
189         ret = omap_mbox_msg_send(mbox, msg, NULL);
190         if (ret)
191                 return -1;
192
193         return count;
194 }
195
196 static ssize_t mbox_attr_read(struct class_device *dev, char *buf)
197 {
198         struct omap_mbox *mbox = class_get_devdata(dev);
199
200         return sprintf(buf, mbox->name);
201 }
202
203 static CLASS_DEVICE_ATTR(mbox, S_IALLUGO, mbox_attr_read, mbox_attr_write);
204
205 static ssize_t mbox_show(struct class *class, char *buf)
206 {
207         return sprintf(buf, "mbox");
208 }
209
210 static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL);
211
212 static struct class omap_mbox_class = {
213         .name = "mbox",
214 };
215
216 static int omap_mbox_init(struct omap_mbox *mbox)
217 {
218         int ret;
219
220         if (likely(mbox->ops->startup)) {
221                 ret = mbox->ops->startup(mbox);
222                 if (unlikely(ret))
223                         return ret;
224         }
225
226         mbox->class_dev.class = &omap_mbox_class;
227         strlcpy(mbox->class_dev.class_id, mbox->name, KOBJ_NAME_LEN);
228         class_set_devdata(&mbox->class_dev, mbox);
229
230         ret = class_device_register(&mbox->class_dev);
231         if (unlikely(ret))
232                 return ret;
233
234         ret = class_device_create_file(&mbox->class_dev, &class_device_attr_mbox);
235         if (unlikely(ret)) {
236                 printk(KERN_ERR
237                        "class_device_create_file failed: %d\n", ret);
238                 goto fail1;
239         }
240
241         spin_lock_init(&mbox->lock);
242         INIT_WORK(&mbox->msg_receive, mbox_msg_receiver, mbox);
243         init_waitqueue_head(&mbox->tx_waitq);
244
245         ret = mbq_init(&mbox->mbq);
246         if (unlikely(ret))
247                 goto fail2;
248
249         ret = request_irq(mbox->irq, mbox_interrupt, SA_INTERRUPT,
250                           mbox->name, mbox);
251         if (unlikely(ret)) {
252                 printk(KERN_ERR
253                        "failed to register mailbox interrupt:%d\n", ret);
254                 goto fail3;
255         }
256         disable_mbox_irq(mbox, IRQ_RX);
257         enable_mbox_irq(mbox, IRQ_RX);
258
259         return 0;
260
261  fail3:
262         kfree(mbox->mbq);
263  fail2:
264         class_remove_file(&omap_mbox_class, &class_attr_mbox);
265  fail1:
266         class_unregister(&omap_mbox_class);
267         if (unlikely(mbox->ops->shutdown))
268                 mbox->ops->shutdown(mbox);
269
270         return ret;
271 }
272
273 static void omap_mbox_shutdown(struct omap_mbox *mbox)
274 {
275         free_irq(mbox->irq, mbox);
276         kfree(mbox->mbq);
277         class_remove_file(&omap_mbox_class, &class_attr_mbox);
278         class_unregister(&omap_mbox_class);
279
280         if (unlikely(mbox->ops->shutdown))
281                 mbox->ops->shutdown(mbox);
282 }
283
284 int omap_mbox_register(struct omap_mbox *mbox)
285 {
286         int ret = 0;
287         struct omap_mbox **tmp;
288
289         if (!mbox)
290                 return -EINVAL;
291         if (mbox->next)
292                 return -EBUSY;
293
294         ret = omap_mbox_init(mbox);
295         if (ret)
296                 return ret;
297
298         write_lock(&mboxes_lock);
299         tmp = find_mboxes(mbox->name);
300         if (*tmp)
301                 ret = -EBUSY;
302         else
303                 *tmp = mbox;
304         write_unlock(&mboxes_lock);
305
306         return ret;
307 }
308 EXPORT_SYMBOL(omap_mbox_register);
309
310 int omap_mbox_unregister(struct omap_mbox *mbox)
311 {
312         struct omap_mbox **tmp;
313
314         write_lock(&mboxes_lock);
315         tmp = &mboxes;
316         while (*tmp) {
317                 if (mbox == *tmp) {
318                         *tmp = mbox->next;
319                         mbox->next = NULL;
320                         write_unlock(&mboxes_lock);
321
322                         omap_mbox_shutdown(mbox);
323
324                         return 0;
325                 }
326                 tmp = &(*tmp)->next;
327         }
328         write_unlock(&mboxes_lock);
329
330         return -EINVAL;
331 }
332 EXPORT_SYMBOL(omap_mbox_unregister);
333
334 static int __init omap_mbox_class_init(void)
335 {
336         int ret = class_register(&omap_mbox_class);
337         if (!ret)
338                 ret = class_create_file(&omap_mbox_class, &class_attr_mbox);
339
340         return ret;
341 }
342
343 static void __exit omap_mbox_class_exit(void)
344 {
345         class_remove_file(&omap_mbox_class, &class_attr_mbox);
346         class_unregister(&omap_mbox_class);
347 }
348
349 subsys_initcall(omap_mbox_class_init);
350 module_exit(omap_mbox_class_exit);
351
352 MODULE_LICENSE("GPL");