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 34static struct workqueue_struct *mboxd; 35static struct omap_mbox **mboxes; 36static bool rq_full; 37 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)"); 44 45/* Mailbox FIFO handle functions */ 46static inline mbox_msg_t mbox_fifo_read(struct omap_mbox *mbox) 47{ 48 return mbox->ops->fifo_read(mbox); 49} 50static inline void mbox_fifo_write(struct omap_mbox *mbox, mbox_msg_t msg) 51{ 52 mbox->ops->fifo_write(mbox, msg); 53} 54static inline int mbox_fifo_empty(struct omap_mbox *mbox) 55{ 56 return mbox->ops->fifo_empty(mbox); 57} 58static inline int mbox_fifo_full(struct omap_mbox *mbox) 59{ 60 return mbox->ops->fifo_full(mbox); 61} 62 63/* Mailbox IRQ handle functions */ 64static 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} 69static 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 */ 77static 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 91int 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(&mq->lock); 97 98 if (kfifo_avail(&mq->fifo) < sizeof(msg)) { 99 ret = -ENOMEM; 100 goto out; 101 } 102 103 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); 104 WARN_ON(len != sizeof(msg)); 105 106 tasklet_schedule(&mbox->txq->tasklet); 107 108out: 109 spin_unlock(&mq->lock); 110 return ret; 111} 112EXPORT_SYMBOL(omap_mbox_msg_send); 113 114static void mbox_tx_tasklet(unsigned long tx_data) 115{ 116 struct omap_mbox *mbox = (struct omap_mbox *)tx_data; 117 struct omap_mbox_queue *mq = mbox->txq; 118 mbox_msg_t msg; 119 int ret; 120 121 while (kfifo_len(&mq->fifo)) { 122 if (__mbox_poll_for_space(mbox)) { 123 omap_mbox_enable_irq(mbox, IRQ_TX); 124 break; 125 } 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); 132 } 133} 134 135/* 136 * Message receiver(workqueue) 137 */ 138static void mbox_rx_work(struct work_struct *work) 139{ 140 struct omap_mbox_queue *mq = 141 container_of(work, struct omap_mbox_queue, work); 142 mbox_msg_t msg; 143 int len; 144 145 while (kfifo_len(&mq->fifo) >= sizeof(msg)) { 146 len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); 147 WARN_ON(len != sizeof(msg)); 148 149 if (mq->callback) 150 mq->callback((void *)msg); 151 } 152} 153 154/* 155 * Mailbox interrupt handler 156 */ 157static void __mbox_tx_interrupt(struct omap_mbox *mbox) 158{ 159 omap_mbox_disable_irq(mbox, IRQ_TX); 160 ack_mbox_irq(mbox, IRQ_TX); 161 tasklet_schedule(&mbox->txq->tasklet); 162} 163 164static void __mbox_rx_interrupt(struct omap_mbox *mbox) 165{ 166 struct omap_mbox_queue *mq = mbox->rxq; 167 mbox_msg_t msg; 168 int len; 169 170 while (!mbox_fifo_empty(mbox)) { 171 if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { 172 omap_mbox_disable_irq(mbox, IRQ_RX); 173 rq_full = true; 174 goto nomem; 175 } 176 177 msg = mbox_fifo_read(mbox); 178 179 len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); 180 WARN_ON(len != sizeof(msg)); 181 182 if (mbox->ops->type == OMAP_MBOX_TYPE1) 183 break; 184 } 185 186 /* no more messages in the fifo. clear IRQ source. */ 187 ack_mbox_irq(mbox, IRQ_RX); 188nomem: 189 queue_work(mboxd, &mbox->rxq->work); 190} 191 192static irqreturn_t mbox_interrupt(int irq, void *p) 193{ 194 struct omap_mbox *mbox = p; 195 196 if (is_mbox_irq(mbox, IRQ_TX)) 197 __mbox_tx_interrupt(mbox); 198 199 if (is_mbox_irq(mbox, IRQ_RX)) 200 __mbox_rx_interrupt(mbox); 201 202 return IRQ_HANDLED; 203} 204 205static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, 206 void (*work) (struct work_struct *), 207 void (*tasklet)(unsigned long)) 208{ 209 struct omap_mbox_queue *mq; 210 211 mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); 212 if (!mq) 213 return NULL; 214 215 spin_lock_init(&mq->lock); 216 217 if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL)) 218 goto error; 219 220 if (work) 221 INIT_WORK(&mq->work, work); 222 223 if (tasklet) 224 tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox); 225 return mq; 226error: 227 kfree(mq); 228 return NULL; 229} 230 231static void mbox_queue_free(struct omap_mbox_queue *q) 232{ 233 kfifo_free(&q->fifo); 234 kfree(q); 235} 236 237static int omap_mbox_startup(struct omap_mbox *mbox) 238{ 239 int ret = 0; 240 struct omap_mbox_queue *mq; 241 242 if (mbox->ops->startup) { 243 mutex_lock(&mbox_configured_lock); 244 if (!mbox_configured) 245 ret = mbox->ops->startup(mbox); 246 247 if (ret) { 248 mutex_unlock(&mbox_configured_lock); 249 return ret; 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 } 262 263 mq = mbox_queue_alloc(mbox, NULL, mbox_tx_tasklet); 264 if (!mq) { 265 ret = -ENOMEM; 266 goto fail_alloc_txq; 267 } 268 mbox->txq = mq; 269 270 mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); 271 if (!mq) { 272 ret = -ENOMEM; 273 goto fail_alloc_rxq; 274 } 275 mbox->rxq = mq; 276 277 return 0; 278 279 fail_alloc_rxq: 280 mbox_queue_free(mbox->txq); 281 fail_alloc_txq: 282 free_irq(mbox->irq, mbox); 283 fail_request_irq: 284 if (mbox->ops->shutdown) 285 mbox->ops->shutdown(mbox); 286 287 return ret; 288} 289 290static void omap_mbox_fini(struct omap_mbox *mbox) 291{ 292 free_irq(mbox->irq, mbox); 293 tasklet_kill(&mbox->txq->tasklet); 294 flush_work(&mbox->rxq->work); 295 mbox_queue_free(mbox->txq); 296 mbox_queue_free(mbox->rxq); 297 298 if (mbox->ops->shutdown) { 299 mutex_lock(&mbox_configured_lock); 300 if (mbox_configured > 0) 301 mbox_configured--; 302 if (!mbox_configured) 303 mbox->ops->shutdown(mbox); 304 mutex_unlock(&mbox_configured_lock); 305 } 306} 307 308struct omap_mbox *omap_mbox_get(const char *name) 309{ 310 struct omap_mbox *mbox; 311 int ret; 312 313 if (!mboxes) 314 return ERR_PTR(-EINVAL); 315 316 for (mbox = *mboxes; mbox; mbox++) 317 if (!strcmp(mbox->name, name)) 318 break; 319 320 if (!mbox) 321 return ERR_PTR(-ENOENT); 322 323 ret = omap_mbox_startup(mbox); 324 if (ret) 325 return ERR_PTR(-ENODEV); 326 327 return mbox; 328} 329EXPORT_SYMBOL(omap_mbox_get); 330 331void omap_mbox_put(struct omap_mbox *mbox) 332{ 333 omap_mbox_fini(mbox); 334} 335EXPORT_SYMBOL(omap_mbox_put); 336 337static struct class omap_mbox_class = { .name = "mbox", }; 338 339int omap_mbox_register(struct device *parent, struct omap_mbox **list) 340{ 341 int ret; 342 int i; 343 344 mboxes = list; 345 if (!mboxes) 346 return -EINVAL; 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 } 357 return 0; 358 359err_out: 360 while (i--) 361 device_unregister(mboxes[i]->dev); 362 return ret; 363} 364EXPORT_SYMBOL(omap_mbox_register); 365 366int omap_mbox_unregister(void) 367{ 368 int i; 369 370 if (!mboxes) 371 return -EINVAL; 372 373 for (i = 0; mboxes[i]; i++) 374 device_unregister(mboxes[i]->dev); 375 mboxes = NULL; 376 return 0; 377} 378EXPORT_SYMBOL(omap_mbox_unregister); 379 380static int __init omap_mbox_init(void) 381{ 382 int err; 383 384 err = class_register(&omap_mbox_class); 385 if (err) 386 return err; 387 388 mboxd = create_workqueue("mboxd"); 389 if (!mboxd) 390 return -ENOMEM; 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 396 return 0; 397} 398subsys_initcall(omap_mbox_init); 399 400static void __exit omap_mbox_exit(void) 401{ 402 destroy_workqueue(mboxd); 403 class_unregister(&omap_mbox_class); 404} 405module_exit(omap_mbox_exit); 406 407MODULE_LICENSE("GPL v2"); 408MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); 409MODULE_AUTHOR("Toshihiro Kobayashi"); 410MODULE_AUTHOR("Hiroshi DOYU"); 411