diff options
Diffstat (limited to 'arch/arm/plat-omap/mailbox.c')
-rw-r--r-- | arch/arm/plat-omap/mailbox.c | 130 |
1 files changed, 76 insertions, 54 deletions
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index d2fafb892f7f..459b319a9fad 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c | |||
@@ -28,12 +28,12 @@ | |||
28 | #include <linux/slab.h> | 28 | #include <linux/slab.h> |
29 | #include <linux/kfifo.h> | 29 | #include <linux/kfifo.h> |
30 | #include <linux/err.h> | 30 | #include <linux/err.h> |
31 | #include <linux/notifier.h> | ||
31 | 32 | ||
32 | #include <plat/mailbox.h> | 33 | #include <plat/mailbox.h> |
33 | 34 | ||
34 | static struct workqueue_struct *mboxd; | 35 | static struct workqueue_struct *mboxd; |
35 | static struct omap_mbox **mboxes; | 36 | static struct omap_mbox **mboxes; |
36 | static bool rq_full; | ||
37 | 37 | ||
38 | static int mbox_configured; | 38 | static int mbox_configured; |
39 | static DEFINE_MUTEX(mbox_configured_lock); | 39 | static DEFINE_MUTEX(mbox_configured_lock); |
@@ -93,20 +93,25 @@ int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg) | |||
93 | struct omap_mbox_queue *mq = mbox->txq; | 93 | struct omap_mbox_queue *mq = mbox->txq; |
94 | int ret = 0, len; | 94 | int ret = 0, len; |
95 | 95 | ||
96 | spin_lock(&mq->lock); | 96 | spin_lock_bh(&mq->lock); |
97 | 97 | ||
98 | if (kfifo_avail(&mq->fifo) < sizeof(msg)) { | 98 | if (kfifo_avail(&mq->fifo) < sizeof(msg)) { |
99 | ret = -ENOMEM; | 99 | ret = -ENOMEM; |
100 | goto out; | 100 | goto out; |
101 | } | 101 | } |
102 | 102 | ||
103 | if (kfifo_is_empty(&mq->fifo) && !__mbox_poll_for_space(mbox)) { | ||
104 | mbox_fifo_write(mbox, msg); | ||
105 | goto out; | ||
106 | } | ||
107 | |||
103 | len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | 108 | len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); |
104 | WARN_ON(len != sizeof(msg)); | 109 | WARN_ON(len != sizeof(msg)); |
105 | 110 | ||
106 | tasklet_schedule(&mbox->txq->tasklet); | 111 | tasklet_schedule(&mbox->txq->tasklet); |
107 | 112 | ||
108 | out: | 113 | out: |
109 | spin_unlock(&mq->lock); | 114 | spin_unlock_bh(&mq->lock); |
110 | return ret; | 115 | return ret; |
111 | } | 116 | } |
112 | EXPORT_SYMBOL(omap_mbox_msg_send); | 117 | EXPORT_SYMBOL(omap_mbox_msg_send); |
@@ -146,8 +151,14 @@ static void mbox_rx_work(struct work_struct *work) | |||
146 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | 151 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); |
147 | WARN_ON(len != sizeof(msg)); | 152 | WARN_ON(len != sizeof(msg)); |
148 | 153 | ||
149 | if (mq->callback) | 154 | blocking_notifier_call_chain(&mq->mbox->notifier, len, |
150 | mq->callback((void *)msg); | 155 | (void *)msg); |
156 | spin_lock_irq(&mq->lock); | ||
157 | if (mq->full) { | ||
158 | mq->full = false; | ||
159 | omap_mbox_enable_irq(mq->mbox, IRQ_RX); | ||
160 | } | ||
161 | spin_unlock_irq(&mq->lock); | ||
151 | } | 162 | } |
152 | } | 163 | } |
153 | 164 | ||
@@ -170,7 +181,7 @@ static void __mbox_rx_interrupt(struct omap_mbox *mbox) | |||
170 | while (!mbox_fifo_empty(mbox)) { | 181 | while (!mbox_fifo_empty(mbox)) { |
171 | if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { | 182 | if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { |
172 | omap_mbox_disable_irq(mbox, IRQ_RX); | 183 | omap_mbox_disable_irq(mbox, IRQ_RX); |
173 | rq_full = true; | 184 | mq->full = true; |
174 | goto nomem; | 185 | goto nomem; |
175 | } | 186 | } |
176 | 187 | ||
@@ -239,73 +250,77 @@ static int omap_mbox_startup(struct omap_mbox *mbox) | |||
239 | int ret = 0; | 250 | int ret = 0; |
240 | struct omap_mbox_queue *mq; | 251 | struct omap_mbox_queue *mq; |
241 | 252 | ||
242 | if (mbox->ops->startup) { | 253 | mutex_lock(&mbox_configured_lock); |
243 | mutex_lock(&mbox_configured_lock); | 254 | if (!mbox_configured++) { |
244 | if (!mbox_configured) | 255 | if (likely(mbox->ops->startup)) { |
245 | ret = mbox->ops->startup(mbox); | 256 | ret = mbox->ops->startup(mbox); |
246 | 257 | if (unlikely(ret)) | |
247 | if (ret) { | 258 | goto fail_startup; |
248 | mutex_unlock(&mbox_configured_lock); | 259 | } else |
249 | return ret; | 260 | goto fail_startup; |
250 | } | ||
251 | mbox_configured++; | ||
252 | mutex_unlock(&mbox_configured_lock); | ||
253 | } | ||
254 | |||
255 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, | ||
256 | mbox->name, mbox); | ||
257 | if (ret) { | ||
258 | printk(KERN_ERR | ||
259 | "failed to register mailbox interrupt:%d\n", ret); | ||
260 | goto fail_request_irq; | ||
261 | } | 261 | } |
262 | 262 | ||
263 | mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); | 263 | if (!mbox->use_count++) { |
264 | if (!mq) { | 264 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, |
265 | ret = -ENOMEM; | 265 | mbox->name, mbox); |
266 | goto fail_alloc_txq; | 266 | if (unlikely(ret)) { |
267 | } | 267 | pr_err("failed to register mailbox interrupt:%d\n", |
268 | mbox->txq = mq; | 268 | ret); |
269 | goto fail_request_irq; | ||
270 | } | ||
271 | mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); | ||
272 | if (!mq) { | ||
273 | ret = -ENOMEM; | ||
274 | goto fail_alloc_txq; | ||
275 | } | ||
276 | mbox->txq = mq; | ||
269 | 277 | ||
270 | mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); | 278 | mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); |
271 | if (!mq) { | 279 | if (!mq) { |
272 | ret = -ENOMEM; | 280 | ret = -ENOMEM; |
273 | goto fail_alloc_rxq; | 281 | goto fail_alloc_rxq; |
282 | } | ||
283 | mbox->rxq = mq; | ||
284 | mq->mbox = mbox; | ||
274 | } | 285 | } |
275 | mbox->rxq = mq; | 286 | mutex_unlock(&mbox_configured_lock); |
276 | |||
277 | return 0; | 287 | return 0; |
278 | 288 | ||
279 | fail_alloc_rxq: | 289 | fail_alloc_rxq: |
280 | mbox_queue_free(mbox->txq); | 290 | mbox_queue_free(mbox->txq); |
281 | fail_alloc_txq: | 291 | fail_alloc_txq: |
282 | free_irq(mbox->irq, mbox); | 292 | free_irq(mbox->irq, mbox); |
283 | fail_request_irq: | 293 | fail_request_irq: |
284 | if (mbox->ops->shutdown) | 294 | if (mbox->ops->shutdown) |
285 | mbox->ops->shutdown(mbox); | 295 | mbox->ops->shutdown(mbox); |
286 | 296 | mbox->use_count--; | |
297 | fail_startup: | ||
298 | mbox_configured--; | ||
299 | mutex_unlock(&mbox_configured_lock); | ||
287 | return ret; | 300 | return ret; |
288 | } | 301 | } |
289 | 302 | ||
290 | static void omap_mbox_fini(struct omap_mbox *mbox) | 303 | static void omap_mbox_fini(struct omap_mbox *mbox) |
291 | { | 304 | { |
292 | free_irq(mbox->irq, mbox); | 305 | mutex_lock(&mbox_configured_lock); |
293 | tasklet_kill(&mbox->txq->tasklet); | 306 | |
294 | flush_work(&mbox->rxq->work); | 307 | if (!--mbox->use_count) { |
295 | mbox_queue_free(mbox->txq); | 308 | free_irq(mbox->irq, mbox); |
296 | mbox_queue_free(mbox->rxq); | 309 | tasklet_kill(&mbox->txq->tasklet); |
310 | flush_work(&mbox->rxq->work); | ||
311 | mbox_queue_free(mbox->txq); | ||
312 | mbox_queue_free(mbox->rxq); | ||
313 | } | ||
297 | 314 | ||
298 | if (mbox->ops->shutdown) { | 315 | if (likely(mbox->ops->shutdown)) { |
299 | mutex_lock(&mbox_configured_lock); | 316 | if (!--mbox_configured) |
300 | if (mbox_configured > 0) | ||
301 | mbox_configured--; | ||
302 | if (!mbox_configured) | ||
303 | mbox->ops->shutdown(mbox); | 317 | mbox->ops->shutdown(mbox); |
304 | mutex_unlock(&mbox_configured_lock); | ||
305 | } | 318 | } |
319 | |||
320 | mutex_unlock(&mbox_configured_lock); | ||
306 | } | 321 | } |
307 | 322 | ||
308 | struct omap_mbox *omap_mbox_get(const char *name) | 323 | struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb) |
309 | { | 324 | { |
310 | struct omap_mbox *mbox; | 325 | struct omap_mbox *mbox; |
311 | int ret; | 326 | int ret; |
@@ -324,12 +339,16 @@ struct omap_mbox *omap_mbox_get(const char *name) | |||
324 | if (ret) | 339 | if (ret) |
325 | return ERR_PTR(-ENODEV); | 340 | return ERR_PTR(-ENODEV); |
326 | 341 | ||
342 | if (nb) | ||
343 | blocking_notifier_chain_register(&mbox->notifier, nb); | ||
344 | |||
327 | return mbox; | 345 | return mbox; |
328 | } | 346 | } |
329 | EXPORT_SYMBOL(omap_mbox_get); | 347 | EXPORT_SYMBOL(omap_mbox_get); |
330 | 348 | ||
331 | void omap_mbox_put(struct omap_mbox *mbox) | 349 | void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb) |
332 | { | 350 | { |
351 | blocking_notifier_chain_unregister(&mbox->notifier, nb); | ||
333 | omap_mbox_fini(mbox); | 352 | omap_mbox_fini(mbox); |
334 | } | 353 | } |
335 | EXPORT_SYMBOL(omap_mbox_put); | 354 | EXPORT_SYMBOL(omap_mbox_put); |
@@ -353,6 +372,8 @@ int omap_mbox_register(struct device *parent, struct omap_mbox **list) | |||
353 | ret = PTR_ERR(mbox->dev); | 372 | ret = PTR_ERR(mbox->dev); |
354 | goto err_out; | 373 | goto err_out; |
355 | } | 374 | } |
375 | |||
376 | BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier); | ||
356 | } | 377 | } |
357 | return 0; | 378 | return 0; |
358 | 379 | ||
@@ -391,7 +412,8 @@ static int __init omap_mbox_init(void) | |||
391 | 412 | ||
392 | /* kfifo size sanity check: alignment and minimal size */ | 413 | /* kfifo size sanity check: alignment and minimal size */ |
393 | mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t)); | 414 | 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)); | 415 | mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, |
416 | sizeof(mbox_msg_t)); | ||
395 | 417 | ||
396 | return 0; | 418 | return 0; |
397 | } | 419 | } |