OMAP: mailbox: support save/restore context using runtime PM
[rpmsg:rpmsg.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 #include <linux/notifier.h>
32 #include <linux/module.h>
33 #include <linux/pm.h>
34
35 #include <plat/mailbox.h>
36
37 static struct omap_mbox **mboxes;
38
39 static int mbox_configured;
40 static DEFINE_MUTEX(mbox_configured_lock);
41
42 #define SET_MPU_CORE_CONSTRAINT 10
43 #define CLEAR_MPU_CORE_CONSTRAINT PM_QOS_DEFAULT_VALUE
44
45 static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
46 module_param(mbox_kfifo_size, uint, S_IRUGO);
47 MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
48
49 /* Runtime PM */
50 static int omap_mbox_save_ctx(struct device *dev, void *data)
51 {
52         struct omap_mbox *mbox = dev_get_drvdata(dev);
53
54         if (!mbox->ops->save_ctx) {
55                 dev_err(mbox->dev, "%s:\tno save\n", __func__);
56                 return -EINVAL;
57         }
58
59         mbox->ops->save_ctx(mbox);
60
61         return 0;
62 }
63
64 static int omap_mbox_restore_ctx(struct device *dev, void *data)
65 {
66         struct omap_mbox *mbox = dev_get_drvdata(dev);
67
68         if (!mbox->ops->restore_ctx) {
69                 dev_err(mbox->dev, "%s:\tno restore\n", __func__);
70                 return -EINVAL;
71         }
72
73         mbox->ops->restore_ctx(mbox);
74
75         return 0;
76 }
77
78 static int mbox_runtime_resume(struct device *dev)
79 {
80         return device_for_each_child(dev, NULL, omap_mbox_restore_ctx);
81 }
82
83 static int mbox_runtime_suspend(struct device *dev)
84 {
85         return device_for_each_child(dev, NULL, omap_mbox_save_ctx);
86 }
87
88 const struct dev_pm_ops mbox_pm_ops = {
89         SET_RUNTIME_PM_OPS(mbox_runtime_suspend, mbox_runtime_resume, NULL)
90 };
91
92 /* Mailbox FIFO handle functions */
93 static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
94 {
95         return mbox->ops->fifo_read(mbox);
96 }
97 static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg)
98 {
99         mbox->ops->fifo_write(mbox, msg);
100 }
101 static inline int mbox_fifo_empty(struct omap_mbox *mbox)
102 {
103         return mbox->ops->fifo_empty(mbox);
104 }
105 static inline int mbox_fifo_full(struct omap_mbox *mbox)
106 {
107         return mbox->ops->fifo_full(mbox);
108 }
109
110 /* Mailbox IRQ handle functions */
111 static inline void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
112 {
113         if (mbox->ops->ack_irq)
114                 mbox->ops->ack_irq(mbox, irq);
115 }
116 static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
117 {
118         return mbox->ops->is_irq(mbox, irq);
119 }
120
121 /*
122  * message sender
123  */
124 static int __mbox_poll_for_space(struct omap_mbox *mbox)
125 {
126         int ret = 0, i = 1000;
127
128         while (mbox_fifo_full(mbox)) {
129                 if (mbox->ops->type == OMAP_MBOX_TYPE2)
130                         return -1;
131                 if (--i == 0)
132                         return -1;
133                 udelay(1);
134         }
135         return ret;
136 }
137
138 int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
139 {
140         struct omap_mbox_queue *mq = mbox->txq;
141         int ret = 0, len;
142
143         spin_lock_bh(&mq->lock);
144
145         if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
146                 ret = -ENOMEM;
147                 goto out;
148         }
149
150         if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) {
151                 mbox_fifo_write(mbox, msg);
152                 goto out;
153         }
154
155         len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
156         WARN_ON(len != sizeof(msg));
157
158         tasklet_schedule(&mbox->txq->tasklet);
159
160 out:
161         spin_unlock_bh(&mq->lock);
162         return ret;
163 }
164 EXPORT_SYMBOL(omap_mbox_msg_send);
165
166 static void mbox_tx_tasklet(unsigned long tx_data)
167 {
168         struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
169         struct omap_mbox_queue *mq = mbox->txq;
170         mbox_msg_t msg;
171         int ret;
172
173         while (kfifo_len(&mq->fifo)) {
174                 if (__mbox_poll_for_space(mbox)) {
175                         omap_mbox_enable_irq(mbox, IRQ_TX);
176                         break;
177                 }
178
179                 ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
180                                                                 sizeof(msg));
181                 WARN_ON(ret != sizeof(msg));
182
183                 mbox_fifo_write(mbox, msg);
184         }
185 }
186
187 /*
188  * Message receiver(workqueue)
189  */
190 static void mbox_rx_work(struct work_struct *work)
191 {
192         struct omap_mbox_queue *mq =
193                         container_of(work, struct omap_mbox_queue, work);
194         mbox_msg_t msg;
195         int len;
196
197         while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
198                 len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
199                 WARN_ON(len != sizeof(msg));
200
201                 blocking_notifier_call_chain(&mq->mbox->notifier, len,
202                                                                 (void *)msg);
203                 spin_lock_irq(&mq->lock);
204                 if (mq->full) {
205                         mq->full = false;
206                         omap_mbox_enable_irq(mq->mbox, IRQ_RX);
207                 }
208                 spin_unlock_irq(&mq->lock);
209         }
210 }
211
212 /*
213  * Mailbox interrupt handler
214  */
215 static void __mbox_tx_interrupt(struct omap_mbox *mbox)
216 {
217         omap_mbox_disable_irq(mbox, IRQ_TX);
218         ack_mbox_irq(mbox, IRQ_TX);
219         tasklet_schedule(&mbox->txq->tasklet);
220 }
221
222 static void __mbox_rx_interrupt(struct omap_mbox *mbox)
223 {
224         struct omap_mbox_queue *mq = mbox->rxq;
225         mbox_msg_t msg;
226         int len;
227
228         while (!mbox_fifo_empty(mbox)) {
229                 if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
230                         omap_mbox_disable_irq(mbox, IRQ_RX);
231                         mq->full = true;
232                         goto nomem;
233                 }
234
235                 msg = mbox_fifo_read(mbox);
236
237                 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
238                 WARN_ON(len != sizeof(msg));
239
240                 if (mbox->ops->type == OMAP_MBOX_TYPE1)
241                         break;
242         }
243
244         /* no more messages in the fifo. clear IRQ source. */
245         ack_mbox_irq(mbox, IRQ_RX);
246 nomem:
247         schedule_work(&mbox->rxq->work);
248 }
249
250 static irqreturn_t mbox_interrupt(int irq, void *p)
251 {
252         struct omap_mbox *mbox = p;
253
254         if (is_mbox_irq(mbox, IRQ_TX))
255                 __mbox_tx_interrupt(mbox);
256
257         if (is_mbox_irq(mbox, IRQ_RX))
258                 __mbox_rx_interrupt(mbox);
259
260         return IRQ_HANDLED;
261 }
262
263 static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
264                                         void (*work) (struct work_struct *),
265                                         void (*tasklet)(unsigned long))
266 {
267         struct omap_mbox_queue *mq;
268
269         mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
270         if (!mq)
271                 return NULL;
272
273         spin_lock_init(&mq->lock);
274
275         if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
276                 goto error;
277
278         if (work)
279                 INIT_WORK(&mq->work, work);
280
281         if (tasklet)
282                 tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox);
283         return mq;
284 error:
285         kfree(mq);
286         return NULL;
287 }
288
289 static void mbox_queue_free(struct omap_mbox_queue *q)
290 {
291         kfifo_free(&q->fifo);
292         kfree(q);
293 }
294
295 static int omap_mbox_startup(struct omap_mbox *mbox)
296 {
297         int ret = 0;
298         struct omap_mbox_queue *mq;
299
300         mutex_lock(&mbox_configured_lock);
301         if (!mbox_configured++) {
302                 dev_pm_qos_update_request(&mbox->qos_request,
303                                         SET_MPU_CORE_CONSTRAINT);
304                 if (likely(mbox->ops->startup)) {
305                         ret = mbox->ops->startup(mbox);
306                         if (unlikely(ret))
307                                 goto fail_startup;
308                 } else
309                         goto fail_startup;
310         }
311
312         if (!mbox->use_count++) {
313                 ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
314                                                         mbox->name, mbox);
315                 if (unlikely(ret)) {
316                         pr_err("failed to register mailbox interrupt:%d\n",
317                                                                         ret);
318                         goto fail_request_irq;
319                 }
320                 mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
321                 if (!mq) {
322                         ret = -ENOMEM;
323                         goto fail_alloc_txq;
324                 }
325                 mbox->txq = mq;
326
327                 mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
328                 if (!mq) {
329                         ret = -ENOMEM;
330                         goto fail_alloc_rxq;
331                 }
332                 mbox->rxq = mq;
333                 mq->mbox = mbox;
334         }
335         mutex_unlock(&mbox_configured_lock);
336         return 0;
337
338 fail_alloc_rxq:
339         mbox_queue_free(mbox->txq);
340 fail_alloc_txq:
341         free_irq(mbox->irq, mbox);
342 fail_request_irq:
343         if (mbox->ops->shutdown)
344                 mbox->ops->shutdown(mbox);
345         mbox->use_count--;
346 fail_startup:
347         if (!--mbox_configured)
348                 dev_pm_qos_update_request(&mbox->qos_request,
349                                          CLEAR_MPU_CORE_CONSTRAINT);
350         mutex_unlock(&mbox_configured_lock);
351         return ret;
352 }
353
354 static void omap_mbox_fini(struct omap_mbox *mbox)
355 {
356         mutex_lock(&mbox_configured_lock);
357
358         if (!--mbox->use_count) {
359                 free_irq(mbox->irq, mbox);
360                 tasklet_kill(&mbox->txq->tasklet);
361                 flush_work_sync(&mbox->rxq->work);
362                 mbox_queue_free(mbox->txq);
363                 mbox_queue_free(mbox->rxq);
364         }
365
366         if (likely(mbox->ops->shutdown)) {
367                 if (!--mbox_configured) {
368                         mbox->ops->shutdown(mbox);
369                         dev_pm_qos_update_request(&mbox->qos_request,
370                                                 CLEAR_MPU_CORE_CONSTRAINT);
371                 }
372         }
373
374         mutex_unlock(&mbox_configured_lock);
375 }
376
377 struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb)
378 {
379         struct omap_mbox *_mbox, *mbox = NULL;
380         int i, ret;
381
382         if (!mboxes)
383                 return ERR_PTR(-EINVAL);
384
385         for (i = 0; (_mbox = mboxes[i]); i++) {
386                 if (!strcmp(_mbox->name, name)) {
387                         mbox = _mbox;
388                         break;
389                 }
390         }
391
392         if (!mbox)
393                 return ERR_PTR(-ENOENT);
394
395         ret = omap_mbox_startup(mbox);
396         if (ret)
397                 return ERR_PTR(-ENODEV);
398
399         if (nb)
400                 blocking_notifier_chain_register(&mbox->notifier, nb);
401
402         return mbox;
403 }
404 EXPORT_SYMBOL(omap_mbox_get);
405
406 void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb)
407 {
408         blocking_notifier_chain_unregister(&mbox->notifier, nb);
409         omap_mbox_fini(mbox);
410 }
411 EXPORT_SYMBOL(omap_mbox_put);
412
413 static struct class omap_mbox_class = { .name = "mbox", };
414
415 int omap_mbox_register(struct device *parent, struct omap_mbox **list)
416 {
417         int ret;
418         int i;
419
420         mboxes = list;
421         if (!mboxes)
422                 return -EINVAL;
423
424         for (i = 0; mboxes[i]; i++) {
425                 struct omap_mbox *mbox = mboxes[i];
426                 mbox->dev = device_create(&omap_mbox_class,
427                                 parent, 0, mbox, "%s", mbox->name);
428                 if (IS_ERR(mbox->dev)) {
429                         ret = PTR_ERR(mbox->dev);
430                         goto err_out;
431                 }
432
433                 ret = dev_pm_qos_add_request(parent, &mbox->qos_request,
434                                                 PM_QOS_DEFAULT_VALUE);
435                 if (ret < 0)
436                         goto err_out;
437
438                 BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier);
439         }
440         return 0;
441
442 err_out:
443         while (i--) {
444                 dev_pm_qos_remove_request(&mboxes[i]->qos_request);
445                 device_unregister(mboxes[i]->dev);
446         }
447         return ret;
448 }
449 EXPORT_SYMBOL(omap_mbox_register);
450
451 int omap_mbox_unregister(void)
452 {
453         int i;
454
455         if (!mboxes)
456                 return -EINVAL;
457
458         for (i = 0; mboxes[i]; i++) {
459                 dev_pm_qos_remove_request(&mboxes[i]->qos_request);
460                 device_unregister(mboxes[i]->dev);
461         }
462         mboxes = NULL;
463         return 0;
464 }
465 EXPORT_SYMBOL(omap_mbox_unregister);
466
467 static int __init omap_mbox_init(void)
468 {
469         int err;
470
471         err = class_register(&omap_mbox_class);
472         if (err)
473                 return err;
474
475         /* kfifo size sanity check: alignment and minimal size */
476         mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
477         mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size,
478                                                         sizeof(mbox_msg_t));
479         return 0;
480 }
481 subsys_initcall(omap_mbox_init);
482
483 static void __exit omap_mbox_exit(void)
484 {
485         class_unregister(&omap_mbox_class);
486 }
487 module_exit(omap_mbox_exit);
488
489 MODULE_LICENSE("GPL v2");
490 MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
491 MODULE_AUTHOR("Toshihiro Kobayashi");
492 MODULE_AUTHOR("Hiroshi DOYU");