1 | // SPDX-License-Identifier: GPL-2.0-only OR MIT |
2 | /* |
3 | * Apple mailbox driver |
4 | * |
5 | * Copyright The Asahi Linux Contributors |
6 | * |
7 | * This driver adds support for two mailbox variants (called ASC and M3 by |
8 | * Apple) found in Apple SoCs such as the M1. It consists of two FIFOs used to |
9 | * exchange 64+32 bit messages between the main CPU and a co-processor. |
10 | * Various coprocessors implement different IPC protocols based on these simple |
11 | * messages and shared memory buffers. |
12 | * |
13 | * Both the main CPU and the co-processor see the same set of registers but |
14 | * the first FIFO (A2I) is always used to transfer messages from the application |
15 | * processor (us) to the I/O processor and the second one (I2A) for the |
16 | * other direction. |
17 | */ |
18 | |
19 | #include <linux/bitfield.h> |
20 | #include <linux/bits.h> |
21 | #include <linux/delay.h> |
22 | #include <linux/device.h> |
23 | #include <linux/interrupt.h> |
24 | #include <linux/io.h> |
25 | #include <linux/iopoll.h> |
26 | #include <linux/module.h> |
27 | #include <linux/of.h> |
28 | #include <linux/of_platform.h> |
29 | #include <linux/platform_device.h> |
30 | #include <linux/pm_runtime.h> |
31 | #include <linux/spinlock.h> |
32 | #include <linux/types.h> |
33 | #include "mailbox.h" |
34 | |
35 | #define APPLE_ASC_MBOX_CONTROL_FULL BIT(16) |
36 | #define APPLE_ASC_MBOX_CONTROL_EMPTY BIT(17) |
37 | |
38 | #define APPLE_ASC_MBOX_A2I_CONTROL 0x110 |
39 | #define APPLE_ASC_MBOX_A2I_SEND0 0x800 |
40 | #define APPLE_ASC_MBOX_A2I_SEND1 0x808 |
41 | #define APPLE_ASC_MBOX_A2I_RECV0 0x810 |
42 | #define APPLE_ASC_MBOX_A2I_RECV1 0x818 |
43 | |
44 | #define APPLE_ASC_MBOX_I2A_CONTROL 0x114 |
45 | #define APPLE_ASC_MBOX_I2A_SEND0 0x820 |
46 | #define APPLE_ASC_MBOX_I2A_SEND1 0x828 |
47 | #define APPLE_ASC_MBOX_I2A_RECV0 0x830 |
48 | #define APPLE_ASC_MBOX_I2A_RECV1 0x838 |
49 | |
50 | #define APPLE_M3_MBOX_CONTROL_FULL BIT(16) |
51 | #define APPLE_M3_MBOX_CONTROL_EMPTY BIT(17) |
52 | |
53 | #define APPLE_M3_MBOX_A2I_CONTROL 0x50 |
54 | #define APPLE_M3_MBOX_A2I_SEND0 0x60 |
55 | #define APPLE_M3_MBOX_A2I_SEND1 0x68 |
56 | #define APPLE_M3_MBOX_A2I_RECV0 0x70 |
57 | #define APPLE_M3_MBOX_A2I_RECV1 0x78 |
58 | |
59 | #define APPLE_M3_MBOX_I2A_CONTROL 0x80 |
60 | #define APPLE_M3_MBOX_I2A_SEND0 0x90 |
61 | #define APPLE_M3_MBOX_I2A_SEND1 0x98 |
62 | #define APPLE_M3_MBOX_I2A_RECV0 0xa0 |
63 | #define APPLE_M3_MBOX_I2A_RECV1 0xa8 |
64 | |
65 | #define APPLE_M3_MBOX_IRQ_ENABLE 0x48 |
66 | #define APPLE_M3_MBOX_IRQ_ACK 0x4c |
67 | #define APPLE_M3_MBOX_IRQ_A2I_EMPTY BIT(0) |
68 | #define APPLE_M3_MBOX_IRQ_A2I_NOT_EMPTY BIT(1) |
69 | #define APPLE_M3_MBOX_IRQ_I2A_EMPTY BIT(2) |
70 | #define APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY BIT(3) |
71 | |
72 | #define APPLE_MBOX_MSG1_OUTCNT GENMASK(56, 52) |
73 | #define APPLE_MBOX_MSG1_INCNT GENMASK(51, 48) |
74 | #define APPLE_MBOX_MSG1_OUTPTR GENMASK(47, 44) |
75 | #define APPLE_MBOX_MSG1_INPTR GENMASK(43, 40) |
76 | #define APPLE_MBOX_MSG1_MSG GENMASK(31, 0) |
77 | |
78 | #define APPLE_MBOX_TX_TIMEOUT 500 |
79 | |
80 | struct apple_mbox_hw { |
81 | unsigned int control_full; |
82 | unsigned int control_empty; |
83 | |
84 | unsigned int a2i_control; |
85 | unsigned int a2i_send0; |
86 | unsigned int a2i_send1; |
87 | |
88 | unsigned int i2a_control; |
89 | unsigned int i2a_recv0; |
90 | unsigned int i2a_recv1; |
91 | |
92 | bool has_irq_controls; |
93 | unsigned int irq_enable; |
94 | unsigned int irq_ack; |
95 | unsigned int irq_bit_recv_not_empty; |
96 | unsigned int irq_bit_send_empty; |
97 | }; |
98 | |
99 | int apple_mbox_send(struct apple_mbox *mbox, const struct apple_mbox_msg msg, |
100 | bool atomic) |
101 | { |
102 | unsigned long flags; |
103 | int ret; |
104 | u32 mbox_ctrl; |
105 | long t; |
106 | |
107 | spin_lock_irqsave(&mbox->tx_lock, flags); |
108 | mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->a2i_control); |
109 | |
110 | while (mbox_ctrl & mbox->hw->control_full) { |
111 | if (atomic) { |
112 | ret = readl_poll_timeout_atomic( |
113 | mbox->regs + mbox->hw->a2i_control, mbox_ctrl, |
114 | !(mbox_ctrl & mbox->hw->control_full), 100, |
115 | APPLE_MBOX_TX_TIMEOUT * 1000); |
116 | |
117 | if (ret) { |
118 | spin_unlock_irqrestore(lock: &mbox->tx_lock, flags); |
119 | return ret; |
120 | } |
121 | |
122 | break; |
123 | } |
124 | /* |
125 | * The interrupt is level triggered and will keep firing as long as the |
126 | * FIFO is empty. It will also keep firing if the FIFO was empty |
127 | * at any point in the past until it has been acknowledged at the |
128 | * mailbox level. By acknowledging it here we can ensure that we will |
129 | * only get the interrupt once the FIFO has been cleared again. |
130 | * If the FIFO is already empty before the ack it will fire again |
131 | * immediately after the ack. |
132 | */ |
133 | if (mbox->hw->has_irq_controls) { |
134 | writel_relaxed(mbox->hw->irq_bit_send_empty, |
135 | mbox->regs + mbox->hw->irq_ack); |
136 | } |
137 | enable_irq(irq: mbox->irq_send_empty); |
138 | reinit_completion(x: &mbox->tx_empty); |
139 | spin_unlock_irqrestore(lock: &mbox->tx_lock, flags); |
140 | |
141 | t = wait_for_completion_interruptible_timeout( |
142 | x: &mbox->tx_empty, |
143 | timeout: msecs_to_jiffies(APPLE_MBOX_TX_TIMEOUT)); |
144 | if (t < 0) |
145 | return t; |
146 | else if (t == 0) |
147 | return -ETIMEDOUT; |
148 | |
149 | spin_lock_irqsave(&mbox->tx_lock, flags); |
150 | mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->a2i_control); |
151 | } |
152 | |
153 | writeq_relaxed(msg.msg0, mbox->regs + mbox->hw->a2i_send0); |
154 | writeq_relaxed(FIELD_PREP(APPLE_MBOX_MSG1_MSG, msg.msg1), |
155 | mbox->regs + mbox->hw->a2i_send1); |
156 | |
157 | spin_unlock_irqrestore(lock: &mbox->tx_lock, flags); |
158 | |
159 | return 0; |
160 | } |
161 | EXPORT_SYMBOL(apple_mbox_send); |
162 | |
163 | static irqreturn_t apple_mbox_send_empty_irq(int irq, void *data) |
164 | { |
165 | struct apple_mbox *mbox = data; |
166 | |
167 | /* |
168 | * We don't need to acknowledge the interrupt at the mailbox level |
169 | * here even if supported by the hardware. It will keep firing but that |
170 | * doesn't matter since it's disabled at the main interrupt controller. |
171 | * apple_mbox_send will acknowledge it before enabling |
172 | * it at the main controller again. |
173 | */ |
174 | spin_lock(lock: &mbox->tx_lock); |
175 | disable_irq_nosync(irq: mbox->irq_send_empty); |
176 | complete(&mbox->tx_empty); |
177 | spin_unlock(lock: &mbox->tx_lock); |
178 | |
179 | return IRQ_HANDLED; |
180 | } |
181 | |
182 | static int apple_mbox_poll_locked(struct apple_mbox *mbox) |
183 | { |
184 | struct apple_mbox_msg msg; |
185 | int ret = 0; |
186 | |
187 | u32 mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->i2a_control); |
188 | |
189 | while (!(mbox_ctrl & mbox->hw->control_empty)) { |
190 | msg.msg0 = readq_relaxed(mbox->regs + mbox->hw->i2a_recv0); |
191 | msg.msg1 = FIELD_GET( |
192 | APPLE_MBOX_MSG1_MSG, |
193 | readq_relaxed(mbox->regs + mbox->hw->i2a_recv1)); |
194 | |
195 | mbox->rx(mbox, msg, mbox->cookie); |
196 | ret++; |
197 | mbox_ctrl = readl_relaxed(mbox->regs + mbox->hw->i2a_control); |
198 | } |
199 | |
200 | /* |
201 | * The interrupt will keep firing even if there are no more messages |
202 | * unless we also acknowledge it at the mailbox level here. |
203 | * There's no race if a message comes in between the check in the while |
204 | * loop above and the ack below: If a new messages arrives inbetween |
205 | * those two the interrupt will just fire again immediately after the |
206 | * ack since it's level triggered. |
207 | */ |
208 | if (mbox->hw->has_irq_controls) { |
209 | writel_relaxed(mbox->hw->irq_bit_recv_not_empty, |
210 | mbox->regs + mbox->hw->irq_ack); |
211 | } |
212 | |
213 | return ret; |
214 | } |
215 | |
216 | static irqreturn_t apple_mbox_recv_irq(int irq, void *data) |
217 | { |
218 | struct apple_mbox *mbox = data; |
219 | |
220 | spin_lock(lock: &mbox->rx_lock); |
221 | apple_mbox_poll_locked(mbox); |
222 | spin_unlock(lock: &mbox->rx_lock); |
223 | |
224 | return IRQ_HANDLED; |
225 | } |
226 | |
227 | int apple_mbox_poll(struct apple_mbox *mbox) |
228 | { |
229 | unsigned long flags; |
230 | int ret; |
231 | |
232 | spin_lock_irqsave(&mbox->rx_lock, flags); |
233 | ret = apple_mbox_poll_locked(mbox); |
234 | spin_unlock_irqrestore(lock: &mbox->rx_lock, flags); |
235 | |
236 | return ret; |
237 | } |
238 | EXPORT_SYMBOL(apple_mbox_poll); |
239 | |
240 | int apple_mbox_start(struct apple_mbox *mbox) |
241 | { |
242 | int ret; |
243 | |
244 | if (mbox->active) |
245 | return 0; |
246 | |
247 | ret = pm_runtime_resume_and_get(dev: mbox->dev); |
248 | if (ret) |
249 | return ret; |
250 | |
251 | /* |
252 | * Only some variants of this mailbox HW provide interrupt control |
253 | * at the mailbox level. We therefore need to handle enabling/disabling |
254 | * interrupts at the main interrupt controller anyway for hardware that |
255 | * doesn't. Just always keep the interrupts we care about enabled at |
256 | * the mailbox level so that both hardware revisions behave almost |
257 | * the same. |
258 | */ |
259 | if (mbox->hw->has_irq_controls) { |
260 | writel_relaxed(mbox->hw->irq_bit_recv_not_empty | |
261 | mbox->hw->irq_bit_send_empty, |
262 | mbox->regs + mbox->hw->irq_enable); |
263 | } |
264 | |
265 | enable_irq(irq: mbox->irq_recv_not_empty); |
266 | mbox->active = true; |
267 | return 0; |
268 | } |
269 | EXPORT_SYMBOL(apple_mbox_start); |
270 | |
271 | void apple_mbox_stop(struct apple_mbox *mbox) |
272 | { |
273 | if (!mbox->active) |
274 | return; |
275 | |
276 | mbox->active = false; |
277 | disable_irq(irq: mbox->irq_recv_not_empty); |
278 | pm_runtime_mark_last_busy(dev: mbox->dev); |
279 | pm_runtime_put_autosuspend(dev: mbox->dev); |
280 | } |
281 | EXPORT_SYMBOL(apple_mbox_stop); |
282 | |
283 | struct apple_mbox *apple_mbox_get(struct device *dev, int index) |
284 | { |
285 | struct of_phandle_args args; |
286 | struct platform_device *pdev; |
287 | struct apple_mbox *mbox; |
288 | int ret; |
289 | |
290 | ret = of_parse_phandle_with_args(np: dev->of_node, list_name: "mboxes" , cells_name: "#mbox-cells" , |
291 | index, out_args: &args); |
292 | if (ret || !args.np) |
293 | return ERR_PTR(error: ret); |
294 | |
295 | pdev = of_find_device_by_node(np: args.np); |
296 | of_node_put(node: args.np); |
297 | |
298 | if (!pdev) |
299 | return ERR_PTR(error: -EPROBE_DEFER); |
300 | |
301 | mbox = platform_get_drvdata(pdev); |
302 | if (!mbox) |
303 | return ERR_PTR(error: -EPROBE_DEFER); |
304 | |
305 | if (!device_link_add(consumer: dev, supplier: &pdev->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) |
306 | return ERR_PTR(error: -ENODEV); |
307 | |
308 | return mbox; |
309 | } |
310 | EXPORT_SYMBOL(apple_mbox_get); |
311 | |
312 | struct apple_mbox *apple_mbox_get_byname(struct device *dev, const char *name) |
313 | { |
314 | int index; |
315 | |
316 | index = of_property_match_string(np: dev->of_node, propname: "mbox-names" , string: name); |
317 | if (index < 0) |
318 | return ERR_PTR(error: index); |
319 | |
320 | return apple_mbox_get(dev, index); |
321 | } |
322 | EXPORT_SYMBOL(apple_mbox_get_byname); |
323 | |
324 | static int apple_mbox_probe(struct platform_device *pdev) |
325 | { |
326 | int ret; |
327 | char *irqname; |
328 | struct apple_mbox *mbox; |
329 | struct device *dev = &pdev->dev; |
330 | |
331 | mbox = devm_kzalloc(dev, size: sizeof(*mbox), GFP_KERNEL); |
332 | if (!mbox) |
333 | return -ENOMEM; |
334 | |
335 | mbox->dev = &pdev->dev; |
336 | mbox->hw = of_device_get_match_data(dev); |
337 | if (!mbox->hw) |
338 | return -EINVAL; |
339 | |
340 | mbox->regs = devm_platform_ioremap_resource(pdev, index: 0); |
341 | if (IS_ERR(ptr: mbox->regs)) |
342 | return PTR_ERR(ptr: mbox->regs); |
343 | |
344 | mbox->irq_recv_not_empty = |
345 | platform_get_irq_byname(pdev, "recv-not-empty" ); |
346 | if (mbox->irq_recv_not_empty < 0) |
347 | return -ENODEV; |
348 | |
349 | mbox->irq_send_empty = platform_get_irq_byname(pdev, "send-empty" ); |
350 | if (mbox->irq_send_empty < 0) |
351 | return -ENODEV; |
352 | |
353 | spin_lock_init(&mbox->rx_lock); |
354 | spin_lock_init(&mbox->tx_lock); |
355 | init_completion(x: &mbox->tx_empty); |
356 | |
357 | irqname = devm_kasprintf(dev, GFP_KERNEL, fmt: "%s-recv" , dev_name(dev)); |
358 | if (!irqname) |
359 | return -ENOMEM; |
360 | |
361 | ret = devm_request_irq(dev, irq: mbox->irq_recv_not_empty, |
362 | handler: apple_mbox_recv_irq, |
363 | IRQF_NO_AUTOEN | IRQF_NO_SUSPEND, devname: irqname, dev_id: mbox); |
364 | if (ret) |
365 | return ret; |
366 | |
367 | irqname = devm_kasprintf(dev, GFP_KERNEL, fmt: "%s-send" , dev_name(dev)); |
368 | if (!irqname) |
369 | return -ENOMEM; |
370 | |
371 | ret = devm_request_irq(dev, irq: mbox->irq_send_empty, |
372 | handler: apple_mbox_send_empty_irq, |
373 | IRQF_NO_AUTOEN | IRQF_NO_SUSPEND, devname: irqname, dev_id: mbox); |
374 | if (ret) |
375 | return ret; |
376 | |
377 | ret = devm_pm_runtime_enable(dev); |
378 | if (ret) |
379 | return ret; |
380 | |
381 | platform_set_drvdata(pdev, data: mbox); |
382 | return 0; |
383 | } |
384 | |
385 | static const struct apple_mbox_hw apple_mbox_asc_hw = { |
386 | .control_full = APPLE_ASC_MBOX_CONTROL_FULL, |
387 | .control_empty = APPLE_ASC_MBOX_CONTROL_EMPTY, |
388 | |
389 | .a2i_control = APPLE_ASC_MBOX_A2I_CONTROL, |
390 | .a2i_send0 = APPLE_ASC_MBOX_A2I_SEND0, |
391 | .a2i_send1 = APPLE_ASC_MBOX_A2I_SEND1, |
392 | |
393 | .i2a_control = APPLE_ASC_MBOX_I2A_CONTROL, |
394 | .i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0, |
395 | .i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1, |
396 | |
397 | .has_irq_controls = false, |
398 | }; |
399 | |
400 | static const struct apple_mbox_hw apple_mbox_m3_hw = { |
401 | .control_full = APPLE_M3_MBOX_CONTROL_FULL, |
402 | .control_empty = APPLE_M3_MBOX_CONTROL_EMPTY, |
403 | |
404 | .a2i_control = APPLE_M3_MBOX_A2I_CONTROL, |
405 | .a2i_send0 = APPLE_M3_MBOX_A2I_SEND0, |
406 | .a2i_send1 = APPLE_M3_MBOX_A2I_SEND1, |
407 | |
408 | .i2a_control = APPLE_M3_MBOX_I2A_CONTROL, |
409 | .i2a_recv0 = APPLE_M3_MBOX_I2A_RECV0, |
410 | .i2a_recv1 = APPLE_M3_MBOX_I2A_RECV1, |
411 | |
412 | .has_irq_controls = true, |
413 | .irq_enable = APPLE_M3_MBOX_IRQ_ENABLE, |
414 | .irq_ack = APPLE_M3_MBOX_IRQ_ACK, |
415 | .irq_bit_recv_not_empty = APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY, |
416 | .irq_bit_send_empty = APPLE_M3_MBOX_IRQ_A2I_EMPTY, |
417 | }; |
418 | |
419 | static const struct of_device_id apple_mbox_of_match[] = { |
420 | { .compatible = "apple,asc-mailbox-v4" , .data = &apple_mbox_asc_hw }, |
421 | { .compatible = "apple,m3-mailbox-v2" , .data = &apple_mbox_m3_hw }, |
422 | {} |
423 | }; |
424 | MODULE_DEVICE_TABLE(of, apple_mbox_of_match); |
425 | |
426 | static struct platform_driver apple_mbox_driver = { |
427 | .driver = { |
428 | .name = "apple-mailbox" , |
429 | .of_match_table = apple_mbox_of_match, |
430 | }, |
431 | .probe = apple_mbox_probe, |
432 | }; |
433 | module_platform_driver(apple_mbox_driver); |
434 | |
435 | MODULE_LICENSE("Dual MIT/GPL" ); |
436 | MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>" ); |
437 | MODULE_DESCRIPTION("Apple Mailbox driver" ); |
438 | |