]>
Commit | Line | Data |
---|---|---|
41c0e939 KZ |
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 | */ | |
55 | struct 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 | */ | |
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 | ||
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 | ||
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 | ||
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 | ||
178 | static 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 | ||
209 | static struct mbox_chan_ops hi3660_mbox_ops = { | |
210 | .startup = hi3660_mbox_startup, | |
211 | .send_data = hi3660_mbox_send_data, | |
212 | }; | |
213 | ||
214 | static 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 | ||
233 | static const struct of_device_id hi3660_mbox_of_match[] = { | |
234 | { .compatible = "hisilicon,hi3660-mbox", }, | |
235 | {}, | |
236 | }; | |
237 | ||
238 | MODULE_DEVICE_TABLE(of, hi3660_mbox_of_match); | |
239 | ||
240 | static 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 | ||
281 | static 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 | ||
289 | static 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 | ||
298 | static int __init hi3660_mbox_init(void) | |
299 | { | |
300 | return platform_driver_register(&hi3660_mbox_driver); | |
301 | } | |
302 | core_initcall(hi3660_mbox_init); | |
303 | ||
304 | static void __exit hi3660_mbox_exit(void) | |
305 | { | |
306 | platform_driver_unregister(&hi3660_mbox_driver); | |
307 | } | |
308 | module_exit(hi3660_mbox_exit); | |
309 | ||
310 | MODULE_LICENSE("GPL"); | |
311 | MODULE_DESCRIPTION("Hisilicon Hi3660 Mailbox Controller"); | |
312 | MODULE_AUTHOR("Leo Yan <[email protected]>"); |