Commit | Line | Data |
---|---|---|
41c0e939 | 1 | // SPDX-License-Identifier: GPL-2.0 |
c7701684 | 2 | // Copyright (c) 2017-2018 HiSilicon Limited. |
41c0e939 KZ |
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> | |
e9803aac | 14 | #include <linux/of.h> |
41c0e939 KZ |
15 | #include <linux/platform_device.h> |
16 | #include <linux/slab.h> | |
17 | ||
18 | #include "mailbox.h" | |
19 | ||
20 | #define MBOX_CHAN_MAX 32 | |
21 | ||
22 | #define MBOX_RX 0x0 | |
23 | #define MBOX_TX 0x1 | |
24 | ||
25 | #define MBOX_BASE(mbox, ch) ((mbox)->base + ((ch) * 0x40)) | |
26 | #define MBOX_SRC_REG 0x00 | |
27 | #define MBOX_DST_REG 0x04 | |
28 | #define MBOX_DCLR_REG 0x08 | |
29 | #define MBOX_DSTAT_REG 0x0c | |
30 | #define MBOX_MODE_REG 0x10 | |
31 | #define MBOX_IMASK_REG 0x14 | |
32 | #define MBOX_ICLR_REG 0x18 | |
33 | #define MBOX_SEND_REG 0x1c | |
34 | #define MBOX_DATA_REG 0x20 | |
35 | ||
36 | #define MBOX_IPC_LOCK_REG 0xa00 | |
37 | #define MBOX_IPC_UNLOCK 0x1acce551 | |
38 | ||
39 | #define MBOX_AUTOMATIC_ACK 1 | |
40 | ||
41 | #define MBOX_STATE_IDLE BIT(4) | |
2e4ac7cc | 42 | #define MBOX_STATE_READY BIT(5) |
41c0e939 KZ |
43 | #define MBOX_STATE_ACK BIT(7) |
44 | ||
45 | #define MBOX_MSG_LEN 8 | |
46 | ||
47 | /** | |
79daec8b RD |
48 | * struct hi3660_chan_info - Hi3660 mailbox channel information |
49 | * @dst_irq: Interrupt vector for remote processor | |
50 | * @ack_irq: Interrupt vector for local processor | |
41c0e939 KZ |
51 | * |
52 | * A channel can be used for TX or RX, it can trigger remote | |
53 | * processor interrupt to notify remote processor and can receive | |
79daec8b | 54 | * interrupt if it has an incoming message. |
41c0e939 KZ |
55 | */ |
56 | struct hi3660_chan_info { | |
57 | unsigned int dst_irq; | |
58 | unsigned int ack_irq; | |
59 | }; | |
60 | ||
61 | /** | |
79daec8b | 62 | * struct hi3660_mbox - Hi3660 mailbox controller data |
41c0e939 KZ |
63 | * @dev: Device to which it is attached |
64 | * @base: Base address of the register mapping region | |
65 | * @chan: Representation of channels in mailbox controller | |
66 | * @mchan: Representation of channel info | |
67 | * @controller: Representation of a communication channel controller | |
79daec8b RD |
68 | * |
69 | * Mailbox controller includes 32 channels and can allocate | |
70 | * channel for message transferring. | |
41c0e939 KZ |
71 | */ |
72 | struct 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 | ||
80 | static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox) | |
81 | { | |
82 | return container_of(mbox, struct hi3660_mbox, controller); | |
83 | } | |
84 | ||
85 | static 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 | ||
2e4ac7cc KW |
94 | /* Mailbox is ready to use */ |
95 | if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY) | |
41c0e939 KZ |
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 | ||
2e4ac7cc KW |
106 | /* clear ack state, mailbox will get back to ready state */ |
107 | writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG); | |
108 | ||
41c0e939 KZ |
109 | return 0; |
110 | } | |
111 | ||
112 | static 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 | ||
133 | static 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 | ||
159 | static int hi3660_mbox_startup(struct mbox_chan *chan) | |
160 | { | |
161 | int ret; | |
162 | ||
41c0e939 KZ |
163 | ret = hi3660_mbox_unlock(chan); |
164 | if (ret) | |
165 | return ret; | |
166 | ||
167 | ret = hi3660_mbox_acquire_channel(chan); | |
168 | if (ret) | |
169 | return ret; | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
174 | static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg) | |
175 | { | |
176 | unsigned long ch = (unsigned long)chan->con_priv; | |
177 | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | |
178 | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | |
179 | void __iomem *base = MBOX_BASE(mbox, ch); | |
180 | u32 *buf = msg; | |
181 | unsigned int i; | |
2e4ac7cc | 182 | int ret; |
41c0e939 | 183 | |
2e4ac7cc KW |
184 | ret = hi3660_mbox_check_state(chan); |
185 | if (ret) | |
186 | return ret; | |
41c0e939 KZ |
187 | |
188 | /* Clear mask for destination interrupt */ | |
189 | writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG); | |
190 | ||
191 | /* Config destination for interrupt vector */ | |
192 | writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG); | |
193 | ||
194 | /* Automatic acknowledge mode */ | |
195 | writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG); | |
196 | ||
197 | /* Fill message data */ | |
198 | for (i = 0; i < MBOX_MSG_LEN; i++) | |
199 | writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4); | |
200 | ||
201 | /* Trigger data transferring */ | |
202 | writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG); | |
203 | return 0; | |
204 | } | |
205 | ||
b5452838 | 206 | static const struct mbox_chan_ops hi3660_mbox_ops = { |
41c0e939 KZ |
207 | .startup = hi3660_mbox_startup, |
208 | .send_data = hi3660_mbox_send_data, | |
209 | }; | |
210 | ||
211 | static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller, | |
212 | const struct of_phandle_args *spec) | |
213 | { | |
214 | struct hi3660_mbox *mbox = to_hi3660_mbox(controller); | |
215 | struct hi3660_chan_info *mchan; | |
216 | unsigned int ch = spec->args[0]; | |
217 | ||
218 | if (ch >= MBOX_CHAN_MAX) { | |
219 | dev_err(mbox->dev, "Invalid channel idx %d\n", ch); | |
220 | return ERR_PTR(-EINVAL); | |
221 | } | |
222 | ||
223 | mchan = &mbox->mchan[ch]; | |
224 | mchan->dst_irq = spec->args[1]; | |
225 | mchan->ack_irq = spec->args[2]; | |
226 | ||
227 | return &mbox->chan[ch]; | |
228 | } | |
229 | ||
230 | static const struct of_device_id hi3660_mbox_of_match[] = { | |
231 | { .compatible = "hisilicon,hi3660-mbox", }, | |
232 | {}, | |
233 | }; | |
234 | ||
235 | MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match); | |
236 | ||
237 | static int hi3660_mbox_probe(struct platform_device *pdev) | |
238 | { | |
239 | struct device *dev = &pdev->dev; | |
240 | struct hi3660_mbox *mbox; | |
241 | struct mbox_chan *chan; | |
41c0e939 KZ |
242 | unsigned long ch; |
243 | int err; | |
244 | ||
245 | mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); | |
246 | if (!mbox) | |
247 | return -ENOMEM; | |
248 | ||
2801a33d | 249 | mbox->base = devm_platform_ioremap_resource(pdev, 0); |
41c0e939 KZ |
250 | if (IS_ERR(mbox->base)) |
251 | return PTR_ERR(mbox->base); | |
252 | ||
253 | mbox->dev = dev; | |
254 | mbox->controller.dev = dev; | |
255 | mbox->controller.chans = mbox->chan; | |
256 | mbox->controller.num_chans = MBOX_CHAN_MAX; | |
257 | mbox->controller.ops = &hi3660_mbox_ops; | |
258 | mbox->controller.of_xlate = hi3660_mbox_xlate; | |
259 | ||
260 | /* Initialize mailbox channel data */ | |
261 | chan = mbox->chan; | |
262 | for (ch = 0; ch < MBOX_CHAN_MAX; ch++) | |
263 | chan[ch].con_priv = (void *)ch; | |
264 | ||
e73cb83c | 265 | err = devm_mbox_controller_register(dev, &mbox->controller); |
41c0e939 KZ |
266 | if (err) { |
267 | dev_err(dev, "Failed to register mailbox %d\n", err); | |
268 | return err; | |
269 | } | |
270 | ||
271 | platform_set_drvdata(pdev, mbox); | |
272 | dev_info(dev, "Mailbox enabled\n"); | |
273 | return 0; | |
274 | } | |
275 | ||
41c0e939 KZ |
276 | static struct platform_driver hi3660_mbox_driver = { |
277 | .probe = hi3660_mbox_probe, | |
41c0e939 KZ |
278 | .driver = { |
279 | .name = "hi3660-mbox", | |
280 | .of_match_table = hi3660_mbox_of_match, | |
281 | }, | |
282 | }; | |
283 | ||
284 | static int __init hi3660_mbox_init(void) | |
285 | { | |
286 | return platform_driver_register(&hi3660_mbox_driver); | |
287 | } | |
288 | core_initcall(hi3660_mbox_init); | |
289 | ||
290 | static void __exit hi3660_mbox_exit(void) | |
291 | { | |
292 | platform_driver_unregister(&hi3660_mbox_driver); | |
293 | } | |
294 | module_exit(hi3660_mbox_exit); | |
295 | ||
296 | MODULE_LICENSE("GPL"); | |
297 | MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); | |
298 | MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); |