OMAP: mailbox: change full flag per mailbox queue instead of global
[pandora-kernel.git] / arch / arm / plat-omap / mailbox.c
1 /*
2  * OMAP mailbox driver
3  *
4  * Copyright (C) 2006-2009 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/interrupt.h>
25 #include <linux/spinlock.h>
26 #include <linux/mutex.h>
27 #include <linux/delay.h>
28 #include <linux/slab.h>
29 #include <linux/kfifo.h>
30 #include <linux/err.h>
31
32 #include <plat/mailbox.h>
33
34 static struct workqueue_struct *mboxd;
35 static struct omap_mbox **mboxes;
36
37 static int mbox_configured;
38 static DEFINE_MUTEX(mbox_configured_lock);
39
40 static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
41 module_param(mbox_kfifo_size, uint, S_IRUGO);
42 MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
43
44 /* Mailbox FIFO handle functions */
45 static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
46 {
47         return mbox->ops->fifo_read(mbox);
48 }
49 static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
50 {
51         mbox->ops->fifo_write(mbox, msg);
52 }
53 static inline int mbox_fifo_empty(struct omap_mbox *mbox)
54 {
55         return mbox->ops->fifo_empty(mbox);
56 }
57 static inline int mbox_fifo_full(struct omap_mbox *mbox)
58 {
59         return mbox->ops->fifo_full(mbox);
60 }
61
62 /* Mailbox IRQ handle functions */
63 static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
64 {
65         if (mbox->ops->ack_irq)
66                 mbox->ops->ack_irq(mbox, irq);
67 }
68 static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
69 {
70         return mbox->ops->is_irq(mbox, irq);
71 }
72
73 /*
74  * message sender
75  */
76 static int __mbox_poll_for_space(struct omap_mbox *mbox)
77 {
78         int ret = 0, i = 1000;
79
80         while (mbox_fifo_full(mbox)) {
81                 if (mbox->ops->type == OMAP_MBOX_TYPE2)
82                         return -1;
83                 if (--i == 0)
84                         return -1;
85                 udelay(1);
86         }
87         return ret;
88 }
89
90 int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
91 {
92         struct omap_mbox_queue *mq = mbox->txq;
93         int ret = 0, len;
94
95         spin_lock(&mq->lock);
96
97         if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
98                 ret = -ENOMEM;
99                 goto out;
100         }
101
102         len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
103         WARN_ON(len != sizeof(msg));
104
105         tasklet_schedule(&mbox->txq->tasklet);
106
107 out:
108         spin_unlock(&mq->lock);
109         return ret;
110 }
111 EXPORT_SYMBOL(omap_mbox_msg_send);
112
113 static void mbox_tx_tasklet(unsigned long tx_data)
114 {
115         struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
116         struct omap_mbox_queue *mq = mbox->txq;
117         mbox_msg_t msg;
118         int ret;
119
120         while (kfifo_len(&mq->fifo)) {
121                 if (__mbox_poll_for_space(mbox)) {
122                         omap_mbox_enable_irq(mbox, IRQ_TX);
123                         break;
124                 }
125
126                 ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
127                                                                 sizeof(msg));
128                 WARN_ON(ret != sizeof(msg));
129
130                 mbox_fifo_write(mbox, msg);
131         }
132 }
133
134 /*
135  * Message receiver(workqueue)
136  */
137 static void mbox_rx_work(struct work_struct *work)
138 {
139         struct omap_mbox_queue *mq =
140                         container_of(work, struct omap_mbox_queue, work);
141         mbox_msg_t msg;
142         int len;
143
144         while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
145                 len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
146                 WARN_ON(len != sizeof(msg));
147
148                 if (mq->callback)
149                         mq->callback((void *)msg);
150                 spin_lock_irq(&mq->lock);
151                 if (mq->full) {
152                         mq->full = false;
153                         omap_mbox_enable_irq(mq->mbox, IRQ_RX);
154                 }
155                 spin_unlock_irq(&mq->lock);
156         }
157 }
158
159 /*
160  * Mailbox interrupt handler
161  */
162 static void __mbox_tx_interrupt(struct omap_mbox *mbox)
163 {
164         omap_mbox_disable_irq(mbox, IRQ_TX);
165         ack_mbox_irq(mbox, IRQ_TX);
166         tasklet_schedule(&mbox->txq->tasklet);
167 }
168
169 static void __mbox_rx_interrupt(struct omap_mbox *mbox)
170 {
171         struct omap_mbox_queue *mq = mbox->rxq;
172         mbox_msg_t msg;
173         int len;
174
175         while (!mbox_fifo_empty(mbox)) {
176                 if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
177                         omap_mbox_disable_irq(mbox, IRQ_RX);
178                         mq->full = true;
179                         goto nomem;
180                 }
181
182                 msg = mbox_fifo_read(mbox);
183
184                 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
185                 WARN_ON(len != sizeof(msg));
186
187                 if (mbox->ops->type == OMAP_MBOX_TYPE1)
188                         break;
189         }
190
191         /* no more messages in the fifo. clear IRQ source. */
192         ack_mbox_irq(mbox, IRQ_RX);
193 nomem:
194         queue_work(mboxd, &mbox->rxq->work);
195 }
196
197 static irqreturn_t mbox_interrupt(int irq, void *p)
198 {
199         struct omap_mbox *mbox = p;
200
201         if (is_mbox_irq(mbox, IRQ_TX))
202                 __mbox_tx_interrupt(mbox);
203
204         if (is_mbox_irq(mbox, IRQ_RX))
205                 __mbox_rx_interrupt(mbox);
206
207         return IRQ_HANDLED;
208 }
209
210 static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
211                                         void (*work) (struct work_struct *),
212                                         void (*tasklet)(unsigned long))
213 {
214         struct omap_mbox_queue *mq;
215
216         mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
217         if (!mq)
218                 return NULL;
219
220         spin_lock_init(&mq->lock);
221
222         if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
223                 goto error;
224
225         if (work)
226                 INIT_WORK(&mq->work, work);
227
228         if (tasklet)
229                 tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox);
230         return mq;
231 error:
232         kfree(mq);
233         return NULL;
234 }
235
236 static void mbox_queue_free(struct omap_mbox_queue *q)
237 {
238         kfifo_free(&q->fifo);
239         kfree(q);
240 }
241
242 static int omap_mbox_startup(struct omap_mbox *mbox)
243 {
244         int ret = 0;
245         struct omap_mbox_queue *mq;
246
247         if (mbox->ops->startup) {
248                 mutex_lock(&mbox_configured_lock);
249                 if (!mbox_configured)
250                         ret = mbox->ops->startup(mbox);
251
252                 if (ret) {
253                         mutex_unlock(&mbox_configured_lock);
254                         return ret;
255                 }
256                 mbox_configured++;
257                 mutex_unlock(&mbox_configured_lock);
258         }
259
260         ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
261                                 mbox->name, mbox);
262         if (ret) {
263                 printk(KERN_ERR
264                         "failed to register mailbox interrupt:%d\n", ret);
265                 goto fail_request_irq;
266         }
267
268         mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
269         if (!mq) {
270                 ret = -ENOMEM;
271                 goto fail_alloc_txq;
272         }
273         mbox->txq = mq;
274
275         mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
276         if (!mq) {
277                 ret = -ENOMEM;
278                 goto fail_alloc_rxq;
279         }
280         mbox->rxq = mq;
281
282         return 0;
283
284  fail_alloc_rxq:
285         mbox_queue_free(mbox->txq);
286  fail_alloc_txq:
287         free_irq(mbox->irq, mbox);
288  fail_request_irq:
289         if (mbox->ops->shutdown)
290                 mbox->ops->shutdown(mbox);
291
292         return ret;
293 }
294
295 static void omap_mbox_fini(struct omap_mbox *mbox)
296 {
297         free_irq(mbox->irq, mbox);
298         tasklet_kill(&mbox->txq->tasklet);
299         flush_work(&mbox->rxq->work);
300         mbox_queue_free(mbox->txq);
301         mbox_queue_free(mbox->rxq);
302
303         if (mbox->ops->shutdown) {
304                 mutex_lock(&mbox_configured_lock);
305                 if (mbox_configured > 0)
306                         mbox_configured--;
307                 if (!mbox_configured)
308                         mbox->ops->shutdown(mbox);
309                 mutex_unlock(&mbox_configured_lock);
310         }
311 }
312
313 struct omap_mbox *omap_mbox_get(const char *name)
314 {
315         struct omap_mbox *mbox;
316         int ret;
317
318         if (!mboxes)
319                 return ERR_PTR(-EINVAL);
320
321         for (mbox = *mboxes; mbox; mbox++)
322                 if (!strcmp(mbox->name, name))
323                         break;
324
325         if (!mbox)
326                 return ERR_PTR(-ENOENT);
327
328         ret = omap_mbox_startup(mbox);
329         if (ret)
330                 return ERR_PTR(-ENODEV);
331
332         return mbox;
333 }
334 EXPORT_SYMBOL(omap_mbox_get);
335
336 void omap_mbox_put(struct omap_mbox *mbox)
337 {
338         omap_mbox_fini(mbox);
339 }
340 EXPORT_SYMBOL(omap_mbox_put);
341
342 static struct class omap_mbox_class = { .name = "mbox", };
343
344 int omap_mbox_register(struct device *parent, struct omap_mbox **list)
345 {
346         int ret;
347         int i;
348
349         mboxes = list;
350         if (!mboxes)
351                 return -EINVAL;
352
353         for (i = 0; mboxes[i]; i++) {
354                 struct omap_mbox *mbox = mboxes[i];
355                 mbox->dev = device_create(&omap_mbox_class,
356                                 parent, 0, mbox, "%s", mbox->name);
357                 if (IS_ERR(mbox->dev)) {
358                         ret = PTR_ERR(mbox->dev);
359                         goto err_out;
360                 }
361         }
362         return 0;
363
364 err_out:
365         while (i--)
366                 device_unregister(mboxes[i]->dev);
367         return ret;
368 }
369 EXPORT_SYMBOL(omap_mbox_register);
370
371 int omap_mbox_unregister(void)
372 {
373         int i;
374
375         if (!mboxes)
376                 return -EINVAL;
377
378         for (i = 0; mboxes[i]; i++)
379                 device_unregister(mboxes[i]->dev);
380         mboxes = NULL;
381         return 0;
382 }
383 EXPORT_SYMBOL(omap_mbox_unregister);
384
385 static int __init omap_mbox_init(void)
386 {
387         int err;
388
389         err = class_register(&omap_mbox_class);
390         if (err)
391                 return err;
392
393         mboxd = create_workqueue("mboxd");
394         if (!mboxd)
395                 return -ENOMEM;
396
397         /* kfifo size sanity check: alignment and minimal size */
398         mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
399         mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(mbox_msg_t));
400
401         return 0;
402 }
403 subsys_initcall(omap_mbox_init);
404
405 static void __exit omap_mbox_exit(void)
406 {
407         destroy_workqueue(mboxd);
408         class_unregister(&omap_mbox_class);
409 }
410 module_exit(omap_mbox_exit);
411
412 MODULE_LICENSE("GPL v2");
413 MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
414 MODULE_AUTHOR("Toshihiro Kobayashi");
415 MODULE_AUTHOR("Hiroshi DOYU");