162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only OR MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Apple mailbox driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2021 The Asahi Linux Contributors
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * This driver adds support for two mailbox variants (called ASC and M3 by
862306a36Sopenharmony_ci * Apple) found in Apple SoCs such as the M1. It consists of two FIFOs used to
962306a36Sopenharmony_ci * exchange 64+32 bit messages between the main CPU and a co-processor.
1062306a36Sopenharmony_ci * Various coprocessors implement different IPC protocols based on these simple
1162306a36Sopenharmony_ci * messages and shared memory buffers.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Both the main CPU and the co-processor see the same set of registers but
1462306a36Sopenharmony_ci * the first FIFO (A2I) is always used to transfer messages from the application
1562306a36Sopenharmony_ci * processor (us) to the I/O processor and the second one (I2A) for the
1662306a36Sopenharmony_ci * other direction.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/apple-mailbox.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/device.h>
2262306a36Sopenharmony_ci#include <linux/gfp.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/io.h>
2562306a36Sopenharmony_ci#include <linux/mailbox_controller.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/of.h>
2862306a36Sopenharmony_ci#include <linux/platform_device.h>
2962306a36Sopenharmony_ci#include <linux/spinlock.h>
3062306a36Sopenharmony_ci#include <linux/types.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define APPLE_ASC_MBOX_CONTROL_FULL  BIT(16)
3362306a36Sopenharmony_ci#define APPLE_ASC_MBOX_CONTROL_EMPTY BIT(17)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define APPLE_ASC_MBOX_A2I_CONTROL 0x110
3662306a36Sopenharmony_ci#define APPLE_ASC_MBOX_A2I_SEND0   0x800
3762306a36Sopenharmony_ci#define APPLE_ASC_MBOX_A2I_SEND1   0x808
3862306a36Sopenharmony_ci#define APPLE_ASC_MBOX_A2I_RECV0   0x810
3962306a36Sopenharmony_ci#define APPLE_ASC_MBOX_A2I_RECV1   0x818
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define APPLE_ASC_MBOX_I2A_CONTROL 0x114
4262306a36Sopenharmony_ci#define APPLE_ASC_MBOX_I2A_SEND0   0x820
4362306a36Sopenharmony_ci#define APPLE_ASC_MBOX_I2A_SEND1   0x828
4462306a36Sopenharmony_ci#define APPLE_ASC_MBOX_I2A_RECV0   0x830
4562306a36Sopenharmony_ci#define APPLE_ASC_MBOX_I2A_RECV1   0x838
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define APPLE_M3_MBOX_CONTROL_FULL  BIT(16)
4862306a36Sopenharmony_ci#define APPLE_M3_MBOX_CONTROL_EMPTY BIT(17)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define APPLE_M3_MBOX_A2I_CONTROL 0x50
5162306a36Sopenharmony_ci#define APPLE_M3_MBOX_A2I_SEND0	  0x60
5262306a36Sopenharmony_ci#define APPLE_M3_MBOX_A2I_SEND1	  0x68
5362306a36Sopenharmony_ci#define APPLE_M3_MBOX_A2I_RECV0	  0x70
5462306a36Sopenharmony_ci#define APPLE_M3_MBOX_A2I_RECV1	  0x78
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define APPLE_M3_MBOX_I2A_CONTROL 0x80
5762306a36Sopenharmony_ci#define APPLE_M3_MBOX_I2A_SEND0	  0x90
5862306a36Sopenharmony_ci#define APPLE_M3_MBOX_I2A_SEND1	  0x98
5962306a36Sopenharmony_ci#define APPLE_M3_MBOX_I2A_RECV0	  0xa0
6062306a36Sopenharmony_ci#define APPLE_M3_MBOX_I2A_RECV1	  0xa8
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define APPLE_M3_MBOX_IRQ_ENABLE	0x48
6362306a36Sopenharmony_ci#define APPLE_M3_MBOX_IRQ_ACK		0x4c
6462306a36Sopenharmony_ci#define APPLE_M3_MBOX_IRQ_A2I_EMPTY	BIT(0)
6562306a36Sopenharmony_ci#define APPLE_M3_MBOX_IRQ_A2I_NOT_EMPTY BIT(1)
6662306a36Sopenharmony_ci#define APPLE_M3_MBOX_IRQ_I2A_EMPTY	BIT(2)
6762306a36Sopenharmony_ci#define APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY BIT(3)
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci#define APPLE_MBOX_MSG1_OUTCNT GENMASK(56, 52)
7062306a36Sopenharmony_ci#define APPLE_MBOX_MSG1_INCNT  GENMASK(51, 48)
7162306a36Sopenharmony_ci#define APPLE_MBOX_MSG1_OUTPTR GENMASK(47, 44)
7262306a36Sopenharmony_ci#define APPLE_MBOX_MSG1_INPTR  GENMASK(43, 40)
7362306a36Sopenharmony_ci#define APPLE_MBOX_MSG1_MSG    GENMASK(31, 0)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct apple_mbox_hw {
7662306a36Sopenharmony_ci	unsigned int control_full;
7762306a36Sopenharmony_ci	unsigned int control_empty;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	unsigned int a2i_control;
8062306a36Sopenharmony_ci	unsigned int a2i_send0;
8162306a36Sopenharmony_ci	unsigned int a2i_send1;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	unsigned int i2a_control;
8462306a36Sopenharmony_ci	unsigned int i2a_recv0;
8562306a36Sopenharmony_ci	unsigned int i2a_recv1;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	bool has_irq_controls;
8862306a36Sopenharmony_ci	unsigned int irq_enable;
8962306a36Sopenharmony_ci	unsigned int irq_ack;
9062306a36Sopenharmony_ci	unsigned int irq_bit_recv_not_empty;
9162306a36Sopenharmony_ci	unsigned int irq_bit_send_empty;
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct apple_mbox {
9562306a36Sopenharmony_ci	void __iomem *regs;
9662306a36Sopenharmony_ci	const struct apple_mbox_hw *hw;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	int irq_recv_not_empty;
9962306a36Sopenharmony_ci	int irq_send_empty;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	struct mbox_chan chan;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	struct device *dev;
10462306a36Sopenharmony_ci	struct mbox_controller controller;
10562306a36Sopenharmony_ci	spinlock_t rx_lock;
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic const struct of_device_id apple_mbox_of_match[];
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic bool apple_mbox_hw_can_send(struct apple_mbox *apple_mbox)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	u32 mbox_ctrl =
11362306a36Sopenharmony_ci		readl_relaxed(apple_mbox->regs + apple_mbox->hw->a2i_control);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return !(mbox_ctrl & apple_mbox->hw->control_full);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic bool apple_mbox_hw_send_empty(struct apple_mbox *apple_mbox)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	u32 mbox_ctrl =
12162306a36Sopenharmony_ci		readl_relaxed(apple_mbox->regs + apple_mbox->hw->a2i_control);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return mbox_ctrl & apple_mbox->hw->control_empty;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int apple_mbox_hw_send(struct apple_mbox *apple_mbox,
12762306a36Sopenharmony_ci			      struct apple_mbox_msg *msg)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (!apple_mbox_hw_can_send(apple_mbox))
13062306a36Sopenharmony_ci		return -EBUSY;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	dev_dbg(apple_mbox->dev, "> TX %016llx %08x\n", msg->msg0, msg->msg1);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	writeq_relaxed(msg->msg0, apple_mbox->regs + apple_mbox->hw->a2i_send0);
13562306a36Sopenharmony_ci	writeq_relaxed(FIELD_PREP(APPLE_MBOX_MSG1_MSG, msg->msg1),
13662306a36Sopenharmony_ci		       apple_mbox->regs + apple_mbox->hw->a2i_send1);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic bool apple_mbox_hw_can_recv(struct apple_mbox *apple_mbox)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	u32 mbox_ctrl =
14462306a36Sopenharmony_ci		readl_relaxed(apple_mbox->regs + apple_mbox->hw->i2a_control);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return !(mbox_ctrl & apple_mbox->hw->control_empty);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int apple_mbox_hw_recv(struct apple_mbox *apple_mbox,
15062306a36Sopenharmony_ci			      struct apple_mbox_msg *msg)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	if (!apple_mbox_hw_can_recv(apple_mbox))
15362306a36Sopenharmony_ci		return -ENOMSG;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	msg->msg0 = readq_relaxed(apple_mbox->regs + apple_mbox->hw->i2a_recv0);
15662306a36Sopenharmony_ci	msg->msg1 = FIELD_GET(
15762306a36Sopenharmony_ci		APPLE_MBOX_MSG1_MSG,
15862306a36Sopenharmony_ci		readq_relaxed(apple_mbox->regs + apple_mbox->hw->i2a_recv1));
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	dev_dbg(apple_mbox->dev, "< RX %016llx %08x\n", msg->msg0, msg->msg1);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic int apple_mbox_chan_send_data(struct mbox_chan *chan, void *data)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = chan->con_priv;
16862306a36Sopenharmony_ci	struct apple_mbox_msg *msg = data;
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ret = apple_mbox_hw_send(apple_mbox, msg);
17262306a36Sopenharmony_ci	if (ret)
17362306a36Sopenharmony_ci		return ret;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * The interrupt is level triggered and will keep firing as long as the
17762306a36Sopenharmony_ci	 * FIFO is empty. It will also keep firing if the FIFO was empty
17862306a36Sopenharmony_ci	 * at any point in the past until it has been acknowledged at the
17962306a36Sopenharmony_ci	 * mailbox level. By acknowledging it here we can ensure that we will
18062306a36Sopenharmony_ci	 * only get the interrupt once the FIFO has been cleared again.
18162306a36Sopenharmony_ci	 * If the FIFO is already empty before the ack it will fire again
18262306a36Sopenharmony_ci	 * immediately after the ack.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	if (apple_mbox->hw->has_irq_controls) {
18562306a36Sopenharmony_ci		writel_relaxed(apple_mbox->hw->irq_bit_send_empty,
18662306a36Sopenharmony_ci			       apple_mbox->regs + apple_mbox->hw->irq_ack);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	enable_irq(apple_mbox->irq_send_empty);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return 0;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic irqreturn_t apple_mbox_send_empty_irq(int irq, void *data)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = data;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/*
19862306a36Sopenharmony_ci	 * We don't need to acknowledge the interrupt at the mailbox level
19962306a36Sopenharmony_ci	 * here even if supported by the hardware. It will keep firing but that
20062306a36Sopenharmony_ci	 * doesn't matter since it's disabled at the main interrupt controller.
20162306a36Sopenharmony_ci	 * apple_mbox_chan_send_data will acknowledge it before enabling
20262306a36Sopenharmony_ci	 * it at the main controller again.
20362306a36Sopenharmony_ci	 */
20462306a36Sopenharmony_ci	disable_irq_nosync(apple_mbox->irq_send_empty);
20562306a36Sopenharmony_ci	mbox_chan_txdone(&apple_mbox->chan, 0);
20662306a36Sopenharmony_ci	return IRQ_HANDLED;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int apple_mbox_poll(struct apple_mbox *apple_mbox)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct apple_mbox_msg msg;
21262306a36Sopenharmony_ci	int ret = 0;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	while (apple_mbox_hw_recv(apple_mbox, &msg) == 0) {
21562306a36Sopenharmony_ci		mbox_chan_received_data(&apple_mbox->chan, (void *)&msg);
21662306a36Sopenharmony_ci		ret++;
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	/*
22062306a36Sopenharmony_ci	 * The interrupt will keep firing even if there are no more messages
22162306a36Sopenharmony_ci	 * unless we also acknowledge it at the mailbox level here.
22262306a36Sopenharmony_ci	 * There's no race if a message comes in between the check in the while
22362306a36Sopenharmony_ci	 * loop above and the ack below: If a new messages arrives inbetween
22462306a36Sopenharmony_ci	 * those two the interrupt will just fire again immediately after the
22562306a36Sopenharmony_ci	 * ack since it's level triggered.
22662306a36Sopenharmony_ci	 */
22762306a36Sopenharmony_ci	if (apple_mbox->hw->has_irq_controls) {
22862306a36Sopenharmony_ci		writel_relaxed(apple_mbox->hw->irq_bit_recv_not_empty,
22962306a36Sopenharmony_ci			       apple_mbox->regs + apple_mbox->hw->irq_ack);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return ret;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic irqreturn_t apple_mbox_recv_irq(int irq, void *data)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = data;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	spin_lock(&apple_mbox->rx_lock);
24062306a36Sopenharmony_ci	apple_mbox_poll(apple_mbox);
24162306a36Sopenharmony_ci	spin_unlock(&apple_mbox->rx_lock);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return IRQ_HANDLED;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic bool apple_mbox_chan_peek_data(struct mbox_chan *chan)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = chan->con_priv;
24962306a36Sopenharmony_ci	unsigned long flags;
25062306a36Sopenharmony_ci	int ret;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	spin_lock_irqsave(&apple_mbox->rx_lock, flags);
25362306a36Sopenharmony_ci	ret = apple_mbox_poll(apple_mbox);
25462306a36Sopenharmony_ci	spin_unlock_irqrestore(&apple_mbox->rx_lock, flags);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return ret > 0;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int apple_mbox_chan_flush(struct mbox_chan *chan, unsigned long timeout)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = chan->con_priv;
26262306a36Sopenharmony_ci	unsigned long deadline = jiffies + msecs_to_jiffies(timeout);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	while (time_before(jiffies, deadline)) {
26562306a36Sopenharmony_ci		if (apple_mbox_hw_send_empty(apple_mbox)) {
26662306a36Sopenharmony_ci			mbox_chan_txdone(&apple_mbox->chan, 0);
26762306a36Sopenharmony_ci			return 0;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		udelay(1);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return -ETIME;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int apple_mbox_chan_startup(struct mbox_chan *chan)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = chan->con_priv;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	/*
28162306a36Sopenharmony_ci	 * Only some variants of this mailbox HW provide interrupt control
28262306a36Sopenharmony_ci	 * at the mailbox level. We therefore need to handle enabling/disabling
28362306a36Sopenharmony_ci	 * interrupts at the main interrupt controller anyway for hardware that
28462306a36Sopenharmony_ci	 * doesn't. Just always keep the interrupts we care about enabled at
28562306a36Sopenharmony_ci	 * the mailbox level so that both hardware revisions behave almost
28662306a36Sopenharmony_ci	 * the same.
28762306a36Sopenharmony_ci	 */
28862306a36Sopenharmony_ci	if (apple_mbox->hw->has_irq_controls) {
28962306a36Sopenharmony_ci		writel_relaxed(apple_mbox->hw->irq_bit_recv_not_empty |
29062306a36Sopenharmony_ci				       apple_mbox->hw->irq_bit_send_empty,
29162306a36Sopenharmony_ci			       apple_mbox->regs + apple_mbox->hw->irq_enable);
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	enable_irq(apple_mbox->irq_recv_not_empty);
29562306a36Sopenharmony_ci	return 0;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void apple_mbox_chan_shutdown(struct mbox_chan *chan)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct apple_mbox *apple_mbox = chan->con_priv;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	disable_irq(apple_mbox->irq_recv_not_empty);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic const struct mbox_chan_ops apple_mbox_ops = {
30662306a36Sopenharmony_ci	.send_data = apple_mbox_chan_send_data,
30762306a36Sopenharmony_ci	.peek_data = apple_mbox_chan_peek_data,
30862306a36Sopenharmony_ci	.flush = apple_mbox_chan_flush,
30962306a36Sopenharmony_ci	.startup = apple_mbox_chan_startup,
31062306a36Sopenharmony_ci	.shutdown = apple_mbox_chan_shutdown,
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic struct mbox_chan *apple_mbox_of_xlate(struct mbox_controller *mbox,
31462306a36Sopenharmony_ci					     const struct of_phandle_args *args)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	if (args->args_count != 0)
31762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return &mbox->chans[0];
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic int apple_mbox_probe(struct platform_device *pdev)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	int ret;
32562306a36Sopenharmony_ci	const struct of_device_id *match;
32662306a36Sopenharmony_ci	char *irqname;
32762306a36Sopenharmony_ci	struct apple_mbox *mbox;
32862306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	match = of_match_node(apple_mbox_of_match, pdev->dev.of_node);
33162306a36Sopenharmony_ci	if (!match)
33262306a36Sopenharmony_ci		return -EINVAL;
33362306a36Sopenharmony_ci	if (!match->data)
33462306a36Sopenharmony_ci		return -EINVAL;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
33762306a36Sopenharmony_ci	if (!mbox)
33862306a36Sopenharmony_ci		return -ENOMEM;
33962306a36Sopenharmony_ci	platform_set_drvdata(pdev, mbox);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	mbox->dev = dev;
34262306a36Sopenharmony_ci	mbox->regs = devm_platform_ioremap_resource(pdev, 0);
34362306a36Sopenharmony_ci	if (IS_ERR(mbox->regs))
34462306a36Sopenharmony_ci		return PTR_ERR(mbox->regs);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	mbox->hw = match->data;
34762306a36Sopenharmony_ci	mbox->irq_recv_not_empty =
34862306a36Sopenharmony_ci		platform_get_irq_byname(pdev, "recv-not-empty");
34962306a36Sopenharmony_ci	if (mbox->irq_recv_not_empty < 0)
35062306a36Sopenharmony_ci		return -ENODEV;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	mbox->irq_send_empty = platform_get_irq_byname(pdev, "send-empty");
35362306a36Sopenharmony_ci	if (mbox->irq_send_empty < 0)
35462306a36Sopenharmony_ci		return -ENODEV;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	mbox->controller.dev = mbox->dev;
35762306a36Sopenharmony_ci	mbox->controller.num_chans = 1;
35862306a36Sopenharmony_ci	mbox->controller.chans = &mbox->chan;
35962306a36Sopenharmony_ci	mbox->controller.ops = &apple_mbox_ops;
36062306a36Sopenharmony_ci	mbox->controller.txdone_irq = true;
36162306a36Sopenharmony_ci	mbox->controller.of_xlate = apple_mbox_of_xlate;
36262306a36Sopenharmony_ci	mbox->chan.con_priv = mbox;
36362306a36Sopenharmony_ci	spin_lock_init(&mbox->rx_lock);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-recv", dev_name(dev));
36662306a36Sopenharmony_ci	if (!irqname)
36762306a36Sopenharmony_ci		return -ENOMEM;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	ret = devm_request_threaded_irq(dev, mbox->irq_recv_not_empty, NULL,
37062306a36Sopenharmony_ci					apple_mbox_recv_irq,
37162306a36Sopenharmony_ci					IRQF_NO_AUTOEN | IRQF_ONESHOT, irqname,
37262306a36Sopenharmony_ci					mbox);
37362306a36Sopenharmony_ci	if (ret)
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	irqname = devm_kasprintf(dev, GFP_KERNEL, "%s-send", dev_name(dev));
37762306a36Sopenharmony_ci	if (!irqname)
37862306a36Sopenharmony_ci		return -ENOMEM;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	ret = devm_request_irq(dev, mbox->irq_send_empty,
38162306a36Sopenharmony_ci			       apple_mbox_send_empty_irq, IRQF_NO_AUTOEN,
38262306a36Sopenharmony_ci			       irqname, mbox);
38362306a36Sopenharmony_ci	if (ret)
38462306a36Sopenharmony_ci		return ret;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return devm_mbox_controller_register(dev, &mbox->controller);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic const struct apple_mbox_hw apple_mbox_asc_hw = {
39062306a36Sopenharmony_ci	.control_full = APPLE_ASC_MBOX_CONTROL_FULL,
39162306a36Sopenharmony_ci	.control_empty = APPLE_ASC_MBOX_CONTROL_EMPTY,
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	.a2i_control = APPLE_ASC_MBOX_A2I_CONTROL,
39462306a36Sopenharmony_ci	.a2i_send0 = APPLE_ASC_MBOX_A2I_SEND0,
39562306a36Sopenharmony_ci	.a2i_send1 = APPLE_ASC_MBOX_A2I_SEND1,
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	.i2a_control = APPLE_ASC_MBOX_I2A_CONTROL,
39862306a36Sopenharmony_ci	.i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0,
39962306a36Sopenharmony_ci	.i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1,
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	.has_irq_controls = false,
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic const struct apple_mbox_hw apple_mbox_m3_hw = {
40562306a36Sopenharmony_ci	.control_full = APPLE_M3_MBOX_CONTROL_FULL,
40662306a36Sopenharmony_ci	.control_empty = APPLE_M3_MBOX_CONTROL_EMPTY,
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	.a2i_control = APPLE_M3_MBOX_A2I_CONTROL,
40962306a36Sopenharmony_ci	.a2i_send0 = APPLE_M3_MBOX_A2I_SEND0,
41062306a36Sopenharmony_ci	.a2i_send1 = APPLE_M3_MBOX_A2I_SEND1,
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	.i2a_control = APPLE_M3_MBOX_I2A_CONTROL,
41362306a36Sopenharmony_ci	.i2a_recv0 = APPLE_M3_MBOX_I2A_RECV0,
41462306a36Sopenharmony_ci	.i2a_recv1 = APPLE_M3_MBOX_I2A_RECV1,
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	.has_irq_controls = true,
41762306a36Sopenharmony_ci	.irq_enable = APPLE_M3_MBOX_IRQ_ENABLE,
41862306a36Sopenharmony_ci	.irq_ack = APPLE_M3_MBOX_IRQ_ACK,
41962306a36Sopenharmony_ci	.irq_bit_recv_not_empty = APPLE_M3_MBOX_IRQ_I2A_NOT_EMPTY,
42062306a36Sopenharmony_ci	.irq_bit_send_empty = APPLE_M3_MBOX_IRQ_A2I_EMPTY,
42162306a36Sopenharmony_ci};
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic const struct of_device_id apple_mbox_of_match[] = {
42462306a36Sopenharmony_ci	{ .compatible = "apple,asc-mailbox-v4", .data = &apple_mbox_asc_hw },
42562306a36Sopenharmony_ci	{ .compatible = "apple,m3-mailbox-v2", .data = &apple_mbox_m3_hw },
42662306a36Sopenharmony_ci	{}
42762306a36Sopenharmony_ci};
42862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, apple_mbox_of_match);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic struct platform_driver apple_mbox_driver = {
43162306a36Sopenharmony_ci	.driver = {
43262306a36Sopenharmony_ci		.name = "apple-mailbox",
43362306a36Sopenharmony_ci		.of_match_table = apple_mbox_of_match,
43462306a36Sopenharmony_ci	},
43562306a36Sopenharmony_ci	.probe = apple_mbox_probe,
43662306a36Sopenharmony_ci};
43762306a36Sopenharmony_cimodule_platform_driver(apple_mbox_driver);
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL");
44062306a36Sopenharmony_ciMODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
44162306a36Sopenharmony_ciMODULE_DESCRIPTION("Apple Mailbox driver");
442