aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-omap/mailbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap/mailbox.c')
-rw-r--r--arch/arm/plat-omap/mailbox.c248
1 files changed, 118 insertions, 130 deletions
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c
index 08a2df76628..d2fafb892f7 100644
--- a/arch/arm/plat-omap/mailbox.c
+++ b/arch/arm/plat-omap/mailbox.c
@@ -21,19 +21,26 @@
21 * 21 *
22 */ 22 */
23 23
24#include <linux/module.h>
25#include <linux/interrupt.h> 24#include <linux/interrupt.h>
26#include <linux/device.h> 25#include <linux/spinlock.h>
26#include <linux/mutex.h>
27#include <linux/delay.h> 27#include <linux/delay.h>
28#include <linux/slab.h> 28#include <linux/slab.h>
29#include <linux/kfifo.h>
30#include <linux/err.h>
29 31
30#include <plat/mailbox.h> 32#include <plat/mailbox.h>
31 33
32static struct workqueue_struct *mboxd; 34static struct workqueue_struct *mboxd;
33static struct omap_mbox *mboxes; 35static struct omap_mbox **mboxes;
34static DEFINE_RWLOCK(mboxes_lock); 36static bool rq_full;
35 37
36static int mbox_configured; 38static int mbox_configured;
39static DEFINE_MUTEX(mbox_configured_lock);
40
41static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE;
42module_param(mbox_kfifo_size, uint, S_IRUGO);
43MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)");
37 44
38/* Mailbox FIFO handle functions */ 45/* Mailbox FIFO handle functions */
39static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) 46static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox)
@@ -67,7 +74,7 @@ static inline int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq)
67/* 74/*
68 * message sender 75 * message sender
69 */ 76 */
70static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) 77static int __mbox_poll_for_space(struct omap_mbox *mbox)
71{ 78{
72 int ret = 0, i = 1000; 79 int ret = 0, i = 1000;
73 80
@@ -78,49 +85,50 @@ static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
78 return -1; 85 return -1;
79 udelay(1); 86 udelay(1);
80 } 87 }
81 mbox_fifo_write(mbox, msg);
82 return ret; 88 return ret;
83} 89}
84 90
85
86int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) 91int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg)
87{ 92{
93 struct omap_mbox_queue *mq = mbox->txq;
94 int ret = 0, len;
88 95
89 struct request *rq; 96 spin_lock(&mq->lock);
90 struct request_queue *q = mbox->txq->queue;
91 97
92 rq = blk_get_request(q, WRITE, GFP_ATOMIC); 98 if (kfifo_avail(&mq->fifo) < sizeof(msg)) {
93 if (unlikely(!rq)) 99 ret = -ENOMEM;
94 return -ENOMEM; 100 goto out;
101 }
102
103 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
104 WARN_ON(len != sizeof(msg));
95 105
96 blk_insert_request(q, rq, 0, (void *) msg);
97 tasklet_schedule(&mbox->txq->tasklet); 106 tasklet_schedule(&mbox->txq->tasklet);
98 107
99 return 0; 108out:
109 spin_unlock(&mq->lock);
110 return ret;
100} 111}
101EXPORT_SYMBOL(omap_mbox_msg_send); 112EXPORT_SYMBOL(omap_mbox_msg_send);
102 113
103static void mbox_tx_tasklet(unsigned long tx_data) 114static void mbox_tx_tasklet(unsigned long tx_data)
104{ 115{
105 int ret;
106 struct request *rq;
107 struct omap_mbox *mbox = (struct omap_mbox *)tx_data; 116 struct omap_mbox *mbox = (struct omap_mbox *)tx_data;
108 struct request_queue *q = mbox->txq->queue; 117 struct omap_mbox_queue *mq = mbox->txq;
109 118 mbox_msg_t msg;
110 while (1) { 119 int ret;
111
112 rq = blk_fetch_request(q);
113
114 if (!rq)
115 break;
116 120
117 ret = __mbox_msg_send(mbox, (mbox_msg_t)rq->special); 121 while (kfifo_len(&mq->fifo)) {
118 if (ret) { 122 if (__mbox_poll_for_space(mbox)) {
119 omap_mbox_enable_irq(mbox, IRQ_TX); 123 omap_mbox_enable_irq(mbox, IRQ_TX);
120 blk_requeue_request(q, rq); 124 break;
121 return;
122 } 125 }
123 blk_end_request_all(rq, 0); 126
127 ret = kfifo_out(&mq->fifo, (unsigned char *)&msg,
128 sizeof(msg));
129 WARN_ON(ret != sizeof(msg));
130
131 mbox_fifo_write(mbox, msg);
124 } 132 }
125} 133}
126 134
@@ -131,36 +139,21 @@ static void mbox_rx_work(struct work_struct *work)
131{ 139{
132 struct omap_mbox_queue *mq = 140 struct omap_mbox_queue *mq =
133 container_of(work, struct omap_mbox_queue, work); 141 container_of(work, struct omap_mbox_queue, work);
134 struct omap_mbox *mbox = mq->queue->queuedata;
135 struct request_queue *q = mbox->rxq->queue;
136 struct request *rq;
137 mbox_msg_t msg; 142 mbox_msg_t msg;
138 unsigned long flags; 143 int len;
139 144
140 while (1) { 145 while (kfifo_len(&mq->fifo) >= sizeof(msg)) {
141 spin_lock_irqsave(q->queue_lock, flags); 146 len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
142 rq = blk_fetch_request(q); 147 WARN_ON(len != sizeof(msg));
143 spin_unlock_irqrestore(q->queue_lock, flags);
144 if (!rq)
145 break;
146 148
147 msg = (mbox_msg_t)rq->special; 149 if (mq->callback)
148 blk_end_request_all(rq, 0); 150 mq->callback((void *)msg);
149 mbox->rxq->callback((void *)msg);
150 } 151 }
151} 152}
152 153
153/* 154/*
154 * Mailbox interrupt handler 155 * Mailbox interrupt handler
155 */ 156 */
156static void mbox_txq_fn(struct request_queue *q)
157{
158}
159
160static void mbox_rxq_fn(struct request_queue *q)
161{
162}
163
164static void __mbox_tx_interrupt(struct omap_mbox *mbox) 157static void __mbox_tx_interrupt(struct omap_mbox *mbox)
165{ 158{
166 omap_mbox_disable_irq(mbox, IRQ_TX); 159 omap_mbox_disable_irq(mbox, IRQ_TX);
@@ -170,19 +163,22 @@ static void __mbox_tx_interrupt(struct omap_mbox *mbox)
170 163
171static void __mbox_rx_interrupt(struct omap_mbox *mbox) 164static void __mbox_rx_interrupt(struct omap_mbox *mbox)
172{ 165{
173 struct request *rq; 166 struct omap_mbox_queue *mq = mbox->rxq;
174 mbox_msg_t msg; 167 mbox_msg_t msg;
175 struct request_queue *q = mbox->rxq->queue; 168 int len;
176 169
177 while (!mbox_fifo_empty(mbox)) { 170 while (!mbox_fifo_empty(mbox)) {
178 rq = blk_get_request(q, WRITE, GFP_ATOMIC); 171 if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) {
179 if (unlikely(!rq)) 172 omap_mbox_disable_irq(mbox, IRQ_RX);
173 rq_full = true;
180 goto nomem; 174 goto nomem;
175 }
181 176
182 msg = mbox_fifo_read(mbox); 177 msg = mbox_fifo_read(mbox);
183 178
179 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg));
180 WARN_ON(len != sizeof(msg));
184 181
185 blk_insert_request(q, rq, 0, (void *)msg);
186 if (mbox->ops->type == OMAP_MBOX_TYPE1) 182 if (mbox->ops->type == OMAP_MBOX_TYPE1)
187 break; 183 break;
188 } 184 }
@@ -207,11 +203,9 @@ static irqreturn_t mbox_interrupt(int irq, void *p)
207} 203}
208 204
209static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, 205static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
210 request_fn_proc *proc,
211 void (*work) (struct work_struct *), 206 void (*work) (struct work_struct *),
212 void (*tasklet)(unsigned long)) 207 void (*tasklet)(unsigned long))
213{ 208{
214 struct request_queue *q;
215 struct omap_mbox_queue *mq; 209 struct omap_mbox_queue *mq;
216 210
217 mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); 211 mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL);
@@ -220,11 +214,8 @@ static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox,
220 214
221 spin_lock_init(&mq->lock); 215 spin_lock_init(&mq->lock);
222 216
223 q = blk_init_queue(proc, &mq->lock); 217 if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL))
224 if (!q)
225 goto error; 218 goto error;
226 q->queuedata = mbox;
227 mq->queue = q;
228 219
229 if (work) 220 if (work)
230 INIT_WORK(&mq->work, work); 221 INIT_WORK(&mq->work, work);
@@ -239,7 +230,7 @@ error:
239 230
240static void mbox_queue_free(struct omap_mbox_queue *q) 231static void mbox_queue_free(struct omap_mbox_queue *q)
241{ 232{
242 blk_cleanup_queue(q->queue); 233 kfifo_free(&q->fifo);
243 kfree(q); 234 kfree(q);
244} 235}
245 236
@@ -248,35 +239,35 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
248 int ret = 0; 239 int ret = 0;
249 struct omap_mbox_queue *mq; 240 struct omap_mbox_queue *mq;
250 241
251 if (likely(mbox->ops->startup)) { 242 if (mbox->ops->startup) {
252 write_lock(&mboxes_lock); 243 mutex_lock(&mbox_configured_lock);
253 if (!mbox_configured) 244 if (!mbox_configured)
254 ret = mbox->ops->startup(mbox); 245 ret = mbox->ops->startup(mbox);
255 246
256 if (unlikely(ret)) { 247 if (ret) {
257 write_unlock(&mboxes_lock); 248 mutex_unlock(&mbox_configured_lock);
258 return ret; 249 return ret;
259 } 250 }
260 mbox_configured++; 251 mbox_configured++;
261 write_unlock(&mboxes_lock); 252 mutex_unlock(&mbox_configured_lock);
262 } 253 }
263 254
264 ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, 255 ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED,
265 mbox->name, mbox); 256 mbox->name, mbox);
266 if (unlikely(ret)) { 257 if (ret) {
267 printk(KERN_ERR 258 printk(KERN_ERR
268 "failed to register mailbox interrupt:%d\n", ret); 259 "failed to register mailbox interrupt:%d\n", ret);
269 goto fail_request_irq; 260 goto fail_request_irq;
270 } 261 }
271 262
272 mq = mbox_queue_alloc(mbox, mbox_txq_fn, NULL, mbox_tx_tasklet); 263 mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet);
273 if (!mq) { 264 if (!mq) {
274 ret = -ENOMEM; 265 ret = -ENOMEM;
275 goto fail_alloc_txq; 266 goto fail_alloc_txq;
276 } 267 }
277 mbox->txq = mq; 268 mbox->txq = mq;
278 269
279 mq = mbox_queue_alloc(mbox, mbox_rxq_fn, mbox_rx_work, NULL); 270 mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL);
280 if (!mq) { 271 if (!mq) {
281 ret = -ENOMEM; 272 ret = -ENOMEM;
282 goto fail_alloc_rxq; 273 goto fail_alloc_rxq;
@@ -290,7 +281,7 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
290 fail_alloc_txq: 281 fail_alloc_txq:
291 free_irq(mbox->irq, mbox); 282 free_irq(mbox->irq, mbox);
292 fail_request_irq: 283 fail_request_irq:
293 if (unlikely(mbox->ops->shutdown)) 284 if (mbox->ops->shutdown)
294 mbox->ops->shutdown(mbox); 285 mbox->ops->shutdown(mbox);
295 286
296 return ret; 287 return ret;
@@ -298,31 +289,20 @@ static int omap_mbox_startup(struct omap_mbox *mbox)
298 289
299static void omap_mbox_fini(struct omap_mbox *mbox) 290static void omap_mbox_fini(struct omap_mbox *mbox)
300{ 291{
292 free_irq(mbox->irq, mbox);
293 tasklet_kill(&mbox->txq->tasklet);
294 flush_work(&mbox->rxq->work);
301 mbox_queue_free(mbox->txq); 295 mbox_queue_free(mbox->txq);
302 mbox_queue_free(mbox->rxq); 296 mbox_queue_free(mbox->rxq);
303 297
304 free_irq(mbox->irq, mbox); 298 if (mbox->ops->shutdown) {
305 299 mutex_lock(&mbox_configured_lock);
306 if (unlikely(mbox->ops->shutdown)) {
307 write_lock(&mboxes_lock);
308 if (mbox_configured > 0) 300 if (mbox_configured > 0)
309 mbox_configured--; 301 mbox_configured--;
310 if (!mbox_configured) 302 if (!mbox_configured)
311 mbox->ops->shutdown(mbox); 303 mbox->ops->shutdown(mbox);
312 write_unlock(&mboxes_lock); 304 mutex_unlock(&mbox_configured_lock);
313 }
314}
315
316static struct omap_mbox **find_mboxes(const char *name)
317{
318 struct omap_mbox **p;
319
320 for (p = &mboxes; *p; p = &(*p)->next) {
321 if (strcmp((*p)->name, name) == 0)
322 break;
323 } 305 }
324
325 return p;
326} 306}
327 307
328struct omap_mbox *omap_mbox_get(const char *name) 308struct omap_mbox *omap_mbox_get(const char *name)
@@ -330,14 +310,15 @@ struct omap_mbox *omap_mbox_get(const char *name)
330 struct omap_mbox *mbox; 310 struct omap_mbox *mbox;
331 int ret; 311 int ret;
332 312
333 read_lock(&mboxes_lock); 313 if (!mboxes)
334 mbox = *(find_mboxes(name)); 314 return ERR_PTR(-EINVAL);
335 if (mbox == NULL) {
336 read_unlock(&mboxes_lock);
337 return ERR_PTR(-ENOENT);
338 }
339 315
340 read_unlock(&mboxes_lock); 316 for (mbox = *mboxes; mbox; mbox++)
317 if (!strcmp(mbox->name, name))
318 break;
319
320 if (!mbox)
321 return ERR_PTR(-ENOENT);
341 322
342 ret = omap_mbox_startup(mbox); 323 ret = omap_mbox_startup(mbox);
343 if (ret) 324 if (ret)
@@ -353,70 +334,77 @@ void omap_mbox_put(struct omap_mbox *mbox)
353} 334}
354EXPORT_SYMBOL(omap_mbox_put); 335EXPORT_SYMBOL(omap_mbox_put);
355 336
356int omap_mbox_register(struct device *parent, struct omap_mbox *mbox) 337static struct class omap_mbox_class = { .name = "mbox", };
338
339int omap_mbox_register(struct device *parent, struct omap_mbox **list)
357{ 340{
358 int ret = 0; 341 int ret;
359 struct omap_mbox **tmp; 342 int i;
360 343
361 if (!mbox) 344 mboxes = list;
345 if (!mboxes)
362 return -EINVAL; 346 return -EINVAL;
363 if (mbox->next)
364 return -EBUSY;
365
366 write_lock(&mboxes_lock);
367 tmp = find_mboxes(mbox->name);
368 if (*tmp) {
369 ret = -EBUSY;
370 write_unlock(&mboxes_lock);
371 goto err_find;
372 }
373 *tmp = mbox;
374 write_unlock(&mboxes_lock);
375 347
348 for (i = 0; mboxes[i]; i++) {
349 struct omap_mbox *mbox = mboxes[i];
350 mbox->dev = device_create(&omap_mbox_class,
351 parent, 0, mbox, "%s", mbox->name);
352 if (IS_ERR(mbox->dev)) {
353 ret = PTR_ERR(mbox->dev);
354 goto err_out;
355 }
356 }
376 return 0; 357 return 0;
377 358
378err_find: 359err_out:
360 while (i--)
361 device_unregister(mboxes[i]->dev);
379 return ret; 362 return ret;
380} 363}
381EXPORT_SYMBOL(omap_mbox_register); 364EXPORT_SYMBOL(omap_mbox_register);
382 365
383int omap_mbox_unregister(struct omap_mbox *mbox) 366int omap_mbox_unregister(void)
384{ 367{
385 struct omap_mbox **tmp; 368 int i;
386 369
387 write_lock(&mboxes_lock); 370 if (!mboxes)
388 tmp = &mboxes; 371 return -EINVAL;
389 while (*tmp) {
390 if (mbox == *tmp) {
391 *tmp = mbox->next;
392 mbox->next = NULL;
393 write_unlock(&mboxes_lock);
394 return 0;
395 }
396 tmp = &(*tmp)->next;
397 }
398 write_unlock(&mboxes_lock);
399 372
400 return -EINVAL; 373 for (i = 0; mboxes[i]; i++)
374 device_unregister(mboxes[i]->dev);
375 mboxes = NULL;
376 return 0;
401} 377}
402EXPORT_SYMBOL(omap_mbox_unregister); 378EXPORT_SYMBOL(omap_mbox_unregister);
403 379
404static int __init omap_mbox_init(void) 380static int __init omap_mbox_init(void)
405{ 381{
382 int err;
383
384 err = class_register(&omap_mbox_class);
385 if (err)
386 return err;
387
406 mboxd = create_workqueue("mboxd"); 388 mboxd = create_workqueue("mboxd");
407 if (!mboxd) 389 if (!mboxd)
408 return -ENOMEM; 390 return -ENOMEM;
409 391
392 /* kfifo size sanity check: alignment and minimal size */
393 mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t));
394 mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(mbox_msg_t));
395
410 return 0; 396 return 0;
411} 397}
412module_init(omap_mbox_init); 398subsys_initcall(omap_mbox_init);
413 399
414static void __exit omap_mbox_exit(void) 400static void __exit omap_mbox_exit(void)
415{ 401{
416 destroy_workqueue(mboxd); 402 destroy_workqueue(mboxd);
403 class_unregister(&omap_mbox_class);
417} 404}
418module_exit(omap_mbox_exit); 405module_exit(omap_mbox_exit);
419 406
420MODULE_LICENSE("GPL v2"); 407MODULE_LICENSE("GPL v2");
421MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); 408MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging");
422MODULE_AUTHOR("Toshihiro Kobayashi and Hiroshi DOYU"); 409MODULE_AUTHOR("Toshihiro Kobayashi");
410MODULE_AUTHOR("Hiroshi DOYU");