aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaihua Zhong <zhongkaihua@huawei.com>2018-02-27 23:54:54 -0500
committerJassi Brar <jaswinder.singh@linaro.org>2018-03-19 23:15:54 -0400
commit41c0e939d70ddb6860e340e7532df8984783f867 (patch)
treebb07d7beb34c2242324b00b246c6c4a7035bbfb3
parent648d1382d422076364a874b8b92b9ec3ff37874e (diff)
mailbox: Add support for Hi3660 mailbox
Hi3660 mailbox controller is used to send message within multiple processors, MCU, HIFI, etc. It supports 32 mailbox channels and every channel can only be used for single transferring direction. Once the channel is enabled, it needs to specify the destination interrupt and acknowledge interrupt, these two interrupt vectors are used to create the connection between the mailbox and interrupt controllers. The data transferring supports two modes, one is named as "automatic acknowledge" mode so after send message the kernel doesn't need to wait for acknowledge from remote and directly return; there have another mode is to rely on handling interrupt for acknowledge. This commit is for initial version driver, which only supports "automatic acknowledge" mode to support CPU clock, which is the only one consumer to use mailbox and has been verified. Later may enhance this driver for interrupt mode (e.g. for supporting HIFI). Signed-off-by: Leo Yan <leo.yan@linaro.org> Signed-off-by: Ruyi Wang <wangruyi@huawei.com> Signed-off-by: Kaihua Zhong <zhongkaihua@huawei.com> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
-rw-r--r--drivers/mailbox/Kconfig8
-rw-r--r--drivers/mailbox/Makefile2
-rw-r--r--drivers/mailbox/hi3660-mailbox.c312
3 files changed, 322 insertions, 0 deletions
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 7c24900554a7..a2bb27446dce 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -108,6 +108,14 @@ config TI_MESSAGE_MANAGER
108 multiple processors within the SoC. Select this driver if your 108 multiple processors within the SoC. Select this driver if your
109 platform has support for the hardware block. 109 platform has support for the hardware block.
110 110
111config HI3660_MBOX
112 tristate "Hi3660 Mailbox"
113 depends on ARCH_HISI && OF
114 help
115 An implementation of the hi3660 mailbox. It is used to send message
116 between application processors and other processors/MCU/DSP. Select
117 Y here if you want to use Hi3660 mailbox controller.
118
111config HI6220_MBOX 119config HI6220_MBOX
112 tristate "Hi6220 Mailbox" 120 tristate "Hi6220 Mailbox"
113 depends on ARCH_HISI 121 depends on ARCH_HISI
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 4896f8dcae95..cc23c3a43fcd 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_TI_MESSAGE_MANAGER) += ti-msgmgr.o
27 27
28obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o 28obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
29 29
30obj-$(CONFIG_HI3660_MBOX) += hi3660-mailbox.o
31
30obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o 32obj-$(CONFIG_HI6220_MBOX) += hi6220-mailbox.o
31 33
32obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o 34obj-$(CONFIG_BCM_PDC_MBOX) += bcm-pdc-mailbox.o
diff --git a/drivers/mailbox/hi3660-mailbox.c b/drivers/mailbox/hi3660-mailbox.c
new file mode 100644
index 000000000000..3eea6b642484
--- /dev/null
+++ b/drivers/mailbox/hi3660-mailbox.c
@@ -0,0 +1,312 @@
1// SPDX-License-Identifier: GPL-2.0
2// Copyright (c) 2017-2018 Hisilicon Limited.
3// Copyright (c) 2017-2018 Linaro Limited.
4
5#include <linux/bitops.h>
6#include <linux/delay.h>
7#include <linux/device.h>
8#include <linux/err.h>
9#include <linux/interrupt.h>
10#include <linux/io.h>
11#include <linux/iopoll.h>
12#include <linux/mailbox_controller.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/slab.h>
16
17#include "mailbox.h"
18
19#define MBOX_CHAN_MAX 32
20
21#define MBOX_RX 0x0
22#define MBOX_TX 0x1
23
24#define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40))
25#define MBOX_SRC_REG 0x00
26#define MBOX_DST_REG 0x04
27#define MBOX_DCLR_REG 0x08
28#define MBOX_DSTAT_REG 0x0c
29#define MBOX_MODE_REG 0x10
30#define MBOX_IMASK_REG 0x14
31#define MBOX_ICLR_REG 0x18
32#define MBOX_SEND_REG 0x1c
33#define MBOX_DATA_REG 0x20
34
35#define MBOX_IPC_LOCK_REG 0xa00
36#define MBOX_IPC_UNLOCK 0x1acce551
37
38#define MBOX_AUTOMATIC_ACK 1
39
40#define MBOX_STATE_IDLE BIT(4)
41#define MBOX_STATE_ACK BIT(7)
42
43#define MBOX_MSG_LEN 8
44
45/**
46 * Hi3660 mailbox channel information
47 *
48 * A channel can be used for TX or RX, it can trigger remote
49 * processor interrupt to notify remote processor and can receive
50 * interrupt if has incoming message.
51 *
52 * @dst_irq: Interrupt vector for remote processor
53 * @ack_irq: Interrupt vector for local processor
54 */
55struct hi3660_chan_info {
56 unsigned int dst_irq;
57 unsigned int ack_irq;
58};
59
60/**
61 * Hi3660 mailbox controller data
62 *
63 * Mailbox controller includes 32 channels and can allocate
64 * channel for message transferring.
65 *
66 * @dev: Device to which it is attached
67 * @base: Base address of the register mapping region
68 * @chan: Representation of channels in mailbox controller
69 * @mchan: Representation of channel info
70 * @controller: Representation of a communication channel controller
71 */
72struct hi3660_mbox {
73 struct device *dev;
74 void __iomem *base;
75 struct mbox_chan chan[MBOX_CHAN_MAX];
76 struct hi3660_chan_info mchan[MBOX_CHAN_MAX];
77 struct mbox_controller controller;
78};
79
80static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox)
81{
82 return container_of(mbox, struct hi3660_mbox, controller);
83}
84
85static int hi3660_mbox_check_state(struct mbox_chan *chan)
86{
87 unsigned long ch = (unsigned long)chan->con_priv;
88 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
89 struct hi3660_chan_info *mchan = &mbox->mchan[ch];
90 void __iomem *base = MBOX_BASE(mbox, ch);
91 unsigned long val;
92 unsigned int ret;
93
94 /* Mailbox is idle so directly bail out */
95 if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE)
96 return 0;
97
98 /* Wait for acknowledge from remote */
99 ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG,
100 val, (val & MBOX_STATE_ACK), 1000, 300000);
101 if (ret) {
102 dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__);
103 return ret;
104 }
105
106 /* Ensure channel is released */
107 writel(0xffffffff, base + MBOX_IMASK_REG);
108 writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
109 return 0;
110}
111
112static int hi3660_mbox_unlock(struct mbox_chan *chan)
113{
114 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
115 unsigned int val, retry = 3;
116
117 do {
118 writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG);
119
120 val = readl(mbox->base + MBOX_IPC_LOCK_REG);
121 if (!val)
122 break;
123
124 udelay(10);
125 } while (retry--);
126
127 if (val)
128 dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__);
129
130 return (!val) ? 0 : -ETIMEDOUT;
131}
132
133static int hi3660_mbox_acquire_channel(struct mbox_chan *chan)
134{
135 unsigned long ch = (unsigned long)chan->con_priv;
136 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
137 struct hi3660_chan_info *mchan = &mbox->mchan[ch];
138 void __iomem *base = MBOX_BASE(mbox, ch);
139 unsigned int val, retry;
140
141 for (retry = 10; retry; retry--) {
142 /* Check if channel is in idle state */
143 if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) {
144 writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
145
146 /* Check ack bit has been set successfully */
147 val = readl(base + MBOX_SRC_REG);
148 if (val & BIT(mchan->ack_irq))
149 break;
150 }
151 }
152
153 if (!retry)
154 dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__);
155
156 return retry ? 0 : -ETIMEDOUT;
157}
158
159static int hi3660_mbox_startup(struct mbox_chan *chan)
160{
161 int ret;
162
163 ret = hi3660_mbox_check_state(chan);
164 if (ret)
165 return ret;
166
167 ret = hi3660_mbox_unlock(chan);
168 if (ret)
169 return ret;
170
171 ret = hi3660_mbox_acquire_channel(chan);
172 if (ret)
173 return ret;
174
175 return 0;
176}
177
178static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg)
179{
180 unsigned long ch = (unsigned long)chan->con_priv;
181 struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox);
182 struct hi3660_chan_info *mchan = &mbox->mchan[ch];
183 void __iomem *base = MBOX_BASE(mbox, ch);
184 u32 *buf = msg;
185 unsigned int i;
186
187 /* Ensure channel is released */
188 writel_relaxed(0xffffffff, base + MBOX_IMASK_REG);
189 writel_relaxed(BIT(mchan->ack_irq), base + MBOX_SRC_REG);
190
191 /* Clear mask for destination interrupt */
192 writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG);
193
194 /* Config destination for interrupt vector */
195 writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG);
196
197 /* Automatic acknowledge mode */
198 writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG);
199
200 /* Fill message data */
201 for (i = 0; i < MBOX_MSG_LEN; i++)
202 writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4);
203
204 /* Trigger data transferring */
205 writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG);
206 return 0;
207}
208
209static struct mbox_chan_ops hi3660_mbox_ops = {
210 .startup = hi3660_mbox_startup,
211 .send_data = hi3660_mbox_send_data,
212};
213
214static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller,
215 const struct of_phandle_args *spec)
216{
217 struct hi3660_mbox *mbox = to_hi3660_mbox(controller);
218 struct hi3660_chan_info *mchan;
219 unsigned int ch = spec->args[0];
220
221 if (ch >= MBOX_CHAN_MAX) {
222 dev_err(mbox->dev, "Invalid channel idx %d\n", ch);
223 return ERR_PTR(-EINVAL);
224 }
225
226 mchan = &mbox->mchan[ch];
227 mchan->dst_irq = spec->args[1];
228 mchan->ack_irq = spec->args[2];
229
230 return &mbox->chan[ch];
231}
232
233static const struct of_device_id hi3660_mbox_of_match[] = {
234 { .compatible = "hisilicon,hi3660-mbox", },
235 {},
236};
237
238MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match);
239
240static int hi3660_mbox_probe(struct platform_device *pdev)
241{
242 struct device *dev = &pdev->dev;
243 struct hi3660_mbox *mbox;
244 struct mbox_chan *chan;
245 struct resource *res;
246 unsigned long ch;
247 int err;
248
249 mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
250 if (!mbox)
251 return -ENOMEM;
252
253 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
254 mbox->base = devm_ioremap_resource(dev, res);
255 if (IS_ERR(mbox->base))
256 return PTR_ERR(mbox->base);
257
258 mbox->dev = dev;
259 mbox->controller.dev = dev;
260 mbox->controller.chans = mbox->chan;
261 mbox->controller.num_chans = MBOX_CHAN_MAX;
262 mbox->controller.ops = &hi3660_mbox_ops;
263 mbox->controller.of_xlate = hi3660_mbox_xlate;
264
265 /* Initialize mailbox channel data */
266 chan = mbox->chan;
267 for (ch = 0; ch < MBOX_CHAN_MAX; ch++)
268 chan[ch].con_priv = (void *)ch;
269
270 err = mbox_controller_register(&mbox->controller);
271 if (err) {
272 dev_err(dev, "Failed to register mailbox %d\n", err);
273 return err;
274 }
275
276 platform_set_drvdata(pdev, mbox);
277 dev_info(dev, "Mailbox enabled\n");
278 return 0;
279}
280
281static int hi3660_mbox_remove(struct platform_device *pdev)
282{
283 struct hi3660_mbox *mbox = platform_get_drvdata(pdev);
284
285 mbox_controller_unregister(&mbox->controller);
286 return 0;
287}
288
289static struct platform_driver hi3660_mbox_driver = {
290 .probe = hi3660_mbox_probe,
291 .remove = hi3660_mbox_remove,
292 .driver = {
293 .name = "hi3660-mbox",
294 .of_match_table = hi3660_mbox_of_match,
295 },
296};
297
298static int __init hi3660_mbox_init(void)
299{
300 return platform_driver_register(&hi3660_mbox_driver);
301}
302core_initcall(hi3660_mbox_init);
303
304static void __exit hi3660_mbox_exit(void)
305{
306 platform_driver_unregister(&hi3660_mbox_driver);
307}
308module_exit(hi3660_mbox_exit);
309
310MODULE_LICENSE("GPL");
311MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller");
312MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");