From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D3834EDCB1; Fri, 18 Sep 2026 13:28:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789738120; cv=none; b=XPncfXzL7oMdR/2b25jL+4Xomu+gx3o2NGi8ZTyGuGLTovNlqTel5esZ9Qcol6xlgg0hr074xR3/Wn++/vFKm4hlfYKNV7oFS7m9MPDydOn4q6xFgOZB+jB3gJUjC4SQyZ1j8hK5aF3U555Q7S9C9uvEHU/guXgme9qLAARPRsI= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789738120; c=relaxed/simple; bh=Cm58DSIrb/G+4Sg0ytpzkm65yOH/0amB8MFdhlzrJbk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fBGlzEZTVGFJspSguUHxJuaz+AuqiQgRquHtqb/+JqVxqaF6CsMB/odkJcj+HT0qA+ghLbBIu/ZTzFsV5dD/8FcnYqTNjpqwCSyPQh2FsVzPJUcxBQ0F3buLxDTcuFDDAHk5z90iKlY4OY3Gih/iFEpWgnoACQk8C151GsigxeM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=nsABvMcP; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="nsABvMcP" Received: by smtp.kernel.org (Postfix) with ESMTPS id 3B871C2BD01; Fri, 18 Sep 2026 13:28:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1789738120; bh=Cm58DSIrb/G+4Sg0ytpzkm65yOH/0amB8MFdhlzrJbk=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=nsABvMcPFj6U9cBh9wnUasQiXopb2YYD3exd+sBPmTuIOoaI03j5qK3qSSh/zrSkR yrtJ9jr9uuXBxdVK4DW3cjpeWAQf5ZlDAk7k3yX38mE+t5QaYgi+7i6wqOFKt0qbfE BXcrPO7G6QHgNlQpQ61bPDp1xE8adS7sneEVW7A5skOuhZA44shds+0G+BZptRJdfs HvR+za2giQVoFyykBkxiAmZA7Puyx5VHXbwvfA/AbnRjLgOCUeDsRbLZQLcAj3MUEI ah0TmglUZl+YkuHD3UDpd0xXJuPIuft4glHtHckZoAabP0lsDMJj0+ladfiuKsuRgn xmSnR6/8ASA0w== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1EBA2C982E0; Fri, 18 Sep 2026 13:28:40 +0000 (UTC) From: Michael Reeves via B4 Relay Date: Fri, 18 Sep 2026 23:28:35 +1000 Subject: [PATCH v3 05/10] mailbox: apple: Add DockChannel FIFO controller Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260918-apple-mtp-keyboard-final-v3-5-e356464e454b@gmail.com> References: <20260918-apple-mtp-keyboard-final-v3-0-e356464e454b@gmail.com> In-Reply-To: <20260918-apple-mtp-keyboard-final-v3-0-e356464e454b@gmail.com> To: Sven Peter , Janne Grunau , Neal Gompa , Jassi Brar , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Hector Martin , Michael Reeves , "Joerg Roedel (AMD)" , Will Deacon , Robin Murphy , Dmitry Torokhov , Jiri Kosina , Benjamin Tissoires Cc: asahi@lists.linux.dev, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, iommu@lists.linux.dev, linux-input@vger.kernel.org, Yureka Lilian , Julian Braha , Sasha Finkelstein X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1789738118; l=14201; i=michael.reeves077@gmail.com; s=fedora-20260918; h=from:subject:message-id; bh=JfOc3mv53wX6q509C07iXDBxLQ9JoLzRgZnNsuIBQZs=; b=BShfkNGSEfn1l6Db3eJPY3AakwUDT8qB4h7LCcuRNij4b/i8BskHOCjGw/JMh8Kn2NWDl+fni b0NQ3n6IBSrDKGTMDW6Fxb0+0rDw4hNKhIx/M0Ir1t6LbUEQZ2uf8C5 X-Developer-Key: i=michael.reeves077@gmail.com; a=ed25519; pk=VJ0TvyQ9TvfzYZa908YSE9kpkjFLLD5luv0bERu+boc= X-Endpoint-Received: by B4 Relay for michael.reeves077@gmail.com/fedora-20260918 with auth_id=1035 X-Original-From: Michael Reeves Reply-To: michael.reeves077@gmail.com From: Michael Reeves DockChannel is a hardware FIFO used by Apple coprocessors for low-latency byte-stream communication with the AP. Add a mailbox controller that preallocates RX storage, tracks IRQ enable state in software, and reports TX completion from the TX-empty interrupt. Reject messages larger than the FIFO and return -EBUSY while the previous message is still pending. This keeps the provider usable for future small-message clients such as serial transports without a TX worker. Handle RX and TX interrupts in a thread so mailbox callbacks and locking run outside hard-IRQ context. Co-developed-by: Hector Martin Signed-off-by: Hector Martin Signed-off-by: Michael Reeves --- MAINTAINERS | 2 + drivers/mailbox/Kconfig | 12 + drivers/mailbox/Makefile | 2 + drivers/mailbox/apple-dockchannel.c | 372 ++++++++++++++++++++++++++++++ include/linux/mailbox/apple-dockchannel.h | 29 +++ 5 files changed, 417 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 7fe2ebb5e..e5be5934c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2677,6 +2677,7 @@ F: drivers/input/touchscreen/apple_z2.c F: drivers/iommu/apple-dart.c F: drivers/iommu/io-pgtable-dart.c F: drivers/irqchip/irq-apple-aic.c +F: drivers/mailbox/apple-dockchannel.c F: drivers/mfd/macsmc.c F: drivers/nvme/host/apple.c F: drivers/nvmem/apple-efuses.c @@ -2695,6 +2696,7 @@ F: drivers/video/backlight/apple_dwi_bl.c F: drivers/watchdog/apple_wdt.c F: include/dt-bindings/interrupt-controller/apple-aic.h F: include/dt-bindings/pinctrl/apple.h +F: include/linux/mailbox/apple-dockchannel.h F: include/linux/mfd/macsmc.h F: include/linux/soc/apple/* F: include/uapi/drm/asahi_drm.h diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index ab869769e..503d3286f 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -36,6 +36,18 @@ config ARM_MHU_V3 that provides different means of transports: supported extensions will be discovered and possibly managed at probe-time. +config APPLE_DOCKCHANNEL + tristate "Apple DockChannel FIFO mailbox" + depends on ARCH_APPLE || COMPILE_TEST + depends on HAS_IOMEM + depends on OF + help + DockChannel is a hardware FIFO used on Apple Silicon SoCs for + communication between the application processor and co-processors. + This driver exposes DockChannel FIFOs through the mailbox framework. + + Say Y here if you have an M2 or later Apple MacBook. + config AST2700_MBOX tristate "ASPEED AST2700 IPC driver" depends on ARCH_ASPEED || COMPILE_TEST diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 45dc59bb3..61b15fe45 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_ARM_MHU_V2) += arm_mhuv2.o obj-$(CONFIG_ARM_MHU_V3) += arm_mhuv3.o +obj-$(CONFIG_APPLE_DOCKCHANNEL) += apple-dockchannel.o + obj-$(CONFIG_AST2700_MBOX) += ast2700-mailbox.o obj-$(CONFIG_CV1800_MBOX) += cv1800-mailbox.o diff --git a/drivers/mailbox/apple-dockchannel.c b/drivers/mailbox/apple-dockchannel.c new file mode 100644 index 000000000..a99b8dad0 --- /dev/null +++ b/drivers/mailbox/apple-dockchannel.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple DockChannel mailbox controller + * + * Copyright The Asahi Linux Contributors + * + * DockChannel is a byte FIFO used by Apple co-processors. This driver exposes a + * single FIFO pair as a Linux mailbox channel and moves payload bytes with PIO. + * There is no DMA involved, so relaxed MMIO accessors are sufficient for the + * FIFO accesses themselves. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_DOCKCHANNEL_FIFO_SIZE 0x800 + +#define IRQ_MASK 0x0 +#define IRQ_FLAG 0x4 + +#define IRQ_TX BIT(2) +#define IRQ_RX BIT(3) + +#define CONFIG_TX_THRESH 0x0 +#define CONFIG_RX_THRESH 0x4 + +#define DATA_TX8 0x4 +#define DATA_TX32 0x10 +#define DATA_TX_FREE 0x14 +#define DATA_RX8 0x1c +#define DATA_RX32 0x28 +#define DATA_RX_COUNT 0x2c + +struct apple_dockchannel { + struct device *dev; + struct mbox_controller controller; + struct mbox_chan chan; + + void __iomem *irq_base; + void __iomem *config_base; + void __iomem *data_base; + int irq; + + spinlock_t lock; /* protects IRQ mask and TX state */ + u32 irq_mask; + + const u8 *tx_buf; + size_t tx_len; + size_t tx_pos; + bool tx_active; + + u8 rx_buf[APPLE_DOCKCHANNEL_FIFO_SIZE]; +}; + +static void apple_dockchannel_irq_update(struct apple_dockchannel *dc, + u32 bits, bool enable) +{ + if (enable) + dc->irq_mask |= bits; + else + dc->irq_mask &= ~bits; + writel_relaxed(dc->irq_mask, dc->irq_base + IRQ_MASK); +} + +static void apple_dockchannel_irq_enable(struct apple_dockchannel *dc, u32 bits) +{ + /* + * IRQ_FLAG is write-to-clear. Clear stale latched flags before + * unmasking so the next interrupt reflects current FIFO state. + */ + writel_relaxed(bits, dc->irq_base + IRQ_FLAG); + apple_dockchannel_irq_update(dc, bits, true); +} + +static void apple_dockchannel_irq_disable(struct apple_dockchannel *dc, u32 bits) +{ + apple_dockchannel_irq_update(dc, bits, false); +} + +static bool apple_dockchannel_tx_empty(struct apple_dockchannel *dc) +{ + return readl_relaxed(dc->data_base + DATA_TX_FREE) == + APPLE_DOCKCHANNEL_FIFO_SIZE; +} + +static void apple_dockchannel_write_pending(struct apple_dockchannel *dc) +{ + size_t left = dc->tx_len - dc->tx_pos; + const u8 *p = dc->tx_buf + dc->tx_pos; + + while (left) { + size_t avail; + size_t block; + + avail = readl_relaxed(dc->data_base + DATA_TX_FREE); + if (!avail) + break; + + block = min(left, avail); + + while (block >= sizeof(u32)) { + writel_relaxed(get_unaligned_le32(p), + dc->data_base + DATA_TX32); + p += sizeof(u32); + left -= sizeof(u32); + block -= sizeof(u32); + } + + /* TX data is in bits [7:0]; RX uses those bits for the count. */ + while (block) { + writel_relaxed(*p++, dc->data_base + DATA_TX8); + left--; + block--; + } + } + + dc->tx_pos = dc->tx_len - left; +} + +static void apple_dockchannel_read(struct apple_dockchannel *dc, void *buf, + size_t count) +{ + u8 *p = buf; + size_t left = count; + + while (left >= sizeof(u32)) { + put_unaligned_le32(readl_relaxed(dc->data_base + DATA_RX32), p); + p += sizeof(u32); + left -= sizeof(u32); + } + + while (left) { + /* + * The byte FIFO register returns the byte in bits [15:8] on + * these instances. + */ + *p++ = readl_relaxed(dc->data_base + DATA_RX8) >> 8; + left--; + } +} + +static int apple_dockchannel_send_data(struct mbox_chan *chan, void *data) +{ + struct apple_dockchannel *dc = chan->con_priv; + struct apple_dockchannel_msg *msg = data; + unsigned long flags; + + if (!msg || !msg->data || !msg->len) + return -EINVAL; + + if (msg->len > APPLE_DOCKCHANNEL_FIFO_SIZE) + return -EMSGSIZE; + + spin_lock_irqsave(&dc->lock, flags); + + if (dc->tx_active || !apple_dockchannel_tx_empty(dc)) { + spin_unlock_irqrestore(&dc->lock, flags); + return -EBUSY; + } + + dc->tx_buf = msg->data; + dc->tx_len = msg->len; + dc->tx_pos = 0; + dc->tx_active = true; + + apple_dockchannel_write_pending(dc); + writel_relaxed(APPLE_DOCKCHANNEL_FIFO_SIZE, + dc->config_base + CONFIG_TX_THRESH); + apple_dockchannel_irq_enable(dc, IRQ_TX); + + spin_unlock_irqrestore(&dc->lock, flags); + + return 0; +} + +static int apple_dockchannel_startup(struct mbox_chan *chan) +{ + struct apple_dockchannel *dc = chan->con_priv; + unsigned long flags; + + spin_lock_irqsave(&dc->lock, flags); + /* + * The mailbox framework has no per-client RX threshold. Use byte + * granularity because UART-style DockChannel clients require it. + */ + writel_relaxed(1, dc->config_base + CONFIG_RX_THRESH); + apple_dockchannel_irq_enable(dc, IRQ_RX); + spin_unlock_irqrestore(&dc->lock, flags); + + enable_irq(dc->irq); + + return 0; +} + +static void apple_dockchannel_shutdown(struct mbox_chan *chan) +{ + struct apple_dockchannel *dc = chan->con_priv; + unsigned long flags; + + disable_irq(dc->irq); + + spin_lock_irqsave(&dc->lock, flags); + apple_dockchannel_irq_disable(dc, IRQ_TX | IRQ_RX); + dc->tx_active = false; + spin_unlock_irqrestore(&dc->lock, flags); +} + +static const struct mbox_chan_ops apple_dockchannel_mbox_ops = { + .send_data = apple_dockchannel_send_data, + .startup = apple_dockchannel_startup, + .shutdown = apple_dockchannel_shutdown, +}; + +static irqreturn_t apple_dockchannel_irq_thread(int irq, void *data) +{ + struct apple_dockchannel *dc = data; + unsigned long irqflags; + u32 flags; + u32 pending; + bool tx_done = false; + + flags = readl_relaxed(dc->irq_base + IRQ_FLAG); + + spin_lock_irqsave(&dc->lock, irqflags); + + pending = flags & dc->irq_mask & (IRQ_TX | IRQ_RX); + if (!pending) + goto out_unlock_none; + + if (pending & IRQ_TX) { + if (apple_dockchannel_tx_empty(dc)) { + apple_dockchannel_irq_disable(dc, IRQ_TX); + tx_done = dc->tx_active; + dc->tx_active = false; + } else { + pending &= ~IRQ_TX; + } + } + + writel_relaxed(pending, dc->irq_base + IRQ_FLAG); + + spin_unlock_irqrestore(&dc->lock, irqflags); + + if (tx_done) + mbox_chan_txdone(&dc->chan, 0); + + if (!(pending & IRQ_RX)) + return pending ? IRQ_HANDLED : IRQ_NONE; + + for (;;) { + struct apple_dockchannel_msg msg; + size_t avail; + + avail = readl_relaxed(dc->data_base + DATA_RX_COUNT); + if (!avail) + break; + + avail = min_t(size_t, avail, APPLE_DOCKCHANNEL_FIFO_SIZE); + + apple_dockchannel_read(dc, dc->rx_buf, avail); + + msg.data = dc->rx_buf; + msg.len = avail; + mbox_chan_received_data(&dc->chan, &msg); + } + + return IRQ_HANDLED; + +out_unlock_none: + spin_unlock_irqrestore(&dc->lock, irqflags); + + if (flags & (IRQ_TX | IRQ_RX)) + writel_relaxed(flags & (IRQ_TX | IRQ_RX), + dc->irq_base + IRQ_FLAG); + + return IRQ_NONE; +} + +static struct mbox_chan * +apple_dockchannel_of_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *spec) +{ + if (spec->args_count != 0) + return ERR_PTR(-EINVAL); + + return &mbox->chans[0]; +} + +static int apple_dockchannel_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_dockchannel *dc; + int ret; + + dc = devm_kzalloc(dev, sizeof(*dc), GFP_KERNEL); + if (!dc) + return -ENOMEM; + + dc->dev = dev; + spin_lock_init(&dc->lock); + platform_set_drvdata(pdev, dc); + + dc->irq_base = devm_platform_ioremap_resource_byname(pdev, "irq"); + if (IS_ERR(dc->irq_base)) + return PTR_ERR(dc->irq_base); + + dc->config_base = devm_platform_ioremap_resource_byname(pdev, "config"); + if (IS_ERR(dc->config_base)) + return PTR_ERR(dc->config_base); + + dc->data_base = devm_platform_ioremap_resource_byname(pdev, "data"); + if (IS_ERR(dc->data_base)) + return PTR_ERR(dc->data_base); + + writel_relaxed(0, dc->irq_base + IRQ_MASK); + writel_relaxed(~0, dc->irq_base + IRQ_FLAG); + + dc->irq = platform_get_irq(pdev, 0); + if (dc->irq < 0) + return dc->irq; + + ret = devm_request_threaded_irq(dev, dc->irq, NULL, + apple_dockchannel_irq_thread, IRQF_ONESHOT, + dev_name(dev), dc); + if (ret) + return dev_err_probe(dev, ret, "failed to request IRQ\n"); + + disable_irq(dc->irq); + + dc->chan.con_priv = dc; + dc->controller.dev = dev; + dc->controller.ops = &apple_dockchannel_mbox_ops; + dc->controller.chans = &dc->chan; + dc->controller.num_chans = 1; + dc->controller.txdone_irq = true; + dc->controller.of_xlate = apple_dockchannel_of_xlate; + + ret = devm_mbox_controller_register(dev, &dc->controller); + if (ret) + return dev_err_probe(dev, ret, "failed to register mailbox\n"); + + return 0; +} + +static const struct of_device_id apple_dockchannel_of_match[] = { + { .compatible = "apple,t8122-dockchannel" }, + { .compatible = "apple,t8112-dockchannel" }, + {}, +}; +MODULE_DEVICE_TABLE(of, apple_dockchannel_of_match); + +static struct platform_driver apple_dockchannel_driver = { + .driver = { + .name = "apple-dockchannel", + .of_match_table = apple_dockchannel_of_match, + }, + .probe = apple_dockchannel_probe, +}; +module_platform_driver(apple_dockchannel_driver); + +MODULE_DESCRIPTION("Apple DockChannel mailbox controller"); +MODULE_AUTHOR("Hector Martin "); +MODULE_AUTHOR("Michael Reeves "); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/include/linux/mailbox/apple-dockchannel.h b/include/linux/mailbox/apple-dockchannel.h new file mode 100644 index 000000000..04d2fc44f --- /dev/null +++ b/include/linux/mailbox/apple-dockchannel.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple DockChannel mailbox message format. + * + * Copyright The Asahi Linux Contributors + */ + +#ifndef _LINUX_MAILBOX_APPLE_DOCKCHANNEL_H_ +#define _LINUX_MAILBOX_APPLE_DOCKCHANNEL_H_ + +#include + +/** + * struct apple_dockchannel_msg - DockChannel mailbox payload + * @data: Pointer to the byte stream payload + * @len: Number of payload bytes + * + * For TX, @data must remain valid until mbox_send_message() completes or the + * client receives tx_done in non-blocking mode. + * + * For RX, @data is owned by the controller and is valid only for the duration + * of the rx_callback. + */ +struct apple_dockchannel_msg { + void *data; + size_t len; +}; + +#endif /* _LINUX_MAILBOX_APPLE_DOCKCHANNEL_H_ */ -- 2.55.0