aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mailbox
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 15:56:40 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 15:56:40 -0500
commitbfe9183fdcc0575a648d1401facc649888a1f49a (patch)
tree8d5a9decfb3342f89dbed46d555d8e1c4e30dde1 /drivers/mailbox
parentce01e871a1d44cc97cdd7e5ba6cb0c3613c15552 (diff)
parentf62092f6d77dfd9214ae753a24b76ba4ecd801d7 (diff)
Merge branch 'mailbox-devel' of git://git.linaro.org/landing-teams/working/fujitsu/integration
Pull mailbox framework updates from Jassi Brar. * 'mailbox-devel' of git://git.linaro.org/landing-teams/working/fujitsu/integration: mailbox: Add Altera mailbox driver mailbox: check for bit set before polling Mailbox: Fix return value check in pcc_init()
Diffstat (limited to 'drivers/mailbox')
-rw-r--r--drivers/mailbox/Kconfig6
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/mailbox-altera.c388
-rw-r--r--drivers/mailbox/mailbox.c2
-rw-r--r--drivers/mailbox/pcc.c4
5 files changed, 399 insertions, 3 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index c04fed9eb15d..84325f267acf 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -45,4 +45,10 @@ config PCC
45 states). Select this driver if your platform implements the 45 states). Select this driver if your platform implements the
46 PCC clients mentioned above. 46 PCC clients mentioned above.
47 47
48config ALTERA_MBOX
49 tristate "Altera Mailbox"
50 help
51 An implementation of the Altera Mailbox soft core. It is used
52 to send message between processors. Say Y here if you want to use the
53 Altera mailbox support.
48endif 54endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index dd412c22208b..2e79231154cf 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -7,3 +7,5 @@ obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
7obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o 7obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
8 8
9obj-$(CONFIG_PCC) += pcc.o 9obj-$(CONFIG_PCC) += pcc.o
10
11obj-$(CONFIG_ALTERA_MBOX) += mailbox-altera.o
diff --git a/drivers/mailbox/mailbox-altera.c b/drivers/mailbox/mailbox-altera.c
new file mode 100644
index 000000000000..a266265677d3
--- /dev/null
+++ b/drivers/mailbox/mailbox-altera.c
@@ -0,0 +1,388 @@
1/*
2 * Copyright Altera Corporation (C) 2013-2014. All rights reserved
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <linux/device.h>
18#include <linux/interrupt.h>
19#include <linux/io.h>
20#include <linux/kernel.h>
21#include <linux/mailbox_controller.h>
22#include <linux/module.h>
23#include <linux/of.h>
24#include <linux/platform_device.h>
25
26#define DRIVER_NAME "altera-mailbox"
27
28#define MAILBOX_CMD_REG 0x00
29#define MAILBOX_PTR_REG 0x04
30#define MAILBOX_STS_REG 0x08
31#define MAILBOX_INTMASK_REG 0x0C
32
33#define INT_PENDING_MSK 0x1
34#define INT_SPACE_MSK 0x2
35
36#define STS_PENDING_MSK 0x1
37#define STS_FULL_MSK 0x2
38#define STS_FULL_OFT 0x1
39
40#define MBOX_PENDING(status) (((status) & STS_PENDING_MSK))
41#define MBOX_FULL(status) (((status) & STS_FULL_MSK) >> STS_FULL_OFT)
42
43enum altera_mbox_msg {
44 MBOX_CMD = 0,
45 MBOX_PTR,
46};
47
48#define MBOX_POLLING_MS 5 /* polling interval 5ms */
49
50struct altera_mbox {
51 bool is_sender; /* 1-sender, 0-receiver */
52 bool intr_mode;
53 int irq;
54 void __iomem *mbox_base;
55 struct device *dev;
56 struct mbox_controller controller;
57
58 /* If the controller supports only RX polling mode */
59 struct timer_list rxpoll_timer;
60};
61
62static struct altera_mbox *mbox_chan_to_altera_mbox(struct mbox_chan *chan)
63{
64 if (!chan || !chan->con_priv)
65 return NULL;
66
67 return (struct altera_mbox *)chan->con_priv;
68}
69
70static inline int altera_mbox_full(struct altera_mbox *mbox)
71{
72 u32 status;
73
74 status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG);
75 return MBOX_FULL(status);
76}
77
78static inline int altera_mbox_pending(struct altera_mbox *mbox)
79{
80 u32 status;
81
82 status = readl_relaxed(mbox->mbox_base + MAILBOX_STS_REG);
83 return MBOX_PENDING(status);
84}
85
86static void altera_mbox_rx_intmask(struct altera_mbox *mbox, bool enable)
87{
88 u32 mask;
89
90 mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG);
91 if (enable)
92 mask |= INT_PENDING_MSK;
93 else
94 mask &= ~INT_PENDING_MSK;
95 writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG);
96}
97
98static void altera_mbox_tx_intmask(struct altera_mbox *mbox, bool enable)
99{
100 u32 mask;
101
102 mask = readl_relaxed(mbox->mbox_base + MAILBOX_INTMASK_REG);
103 if (enable)
104 mask |= INT_SPACE_MSK;
105 else
106 mask &= ~INT_SPACE_MSK;
107 writel_relaxed(mask, mbox->mbox_base + MAILBOX_INTMASK_REG);
108}
109
110static bool altera_mbox_is_sender(struct altera_mbox *mbox)
111{
112 u32 reg;
113 /* Write a magic number to PTR register and read back this register.
114 * This register is read-write if it is a sender.
115 */
116 #define MBOX_MAGIC 0xA5A5AA55
117 writel_relaxed(MBOX_MAGIC, mbox->mbox_base + MAILBOX_PTR_REG);
118 reg = readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG);
119 if (reg == MBOX_MAGIC) {
120 /* Clear to 0 */
121 writel_relaxed(0, mbox->mbox_base + MAILBOX_PTR_REG);
122 return true;
123 }
124 return false;
125}
126
127static void altera_mbox_rx_data(struct mbox_chan *chan)
128{
129 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
130 u32 data[2];
131
132 if (altera_mbox_pending(mbox)) {
133 data[MBOX_PTR] =
134 readl_relaxed(mbox->mbox_base + MAILBOX_PTR_REG);
135 data[MBOX_CMD] =
136 readl_relaxed(mbox->mbox_base + MAILBOX_CMD_REG);
137 mbox_chan_received_data(chan, (void *)data);
138 }
139}
140
141static void altera_mbox_poll_rx(unsigned long data)
142{
143 struct mbox_chan *chan = (struct mbox_chan *)data;
144 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
145
146 altera_mbox_rx_data(chan);
147
148 mod_timer(&mbox->rxpoll_timer,
149 jiffies + msecs_to_jiffies(MBOX_POLLING_MS));
150}
151
152static irqreturn_t altera_mbox_tx_interrupt(int irq, void *p)
153{
154 struct mbox_chan *chan = (struct mbox_chan *)p;
155 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
156
157 altera_mbox_tx_intmask(mbox, false);
158 mbox_chan_txdone(chan, 0);
159
160 return IRQ_HANDLED;
161}
162
163static irqreturn_t altera_mbox_rx_interrupt(int irq, void *p)
164{
165 struct mbox_chan *chan = (struct mbox_chan *)p;
166
167 altera_mbox_rx_data(chan);
168 return IRQ_HANDLED;
169}
170
171static int altera_mbox_startup_sender(struct mbox_chan *chan)
172{
173 int ret;
174 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
175
176 if (mbox->intr_mode) {
177 ret = request_irq(mbox->irq, altera_mbox_tx_interrupt, 0,
178 DRIVER_NAME, chan);
179 if (unlikely(ret)) {
180 dev_err(mbox->dev,
181 "failed to register mailbox interrupt:%d\n",
182 ret);
183 return ret;
184 }
185 }
186
187 return 0;
188}
189
190static int altera_mbox_startup_receiver(struct mbox_chan *chan)
191{
192 int ret;
193 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
194
195 if (mbox->intr_mode) {
196 ret = request_irq(mbox->irq, altera_mbox_rx_interrupt, 0,
197 DRIVER_NAME, chan);
198 if (unlikely(ret)) {
199 mbox->intr_mode = false;
200 goto polling; /* use polling if failed */
201 }
202
203 altera_mbox_rx_intmask(mbox, true);
204 return 0;
205 }
206
207polling:
208 /* Setup polling timer */
209 setup_timer(&mbox->rxpoll_timer, altera_mbox_poll_rx,
210 (unsigned long)chan);
211 mod_timer(&mbox->rxpoll_timer,
212 jiffies + msecs_to_jiffies(MBOX_POLLING_MS));
213
214 return 0;
215}
216
217static int altera_mbox_send_data(struct mbox_chan *chan, void *data)
218{
219 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
220 u32 *udata = (u32 *)data;
221
222 if (!mbox || !data)
223 return -EINVAL;
224 if (!mbox->is_sender) {
225 dev_warn(mbox->dev,
226 "failed to send. This is receiver mailbox.\n");
227 return -EINVAL;
228 }
229
230 if (altera_mbox_full(mbox))
231 return -EBUSY;
232
233 /* Enable interrupt before send */
234 if (mbox->intr_mode)
235 altera_mbox_tx_intmask(mbox, true);
236
237 /* Pointer register must write before command register */
238 writel_relaxed(udata[MBOX_PTR], mbox->mbox_base + MAILBOX_PTR_REG);
239 writel_relaxed(udata[MBOX_CMD], mbox->mbox_base + MAILBOX_CMD_REG);
240
241 return 0;
242}
243
244static bool altera_mbox_last_tx_done(struct mbox_chan *chan)
245{
246 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
247
248 /* Return false if mailbox is full */
249 return altera_mbox_full(mbox) ? false : true;
250}
251
252static bool altera_mbox_peek_data(struct mbox_chan *chan)
253{
254 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
255
256 return altera_mbox_pending(mbox) ? true : false;
257}
258
259static int altera_mbox_startup(struct mbox_chan *chan)
260{
261 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
262 int ret = 0;
263
264 if (!mbox)
265 return -EINVAL;
266
267 if (mbox->is_sender)
268 ret = altera_mbox_startup_sender(chan);
269 else
270 ret = altera_mbox_startup_receiver(chan);
271
272 return ret;
273}
274
275static void altera_mbox_shutdown(struct mbox_chan *chan)
276{
277 struct altera_mbox *mbox = mbox_chan_to_altera_mbox(chan);
278
279 if (mbox->intr_mode) {
280 /* Unmask all interrupt masks */
281 writel_relaxed(~0, mbox->mbox_base + MAILBOX_INTMASK_REG);
282 free_irq(mbox->irq, chan);
283 } else if (!mbox->is_sender) {
284 del_timer_sync(&mbox->rxpoll_timer);
285 }
286}
287
288static struct mbox_chan_ops altera_mbox_ops = {
289 .send_data = altera_mbox_send_data,
290 .startup = altera_mbox_startup,
291 .shutdown = altera_mbox_shutdown,
292 .last_tx_done = altera_mbox_last_tx_done,
293 .peek_data = altera_mbox_peek_data,
294};
295
296static int altera_mbox_probe(struct platform_device *pdev)
297{
298 struct altera_mbox *mbox;
299 struct resource *regs;
300 struct mbox_chan *chans;
301 int ret;
302
303 mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox),
304 GFP_KERNEL);
305 if (!mbox)
306 return -ENOMEM;
307
308 /* Allocated one channel */
309 chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL);
310 if (!chans)
311 return -ENOMEM;
312
313 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
314
315 mbox->mbox_base = devm_ioremap_resource(&pdev->dev, regs);
316 if (IS_ERR(mbox->mbox_base))
317 return PTR_ERR(mbox->mbox_base);
318
319 /* Check is it a sender or receiver? */
320 mbox->is_sender = altera_mbox_is_sender(mbox);
321
322 mbox->irq = platform_get_irq(pdev, 0);
323 if (mbox->irq >= 0)
324 mbox->intr_mode = true;
325
326 mbox->dev = &pdev->dev;
327
328 /* Hardware supports only one channel. */
329 chans[0].con_priv = mbox;
330 mbox->controller.dev = mbox->dev;
331 mbox->controller.num_chans = 1;
332 mbox->controller.chans = chans;
333 mbox->controller.ops = &altera_mbox_ops;
334
335 if (mbox->is_sender) {
336 if (mbox->intr_mode) {
337 mbox->controller.txdone_irq = true;
338 } else {
339 mbox->controller.txdone_poll = true;
340 mbox->controller.txpoll_period = MBOX_POLLING_MS;
341 }
342 }
343
344 ret = mbox_controller_register(&mbox->controller);
345 if (ret) {
346 dev_err(&pdev->dev, "Register mailbox failed\n");
347 goto err;
348 }
349
350 platform_set_drvdata(pdev, mbox);
351err:
352 return ret;
353}
354
355static int altera_mbox_remove(struct platform_device *pdev)
356{
357 struct altera_mbox *mbox = platform_get_drvdata(pdev);
358
359 if (!mbox)
360 return -EINVAL;
361
362 mbox_controller_unregister(&mbox->controller);
363
364 return 0;
365}
366
367static const struct of_device_id altera_mbox_match[] = {
368 { .compatible = "altr,mailbox-1.0" },
369 { /* Sentinel */ }
370};
371
372MODULE_DEVICE_TABLE(of, altera_mbox_match);
373
374static struct platform_driver altera_mbox_driver = {
375 .probe = altera_mbox_probe,
376 .remove = altera_mbox_remove,
377 .driver = {
378 .name = DRIVER_NAME,
379 .of_match_table = altera_mbox_match,
380 },
381};
382
383module_platform_driver(altera_mbox_driver);
384
385MODULE_LICENSE("GPL v2");
386MODULE_DESCRIPTION("Altera mailbox specific functions");
387MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
388MODULE_ALIAS("platform:altera-mailbox");
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index 59aad4d5da53..19b491d2964f 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -87,7 +87,7 @@ static void msg_submit(struct mbox_chan *chan)
87exit: 87exit:
88 spin_unlock_irqrestore(&chan->lock, flags); 88 spin_unlock_irqrestore(&chan->lock, flags);
89 89
90 if (!err && chan->txdone_method == TXDONE_BY_POLL) 90 if (!err && (chan->txdone_method & TXDONE_BY_POLL))
91 poll_txdone((unsigned long)chan->mbox); 91 poll_txdone((unsigned long)chan->mbox);
92} 92}
93 93
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index e8902f8dddfc..977c814cdf6f 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -393,9 +393,9 @@ static int __init pcc_init(void)
393 pcc_pdev = platform_create_bundle(&pcc_mbox_driver, 393 pcc_pdev = platform_create_bundle(&pcc_mbox_driver,
394 pcc_mbox_probe, NULL, 0, NULL, 0); 394 pcc_mbox_probe, NULL, 0, NULL, 0);
395 395
396 if (!pcc_pdev) { 396 if (IS_ERR(pcc_pdev)) {
397 pr_debug("Err creating PCC platform bundle\n"); 397 pr_debug("Err creating PCC platform bundle\n");
398 return -ENODEV; 398 return PTR_ERR(pcc_pdev);
399 } 399 }
400 400
401 return 0; 401 return 0;