]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/plat-omap/mailbox.c
e467d935c26afc0a430adf15a61f602c80e17ab1
[linux-2.6-omap-h63xx.git] / arch / arm / plat-omap / mailbox.c
1 /*
2  * OMAP mailbox driver
3  *
4  * Copyright (C) 2006-2008 Nokia Corporation. All rights reserved.
5  *
6  * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23
24 #include <linux/init.h>
25 #include <linux/module.h>
26 #include <linux/sched.h>
27 #include <linux/interrupt.h>
28 #include <linux/device.h>
29 #include <linux/blkdev.h>
30 #include <linux/err.h>
31 #include <linux/delay.h>
32 #include <asm/io.h>
33 #include <mach/mailbox.h>
34
35 static int enable_seq_bit;
36 module_param(enable_seq_bit, bool, 0);
37 MODULE_PARM_DESC(enable_seq_bit, "Enable sequence bit checking.");
38
39 static struct omap_mbox *mboxes;
40 static DEFINE_RWLOCK(mboxes_lock);
41
42 /*
43  * Mailbox sequence bit API
44  */
45
46 /* seq_rcv should be initialized with any value other than
47  * 0 and 1 << 31, to allow either value for the first
48  * message.  */
49 static inline void mbox_seq_init(struct omap_mbox *mbox)
50 {
51         if (!enable_seq_bit)
52                 return;
53
54         /* any value other than 0 and 1 << 31 */
55         mbox->seq_rcv = 0xffffffff;
56 }
57
58 static inline void mbox_seq_toggle(struct omap_mbox *mbox, mbox_msg_t * msg)
59 {
60         if (!enable_seq_bit)
61                 return;
62
63         /* add seq_snd to msg */
64         *msg = (*msg & 0x7fffffff) | mbox->seq_snd;
65         /* flip seq_snd */
66         mbox->seq_snd ^= 1 << 31;
67 }
68
69 static inline int mbox_seq_test(struct omap_mbox *mbox, mbox_msg_t msg)
70 {
71         mbox_msg_t seq;
72
73         if (!enable_seq_bit)
74                 return 0;
75
76         seq = msg & (1 << 31);
77         if (seq == mbox->seq_rcv)
78                 return -1;
79         mbox->seq_rcv = seq;
80         return 0;
81 }
82
83 /* Mailbox FIFO handle functions */
84 static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
85 {
86         return mbox->ops->fifo_read(mbox);
87 }
88 static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
89 {
90         mbox->ops->fifo_write(mbox, msg);
91 }
92 static inline int mbox_fifo_empty(struct omap_mbox *mbox)
93 {
94         return mbox->ops->fifo_empty(mbox);
95 }
96 static inline int mbox_fifo_full(struct omap_mbox *mbox)
97 {
98         return mbox->ops->fifo_full(mbox);
99 }
100
101 /* Mailbox IRQ handle functions */
102 static inline void enable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
103 {
104         mbox->ops->enable_irq(mbox, irq);
105 }
106 static inline void disable_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
107 {
108         mbox->ops->disable_irq(mbox, irq);
109 }
110 static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
111 {
112         if (mbox->ops->ack_irq)
113                 mbox->ops->ack_irq(mbox, irq);
114 }
115 static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
116 {
117         return mbox->ops->is_irq(mbox, irq);
118 }
119
120 /* Mailbox Sequence Bit function */
121 void omap_mbox_init_seq(struct omap_mbox *mbox)
122 {
123         mbox_seq_init(mbox);
124 }
125 EXPORT_SYMBOL(omap_mbox_init_seq);
126
127 /*
128  * message sender
129  */
130 static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void *arg)
131 {
132         int ret = 0, i = 1000;
133
134         while (mbox_fifo_full(mbox)) {
135                 if (mbox->ops->type == OMAP_MBOX_TYPE2)
136                         return -1;
137                 if (--i == 0)
138                         return -1;
139                 udelay(1);
140         }
141
142         if (arg && mbox->txq->callback) {
143                 ret = mbox->txq->callback(arg);
144                 if (ret)
145                         goto out;
146         }
147
148         mbox_seq_toggle(mbox, &msg);
149         mbox_fifo_write(mbox, msg);
150  out:
151         return ret;
152 }
153
154 int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg)
155 {
156         struct request *rq;
157         struct request_queue *q = mbox->txq->queue;
158         int ret = 0;
159
160         rq = blk_get_request(q, WRITE, GFP_ATOMIC);
161         if (unlikely(!rq)) {
162                 ret = -ENOMEM;
163                 goto fail;
164         }
165
166         rq->data = (void *)msg;
167         blk_insert_request(q, rq, 0, arg);
168
169         schedule_work(&mbox->txq->work);
170  fail:
171         return ret;
172 }
173 EXPORT_SYMBOL(omap_mbox_msg_send);
174
175 static void mbox_tx_work(struct work_struct *work)
176 {
177         int ret;
178         struct request *rq;
179         struct omap_mbox_queue *mq = container_of(work,
180                                 struct omap_mbox_queue, work);
181         struct omap_mbox *mbox = mq->queue->queuedata;
182         struct request_queue *q = mbox->txq->queue;
183
184         while (1) {
185                 spin_lock(q->queue_lock);
186                 rq = elv_next_request(q);
187                 spin_unlock(q->queue_lock);
188
189                 if (!rq)
190                         break;
191
192                 ret = __mbox_msg_send(mbox, (mbox_msg_t) rq->data, rq->special);
193                 if (ret) {
194                         enable_mbox_irq(mbox, IRQ_TX);
195                         return;
196                 }
197
198                 spin_lock(q->queue_lock);
199                 if (__blk_end_request(rq, 0, 0))
200                         BUG();
201                 spin_unlock(q->queue_lock);
202         }
203 }
204
205 /*
206  * Message receiver(workqueue)
207  */
208 static void mbox_rx_work(struct work_struct *work)
209 {
210         struct omap_mbox_queue *mq =
211                         container_of(work, struct omap_mbox_queue, work);
212         struct omap_mbox *mbox = mq->queue->queuedata;
213         struct request_queue *q = mbox->rxq->queue;
214         struct request *rq;
215         mbox_msg_t msg;
216         unsigned long flags;
217
218         if (mbox->rxq->callback == NULL) {
219                 sysfs_notify(&mbox->dev->kobj, NULL, "mbox");
220                 return;
221         }
222
223         while (1) {
224                 spin_lock_irqsave(q->queue_lock, flags);
225                 rq = elv_next_request(q);
226                 spin_unlock_irqrestore(q->queue_lock, flags);
227                 if (!rq)
228                         break;
229
230                 msg = (mbox_msg_t) rq->data;
231
232                 if (blk_end_request(rq, 0, 0))
233                         BUG();
234
235                 mbox->rxq->callback((void *)msg);
236         }
237 }
238
239 /*
240  * Mailbox interrupt handler
241  */
242 static void mbox_txq_fn(struct request_queue * q)
243 {
244 }
245
246 static void mbox_rxq_fn(struct request_queue * q)
247 {
248 }
249
250 static void __mbox_tx_interrupt(struct omap_mbox *mbox)
251 {
252         disable_mbox_irq(mbox, IRQ_TX);
253         ack_mbox_irq(mbox, IRQ_TX);
254         schedule_work(&mbox->txq->work);
255 }
256
257 static void __mbox_rx_interrupt(struct omap_mbox *mbox)
258 {
259         struct request *rq;
260         mbox_msg_t msg;
261         struct request_queue *q = mbox->rxq->queue;
262
263         disable_mbox_irq(mbox, IRQ_RX);
264
265         while (!mbox_fifo_empty(mbox)) {
266                 rq = blk_get_request(q, WRITE, GFP_ATOMIC);
267                 if (unlikely(!rq))
268                         goto nomem;
269
270                 msg = mbox_fifo_read(mbox);
271                 rq->data = (void *)msg;
272
273                 if (unlikely(mbox_seq_test(mbox, msg))) {
274                         pr_info("mbox: Illegal seq bit!(%08x)\n", msg);
275                         if (mbox->err_notify)
276                                 mbox->err_notify();
277                 }
278
279                 blk_insert_request(q, rq, 0, NULL);
280                 if (mbox->ops->type == OMAP_MBOX_TYPE1)
281                         break;
282         }
283
284         /* no more messages in the fifo. clear IRQ source. */
285         ack_mbox_irq(mbox, IRQ_RX);
286         enable_mbox_irq(mbox, IRQ_RX);
287 nomem:
288         schedule_work(&mbox->rxq->work);
289 }
290
291 static irqreturn_t mbox_interrupt(int irq, void *p)
292 {
293         struct omap_mbox *mbox = p;
294
295         if (is_mbox_irq(mbox, IRQ_TX))
296                 __mbox_tx_interrupt(mbox);
297
298         if (is_mbox_irq(mbox, IRQ_RX))
299                 __mbox_rx_interrupt(mbox);
300
301         return IRQ_HANDLED;
302 }
303
304 /*
305  * sysfs files
306  */
307 static ssize_t
308 omap_mbox_write(struct device *dev, struct device_attribute *attr,
309                 const char * buf, size_t count)
310 {
311         int ret;
312         mbox_msg_t *p = (mbox_msg_t *)buf;
313         struct omap_mbox *mbox = dev_get_drvdata(dev);
314
315         for (; count >= sizeof(mbox_msg_t); count -= sizeof(mbox_msg_t)) {
316                 ret = omap_mbox_msg_send(mbox, be32_to_cpu(*p), NULL);
317                 if (ret)
318                         return -EAGAIN;
319                 p++;
320         }
321
322         return (size_t)((char *)p - buf);
323 }
324
325 static ssize_t
326 omap_mbox_read(struct device *dev, struct device_attribute *attr, char *buf)
327 {
328         unsigned long flags;
329         struct request *rq;
330         mbox_msg_t *p = (mbox_msg_t *) buf;
331         struct omap_mbox *mbox = dev_get_drvdata(dev);
332         struct request_queue *q = mbox->rxq->queue;
333
334         while (1) {
335                 spin_lock_irqsave(q->queue_lock, flags);
336                 rq = elv_next_request(q);
337                 spin_unlock_irqrestore(q->queue_lock, flags);
338
339                 if (!rq)
340                         break;
341
342                 *p = (mbox_msg_t) rq->data;
343
344                 if (blk_end_request(rq, 0, 0))
345                         BUG();
346
347                 if (unlikely(mbox_seq_test(mbox, *p))) {
348                         pr_info("mbox: Illegal seq bit!(%08x) ignored\n", *p);
349                         continue;
350                 }
351                 p++;
352         }
353
354         pr_debug("%02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]);
355
356         return (size_t) ((char *)p - buf);
357 }
358
359 static DEVICE_ATTR(mbox, S_IRUGO | S_IWUSR, omap_mbox_read, omap_mbox_write);
360
361 static ssize_t mbox_show(struct class *class, char *buf)
362 {
363         return sprintf(buf, "mbox");
364 }
365
366 static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL);
367
368 static struct class omap_mbox_class = {
369         .name = "omap-mailbox",
370 };
371
372 static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
373                                         request_fn_proc * proc,
374                                         void (*work) (struct work_struct *))
375 {
376         struct request_queue *q;
377         struct omap_mbox_queue *mq;
378
379         mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
380         if (!mq)
381                 return NULL;
382
383         spin_lock_init(&mq->lock);
384
385         q = blk_init_queue(proc, &mq->lock);
386         if (!q)
387                 goto error;
388         q->queuedata = mbox;
389         mq->queue = q;
390
391         INIT_WORK(&mq->work, work);
392
393         return mq;
394 error:
395         kfree(mq);
396         return NULL;
397 }
398
399 static void mbox_queue_free(struct omap_mbox_queue *q)
400 {
401         blk_cleanup_queue(q->queue);
402         kfree(q);
403 }
404
405 static int omap_mbox_init(struct omap_mbox *mbox)
406 {
407         int ret;
408         struct omap_mbox_queue *mq;
409
410         if (likely(mbox->ops->startup)) {
411                 ret = mbox->ops->startup(mbox);
412                 if (unlikely(ret))
413                         return ret;
414         }
415
416         ret = request_irq(mbox->irq, mbox_interrupt, IRQF_DISABLED,
417                                 mbox->name, mbox);
418         if (unlikely(ret)) {
419                 printk(KERN_ERR
420                         "failed to register mailbox interrupt:%d\n", ret);
421                 goto fail_request_irq;
422         }
423
424         mq = mbox_queue_alloc(mbox, mbox_txq_fn, mbox_tx_work);
425         if (!mq) {
426                 ret = -ENOMEM;
427                 goto fail_alloc_txq;
428         }
429         mbox->txq = mq;
430
431         mq = mbox_queue_alloc(mbox, mbox_rxq_fn, mbox_rx_work);
432         if (!mq) {
433                 ret = -ENOMEM;
434                 goto fail_alloc_rxq;
435         }
436         mbox->rxq = mq;
437
438         return 0;
439
440  fail_alloc_rxq:
441         mbox_queue_free(mbox->txq);
442  fail_alloc_txq:
443         free_irq(mbox->irq, mbox);
444  fail_request_irq:
445         if (unlikely(mbox->ops->shutdown))
446                 mbox->ops->shutdown(mbox);
447
448         return ret;
449 }
450
451 static void omap_mbox_fini(struct omap_mbox *mbox)
452 {
453         mbox_queue_free(mbox->txq);
454         mbox_queue_free(mbox->rxq);
455
456         free_irq(mbox->irq, mbox);
457
458         if (unlikely(mbox->ops->shutdown))
459                 mbox->ops->shutdown(mbox);
460 }
461
462 static struct omap_mbox **find_mboxes(const char *name)
463 {
464         struct omap_mbox **p;
465
466         for (p = &mboxes; *p; p = &(*p)->next) {
467                 if (strcmp((*p)->name, name) == 0)
468                         break;
469         }
470
471         return p;
472 }
473
474 struct omap_mbox *omap_mbox_get(const char *name)
475 {
476         struct omap_mbox *mbox;
477         int ret;
478
479         read_lock(&mboxes_lock);
480         mbox = *(find_mboxes(name));
481         if (mbox == NULL) {
482                 read_unlock(&mboxes_lock);
483                 return ERR_PTR(-ENOENT);
484         }
485
486         read_unlock(&mboxes_lock);
487
488         ret = omap_mbox_init(mbox);
489         if (ret)
490                 return ERR_PTR(-ENODEV);
491
492         return mbox;
493 }
494 EXPORT_SYMBOL(omap_mbox_get);
495
496 void omap_mbox_put(struct omap_mbox *mbox)
497 {
498         omap_mbox_fini(mbox);
499 }
500 EXPORT_SYMBOL(omap_mbox_put);
501
502 int omap_mbox_register(struct device *parent, struct omap_mbox *mbox)
503 {
504         int ret = 0;
505         struct omap_mbox **tmp;
506
507         if (!mbox)
508                 return -EINVAL;
509         if (mbox->next)
510                 return -EBUSY;
511
512         mbox->dev = device_create(&omap_mbox_class,
513                                   parent, 0, mbox, "%s", mbox->name);
514         if (IS_ERR(mbox->dev))
515                 return PTR_ERR(mbox->dev);
516
517         ret = device_create_file(mbox->dev, &dev_attr_mbox);
518         if (ret)
519                 goto err_sysfs;
520
521         write_lock(&mboxes_lock);
522         tmp = find_mboxes(mbox->name);
523         if (*tmp) {
524                 ret = -EBUSY;
525                 write_unlock(&mboxes_lock);
526                 goto err_find;
527         }
528         *tmp = mbox;
529         write_unlock(&mboxes_lock);
530
531         return 0;
532
533 err_find:
534         device_remove_file(mbox->dev, &dev_attr_mbox);
535 err_sysfs:
536         device_unregister(mbox->dev);
537         return ret;
538 }
539 EXPORT_SYMBOL(omap_mbox_register);
540
541 int omap_mbox_unregister(struct omap_mbox *mbox)
542 {
543         struct omap_mbox **tmp;
544
545         write_lock(&mboxes_lock);
546         tmp = &mboxes;
547         while (*tmp) {
548                 if (mbox == *tmp) {
549                         *tmp = mbox->next;
550                         mbox->next = NULL;
551                         write_unlock(&mboxes_lock);
552                         device_remove_file(mbox->dev, &dev_attr_mbox);
553                         device_unregister(mbox->dev);
554                         return 0;
555                 }
556                 tmp = &(*tmp)->next;
557         }
558         write_unlock(&mboxes_lock);
559
560         return -EINVAL;
561 }
562 EXPORT_SYMBOL(omap_mbox_unregister);
563
564 static int __init omap_mbox_class_init(void)
565 {
566         int ret = class_register(&omap_mbox_class);
567         if (!ret)
568                 ret = class_create_file(&omap_mbox_class, &class_attr_mbox);
569
570         return ret;
571 }
572
573 static void __exit omap_mbox_class_exit(void)
574 {
575         class_remove_file(&omap_mbox_class, &class_attr_mbox);
576         class_unregister(&omap_mbox_class);
577 }
578
579 subsys_initcall(omap_mbox_class_init);
580 module_exit(omap_mbox_class_exit);
581
582 MODULE_LICENSE("GPL v2");
583 MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
584 MODULE_AUTHOR("Toshihiro Kobayashi and Hiroshi DOYU");