aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJassi Brar <jaswinder.singh@linaro.org>2014-06-26 09:39:42 -0400
committerJassi Brar <jaswinder.singh@linaro.org>2015-03-17 01:42:01 -0400
commitee23d66af9219dfe2407a9b08ef9a165dbe6f994 (patch)
tree5091f3e2b8eb928c982ec1af7593804372956fbe
parent33350e6b1833b15cf9c9c4c84d8534ad68b0c7b8 (diff)
mailbox: arm_mhu: add driver for ARM MHU controller
Add driver for the ARM Primecell Message-Handling-Unit(MHU) controller. Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org> Signed-off-by: Andy Green <andy.green@linaro.org> Signed-off-by: Vincent Yang <vincent.yang@socionext.com> Signed-off-by: Tetsuya Nuriya <nuriya.tetsuya@socionext.com>
-rw-r--r--Documentation/devicetree/bindings/mailbox/arm-mhu.txt43
-rw-r--r--drivers/mailbox/Kconfig9
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/arm_mhu.c195
4 files changed, 249 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/mailbox/arm-mhu.txt b/Documentation/devicetree/bindings/mailbox/arm-mhu.txt
new file mode 100644
index 000000000000..4971f03f0b33
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/arm-mhu.txt
@@ -0,0 +1,43 @@
1ARM MHU Mailbox Driver
2======================
3
4The ARM's Message-Handling-Unit (MHU) is a mailbox controller that has
53 independent channels/links to communicate with remote processor(s).
6 MHU links are hardwired on a platform. A link raises interrupt for any
7received data. However, there is no specified way of knowing if the sent
8data has been read by the remote. This driver assumes the sender polls
9STAT register and the remote clears it after having read the data.
10The last channel is specified to be a 'Secure' resource, hence can't be
11used by Linux running NS.
12
13Mailbox Device Node:
14====================
15
16Required properties:
17--------------------
18- compatible: Shall be "arm,mhu" & "arm,primecell"
19- reg: Contains the mailbox register address range (base
20 address and length)
21- #mbox-cells Shall be 1 - the index of the channel needed.
22- interrupts: Contains the interrupt information corresponding to
23 each of the 3 links of MHU.
24
25Example:
26--------
27
28 mhu: mailbox@2b1f0000 {
29 #mbox-cells = <1>;
30 compatible = "arm,mhu", "arm,primecell";
31 reg = <0 0x2b1f0000 0x1000>;
32 interrupts = <0 36 4>, /* LP-NonSecure */
33 <0 35 4>, /* HP-NonSecure */
34 <0 37 4>; /* Secure */
35 clocks = <&clock 0 2 1>;
36 clock-names = "apb_pclk";
37 };
38
39 mhu_client: scb@2e000000 {
40 compatible = "fujitsu,mb86s70-scb-1.0";
41 reg = <0 0x2e000000 0x4000>;
42 mboxes = <&mhu 1>; /* HP-NonSecure */
43 };
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 84325f267acf..84b0a2d74d60 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -6,6 +6,15 @@ menuconfig MAILBOX
6 signals. Say Y if your platform supports hardware mailboxes. 6 signals. Say Y if your platform supports hardware mailboxes.
7 7
8if MAILBOX 8if MAILBOX
9
10config ARM_MHU
11 tristate "ARM MHU Mailbox"
12 depends on ARM_AMBA
13 help
14 Say Y here if you want to build the ARM MHU controller driver.
15 The controller has 3 mailbox channels, the last of which can be
16 used in Secure mode only.
17
9config PL320_MBOX 18config PL320_MBOX
10 bool "ARM PL320 Mailbox" 19 bool "ARM PL320 Mailbox"
11 depends on ARM_AMBA 20 depends on ARM_AMBA
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 2e79231154cf..b18201e97e29 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -2,6 +2,8 @@
2 2
3obj-$(CONFIG_MAILBOX) += mailbox.o 3obj-$(CONFIG_MAILBOX) += mailbox.o
4 4
5obj-$(CONFIG_ARM_MHU) += arm_mhu.o
6
5obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o 7obj-$(CONFIG_PL320_MBOX) += pl320-ipc.o
6 8
7obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o 9obj-$(CONFIG_OMAP2PLUS_MBOX) += omap-mailbox.o
diff --git a/drivers/mailbox/arm_mhu.c b/drivers/mailbox/arm_mhu.c
new file mode 100644
index 000000000000..ac693c635357
--- /dev/null
+++ b/drivers/mailbox/arm_mhu.c
@@ -0,0 +1,195 @@
1/*
2 * Copyright (C) 2013-2015 Fujitsu Semiconductor Ltd.
3 * Copyright (C) 2015 Linaro Ltd.
4 * Author: Jassi Brar <jaswinder.singh@linaro.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/interrupt.h>
17#include <linux/spinlock.h>
18#include <linux/mutex.h>
19#include <linux/delay.h>
20#include <linux/slab.h>
21#include <linux/err.h>
22#include <linux/io.h>
23#include <linux/module.h>
24#include <linux/amba/bus.h>
25#include <linux/mailbox_controller.h>
26
27#define INTR_STAT_OFS 0x0
28#define INTR_SET_OFS 0x8
29#define INTR_CLR_OFS 0x10
30
31#define MHU_LP_OFFSET 0x0
32#define MHU_HP_OFFSET 0x20
33#define MHU_SEC_OFFSET 0x200
34#define TX_REG_OFFSET 0x100
35
36#define MHU_CHANS 3
37
38struct mhu_link {
39 unsigned irq;
40 void __iomem *tx_reg;
41 void __iomem *rx_reg;
42};
43
44struct arm_mhu {
45 void __iomem *base;
46 struct mhu_link mlink[MHU_CHANS];
47 struct mbox_chan chan[MHU_CHANS];
48 struct mbox_controller mbox;
49};
50
51static irqreturn_t mhu_rx_interrupt(int irq, void *p)
52{
53 struct mbox_chan *chan = p;
54 struct mhu_link *mlink = chan->con_priv;
55 u32 val;
56
57 val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
58 if (!val)
59 return IRQ_NONE;
60
61 mbox_chan_received_data(chan, (void *)&val);
62
63 writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
64
65 return IRQ_HANDLED;
66}
67
68static bool mhu_last_tx_done(struct mbox_chan *chan)
69{
70 struct mhu_link *mlink = chan->con_priv;
71 u32 val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
72
73 return (val == 0);
74}
75
76static int mhu_send_data(struct mbox_chan *chan, void *data)
77{
78 struct mhu_link *mlink = chan->con_priv;
79 u32 *arg = data;
80
81 writel_relaxed(*arg, mlink->tx_reg + INTR_SET_OFS);
82
83 return 0;
84}
85
86static int mhu_startup(struct mbox_chan *chan)
87{
88 struct mhu_link *mlink = chan->con_priv;
89 u32 val;
90 int ret;
91
92 val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
93 writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
94
95 ret = request_irq(mlink->irq, mhu_rx_interrupt,
96 IRQF_SHARED, "mhu_link", chan);
97 if (ret) {
98 dev_err(chan->mbox->dev,
99 "Unable to aquire IRQ %d\n", mlink->irq);
100 return ret;
101 }
102
103 return 0;
104}
105
106static void mhu_shutdown(struct mbox_chan *chan)
107{
108 struct mhu_link *mlink = chan->con_priv;
109
110 free_irq(mlink->irq, chan);
111}
112
113static struct mbox_chan_ops mhu_ops = {
114 .send_data = mhu_send_data,
115 .startup = mhu_startup,
116 .shutdown = mhu_shutdown,
117 .last_tx_done = mhu_last_tx_done,
118};
119
120static int mhu_probe(struct amba_device *adev, const struct amba_id *id)
121{
122 int i, err;
123 struct arm_mhu *mhu;
124 struct device *dev = &adev->dev;
125 int mhu_reg[MHU_CHANS] = {MHU_LP_OFFSET, MHU_HP_OFFSET, MHU_SEC_OFFSET};
126
127 /* Allocate memory for device */
128 mhu = devm_kzalloc(dev, sizeof(*mhu), GFP_KERNEL);
129 if (!mhu)
130 return -ENOMEM;
131
132 mhu->base = devm_ioremap_resource(dev, &adev->res);
133 if (IS_ERR(mhu->base)) {
134 dev_err(dev, "ioremap failed\n");
135 return PTR_ERR(mhu->base);
136 }
137
138 for (i = 0; i < MHU_CHANS; i++) {
139 mhu->chan[i].con_priv = &mhu->mlink[i];
140 mhu->mlink[i].irq = adev->irq[i];
141 mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
142 mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + TX_REG_OFFSET;
143 }
144
145 mhu->mbox.dev = dev;
146 mhu->mbox.chans = &mhu->chan[0];
147 mhu->mbox.num_chans = MHU_CHANS;
148 mhu->mbox.ops = &mhu_ops;
149 mhu->mbox.txdone_irq = false;
150 mhu->mbox.txdone_poll = true;
151 mhu->mbox.txpoll_period = 10;
152
153 amba_set_drvdata(adev, mhu);
154
155 err = mbox_controller_register(&mhu->mbox);
156 if (err) {
157 dev_err(dev, "Failed to register mailboxes %d\n", err);
158 return err;
159 }
160
161 dev_info(dev, "ARM MHU Mailbox registered\n");
162 return 0;
163}
164
165static int mhu_remove(struct amba_device *adev)
166{
167 struct arm_mhu *mhu = amba_get_drvdata(adev);
168
169 mbox_controller_unregister(&mhu->mbox);
170
171 return 0;
172}
173
174static struct amba_id mhu_ids[] = {
175 {
176 .id = 0x1bb098,
177 .mask = 0xffffff,
178 },
179 { 0, 0 },
180};
181MODULE_DEVICE_TABLE(amba, mhu_ids);
182
183static struct amba_driver arm_mhu_driver = {
184 .drv = {
185 .name = "mhu",
186 },
187 .id_table = mhu_ids,
188 .probe = mhu_probe,
189 .remove = mhu_remove,
190};
191module_amba_driver(arm_mhu_driver);
192
193MODULE_LICENSE("GPL v2");
194MODULE_DESCRIPTION("ARM MHU Driver");
195MODULE_AUTHOR("Jassi Brar <jassisinghbrar@gmail.com>");