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