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