diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-02 17:33:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-07-02 17:33:21 -0400 |
commit | 0bf6a210a43f7118d858806200127e421649fc4e (patch) | |
tree | 9a17d88ebd1b9bc693fba7f39c12123dec96e930 /arch/arm/plat-omap | |
parent | ee1a8d402e7e204d57fb108aa40003b6d1633036 (diff) | |
parent | 5c913a9a9772f4b434aaea7328836419287b5d1c (diff) |
Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC driver specific changes from Arnd Bergmann:
"These changes are all driver specific and cross over between arm-soc
contents and some other subsystem, in these cases cpufreq, crypto,
dma, pinctrl, mailbox and usb, and the subsystem owners agreed to have
these changes merged through arm-soc.
As we proceed to untangle the dependencies between platform code and
driver code, the amount of changes in this category is fortunately
shrinking, for 3.11 we have 16 branches here and 101 non-merge
changesets, the majority of which are for the stedma40 dma engine
driver used in the ux500 platform. Cleaning up that code touches
multiple subsystems, but gets rid of the dependency in the end.
The mailbox code moved out from mach-omap2 to drivers/mailbox is an
intermediate step and is still omap specific at the moment. Patches
exist to generalize the subsystem and add other drivers with the same
API, but those did not make it for 3.11."
* tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (101 commits)
crypto: ux500: use dmaengine_submit API
crypto: ux500: use dmaengine_prep_slave_sg API
crypto: ux500: use dmaengine_device_control API
crypto: ux500/crypt: add missing __iomem qualifiers
crypto: ux500/hash: add missing static qualifiers
crypto: ux500/hash: use readl on iomem addresses
dmaengine: ste_dma40: Declare memcpy config as static
ARM: ux500: Remove mop500_snowball_ethernet_clock_enable()
ARM: ux500: Correct the EN_3v3 regulator's on/off GPIO
ARM: ux500: Provide a AB8500 GPIO Device Tree node
gpio: rcar: fix gpio_rcar_of_table
gpio-rcar: Remove #ifdef CONFIG_OF around OF-specific sections
gpio-rcar: Reference core gpio documentation in the DT bindings
clk: exynos5250: Add enum entries for divider clock of i2s1 and i2s2
ARM: dts: Update Samsung I2S documentation
ARM: dts: add clock provider information for i2s controllers in Exynos5250
ARM: dts: add Exynos audio subsystem clock controller node
clk: samsung: register audio subsystem clocks using common clock framework
ARM: dts: use #include for all device trees for Samsung
pinctrl: s3c24xx: use correct header for chained_irq functions
...
Diffstat (limited to 'arch/arm/plat-omap')
-rw-r--r-- | arch/arm/plat-omap/Kconfig | 16 | ||||
-rw-r--r-- | arch/arm/plat-omap/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/mailbox.h | 105 | ||||
-rw-r--r-- | arch/arm/plat-omap/mailbox.c | 435 |
4 files changed, 0 insertions, 559 deletions
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index ce66eb9be481..f82bae2171eb 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig | |||
@@ -86,22 +86,6 @@ config OMAP_MUX_WARNINGS | |||
86 | to change the pin multiplexing setup. When there are no warnings | 86 | to change the pin multiplexing setup. When there are no warnings |
87 | printed, it's safe to deselect OMAP_MUX for your product. | 87 | printed, it's safe to deselect OMAP_MUX for your product. |
88 | 88 | ||
89 | config OMAP_MBOX_FWK | ||
90 | tristate "Mailbox framework support" | ||
91 | depends on ARCH_OMAP && !ARCH_MULTIPLATFORM | ||
92 | help | ||
93 | Say Y here if you want to use OMAP Mailbox framework support for | ||
94 | DSP, IVA1.0 and IVA2 in OMAP1/2/3. | ||
95 | |||
96 | config OMAP_MBOX_KFIFO_SIZE | ||
97 | int "Mailbox kfifo default buffer size (bytes)" | ||
98 | depends on OMAP_MBOX_FWK | ||
99 | default 256 | ||
100 | help | ||
101 | Specify the default size of mailbox's kfifo buffers (bytes). | ||
102 | This can also be changed at runtime (via the mbox_kfifo_size | ||
103 | module parameter). | ||
104 | |||
105 | config OMAP_IOMMU_IVA2 | 89 | config OMAP_IOMMU_IVA2 |
106 | bool | 90 | bool |
107 | 91 | ||
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 31199417b56a..0b01b68fd033 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile | |||
@@ -17,6 +17,3 @@ obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o | |||
17 | i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o | 17 | i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o |
18 | obj-y += $(i2c-omap-m) $(i2c-omap-y) | 18 | obj-y += $(i2c-omap-m) $(i2c-omap-y) |
19 | 19 | ||
20 | # OMAP mailbox framework | ||
21 | obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox.o | ||
22 | |||
diff --git a/arch/arm/plat-omap/include/plat/mailbox.h b/arch/arm/plat-omap/include/plat/mailbox.h deleted file mode 100644 index cc3921e9059c..000000000000 --- a/arch/arm/plat-omap/include/plat/mailbox.h +++ /dev/null | |||
@@ -1,105 +0,0 @@ | |||
1 | /* mailbox.h */ | ||
2 | |||
3 | #ifndef MAILBOX_H | ||
4 | #define MAILBOX_H | ||
5 | |||
6 | #include <linux/spinlock.h> | ||
7 | #include <linux/workqueue.h> | ||
8 | #include <linux/interrupt.h> | ||
9 | #include <linux/device.h> | ||
10 | #include <linux/kfifo.h> | ||
11 | |||
12 | typedef u32 mbox_msg_t; | ||
13 | struct omap_mbox; | ||
14 | |||
15 | typedef int __bitwise omap_mbox_irq_t; | ||
16 | #define IRQ_TX ((__force omap_mbox_irq_t) 1) | ||
17 | #define IRQ_RX ((__force omap_mbox_irq_t) 2) | ||
18 | |||
19 | typedef int __bitwise omap_mbox_type_t; | ||
20 | #define OMAP_MBOX_TYPE1 ((__force omap_mbox_type_t) 1) | ||
21 | #define OMAP_MBOX_TYPE2 ((__force omap_mbox_type_t) 2) | ||
22 | |||
23 | struct omap_mbox_ops { | ||
24 | omap_mbox_type_t type; | ||
25 | int (*startup)(struct omap_mbox *mbox); | ||
26 | void (*shutdown)(struct omap_mbox *mbox); | ||
27 | /* fifo */ | ||
28 | mbox_msg_t (*fifo_read)(struct omap_mbox *mbox); | ||
29 | void (*fifo_write)(struct omap_mbox *mbox, mbox_msg_t msg); | ||
30 | int (*fifo_empty)(struct omap_mbox *mbox); | ||
31 | int (*fifo_full)(struct omap_mbox *mbox); | ||
32 | /* irq */ | ||
33 | void (*enable_irq)(struct omap_mbox *mbox, | ||
34 | omap_mbox_irq_t irq); | ||
35 | void (*disable_irq)(struct omap_mbox *mbox, | ||
36 | omap_mbox_irq_t irq); | ||
37 | void (*ack_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); | ||
38 | int (*is_irq)(struct omap_mbox *mbox, omap_mbox_irq_t irq); | ||
39 | /* ctx */ | ||
40 | void (*save_ctx)(struct omap_mbox *mbox); | ||
41 | void (*restore_ctx)(struct omap_mbox *mbox); | ||
42 | }; | ||
43 | |||
44 | struct omap_mbox_queue { | ||
45 | spinlock_t lock; | ||
46 | struct kfifo fifo; | ||
47 | struct work_struct work; | ||
48 | struct tasklet_struct tasklet; | ||
49 | struct omap_mbox *mbox; | ||
50 | bool full; | ||
51 | }; | ||
52 | |||
53 | struct omap_mbox { | ||
54 | char *name; | ||
55 | unsigned int irq; | ||
56 | struct omap_mbox_queue *txq, *rxq; | ||
57 | struct omap_mbox_ops *ops; | ||
58 | struct device *dev; | ||
59 | void *priv; | ||
60 | int use_count; | ||
61 | struct blocking_notifier_head notifier; | ||
62 | }; | ||
63 | |||
64 | int omap_mbox_msg_send(struct omap_mbox *, mbox_msg_t msg); | ||
65 | void omap_mbox_init_seq(struct omap_mbox *); | ||
66 | |||
67 | struct omap_mbox *omap_mbox_get(const char *, struct notifier_block *nb); | ||
68 | void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb); | ||
69 | |||
70 | int omap_mbox_register(struct device *parent, struct omap_mbox **); | ||
71 | int omap_mbox_unregister(void); | ||
72 | |||
73 | static inline void omap_mbox_save_ctx(struct omap_mbox *mbox) | ||
74 | { | ||
75 | if (!mbox->ops->save_ctx) { | ||
76 | dev_err(mbox->dev, "%s:\tno save\n", __func__); | ||
77 | return; | ||
78 | } | ||
79 | |||
80 | mbox->ops->save_ctx(mbox); | ||
81 | } | ||
82 | |||
83 | static inline void omap_mbox_restore_ctx(struct omap_mbox *mbox) | ||
84 | { | ||
85 | if (!mbox->ops->restore_ctx) { | ||
86 | dev_err(mbox->dev, "%s:\tno restore\n", __func__); | ||
87 | return; | ||
88 | } | ||
89 | |||
90 | mbox->ops->restore_ctx(mbox); | ||
91 | } | ||
92 | |||
93 | static inline void omap_mbox_enable_irq(struct omap_mbox *mbox, | ||
94 | omap_mbox_irq_t irq) | ||
95 | { | ||
96 | mbox->ops->enable_irq(mbox, irq); | ||
97 | } | ||
98 | |||
99 | static inline void omap_mbox_disable_irq(struct omap_mbox *mbox, | ||
100 | omap_mbox_irq_t irq) | ||
101 | { | ||
102 | mbox->ops->disable_irq(mbox, irq); | ||
103 | } | ||
104 | |||
105 | #endif /* MAILBOX_H */ | ||
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c deleted file mode 100644 index 42377ef9ea3d..000000000000 --- a/arch/arm/plat-omap/mailbox.c +++ /dev/null | |||
@@ -1,435 +0,0 @@ | |||
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 <plat/mailbox.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 | static void mbox_tx_tasklet(unsigned long tx_data) | ||
120 | { | ||
121 | struct omap_mbox *mbox = (struct omap_mbox *)tx_data; | ||
122 | struct omap_mbox_queue *mq = mbox->txq; | ||
123 | mbox_msg_t msg; | ||
124 | int ret; | ||
125 | |||
126 | while (kfifo_len(&mq->fifo)) { | ||
127 | if (__mbox_poll_for_space(mbox)) { | ||
128 | omap_mbox_enable_irq(mbox, IRQ_TX); | ||
129 | break; | ||
130 | } | ||
131 | |||
132 | ret = kfifo_out(&mq->fifo, (unsigned char *)&msg, | ||
133 | sizeof(msg)); | ||
134 | WARN_ON(ret != sizeof(msg)); | ||
135 | |||
136 | mbox_fifo_write(mbox, msg); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Message receiver(workqueue) | ||
142 | */ | ||
143 | static void mbox_rx_work(struct work_struct *work) | ||
144 | { | ||
145 | struct omap_mbox_queue *mq = | ||
146 | container_of(work, struct omap_mbox_queue, work); | ||
147 | mbox_msg_t msg; | ||
148 | int len; | ||
149 | |||
150 | while (kfifo_len(&mq->fifo) >= sizeof(msg)) { | ||
151 | len = kfifo_out(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | ||
152 | WARN_ON(len != sizeof(msg)); | ||
153 | |||
154 | blocking_notifier_call_chain(&mq->mbox->notifier, len, | ||
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); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Mailbox interrupt handler | ||
167 | */ | ||
168 | static void __mbox_tx_interrupt(struct omap_mbox *mbox) | ||
169 | { | ||
170 | omap_mbox_disable_irq(mbox, IRQ_TX); | ||
171 | ack_mbox_irq(mbox, IRQ_TX); | ||
172 | tasklet_schedule(&mbox->txq->tasklet); | ||
173 | } | ||
174 | |||
175 | static void __mbox_rx_interrupt(struct omap_mbox *mbox) | ||
176 | { | ||
177 | struct omap_mbox_queue *mq = mbox->rxq; | ||
178 | mbox_msg_t msg; | ||
179 | int len; | ||
180 | |||
181 | while (!mbox_fifo_empty(mbox)) { | ||
182 | if (unlikely(kfifo_avail(&mq->fifo) < sizeof(msg))) { | ||
183 | omap_mbox_disable_irq(mbox, IRQ_RX); | ||
184 | mq->full = true; | ||
185 | goto nomem; | ||
186 | } | ||
187 | |||
188 | msg = mbox_fifo_read(mbox); | ||
189 | |||
190 | len = kfifo_in(&mq->fifo, (unsigned char *)&msg, sizeof(msg)); | ||
191 | WARN_ON(len != sizeof(msg)); | ||
192 | |||
193 | if (mbox->ops->type == OMAP_MBOX_TYPE1) | ||
194 | break; | ||
195 | } | ||
196 | |||
197 | /* no more messages in the fifo. clear IRQ source. */ | ||
198 | ack_mbox_irq(mbox, IRQ_RX); | ||
199 | nomem: | ||
200 | schedule_work(&mbox->rxq->work); | ||
201 | } | ||
202 | |||
203 | static irqreturn_t mbox_interrupt(int irq, void *p) | ||
204 | { | ||
205 | struct omap_mbox *mbox = p; | ||
206 | |||
207 | if (is_mbox_irq(mbox, IRQ_TX)) | ||
208 | __mbox_tx_interrupt(mbox); | ||
209 | |||
210 | if (is_mbox_irq(mbox, IRQ_RX)) | ||
211 | __mbox_rx_interrupt(mbox); | ||
212 | |||
213 | return IRQ_HANDLED; | ||
214 | } | ||
215 | |||
216 | static struct omap_mbox_queue *mbox_queue_alloc(struct omap_mbox *mbox, | ||
217 | void (*work) (struct work_struct *), | ||
218 | void (*tasklet)(unsigned long)) | ||
219 | { | ||
220 | struct omap_mbox_queue *mq; | ||
221 | |||
222 | mq = kzalloc(sizeof(struct omap_mbox_queue), GFP_KERNEL); | ||
223 | if (!mq) | ||
224 | return NULL; | ||
225 | |||
226 | spin_lock_init(&mq->lock); | ||
227 | |||
228 | if (kfifo_alloc(&mq->fifo, mbox_kfifo_size, GFP_KERNEL)) | ||
229 | goto error; | ||
230 | |||
231 | if (work) | ||
232 | INIT_WORK(&mq->work, work); | ||
233 | |||
234 | if (tasklet) | ||
235 | tasklet_init(&mq->tasklet, tasklet, (unsigned long)mbox); | ||
236 | return mq; | ||
237 | error: | ||
238 | kfree(mq); | ||
239 | return NULL; | ||
240 | } | ||
241 | |||
242 | static void mbox_queue_free(struct omap_mbox_queue *q) | ||
243 | { | ||
244 | kfifo_free(&q->fifo); | ||
245 | kfree(q); | ||
246 | } | ||
247 | |||
248 | static int omap_mbox_startup(struct omap_mbox *mbox) | ||
249 | { | ||
250 | int ret = 0; | ||
251 | struct omap_mbox_queue *mq; | ||
252 | |||
253 | mutex_lock(&mbox_configured_lock); | ||
254 | if (!mbox_configured++) { | ||
255 | if (likely(mbox->ops->startup)) { | ||
256 | ret = mbox->ops->startup(mbox); | ||
257 | if (unlikely(ret)) | ||
258 | goto fail_startup; | ||
259 | } else | ||
260 | goto fail_startup; | ||
261 | } | ||
262 | |||
263 | if (!mbox->use_count++) { | ||
264 | ret = request_irq(mbox->irq, mbox_interrupt, IRQF_SHARED, | ||
265 | mbox->name, mbox); | ||
266 | if (unlikely(ret)) { | ||
267 | pr_err("failed to register mailbox interrupt:%d\n", | ||
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; | ||
277 | |||
278 | mq = mbox_queue_alloc(mbox, mbox_rx_work, NULL); | ||
279 | if (!mq) { | ||
280 | ret = -ENOMEM; | ||
281 | goto fail_alloc_rxq; | ||
282 | } | ||
283 | mbox->rxq = mq; | ||
284 | mq->mbox = mbox; | ||
285 | |||
286 | omap_mbox_enable_irq(mbox, IRQ_RX); | ||
287 | } | ||
288 | mutex_unlock(&mbox_configured_lock); | ||
289 | return 0; | ||
290 | |||
291 | fail_alloc_rxq: | ||
292 | mbox_queue_free(mbox->txq); | ||
293 | fail_alloc_txq: | ||
294 | free_irq(mbox->irq, mbox); | ||
295 | fail_request_irq: | ||
296 | if (mbox->ops->shutdown) | ||
297 | mbox->ops->shutdown(mbox); | ||
298 | mbox->use_count--; | ||
299 | fail_startup: | ||
300 | mbox_configured--; | ||
301 | mutex_unlock(&mbox_configured_lock); | ||
302 | return ret; | ||
303 | } | ||
304 | |||
305 | static void omap_mbox_fini(struct omap_mbox *mbox) | ||
306 | { | ||
307 | mutex_lock(&mbox_configured_lock); | ||
308 | |||
309 | if (!--mbox->use_count) { | ||
310 | omap_mbox_disable_irq(mbox, IRQ_RX); | ||
311 | free_irq(mbox->irq, mbox); | ||
312 | tasklet_kill(&mbox->txq->tasklet); | ||
313 | flush_work(&mbox->rxq->work); | ||
314 | mbox_queue_free(mbox->txq); | ||
315 | mbox_queue_free(mbox->rxq); | ||
316 | } | ||
317 | |||
318 | if (likely(mbox->ops->shutdown)) { | ||
319 | if (!--mbox_configured) | ||
320 | mbox->ops->shutdown(mbox); | ||
321 | } | ||
322 | |||
323 | mutex_unlock(&mbox_configured_lock); | ||
324 | } | ||
325 | |||
326 | struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb) | ||
327 | { | ||
328 | struct omap_mbox *_mbox, *mbox = NULL; | ||
329 | int i, ret; | ||
330 | |||
331 | if (!mboxes) | ||
332 | return ERR_PTR(-EINVAL); | ||
333 | |||
334 | for (i = 0; (_mbox = mboxes[i]); i++) { | ||
335 | if (!strcmp(_mbox->name, name)) { | ||
336 | mbox = _mbox; | ||
337 | break; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | if (!mbox) | ||
342 | return ERR_PTR(-ENOENT); | ||
343 | |||
344 | if (nb) | ||
345 | blocking_notifier_chain_register(&mbox->notifier, nb); | ||
346 | |||
347 | ret = omap_mbox_startup(mbox); | ||
348 | if (ret) { | ||
349 | blocking_notifier_chain_unregister(&mbox->notifier, nb); | ||
350 | return ERR_PTR(-ENODEV); | ||
351 | } | ||
352 | |||
353 | return mbox; | ||
354 | } | ||
355 | EXPORT_SYMBOL(omap_mbox_get); | ||
356 | |||
357 | void omap_mbox_put(struct omap_mbox *mbox, struct notifier_block *nb) | ||
358 | { | ||
359 | blocking_notifier_chain_unregister(&mbox->notifier, nb); | ||
360 | omap_mbox_fini(mbox); | ||
361 | } | ||
362 | EXPORT_SYMBOL(omap_mbox_put); | ||
363 | |||
364 | static struct class omap_mbox_class = { .name = "mbox", }; | ||
365 | |||
366 | int omap_mbox_register(struct device *parent, struct omap_mbox **list) | ||
367 | { | ||
368 | int ret; | ||
369 | int i; | ||
370 | |||
371 | mboxes = list; | ||
372 | if (!mboxes) | ||
373 | return -EINVAL; | ||
374 | |||
375 | for (i = 0; mboxes[i]; i++) { | ||
376 | struct omap_mbox *mbox = mboxes[i]; | ||
377 | mbox->dev = device_create(&omap_mbox_class, | ||
378 | parent, 0, mbox, "%s", mbox->name); | ||
379 | if (IS_ERR(mbox->dev)) { | ||
380 | ret = PTR_ERR(mbox->dev); | ||
381 | goto err_out; | ||
382 | } | ||
383 | |||
384 | BLOCKING_INIT_NOTIFIER_HEAD(&mbox->notifier); | ||
385 | } | ||
386 | return 0; | ||
387 | |||
388 | err_out: | ||
389 | while (i--) | ||
390 | device_unregister(mboxes[i]->dev); | ||
391 | return ret; | ||
392 | } | ||
393 | EXPORT_SYMBOL(omap_mbox_register); | ||
394 | |||
395 | int omap_mbox_unregister(void) | ||
396 | { | ||
397 | int i; | ||
398 | |||
399 | if (!mboxes) | ||
400 | return -EINVAL; | ||
401 | |||
402 | for (i = 0; mboxes[i]; i++) | ||
403 | device_unregister(mboxes[i]->dev); | ||
404 | mboxes = NULL; | ||
405 | return 0; | ||
406 | } | ||
407 | EXPORT_SYMBOL(omap_mbox_unregister); | ||
408 | |||
409 | static int __init omap_mbox_init(void) | ||
410 | { | ||
411 | int err; | ||
412 | |||
413 | err = class_register(&omap_mbox_class); | ||
414 | if (err) | ||
415 | return err; | ||
416 | |||
417 | /* kfifo size sanity check: alignment and minimal size */ | ||
418 | mbox_kfifo_size = ALIGN(mbox_kfifo_size, sizeof(mbox_msg_t)); | ||
419 | mbox_kfifo_size = max_t(unsigned int, mbox_kfifo_size, | ||
420 | sizeof(mbox_msg_t)); | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | subsys_initcall(omap_mbox_init); | ||
425 | |||
426 | static void __exit omap_mbox_exit(void) | ||
427 | { | ||
428 | class_unregister(&omap_mbox_class); | ||
429 | } | ||
430 | module_exit(omap_mbox_exit); | ||
431 | |||
432 | MODULE_LICENSE("GPL v2"); | ||
433 | MODULE_DESCRIPTION("omap mailbox: interrupt driven messaging"); | ||
434 | MODULE_AUTHOR("Toshihiro Kobayashi"); | ||
435 | MODULE_AUTHOR("Hiroshi DOYU"); | ||