1/* 2 * OMAP mailbox driver 3 * 4 * Copyright (C) 2006 Nokia Corporation. All rights reserved. 5 * 6 * Contact: Toshihiro Kobayashi <toshihiro.kobayashi@nokia.com> 7 * Restructured by Hiroshi DOYU <Hiroshi.DOYU@nokia.com> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * version 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 21 * 02110-1301 USA 22 * 23 */ 24 25#include <linux/init.h> 26#include <linux/module.h> 27#include <linux/sched.h> 28#include <linux/interrupt.h> 29#include <linux/device.h> 30#include <linux/blkdev.h> 31#include <linux/err.h> 32#include <linux/delay.h> 33#include <asm/io.h> 34#include <asm/arch/mailbox.h> 35#include "mailbox.h" 36 37static struct omap_mbox *mboxes; 38static DEFINE_RWLOCK(mboxes_lock); 39 40/* Mailbox Sequence Bit function */ 41void omap_mbox_init_seq(struct omap_mbox *mbox) 42{ 43 mbox_seq_init(mbox); 44} 45EXPORT_SYMBOL(omap_mbox_init_seq); 46 47/* 48 * message sender 49 */ 50static int __mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void *arg) 51{ 52 int ret = 0, i = 1000; 53 54 while (mbox_fifo_full(mbox)) { 55 if (mbox->ops->type == OMAP_MBOX_TYPE2) 56 return -1; 57 if (--i == 0) 58 return -1; 59 udelay(1); 60 } 61 62 if (arg && mbox->txq->callback) { 63 ret = mbox->txq->callback(arg); 64 if (ret) 65 goto out; 66 } 67 68 mbox_seq_toggle(mbox, &msg); 69 mbox_fifo_write(mbox, msg); 70 out: 71 return ret; 72} 73 74int omap_mbox_msg_send(struct omap_mbox *mbox, mbox_msg_t msg, void* arg) 75{ 76 struct request *rq; 77 struct request_queue *q = mbox->txq->queue; 78 int ret = 0; 79 80 rq = blk_get_request(q, WRITE, GFP_ATOMIC); 81 if (unlikely(!rq)) { 82 ret = -ENOMEM; 83 goto fail; 84 } 85 86 rq->data = (void *)msg; 87 blk_insert_request(q, rq, 0, arg); 88 89 schedule_work(&mbox->txq->work); 90 fail: 91 return ret; 92} 93EXPORT_SYMBOL(omap_mbox_msg_send); 94 95static void mbox_tx_work(struct work_struct *work) 96{ 97 int ret; 98 struct request *rq; 99 struct omap_mbox_queue *mq = container_of(work, 100 struct omap_mbox_queue, work); 101 struct omap_mbox *mbox = mq->queue->queuedata; 102 struct request_queue *q = mbox->txq->queue; 103 104 while (1) { 105 spin_lock(q->queue_lock); 106 rq = elv_next_request(q); 107 spin_unlock(q->queue_lock); 108 109 if (!rq) 110 break; 111 112 ret = __mbox_msg_send(mbox, (mbox_msg_t) rq->data, rq->special); 113 if (ret) { 114 enable_mbox_irq(mbox, IRQ_TX); 115 return; 116 } 117 118 spin_lock(q->queue_lock); 119 blkdev_dequeue_request(rq); 120 end_that_request_last(rq, 0); 121 spin_unlock(q->queue_lock); 122 } 123} 124 125/* 126 * Message receiver(workqueue) 127 */ 128static void mbox_rx_work(struct work_struct *work) 129{ 130 struct omap_mbox_queue *mq = 131 container_of(work, struct omap_mbox_queue, work); 132 struct omap_mbox *mbox = mq->queue->queuedata; 133 struct request_queue *q = mbox->rxq->queue; 134 struct request *rq; 135 mbox_msg_t msg; 136 unsigned long flags; 137 138 if (mbox->rxq->callback == NULL) { 139 sysfs_notify(&mbox->dev.kobj, NULL, "mbox"); 140 return; 141 } 142 143 while (1) { 144 spin_lock_irqsave(q->queue_lock, flags); 145 rq = elv_next_request(q); 146 spin_unlock_irqrestore(q->queue_lock, flags); 147 if (!rq) 148 break; 149 150 msg = (mbox_msg_t) rq->data; 151 152 spin_lock_irqsave(q->queue_lock, flags); 153 blkdev_dequeue_request(rq); 154 end_that_request_last(rq, 0); 155 spin_unlock_irqrestore(q->queue_lock, flags); 156 157 mbox->rxq->callback((void *)msg); 158 } 159} 160 161/* 162 * Mailbox interrupt handler 163 */ 164static void mbox_txq_fn(request_queue_t * q) 165{ 166} 167 168static void mbox_rxq_fn(request_queue_t * q) 169{ 170} 171 172static void __mbox_tx_interrupt(struct omap_mbox *mbox) 173{ 174 disable_mbox_irq(mbox, IRQ_TX); 175 ack_mbox_irq(mbox, IRQ_TX); 176 schedule_work(&mbox->txq->work); 177} 178 179static void __mbox_rx_interrupt(struct omap_mbox *mbox) 180{ 181 struct request *rq; 182 mbox_msg_t msg; 183 request_queue_t *q = mbox->rxq->queue; 184 185 disable_mbox_irq(mbox, IRQ_RX); 186 187 while (!mbox_fifo_empty(mbox)) { 188 rq = blk_get_request(q, WRITE, GFP_ATOMIC); 189 if (unlikely(!rq)) 190 goto nomem; 191 192 msg = mbox_fifo_read(mbox); 193 rq->data = (void *)msg; 194 195 if (unlikely(mbox_seq_test(mbox, msg))) { 196 pr_info("mbox: Illegal seq bit!(%08x)\n", msg); 197 if (mbox->err_notify) 198 mbox->err_notify(); 199 } 200 201 blk_insert_request(q, rq, 0, NULL); 202 if (mbox->ops->type == OMAP_MBOX_TYPE1) 203 break; 204 } 205 206 /* no more messages in the fifo. clear IRQ source. */ 207 ack_mbox_irq(mbox, IRQ_RX); 208 enable_mbox_irq(mbox, IRQ_RX); 209 nomem: 210 schedule_work(&mbox->rxq->work); 211} 212 213static irqreturn_t mbox_interrupt(int irq, void *p) 214{ 215 struct omap_mbox *mbox = (struct omap_mbox *)p; 216 217 if (is_mbox_irq(mbox, IRQ_TX)) 218 __mbox_tx_interrupt(mbox); 219 220 if (is_mbox_irq(mbox, IRQ_RX)) 221 __mbox_rx_interrupt(mbox); 222 223 return IRQ_HANDLED; 224} 225 226/* 227 * sysfs files 228 */ 229static ssize_t 230omap_mbox_write(struct device *dev, struct device_attribute *attr, 231 const char * buf, size_t count) 232{ 233 int ret; 234 mbox_msg_t *p = (mbox_msg_t *)buf; 235 struct omap_mbox *mbox = dev_get_drvdata(dev); 236 237 for (; count >= sizeof(mbox_msg_t); count -= sizeof(mbox_msg_t)) { 238 ret = omap_mbox_msg_send(mbox, be32_to_cpu(*p), NULL); 239 if (ret) 240 return -EAGAIN; 241 p++; 242 } 243 244 return (size_t)((char *)p - buf); 245} 246 247static ssize_t 248omap_mbox_read(struct device *dev, struct device_attribute *attr, char *buf) 249{ 250 unsigned long flags; 251 struct request *rq; 252 mbox_msg_t *p = (mbox_msg_t *) buf; 253 struct omap_mbox *mbox = dev_get_drvdata(dev); 254 struct request_queue *q = mbox->rxq->queue; 255 256 while (1) { 257 spin_lock_irqsave(q->queue_lock, flags); 258 rq = elv_next_request(q); 259 spin_unlock_irqrestore(q->queue_lock, flags); 260 261 if (!rq) 262 break; 263 264 *p = (mbox_msg_t) rq->data; 265 266 spin_lock_irqsave(q->queue_lock, flags); 267 blkdev_dequeue_request(rq); 268 end_that_request_last(rq, 0); 269 spin_unlock_irqrestore(q->queue_lock, flags); 270 271 if (unlikely(mbox_seq_test(mbox, *p))) { 272 pr_info("mbox: Illegal seq bit!(%08x) ignored\n", *p); 273 continue; 274 } 275 p++; 276 } 277 278 pr_debug("%02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); 279 280 return (size_t) ((char *)p - buf); 281} 282 283static DEVICE_ATTR(mbox, S_IRUGO | S_IWUSR, omap_mbox_read, omap_mbox_write); 284 285static ssize_t mbox_show(struct class *class, char *buf) 286{ 287 return sprintf(buf, "mbox"); 288} 289 290static CLASS_ATTR(mbox, S_IRUGO, mbox_show, NULL); 291 292static struct class omap_mbox_class = { 293 .name = "omap_mbox", 294}; 295 296static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, 297 request_fn_proc * proc, 298 void (*work) (struct work_struct *)) 299{ 300 request_queue_t *q; 301 struct omap_mbox_queue *mq; 302 303 mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); 304 if (!mq) 305 return NULL; 306 307 spin_lock_init(&mq->lock); 308 309 q = blk_init_queue(proc, &mq->lock); 310 if (!q) 311 goto error; 312 q->queuedata = mbox; 313 mq->queue = q; 314 315 INIT_WORK(&mq->work, work); 316 317 return mq; 318error: 319 kfree(mq); 320 return NULL; 321} 322 323static void mbox_queue_free(struct omap_mbox_queue *q) 324{ 325 blk_cleanup_queue(q->queue); 326 kfree(q); 327} 328 329static int omap_mbox_init(struct omap_mbox *mbox) 330{ 331 int ret; 332 struct omap_mbox_queue *mq; 333 334 if (likely(mbox->ops->startup)) { 335 ret = mbox->ops->startup(mbox); 336 if (unlikely(ret)) 337 return ret; 338 } 339 340 mbox->dev.class = &omap_mbox_class; 341 strlcpy(mbox->dev.bus_id, mbox->name, KOBJ_NAME_LEN); 342 dev_set_drvdata(&mbox->dev, mbox); 343 344 ret = device_register(&mbox->dev); 345 if (unlikely(ret)) 346 goto fail_device_reg; 347 348 ret = device_create_file(&mbox->dev, &dev_attr_mbox); 349 if (unlikely(ret)) { 350 printk(KERN_ERR 351 "device_create_file failed: %d\n", ret); 352 goto fail_create_mbox; 353 } 354 355 ret = request_irq(mbox->irq, mbox_interrupt, IRQF_DISABLED, 356 mbox->name, mbox); 357 if (unlikely(ret)) { 358 printk(KERN_ERR 359 "failed to register mailbox interrupt:%d\n", ret); 360 goto fail_request_irq; 361 } 362 enable_mbox_irq(mbox, IRQ_RX); 363 364 mq = mbox_queue_alloc(mbox, mbox_txq_fn, mbox_tx_work); 365 if (!mq) { 366 ret = -ENOMEM; 367 goto fail_alloc_txq; 368 } 369 mbox->txq = mq; 370 371 mq = mbox_queue_alloc(mbox, mbox_rxq_fn, mbox_rx_work); 372 if (!mq) { 373 ret = -ENOMEM; 374 goto fail_alloc_rxq; 375 } 376 mbox->rxq = mq; 377 378 return 0; 379 380 fail_alloc_rxq: 381 mbox_queue_free(mbox->txq); 382 fail_alloc_txq: 383 free_irq(mbox->irq, mbox); 384 fail_request_irq: 385 device_remove_file(&mbox->dev, &dev_attr_mbox); 386 fail_create_mbox: 387 device_unregister(&mbox->dev); 388 fail_device_reg: 389 if (unlikely(mbox->ops->shutdown)) 390 mbox->ops->shutdown(mbox); 391 392 return ret; 393} 394 395static void omap_mbox_fini(struct omap_mbox *mbox) 396{ 397 mbox_queue_free(mbox->txq); 398 mbox_queue_free(mbox->rxq); 399 400 free_irq(mbox->irq, mbox); 401 device_remove_file(&mbox->dev, &dev_attr_mbox); 402 class_unregister(&omap_mbox_class); 403 404 if (unlikely(mbox->ops->shutdown)) 405 mbox->ops->shutdown(mbox); 406} 407 408static struct omap_mbox **find_mboxes(const char *name) 409{ 410 struct omap_mbox **p; 411 412 for (p = &mboxes; *p; p = &(*p)->next) { 413 if (strcmp((*p)->name, name) == 0) 414 break; 415 } 416 417 return p; 418} 419 420struct omap_mbox *omap_mbox_get(const char *name) 421{ 422 struct omap_mbox *mbox; 423 int ret; 424 425 read_lock(&mboxes_lock); 426 mbox = *(find_mboxes(name)); 427 if (mbox == NULL) { 428 read_unlock(&mboxes_lock); 429 return ERR_PTR(-ENOENT); 430 } 431 432 read_unlock(&mboxes_lock); 433 434 ret = omap_mbox_init(mbox); 435 if (ret) 436 return ERR_PTR(-ENODEV); 437 438 return mbox; 439} 440EXPORT_SYMBOL(omap_mbox_get); 441 442void omap_mbox_put(struct omap_mbox *mbox) 443{ 444 omap_mbox_fini(mbox); 445} 446EXPORT_SYMBOL(omap_mbox_put); 447 448int omap_mbox_register(struct omap_mbox *mbox) 449{ 450 int ret = 0; 451 struct omap_mbox **tmp; 452 453 if (!mbox) 454 return -EINVAL; 455 if (mbox->next) 456 return -EBUSY; 457 458 write_lock(&mboxes_lock); 459 tmp = find_mboxes(mbox->name); 460 if (*tmp) 461 ret = -EBUSY; 462 else 463 *tmp = mbox; 464 write_unlock(&mboxes_lock); 465 466 return ret; 467} 468EXPORT_SYMBOL(omap_mbox_register); 469 470int omap_mbox_unregister(struct omap_mbox *mbox) 471{ 472 struct omap_mbox **tmp; 473 474 write_lock(&mboxes_lock); 475 tmp = &mboxes; 476 while (*tmp) { 477 if (mbox == *tmp) { 478 *tmp = mbox->next; 479 mbox->next = NULL; 480 write_unlock(&mboxes_lock); 481 return 0; 482 } 483 tmp = &(*tmp)->next; 484 } 485 write_unlock(&mboxes_lock); 486 487 return -EINVAL; 488} 489EXPORT_SYMBOL(omap_mbox_unregister); 490 491static int __init omap_mbox_class_init(void) 492{ 493 int ret = class_register(&omap_mbox_class); 494 if (!ret) 495 ret = class_create_file(&omap_mbox_class, &class_attr_mbox); 496 497 return ret; 498} 499 500static void __exit omap_mbox_class_exit(void) 501{ 502 class_remove_file(&omap_mbox_class, &class_attr_mbox); 503 class_unregister(&omap_mbox_class); 504} 505 506subsys_initcall(omap_mbox_class_init); 507module_exit(omap_mbox_class_exit); 508 509MODULE_LICENSE("GPL"); 510