]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/mailbox.c
c194c328bdd6c5c549b9725cfa45764a1d9dedec
[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(struct work_struct *work)
113 {
114         struct omap_mbox *mbox =
115                 container_of(work, struct omap_mbox, msg_receive);
116         struct omap_mbq *mbq = mbox->mbq;
117         mbox_msg_t msg;
118         int was_full;
119
120         while (!mbq_empty(mbq)) {
121                 was_full = mbq_full(mbq);
122                 msg = mbq_get(mbq);
123                 if (was_full)   /* now we have a room in the mbq. */
124                         enable_mbox_irq(mbox, IRQ_RX);
125
126                 if (unlikely(mbox_seq_test(mbox, msg))) {
127                         printk(KERN_ERR
128                                "mbox: illegal seq bit! ignoring this command. "
129                                "(%08x)\n", msg);
130                         continue;
131                 }
132
133                 if (likely(mbox->msg_receive_cb))
134                         mbox->msg_receive_cb(msg);
135         }
136 }
137
138 /*
139  * Mailbox interrupt handler
140  */
141 static irqreturn_t mbox_interrupt(int irq, void *p)
142 {
143         mbox_msg_t msg;
144         struct omap_mbox *mbox = (struct omap_mbox *)p;
145
146         if (is_mbox_irq(mbox, IRQ_TX)) {
147                 disable_mbox_irq(mbox, IRQ_TX);
148                 /*
149                  * NOTE: this doesn't seeem to work as explained in the manual.
150                  * IRQSTATUS:NOTFULL can't be cleared even we write 1 to that bit.
151                  * It is always set when it's not full, regardless of IRQENABLE setting.
152                  */
153                 ack_mbox_irq(mbox, IRQ_TX);
154                 wake_up_interruptible_all(&mbox->tx_waitq);
155         }
156
157         if (!is_mbox_irq(mbox, IRQ_RX))
158                 return IRQ_HANDLED;
159
160         while (!mbox_fifo_empty(mbox)) {
161                 msg = mbox_fifo_read(mbox);
162                 if (mbq_add(mbox->mbq, msg)) {  /* mbq full */
163                         disable_mbox_irq(mbox, IRQ_RX);
164                         goto flush_queue;
165                 }
166                 if (mbox->ops->type == OMAP_MBOX_TYPE1)
167                         break;
168         }
169
170         /* no more messages in the fifo. clear IRQ source. */
171         ack_mbox_irq(mbox, IRQ_RX);
172  flush_queue:
173         schedule_work(&mbox->msg_receive);
174
175         return IRQ_HANDLED;
176 }
177
178 /*
179  * sysfs files
180  */
181 static ssize_t mbox_attr_write(struct device *dev,
182                                struct device_attribute *attr,
183                                const char *buf, size_t count)
184 {
185         int ret;
186         mbox_msg_t msg;
187         struct omap_mbox *mbox = dev_get_drvdata(dev);
188
189         msg = (mbox_msg_t) simple_strtoul(buf, NULL, 16);
190
191         ret = omap_mbox_msg_send(mbox, msg, NULL);
192         if (ret)
193                 return -1;
194
195         return count;
196 }
197
198 static ssize_t mbox_attr_read(struct device *dev, struct device_attribute *attr,
199                               char *buf)
200 {
201         struct omap_mbox *mbox = dev_get_drvdata(dev);
202
203         return sprintf(buf, mbox->name);
204 }
205
206 static DEVICE_ATTR(mbox, S_IALLUGO, mbox_attr_read, mbox_attr_write);
207
208 static ssize_t mbox_show(struct class *class, char *buf)
209 {
210         return sprintf(buf, "mbox");
211 }
212
213 static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL);
214
215 static struct class omap_mbox_class = {
216         .name = "mbox",
217 };
218
219 static int omap_mbox_init(struct omap_mbox *mbox)
220 {
221         int ret;
222
223         if (likely(mbox->ops->startup)) {
224                 ret = mbox->ops->startup(mbox);
225                 if (unlikely(ret))
226                         return ret;
227         }
228
229         mbox->dev.class = &omap_mbox_class;
230         strlcpy(mbox->dev.bus_id, mbox->name, KOBJ_NAME_LEN);
231         dev_set_drvdata(&mbox->dev, mbox);
232
233         ret = device_register(&mbox->dev);
234         if (unlikely(ret))
235                 return ret;
236
237         ret = device_create_file(&mbox->dev, &dev_attr_mbox);
238         if (unlikely(ret)) {
239                 printk(KERN_ERR
240                        "device_create_file failed: %d\n", ret);
241                 goto fail1;
242         }
243
244         spin_lock_init(&mbox->lock);
245         INIT_WORK(&mbox->msg_receive, mbox_msg_receiver);
246         init_waitqueue_head(&mbox->tx_waitq);
247
248         ret = mbq_init(&mbox->mbq);
249         if (unlikely(ret))
250                 goto fail2;
251
252         ret = request_irq(mbox->irq, mbox_interrupt, IRQF_DISABLED,
253                           mbox->name, mbox);
254         if (unlikely(ret)) {
255                 printk(KERN_ERR
256                        "failed to register mailbox interrupt:%d\n", ret);
257                 goto fail3;
258         }
259         disable_mbox_irq(mbox, IRQ_RX);
260         enable_mbox_irq(mbox, IRQ_RX);
261
262         return 0;
263
264  fail3:
265         kfree(mbox->mbq);
266  fail2:
267         class_remove_file(&omap_mbox_class, &class_attr_mbox);
268  fail1:
269         class_unregister(&omap_mbox_class);
270         if (unlikely(mbox->ops->shutdown))
271                 mbox->ops->shutdown(mbox);
272
273         return ret;
274 }
275
276 static void omap_mbox_shutdown(struct omap_mbox *mbox)
277 {
278         free_irq(mbox->irq, mbox);
279         kfree(mbox->mbq);
280         class_remove_file(&omap_mbox_class, &class_attr_mbox);
281         class_unregister(&omap_mbox_class);
282
283         if (unlikely(mbox->ops->shutdown))
284                 mbox->ops->shutdown(mbox);
285 }
286
287 int omap_mbox_register(struct omap_mbox *mbox)
288 {
289         int ret = 0;
290         struct omap_mbox **tmp;
291
292         if (!mbox)
293                 return -EINVAL;
294         if (mbox->next)
295                 return -EBUSY;
296
297         ret = omap_mbox_init(mbox);
298         if (ret)
299                 return ret;
300
301         write_lock(&mboxes_lock);
302         tmp = find_mboxes(mbox->name);
303         if (*tmp)
304                 ret = -EBUSY;
305         else
306                 *tmp = mbox;
307         write_unlock(&mboxes_lock);
308
309         return ret;
310 }
311 EXPORT_SYMBOL(omap_mbox_register);
312
313 int omap_mbox_unregister(struct omap_mbox *mbox)
314 {
315         struct omap_mbox **tmp;
316
317         write_lock(&mboxes_lock);
318         tmp = &mboxes;
319         while (*tmp) {
320                 if (mbox == *tmp) {
321                         *tmp = mbox->next;
322                         mbox->next = NULL;
323                         write_unlock(&mboxes_lock);
324
325                         omap_mbox_shutdown(mbox);
326
327                         return 0;
328                 }
329                 tmp = &(*tmp)->next;
330         }
331         write_unlock(&mboxes_lock);
332
333         return -EINVAL;
334 }
335 EXPORT_SYMBOL(omap_mbox_unregister);
336
337 static int __init omap_mbox_class_init(void)
338 {
339         int ret = class_register(&omap_mbox_class);
340         if (!ret)
341                 ret = class_create_file(&omap_mbox_class, &class_attr_mbox);
342
343         return ret;
344 }
345
346 static void __exit omap_mbox_class_exit(void)
347 {
348         class_remove_file(&omap_mbox_class, &class_attr_mbox);
349         class_unregister(&omap_mbox_class);
350 }
351
352 subsys_initcall(omap_mbox_class_init);
353 module_exit(omap_mbox_class_exit);
354
355 MODULE_LICENSE("GPL");