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> | |
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) | |
2e4ac7cc | 41 | #define MBOX_STATE_READY BIT(5) |
41c0e939 KZ |
42 | #define MBOX_STATE_ACK BIT(7) |
43 | ||
44 | #define MBOX_MSG_LEN 8 | |
45 | ||
46 | /** | |
79daec8b RD |
47 | * struct hi3660_chan_info - Hi3660 mailbox channel information |
48 | * @dst_irq: Interrupt vector for remote processor | |
49 | * @ack_irq: Interrupt vector for local processor | |
41c0e939 KZ |
50 | * |
51 | * A channel can be used for TX or RX, it can trigger remote | |
52 | * processor interrupt to notify remote processor and can receive | |
79daec8b | 53 | * interrupt if it has an incoming message. |
41c0e939 KZ |
54 | */ |
55 | struct hi3660_chan_info { | |
56 | unsigned int dst_irq; | |
57 | unsigned int ack_irq; | |
58 | }; | |
59 | ||
60 | /** | |
79daec8b | 61 | * struct hi3660_mbox - Hi3660 mailbox controller data |
41c0e939 KZ |
62 | * @dev: Device to which it is attached |
63 | * @base: Base address of the register mapping region | |
64 | * @chan: Representation of channels in mailbox controller | |
65 | * @mchan: Representation of channel info | |
66 | * @controller: Representation of a communication channel controller | |
79daec8b RD |
67 | * |
68 | * Mailbox controller includes 32 channels and can allocate | |
69 | * channel for message transferring. | |
41c0e939 KZ |
70 | */ |
71 | struct hi3660_mbox { | |
72 | struct device *dev; | |
73 | void __iomem *base; | |
74 | struct mbox_chan chan[MBOX_CHAN_MAX]; | |
75 | struct hi3660_chan_info mchan[MBOX_CHAN_MAX]; | |
76 | struct mbox_controller controller; | |
77 | }; | |
78 | ||
79 | static struct hi3660_mbox *to_hi3660_mbox(struct mbox_controller *mbox) | |
80 | { | |
81 | return container_of(mbox, struct hi3660_mbox, controller); | |
82 | } | |
83 | ||
84 | static int hi3660_mbox_check_state(struct mbox_chan *chan) | |
85 | { | |
86 | unsigned long ch = (unsigned long)chan->con_priv; | |
87 | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | |
88 | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | |
89 | void __iomem *base = MBOX_BASE(mbox, ch); | |
90 | unsigned long val; | |
91 | unsigned int ret; | |
92 | ||
2e4ac7cc KW |
93 | /* Mailbox is ready to use */ |
94 | if (readl(base + MBOX_MODE_REG) & MBOX_STATE_READY) | |
41c0e939 KZ |
95 | return 0; |
96 | ||
97 | /* Wait for acknowledge from remote */ | |
98 | ret = readx_poll_timeout_atomic(readl, base + MBOX_MODE_REG, | |
99 | val, (val & MBOX_STATE_ACK), 1000, 300000); | |
100 | if (ret) { | |
101 | dev_err(mbox->dev, "%s: timeout for receiving ack\n", __func__); | |
102 | return ret; | |
103 | } | |
104 | ||
2e4ac7cc KW |
105 | /* clear ack state, mailbox will get back to ready state */ |
106 | writel(BIT(mchan->ack_irq), base + MBOX_ICLR_REG); | |
107 | ||
41c0e939 KZ |
108 | return 0; |
109 | } | |
110 | ||
111 | static int hi3660_mbox_unlock(struct mbox_chan *chan) | |
112 | { | |
113 | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | |
114 | unsigned int val, retry = 3; | |
115 | ||
116 | do { | |
117 | writel(MBOX_IPC_UNLOCK, mbox->base + MBOX_IPC_LOCK_REG); | |
118 | ||
119 | val = readl(mbox->base + MBOX_IPC_LOCK_REG); | |
120 | if (!val) | |
121 | break; | |
122 | ||
123 | udelay(10); | |
124 | } while (retry--); | |
125 | ||
126 | if (val) | |
127 | dev_err(mbox->dev, "%s: failed to unlock mailbox\n", __func__); | |
128 | ||
129 | return (!val) ? 0 : -ETIMEDOUT; | |
130 | } | |
131 | ||
132 | static int hi3660_mbox_acquire_channel(struct mbox_chan *chan) | |
133 | { | |
134 | unsigned long ch = (unsigned long)chan->con_priv; | |
135 | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | |
136 | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | |
137 | void __iomem *base = MBOX_BASE(mbox, ch); | |
138 | unsigned int val, retry; | |
139 | ||
140 | for (retry = 10; retry; retry--) { | |
141 | /* Check if channel is in idle state */ | |
142 | if (readl(base + MBOX_MODE_REG) & MBOX_STATE_IDLE) { | |
143 | writel(BIT(mchan->ack_irq), base + MBOX_SRC_REG); | |
144 | ||
145 | /* Check ack bit has been set successfully */ | |
146 | val = readl(base + MBOX_SRC_REG); | |
147 | if (val & BIT(mchan->ack_irq)) | |
148 | break; | |
149 | } | |
150 | } | |
151 | ||
152 | if (!retry) | |
153 | dev_err(mbox->dev, "%s: failed to acquire channel\n", __func__); | |
154 | ||
155 | return retry ? 0 : -ETIMEDOUT; | |
156 | } | |
157 | ||
158 | static int hi3660_mbox_startup(struct mbox_chan *chan) | |
159 | { | |
160 | int ret; | |
161 | ||
41c0e939 KZ |
162 | ret = hi3660_mbox_unlock(chan); |
163 | if (ret) | |
164 | return ret; | |
165 | ||
166 | ret = hi3660_mbox_acquire_channel(chan); | |
167 | if (ret) | |
168 | return ret; | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | static int hi3660_mbox_send_data(struct mbox_chan *chan, void *msg) | |
174 | { | |
175 | unsigned long ch = (unsigned long)chan->con_priv; | |
176 | struct hi3660_mbox *mbox = to_hi3660_mbox(chan->mbox); | |
177 | struct hi3660_chan_info *mchan = &mbox->mchan[ch]; | |
178 | void __iomem *base = MBOX_BASE(mbox, ch); | |
179 | u32 *buf = msg; | |
180 | unsigned int i; | |
2e4ac7cc | 181 | int ret; |
41c0e939 | 182 | |
2e4ac7cc KW |
183 | ret = hi3660_mbox_check_state(chan); |
184 | if (ret) | |
185 | return ret; | |
41c0e939 KZ |
186 | |
187 | /* Clear mask for destination interrupt */ | |
188 | writel_relaxed(~BIT(mchan->dst_irq), base + MBOX_IMASK_REG); | |
189 | ||
190 | /* Config destination for interrupt vector */ | |
191 | writel_relaxed(BIT(mchan->dst_irq), base + MBOX_DST_REG); | |
192 | ||
193 | /* Automatic acknowledge mode */ | |
194 | writel_relaxed(MBOX_AUTOMATIC_ACK, base + MBOX_MODE_REG); | |
195 | ||
196 | /* Fill message data */ | |
197 | for (i = 0; i < MBOX_MSG_LEN; i++) | |
198 | writel_relaxed(buf[i], base + MBOX_DATA_REG + i * 4); | |
199 | ||
200 | /* Trigger data transferring */ | |
201 | writel(BIT(mchan->ack_irq), base + MBOX_SEND_REG); | |
202 | return 0; | |
203 | } | |
204 | ||
b5452838 | 205 | static const struct mbox_chan_ops hi3660_mbox_ops = { |
41c0e939 KZ |
206 | .startup = hi3660_mbox_startup, |
207 | .send_data = hi3660_mbox_send_data, | |
208 | }; | |
209 | ||
210 | static struct mbox_chan *hi3660_mbox_xlate(struct mbox_controller *controller, | |
211 | const struct of_phandle_args *spec) | |
212 | { | |
213 | struct hi3660_mbox *mbox = to_hi3660_mbox(controller); | |
214 | struct hi3660_chan_info *mchan; | |
215 | unsigned int ch = spec->args[0]; | |
216 | ||
217 | if (ch >= MBOX_CHAN_MAX) { | |
218 | dev_err(mbox->dev, "Invalid channel idx %d\n", ch); | |
219 | return ERR_PTR(-EINVAL); | |
220 | } | |
221 | ||
222 | mchan = &mbox->mchan[ch]; | |
223 | mchan->dst_irq = spec->args[1]; | |
224 | mchan->ack_irq = spec->args[2]; | |
225 | ||
226 | return &mbox->chan[ch]; | |
227 | } | |
228 | ||
229 | static const struct of_device_id hi3660_mbox_of_match[] = { | |
230 | { .compatible = "hisilicon,hi3660-mbox", }, | |
231 | {}, | |
232 | }; | |
233 | ||
234 | MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match); | |
235 | ||
236 | static int hi3660_mbox_probe(struct platform_device *pdev) | |
237 | { | |
238 | struct device *dev = &pdev->dev; | |
239 | struct hi3660_mbox *mbox; | |
240 | struct mbox_chan *chan; | |
41c0e939 KZ |
241 | unsigned long ch; |
242 | int err; | |
243 | ||
244 | mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); | |
245 | if (!mbox) | |
246 | return -ENOMEM; | |
247 | ||
2801a33d | 248 | mbox->base = devm_platform_ioremap_resource(pdev, 0); |
41c0e939 KZ |
249 | if (IS_ERR(mbox->base)) |
250 | return PTR_ERR(mbox->base); | |
251 | ||
252 | mbox->dev = dev; | |
253 | mbox->controller.dev = dev; | |
254 | mbox->controller.chans = mbox->chan; | |
255 | mbox->controller.num_chans = MBOX_CHAN_MAX; | |
256 | mbox->controller.ops = &hi3660_mbox_ops; | |
257 | mbox->controller.of_xlate = hi3660_mbox_xlate; | |
258 | ||
259 | /* Initialize mailbox channel data */ | |
260 | chan = mbox->chan; | |
261 | for (ch = 0; ch < MBOX_CHAN_MAX; ch++) | |
262 | chan[ch].con_priv = (void *)ch; | |
263 | ||
e73cb83c | 264 | err = devm_mbox_controller_register(dev, &mbox->controller); |
41c0e939 KZ |
265 | if (err) { |
266 | dev_err(dev, "Failed to register mailbox %d\n", err); | |
267 | return err; | |
268 | } | |
269 | ||
270 | platform_set_drvdata(pdev, mbox); | |
271 | dev_info(dev, "Mailbox enabled\n"); | |
272 | return 0; | |
273 | } | |
274 | ||
41c0e939 KZ |
275 | static struct platform_driver hi3660_mbox_driver = { |
276 | .probe = hi3660_mbox_probe, | |
41c0e939 KZ |
277 | .driver = { |
278 | .name = "hi3660-mbox", | |
279 | .of_match_table = hi3660_mbox_of_match, | |
280 | }, | |
281 | }; | |
282 | ||
283 | static int __init hi3660_mbox_init(void) | |
284 | { | |
285 | return platform_driver_register(&hi3660_mbox_driver); | |
286 | } | |
287 | core_initcall(hi3660_mbox_init); | |
288 | ||
289 | static void __exit hi3660_mbox_exit(void) | |
290 | { | |
291 | platform_driver_unregister(&hi3660_mbox_driver); | |
292 | } | |
293 | module_exit(hi3660_mbox_exit); | |
294 | ||
295 | MODULE_LICENSE("GPL"); | |
296 | MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); | |
297 | MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>"); |