1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * OMAP mailbox driver |
4 | * |
5 | * Copyright (C) 2006-2009 Nokia Corporation. All rights reserved. |
6 | * Copyright (C) 2013-2021 Texas Instruments Incorporated - https://www.ti.com |
7 | * |
8 | * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com> |
9 | * Suman Anna <s-anna@ti.com> |
10 | */ |
11 | |
12 | #include <linux/interrupt.h> |
13 | #include <linux/spinlock.h> |
14 | #include <linux/mutex.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/kfifo.h> |
17 | #include <linux/err.h> |
18 | #include <linux/module.h> |
19 | #include <linux/of.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/pm_runtime.h> |
22 | #include <linux/omap-mailbox.h> |
23 | #include <linux/mailbox_controller.h> |
24 | #include <linux/mailbox_client.h> |
25 | |
26 | #include "mailbox.h" |
27 | |
28 | #define MAILBOX_REVISION 0x000 |
29 | #define MAILBOX_MESSAGE(m) (0x040 + 4 * (m)) |
30 | #define MAILBOX_FIFOSTATUS(m) (0x080 + 4 * (m)) |
31 | #define MAILBOX_MSGSTATUS(m) (0x0c0 + 4 * (m)) |
32 | |
33 | #define OMAP2_MAILBOX_IRQSTATUS(u) (0x100 + 8 * (u)) |
34 | #define OMAP2_MAILBOX_IRQENABLE(u) (0x104 + 8 * (u)) |
35 | |
36 | #define OMAP4_MAILBOX_IRQSTATUS(u) (0x104 + 0x10 * (u)) |
37 | #define OMAP4_MAILBOX_IRQENABLE(u) (0x108 + 0x10 * (u)) |
38 | #define OMAP4_MAILBOX_IRQENABLE_CLR(u) (0x10c + 0x10 * (u)) |
39 | |
40 | #define MAILBOX_IRQSTATUS(type, u) (type ? OMAP4_MAILBOX_IRQSTATUS(u) : \ |
41 | OMAP2_MAILBOX_IRQSTATUS(u)) |
42 | #define MAILBOX_IRQENABLE(type, u) (type ? OMAP4_MAILBOX_IRQENABLE(u) : \ |
43 | OMAP2_MAILBOX_IRQENABLE(u)) |
44 | #define MAILBOX_IRQDISABLE(type, u) (type ? OMAP4_MAILBOX_IRQENABLE_CLR(u) \ |
45 | : OMAP2_MAILBOX_IRQENABLE(u)) |
46 | |
47 | #define MAILBOX_IRQ_NEWMSG(m) (1 << (2 * (m))) |
48 | #define MAILBOX_IRQ_NOTFULL(m) (1 << (2 * (m) + 1)) |
49 | |
50 | /* Interrupt register configuration types */ |
51 | #define MBOX_INTR_CFG_TYPE1 0 |
52 | #define MBOX_INTR_CFG_TYPE2 1 |
53 | |
54 | struct omap_mbox_fifo { |
55 | unsigned long msg; |
56 | unsigned long fifo_stat; |
57 | unsigned long msg_stat; |
58 | unsigned long irqenable; |
59 | unsigned long irqstatus; |
60 | unsigned long irqdisable; |
61 | u32 intr_bit; |
62 | }; |
63 | |
64 | struct omap_mbox_queue { |
65 | spinlock_t lock; |
66 | struct kfifo fifo; |
67 | struct work_struct work; |
68 | struct omap_mbox *mbox; |
69 | bool full; |
70 | }; |
71 | |
72 | struct omap_mbox_match_data { |
73 | u32 intr_type; |
74 | }; |
75 | |
76 | struct omap_mbox_device { |
77 | struct device *dev; |
78 | struct mutex cfg_lock; |
79 | void __iomem *mbox_base; |
80 | u32 *irq_ctx; |
81 | u32 num_users; |
82 | u32 num_fifos; |
83 | u32 intr_type; |
84 | struct omap_mbox **mboxes; |
85 | struct mbox_controller controller; |
86 | struct list_head elem; |
87 | }; |
88 | |
89 | struct omap_mbox_fifo_info { |
90 | int tx_id; |
91 | int tx_usr; |
92 | int tx_irq; |
93 | |
94 | int rx_id; |
95 | int rx_usr; |
96 | int rx_irq; |
97 | |
98 | const char *name; |
99 | bool send_no_irq; |
100 | }; |
101 | |
102 | struct omap_mbox { |
103 | const char *name; |
104 | int irq; |
105 | struct omap_mbox_queue *rxq; |
106 | struct device *dev; |
107 | struct omap_mbox_device *parent; |
108 | struct omap_mbox_fifo tx_fifo; |
109 | struct omap_mbox_fifo rx_fifo; |
110 | u32 intr_type; |
111 | struct mbox_chan *chan; |
112 | bool send_no_irq; |
113 | }; |
114 | |
115 | /* global variables for the mailbox devices */ |
116 | static DEFINE_MUTEX(omap_mbox_devices_lock); |
117 | static LIST_HEAD(omap_mbox_devices); |
118 | |
119 | static unsigned int mbox_kfifo_size = CONFIG_OMAP_MBOX_KFIFO_SIZE; |
120 | module_param(mbox_kfifo_size, uint, S_IRUGO); |
121 | MODULE_PARM_DESC(mbox_kfifo_size, "Size of omap's mailbox kfifo (bytes)" ); |
122 | |
123 | static struct omap_mbox *mbox_chan_to_omap_mbox(struct mbox_chan *chan) |
124 | { |
125 | if (!chan || !chan->con_priv) |
126 | return NULL; |
127 | |
128 | return (struct omap_mbox *)chan->con_priv; |
129 | } |
130 | |
131 | static inline |
132 | unsigned int mbox_read_reg(struct omap_mbox_device *mdev, size_t ofs) |
133 | { |
134 | return __raw_readl(addr: mdev->mbox_base + ofs); |
135 | } |
136 | |
137 | static inline |
138 | void mbox_write_reg(struct omap_mbox_device *mdev, u32 val, size_t ofs) |
139 | { |
140 | __raw_writel(val, addr: mdev->mbox_base + ofs); |
141 | } |
142 | |
143 | /* Mailbox FIFO handle functions */ |
144 | static u32 mbox_fifo_read(struct omap_mbox *mbox) |
145 | { |
146 | struct omap_mbox_fifo *fifo = &mbox->rx_fifo; |
147 | |
148 | return mbox_read_reg(mdev: mbox->parent, ofs: fifo->msg); |
149 | } |
150 | |
151 | static void mbox_fifo_write(struct omap_mbox *mbox, u32 msg) |
152 | { |
153 | struct omap_mbox_fifo *fifo = &mbox->tx_fifo; |
154 | |
155 | mbox_write_reg(mdev: mbox->parent, val: msg, ofs: fifo->msg); |
156 | } |
157 | |
158 | static int mbox_fifo_empty(struct omap_mbox *mbox) |
159 | { |
160 | struct omap_mbox_fifo *fifo = &mbox->rx_fifo; |
161 | |
162 | return (mbox_read_reg(mdev: mbox->parent, ofs: fifo->msg_stat) == 0); |
163 | } |
164 | |
165 | static int mbox_fifo_full(struct omap_mbox *mbox) |
166 | { |
167 | struct omap_mbox_fifo *fifo = &mbox->tx_fifo; |
168 | |
169 | return mbox_read_reg(mdev: mbox->parent, ofs: fifo->fifo_stat); |
170 | } |
171 | |
172 | /* Mailbox IRQ handle functions */ |
173 | static void ack_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) |
174 | { |
175 | struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? |
176 | &mbox->tx_fifo : &mbox->rx_fifo; |
177 | u32 bit = fifo->intr_bit; |
178 | u32 irqstatus = fifo->irqstatus; |
179 | |
180 | mbox_write_reg(mdev: mbox->parent, val: bit, ofs: irqstatus); |
181 | |
182 | /* Flush posted write for irq status to avoid spurious interrupts */ |
183 | mbox_read_reg(mdev: mbox->parent, ofs: irqstatus); |
184 | } |
185 | |
186 | static int is_mbox_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) |
187 | { |
188 | struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? |
189 | &mbox->tx_fifo : &mbox->rx_fifo; |
190 | u32 bit = fifo->intr_bit; |
191 | u32 irqenable = fifo->irqenable; |
192 | u32 irqstatus = fifo->irqstatus; |
193 | |
194 | u32 enable = mbox_read_reg(mdev: mbox->parent, ofs: irqenable); |
195 | u32 status = mbox_read_reg(mdev: mbox->parent, ofs: irqstatus); |
196 | |
197 | return (int)(enable & status & bit); |
198 | } |
199 | |
200 | static void _omap_mbox_enable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) |
201 | { |
202 | u32 l; |
203 | struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? |
204 | &mbox->tx_fifo : &mbox->rx_fifo; |
205 | u32 bit = fifo->intr_bit; |
206 | u32 irqenable = fifo->irqenable; |
207 | |
208 | l = mbox_read_reg(mdev: mbox->parent, ofs: irqenable); |
209 | l |= bit; |
210 | mbox_write_reg(mdev: mbox->parent, val: l, ofs: irqenable); |
211 | } |
212 | |
213 | static void _omap_mbox_disable_irq(struct omap_mbox *mbox, omap_mbox_irq_t irq) |
214 | { |
215 | struct omap_mbox_fifo *fifo = (irq == IRQ_TX) ? |
216 | &mbox->tx_fifo : &mbox->rx_fifo; |
217 | u32 bit = fifo->intr_bit; |
218 | u32 irqdisable = fifo->irqdisable; |
219 | |
220 | /* |
221 | * Read and update the interrupt configuration register for pre-OMAP4. |
222 | * OMAP4 and later SoCs have a dedicated interrupt disabling register. |
223 | */ |
224 | if (!mbox->intr_type) |
225 | bit = mbox_read_reg(mdev: mbox->parent, ofs: irqdisable) & ~bit; |
226 | |
227 | mbox_write_reg(mdev: mbox->parent, val: bit, ofs: irqdisable); |
228 | } |
229 | |
230 | void omap_mbox_enable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq) |
231 | { |
232 | struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); |
233 | |
234 | if (WARN_ON(!mbox)) |
235 | return; |
236 | |
237 | _omap_mbox_enable_irq(mbox, irq); |
238 | } |
239 | EXPORT_SYMBOL(omap_mbox_enable_irq); |
240 | |
241 | void omap_mbox_disable_irq(struct mbox_chan *chan, omap_mbox_irq_t irq) |
242 | { |
243 | struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); |
244 | |
245 | if (WARN_ON(!mbox)) |
246 | return; |
247 | |
248 | _omap_mbox_disable_irq(mbox, irq); |
249 | } |
250 | EXPORT_SYMBOL(omap_mbox_disable_irq); |
251 | |
252 | /* |
253 | * Message receiver(workqueue) |
254 | */ |
255 | static void mbox_rx_work(struct work_struct *work) |
256 | { |
257 | struct omap_mbox_queue *mq = |
258 | container_of(work, struct omap_mbox_queue, work); |
259 | mbox_msg_t data; |
260 | u32 msg; |
261 | int len; |
262 | |
263 | while (kfifo_len(&mq->fifo) >= sizeof(msg)) { |
264 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); |
265 | WARN_ON(len != sizeof(msg)); |
266 | data = msg; |
267 | |
268 | mbox_chan_received_data(chan: mq->mbox->chan, data: (void *)data); |
269 | spin_lock_irq(lock: &mq->lock); |
270 | if (mq->full) { |
271 | mq->full = false; |
272 | _omap_mbox_enable_irq(mbox: mq->mbox, IRQ_RX); |
273 | } |
274 | spin_unlock_irq(lock: &mq->lock); |
275 | } |
276 | } |
277 | |
278 | /* |
279 | * Mailbox interrupt handler |
280 | */ |
281 | static void __mbox_tx_interrupt(struct omap_mbox *mbox) |
282 | { |
283 | _omap_mbox_disable_irq(mbox, IRQ_TX); |
284 | ack_mbox_irq(mbox, IRQ_TX); |
285 | mbox_chan_txdone(chan: mbox->chan, r: 0); |
286 | } |
287 | |
288 | static void __mbox_rx_interrupt(struct omap_mbox *mbox) |
289 | { |
290 | struct omap_mbox_queue *mq = mbox->rxq; |
291 | u32 msg; |
292 | int len; |
293 | |
294 | while (!mbox_fifo_empty(mbox)) { |
295 | if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { |
296 | _omap_mbox_disable_irq(mbox, IRQ_RX); |
297 | mq->full = true; |
298 | goto nomem; |
299 | } |
300 | |
301 | msg = mbox_fifo_read(mbox); |
302 | |
303 | len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); |
304 | WARN_ON(len != sizeof(msg)); |
305 | } |
306 | |
307 | /* no more messages in the fifo. clear IRQ source. */ |
308 | ack_mbox_irq(mbox, IRQ_RX); |
309 | nomem: |
310 | schedule_work(work: &mbox->rxq->work); |
311 | } |
312 | |
313 | static irqreturn_t mbox_interrupt(int irq, void *p) |
314 | { |
315 | struct omap_mbox *mbox = p; |
316 | |
317 | if (is_mbox_irq(mbox, IRQ_TX)) |
318 | __mbox_tx_interrupt(mbox); |
319 | |
320 | if (is_mbox_irq(mbox, IRQ_RX)) |
321 | __mbox_rx_interrupt(mbox); |
322 | |
323 | return IRQ_HANDLED; |
324 | } |
325 | |
326 | static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, |
327 | void (*work)(struct work_struct *)) |
328 | { |
329 | struct omap_mbox_queue *mq; |
330 | |
331 | if (!work) |
332 | return NULL; |
333 | |
334 | mq = kzalloc(size: sizeof(*mq), GFP_KERNEL); |
335 | if (!mq) |
336 | return NULL; |
337 | |
338 | spin_lock_init(&mq->lock); |
339 | |
340 | if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL)) |
341 | goto error; |
342 | |
343 | INIT_WORK(&mq->work, work); |
344 | return mq; |
345 | |
346 | error: |
347 | kfree(objp: mq); |
348 | return NULL; |
349 | } |
350 | |
351 | static void mbox_queue_free(struct omap_mbox_queue *q) |
352 | { |
353 | kfifo_free(&q->fifo); |
354 | kfree(objp: q); |
355 | } |
356 | |
357 | static int omap_mbox_startup(struct omap_mbox *mbox) |
358 | { |
359 | int ret = 0; |
360 | struct omap_mbox_queue *mq; |
361 | |
362 | mq = mbox_queue_alloc(mbox, work: mbox_rx_work); |
363 | if (!mq) |
364 | return -ENOMEM; |
365 | mbox->rxq = mq; |
366 | mq->mbox = mbox; |
367 | |
368 | ret = request_irq(irq: mbox->irq, handler: mbox_interrupt, IRQF_SHARED, |
369 | name: mbox->name, dev: mbox); |
370 | if (unlikely(ret)) { |
371 | pr_err("failed to register mailbox interrupt:%d\n" , ret); |
372 | goto fail_request_irq; |
373 | } |
374 | |
375 | if (mbox->send_no_irq) |
376 | mbox->chan->txdone_method = TXDONE_BY_ACK; |
377 | |
378 | _omap_mbox_enable_irq(mbox, IRQ_RX); |
379 | |
380 | return 0; |
381 | |
382 | fail_request_irq: |
383 | mbox_queue_free(q: mbox->rxq); |
384 | return ret; |
385 | } |
386 | |
387 | static void omap_mbox_fini(struct omap_mbox *mbox) |
388 | { |
389 | _omap_mbox_disable_irq(mbox, IRQ_RX); |
390 | free_irq(mbox->irq, mbox); |
391 | flush_work(work: &mbox->rxq->work); |
392 | mbox_queue_free(q: mbox->rxq); |
393 | } |
394 | |
395 | static struct omap_mbox *omap_mbox_device_find(struct omap_mbox_device *mdev, |
396 | const char *mbox_name) |
397 | { |
398 | struct omap_mbox *_mbox, *mbox = NULL; |
399 | struct omap_mbox **mboxes = mdev->mboxes; |
400 | int i; |
401 | |
402 | if (!mboxes) |
403 | return NULL; |
404 | |
405 | for (i = 0; (_mbox = mboxes[i]); i++) { |
406 | if (!strcmp(_mbox->name, mbox_name)) { |
407 | mbox = _mbox; |
408 | break; |
409 | } |
410 | } |
411 | return mbox; |
412 | } |
413 | |
414 | struct mbox_chan *omap_mbox_request_channel(struct mbox_client *cl, |
415 | const char *chan_name) |
416 | { |
417 | struct device *dev = cl->dev; |
418 | struct omap_mbox *mbox = NULL; |
419 | struct omap_mbox_device *mdev; |
420 | int ret; |
421 | |
422 | if (!dev) |
423 | return ERR_PTR(error: -ENODEV); |
424 | |
425 | if (dev->of_node) { |
426 | pr_err("%s: please use mbox_request_channel(), this API is supported only for OMAP non-DT usage\n" , |
427 | __func__); |
428 | return ERR_PTR(error: -ENODEV); |
429 | } |
430 | |
431 | mutex_lock(&omap_mbox_devices_lock); |
432 | list_for_each_entry(mdev, &omap_mbox_devices, elem) { |
433 | mbox = omap_mbox_device_find(mdev, mbox_name: chan_name); |
434 | if (mbox) |
435 | break; |
436 | } |
437 | mutex_unlock(lock: &omap_mbox_devices_lock); |
438 | |
439 | if (!mbox || !mbox->chan) |
440 | return ERR_PTR(error: -ENOENT); |
441 | |
442 | ret = mbox_bind_client(chan: mbox->chan, cl); |
443 | if (ret) |
444 | return ERR_PTR(error: ret); |
445 | |
446 | return mbox->chan; |
447 | } |
448 | EXPORT_SYMBOL(omap_mbox_request_channel); |
449 | |
450 | static struct class omap_mbox_class = { .name = "mbox" , }; |
451 | |
452 | static int omap_mbox_register(struct omap_mbox_device *mdev) |
453 | { |
454 | int ret; |
455 | int i; |
456 | struct omap_mbox **mboxes; |
457 | |
458 | if (!mdev || !mdev->mboxes) |
459 | return -EINVAL; |
460 | |
461 | mboxes = mdev->mboxes; |
462 | for (i = 0; mboxes[i]; i++) { |
463 | struct omap_mbox *mbox = mboxes[i]; |
464 | |
465 | mbox->dev = device_create(cls: &omap_mbox_class, parent: mdev->dev, |
466 | devt: 0, drvdata: mbox, fmt: "%s" , mbox->name); |
467 | if (IS_ERR(ptr: mbox->dev)) { |
468 | ret = PTR_ERR(ptr: mbox->dev); |
469 | goto err_out; |
470 | } |
471 | } |
472 | |
473 | mutex_lock(&omap_mbox_devices_lock); |
474 | list_add(new: &mdev->elem, head: &omap_mbox_devices); |
475 | mutex_unlock(lock: &omap_mbox_devices_lock); |
476 | |
477 | ret = devm_mbox_controller_register(dev: mdev->dev, mbox: &mdev->controller); |
478 | |
479 | err_out: |
480 | if (ret) { |
481 | while (i--) |
482 | device_unregister(dev: mboxes[i]->dev); |
483 | } |
484 | return ret; |
485 | } |
486 | |
487 | static int omap_mbox_unregister(struct omap_mbox_device *mdev) |
488 | { |
489 | int i; |
490 | struct omap_mbox **mboxes; |
491 | |
492 | if (!mdev || !mdev->mboxes) |
493 | return -EINVAL; |
494 | |
495 | mutex_lock(&omap_mbox_devices_lock); |
496 | list_del(entry: &mdev->elem); |
497 | mutex_unlock(lock: &omap_mbox_devices_lock); |
498 | |
499 | mboxes = mdev->mboxes; |
500 | for (i = 0; mboxes[i]; i++) |
501 | device_unregister(dev: mboxes[i]->dev); |
502 | return 0; |
503 | } |
504 | |
505 | static int omap_mbox_chan_startup(struct mbox_chan *chan) |
506 | { |
507 | struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); |
508 | struct omap_mbox_device *mdev = mbox->parent; |
509 | int ret = 0; |
510 | |
511 | mutex_lock(&mdev->cfg_lock); |
512 | pm_runtime_get_sync(dev: mdev->dev); |
513 | ret = omap_mbox_startup(mbox); |
514 | if (ret) |
515 | pm_runtime_put_sync(dev: mdev->dev); |
516 | mutex_unlock(lock: &mdev->cfg_lock); |
517 | return ret; |
518 | } |
519 | |
520 | static void omap_mbox_chan_shutdown(struct mbox_chan *chan) |
521 | { |
522 | struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); |
523 | struct omap_mbox_device *mdev = mbox->parent; |
524 | |
525 | mutex_lock(&mdev->cfg_lock); |
526 | omap_mbox_fini(mbox); |
527 | pm_runtime_put_sync(dev: mdev->dev); |
528 | mutex_unlock(lock: &mdev->cfg_lock); |
529 | } |
530 | |
531 | static int omap_mbox_chan_send_noirq(struct omap_mbox *mbox, u32 msg) |
532 | { |
533 | int ret = -EBUSY; |
534 | |
535 | if (!mbox_fifo_full(mbox)) { |
536 | _omap_mbox_enable_irq(mbox, IRQ_RX); |
537 | mbox_fifo_write(mbox, msg); |
538 | ret = 0; |
539 | _omap_mbox_disable_irq(mbox, IRQ_RX); |
540 | |
541 | /* we must read and ack the interrupt directly from here */ |
542 | mbox_fifo_read(mbox); |
543 | ack_mbox_irq(mbox, IRQ_RX); |
544 | } |
545 | |
546 | return ret; |
547 | } |
548 | |
549 | static int omap_mbox_chan_send(struct omap_mbox *mbox, u32 msg) |
550 | { |
551 | int ret = -EBUSY; |
552 | |
553 | if (!mbox_fifo_full(mbox)) { |
554 | mbox_fifo_write(mbox, msg); |
555 | ret = 0; |
556 | } |
557 | |
558 | /* always enable the interrupt */ |
559 | _omap_mbox_enable_irq(mbox, IRQ_TX); |
560 | return ret; |
561 | } |
562 | |
563 | static int omap_mbox_chan_send_data(struct mbox_chan *chan, void *data) |
564 | { |
565 | struct omap_mbox *mbox = mbox_chan_to_omap_mbox(chan); |
566 | int ret; |
567 | u32 msg = omap_mbox_message(data); |
568 | |
569 | if (!mbox) |
570 | return -EINVAL; |
571 | |
572 | if (mbox->send_no_irq) |
573 | ret = omap_mbox_chan_send_noirq(mbox, msg); |
574 | else |
575 | ret = omap_mbox_chan_send(mbox, msg); |
576 | |
577 | return ret; |
578 | } |
579 | |
580 | static const struct mbox_chan_ops omap_mbox_chan_ops = { |
581 | .startup = omap_mbox_chan_startup, |
582 | .send_data = omap_mbox_chan_send_data, |
583 | .shutdown = omap_mbox_chan_shutdown, |
584 | }; |
585 | |
586 | #ifdef CONFIG_PM_SLEEP |
587 | static int omap_mbox_suspend(struct device *dev) |
588 | { |
589 | struct omap_mbox_device *mdev = dev_get_drvdata(dev); |
590 | u32 usr, fifo, reg; |
591 | |
592 | if (pm_runtime_status_suspended(dev)) |
593 | return 0; |
594 | |
595 | for (fifo = 0; fifo < mdev->num_fifos; fifo++) { |
596 | if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) { |
597 | dev_err(mdev->dev, "fifo %d has unexpected unread messages\n" , |
598 | fifo); |
599 | return -EBUSY; |
600 | } |
601 | } |
602 | |
603 | for (usr = 0; usr < mdev->num_users; usr++) { |
604 | reg = MAILBOX_IRQENABLE(mdev->intr_type, usr); |
605 | mdev->irq_ctx[usr] = mbox_read_reg(mdev, ofs: reg); |
606 | } |
607 | |
608 | return 0; |
609 | } |
610 | |
611 | static int omap_mbox_resume(struct device *dev) |
612 | { |
613 | struct omap_mbox_device *mdev = dev_get_drvdata(dev); |
614 | u32 usr, reg; |
615 | |
616 | if (pm_runtime_status_suspended(dev)) |
617 | return 0; |
618 | |
619 | for (usr = 0; usr < mdev->num_users; usr++) { |
620 | reg = MAILBOX_IRQENABLE(mdev->intr_type, usr); |
621 | mbox_write_reg(mdev, val: mdev->irq_ctx[usr], ofs: reg); |
622 | } |
623 | |
624 | return 0; |
625 | } |
626 | #endif |
627 | |
628 | static const struct dev_pm_ops omap_mbox_pm_ops = { |
629 | SET_SYSTEM_SLEEP_PM_OPS(omap_mbox_suspend, omap_mbox_resume) |
630 | }; |
631 | |
632 | static const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1 }; |
633 | static const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2 }; |
634 | |
635 | static const struct of_device_id omap_mailbox_of_match[] = { |
636 | { |
637 | .compatible = "ti,omap2-mailbox" , |
638 | .data = &omap2_data, |
639 | }, |
640 | { |
641 | .compatible = "ti,omap3-mailbox" , |
642 | .data = &omap2_data, |
643 | }, |
644 | { |
645 | .compatible = "ti,omap4-mailbox" , |
646 | .data = &omap4_data, |
647 | }, |
648 | { |
649 | .compatible = "ti,am654-mailbox" , |
650 | .data = &omap4_data, |
651 | }, |
652 | { |
653 | .compatible = "ti,am64-mailbox" , |
654 | .data = &omap4_data, |
655 | }, |
656 | { |
657 | /* end */ |
658 | }, |
659 | }; |
660 | MODULE_DEVICE_TABLE(of, omap_mailbox_of_match); |
661 | |
662 | static struct mbox_chan *omap_mbox_of_xlate(struct mbox_controller *controller, |
663 | const struct of_phandle_args *sp) |
664 | { |
665 | phandle phandle = sp->args[0]; |
666 | struct device_node *node; |
667 | struct omap_mbox_device *mdev; |
668 | struct omap_mbox *mbox; |
669 | |
670 | mdev = container_of(controller, struct omap_mbox_device, controller); |
671 | if (WARN_ON(!mdev)) |
672 | return ERR_PTR(error: -EINVAL); |
673 | |
674 | node = of_find_node_by_phandle(handle: phandle); |
675 | if (!node) { |
676 | pr_err("%s: could not find node phandle 0x%x\n" , |
677 | __func__, phandle); |
678 | return ERR_PTR(error: -ENODEV); |
679 | } |
680 | |
681 | mbox = omap_mbox_device_find(mdev, mbox_name: node->name); |
682 | of_node_put(node); |
683 | return mbox ? mbox->chan : ERR_PTR(error: -ENOENT); |
684 | } |
685 | |
686 | static int omap_mbox_probe(struct platform_device *pdev) |
687 | { |
688 | int ret; |
689 | struct mbox_chan *chnls; |
690 | struct omap_mbox **list, *mbox, *mboxblk; |
691 | struct omap_mbox_fifo_info *finfo, *finfoblk; |
692 | struct omap_mbox_device *mdev; |
693 | struct omap_mbox_fifo *fifo; |
694 | struct device_node *node = pdev->dev.of_node; |
695 | struct device_node *child; |
696 | const struct omap_mbox_match_data *match_data; |
697 | u32 intr_type, info_count; |
698 | u32 num_users, num_fifos; |
699 | u32 tmp[3]; |
700 | u32 l; |
701 | int i; |
702 | |
703 | if (!node) { |
704 | pr_err("%s: only DT-based devices are supported\n" , __func__); |
705 | return -ENODEV; |
706 | } |
707 | |
708 | match_data = of_device_get_match_data(dev: &pdev->dev); |
709 | if (!match_data) |
710 | return -ENODEV; |
711 | intr_type = match_data->intr_type; |
712 | |
713 | if (of_property_read_u32(np: node, propname: "ti,mbox-num-users" , out_value: &num_users)) |
714 | return -ENODEV; |
715 | |
716 | if (of_property_read_u32(np: node, propname: "ti,mbox-num-fifos" , out_value: &num_fifos)) |
717 | return -ENODEV; |
718 | |
719 | info_count = of_get_available_child_count(np: node); |
720 | if (!info_count) { |
721 | dev_err(&pdev->dev, "no available mbox devices found\n" ); |
722 | return -ENODEV; |
723 | } |
724 | |
725 | finfoblk = devm_kcalloc(dev: &pdev->dev, n: info_count, size: sizeof(*finfoblk), |
726 | GFP_KERNEL); |
727 | if (!finfoblk) |
728 | return -ENOMEM; |
729 | |
730 | finfo = finfoblk; |
731 | child = NULL; |
732 | for (i = 0; i < info_count; i++, finfo++) { |
733 | child = of_get_next_available_child(node, prev: child); |
734 | ret = of_property_read_u32_array(np: child, propname: "ti,mbox-tx" , out_values: tmp, |
735 | ARRAY_SIZE(tmp)); |
736 | if (ret) |
737 | return ret; |
738 | finfo->tx_id = tmp[0]; |
739 | finfo->tx_irq = tmp[1]; |
740 | finfo->tx_usr = tmp[2]; |
741 | |
742 | ret = of_property_read_u32_array(np: child, propname: "ti,mbox-rx" , out_values: tmp, |
743 | ARRAY_SIZE(tmp)); |
744 | if (ret) |
745 | return ret; |
746 | finfo->rx_id = tmp[0]; |
747 | finfo->rx_irq = tmp[1]; |
748 | finfo->rx_usr = tmp[2]; |
749 | |
750 | finfo->name = child->name; |
751 | |
752 | finfo->send_no_irq = of_property_read_bool(np: child, propname: "ti,mbox-send-noirq" ); |
753 | |
754 | if (finfo->tx_id >= num_fifos || finfo->rx_id >= num_fifos || |
755 | finfo->tx_usr >= num_users || finfo->rx_usr >= num_users) |
756 | return -EINVAL; |
757 | } |
758 | |
759 | mdev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*mdev), GFP_KERNEL); |
760 | if (!mdev) |
761 | return -ENOMEM; |
762 | |
763 | mdev->mbox_base = devm_platform_ioremap_resource(pdev, index: 0); |
764 | if (IS_ERR(ptr: mdev->mbox_base)) |
765 | return PTR_ERR(ptr: mdev->mbox_base); |
766 | |
767 | mdev->irq_ctx = devm_kcalloc(dev: &pdev->dev, n: num_users, size: sizeof(u32), |
768 | GFP_KERNEL); |
769 | if (!mdev->irq_ctx) |
770 | return -ENOMEM; |
771 | |
772 | /* allocate one extra for marking end of list */ |
773 | list = devm_kcalloc(dev: &pdev->dev, n: info_count + 1, size: sizeof(*list), |
774 | GFP_KERNEL); |
775 | if (!list) |
776 | return -ENOMEM; |
777 | |
778 | chnls = devm_kcalloc(dev: &pdev->dev, n: info_count + 1, size: sizeof(*chnls), |
779 | GFP_KERNEL); |
780 | if (!chnls) |
781 | return -ENOMEM; |
782 | |
783 | mboxblk = devm_kcalloc(dev: &pdev->dev, n: info_count, size: sizeof(*mbox), |
784 | GFP_KERNEL); |
785 | if (!mboxblk) |
786 | return -ENOMEM; |
787 | |
788 | mbox = mboxblk; |
789 | finfo = finfoblk; |
790 | for (i = 0; i < info_count; i++, finfo++) { |
791 | fifo = &mbox->tx_fifo; |
792 | fifo->msg = MAILBOX_MESSAGE(finfo->tx_id); |
793 | fifo->fifo_stat = MAILBOX_FIFOSTATUS(finfo->tx_id); |
794 | fifo->intr_bit = MAILBOX_IRQ_NOTFULL(finfo->tx_id); |
795 | fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->tx_usr); |
796 | fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->tx_usr); |
797 | fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->tx_usr); |
798 | |
799 | fifo = &mbox->rx_fifo; |
800 | fifo->msg = MAILBOX_MESSAGE(finfo->rx_id); |
801 | fifo->msg_stat = MAILBOX_MSGSTATUS(finfo->rx_id); |
802 | fifo->intr_bit = MAILBOX_IRQ_NEWMSG(finfo->rx_id); |
803 | fifo->irqenable = MAILBOX_IRQENABLE(intr_type, finfo->rx_usr); |
804 | fifo->irqstatus = MAILBOX_IRQSTATUS(intr_type, finfo->rx_usr); |
805 | fifo->irqdisable = MAILBOX_IRQDISABLE(intr_type, finfo->rx_usr); |
806 | |
807 | mbox->send_no_irq = finfo->send_no_irq; |
808 | mbox->intr_type = intr_type; |
809 | |
810 | mbox->parent = mdev; |
811 | mbox->name = finfo->name; |
812 | mbox->irq = platform_get_irq(pdev, finfo->tx_irq); |
813 | if (mbox->irq < 0) |
814 | return mbox->irq; |
815 | mbox->chan = &chnls[i]; |
816 | chnls[i].con_priv = mbox; |
817 | list[i] = mbox++; |
818 | } |
819 | |
820 | mutex_init(&mdev->cfg_lock); |
821 | mdev->dev = &pdev->dev; |
822 | mdev->num_users = num_users; |
823 | mdev->num_fifos = num_fifos; |
824 | mdev->intr_type = intr_type; |
825 | mdev->mboxes = list; |
826 | |
827 | /* |
828 | * OMAP/K3 Mailbox IP does not have a Tx-Done IRQ, but rather a Tx-Ready |
829 | * IRQ and is needed to run the Tx state machine |
830 | */ |
831 | mdev->controller.txdone_irq = true; |
832 | mdev->controller.dev = mdev->dev; |
833 | mdev->controller.ops = &omap_mbox_chan_ops; |
834 | mdev->controller.chans = chnls; |
835 | mdev->controller.num_chans = info_count; |
836 | mdev->controller.of_xlate = omap_mbox_of_xlate; |
837 | ret = omap_mbox_register(mdev); |
838 | if (ret) |
839 | return ret; |
840 | |
841 | platform_set_drvdata(pdev, data: mdev); |
842 | pm_runtime_enable(dev: mdev->dev); |
843 | |
844 | ret = pm_runtime_resume_and_get(dev: mdev->dev); |
845 | if (ret < 0) |
846 | goto unregister; |
847 | |
848 | /* |
849 | * just print the raw revision register, the format is not |
850 | * uniform across all SoCs |
851 | */ |
852 | l = mbox_read_reg(mdev, MAILBOX_REVISION); |
853 | dev_info(mdev->dev, "omap mailbox rev 0x%x\n" , l); |
854 | |
855 | ret = pm_runtime_put_sync(dev: mdev->dev); |
856 | if (ret < 0 && ret != -ENOSYS) |
857 | goto unregister; |
858 | |
859 | devm_kfree(dev: &pdev->dev, p: finfoblk); |
860 | return 0; |
861 | |
862 | unregister: |
863 | pm_runtime_disable(dev: mdev->dev); |
864 | omap_mbox_unregister(mdev); |
865 | return ret; |
866 | } |
867 | |
868 | static void omap_mbox_remove(struct platform_device *pdev) |
869 | { |
870 | struct omap_mbox_device *mdev = platform_get_drvdata(pdev); |
871 | |
872 | pm_runtime_disable(dev: mdev->dev); |
873 | omap_mbox_unregister(mdev); |
874 | } |
875 | |
876 | static struct platform_driver omap_mbox_driver = { |
877 | .probe = omap_mbox_probe, |
878 | .remove_new = omap_mbox_remove, |
879 | .driver = { |
880 | .name = "omap-mailbox" , |
881 | .pm = &omap_mbox_pm_ops, |
882 | .of_match_table = of_match_ptr(omap_mailbox_of_match), |
883 | }, |
884 | }; |
885 | |
886 | static int __init omap_mbox_init(void) |
887 | { |
888 | int err; |
889 | |
890 | err = class_register(class: &omap_mbox_class); |
891 | if (err) |
892 | return err; |
893 | |
894 | /* kfifo size sanity check: alignment and minimal size */ |
895 | mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(u32)); |
896 | mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, sizeof(u32)); |
897 | |
898 | err = platform_driver_register(&omap_mbox_driver); |
899 | if (err) |
900 | class_unregister(class: &omap_mbox_class); |
901 | |
902 | return err; |
903 | } |
904 | subsys_initcall(omap_mbox_init); |
905 | |
906 | static void __exit omap_mbox_exit(void) |
907 | { |
908 | platform_driver_unregister(&omap_mbox_driver); |
909 | class_unregister(class: &omap_mbox_class); |
910 | } |
911 | module_exit(omap_mbox_exit); |
912 | |
913 | MODULE_LICENSE("GPL v2" ); |
914 | MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging" ); |
915 | MODULE_AUTHOR("Toshihiro Kobayashi" ); |
916 | MODULE_AUTHOR("Hiroshi DOYU" ); |
917 | |