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