From b16f72bce3d9a51bff7176d908835b0faa48c86a Mon Sep 17 00:00:00 2001 From: Jeff Queisser Date: Mon, 2 Nov 2026 02:23:17 -0800 Subject: [PATCH 1/4] dt-bindings: iommu: apple,sart: Add the T6050 SART The T6050 (M5 Ultra) SART keeps SARTv3's entries but moves the address and size arrays, so it is not compatible with apple,t6000-sart and has no fallback. Signed-off-by: Jeff Queisser Co-Authored-By: Claude Opus 5.5 Signed-off-by: Erik Bourget --- Documentation/devicetree/bindings/iommu/apple,sart.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iommu/apple,sart.yaml b/Documentation/devicetree/bindings/iommu/apple,sart.yaml index 493e48de06a92..19ffe1bf167f7 100644 --- a/Documentation/devicetree/bindings/iommu/apple,sart.yaml +++ b/Documentation/devicetree/bindings/iommu/apple,sart.yaml @@ -26,6 +26,9 @@ description: SART3, first seen in M1 Pro/Max, extends both the address space and filter entry size to 42 bit. + SART4, seen on M5 Ultra (T6050), keeps SART3's entries but moves the + address and size arrays, so it has no SART3 fallback. + properties: compatible: oneOf: From f44022cdf8ae8602912e3d8345d38edf6a0ae4a9 Mon Sep 17 00:00:00 2001 From: Jeff Queisser Date: Mon, 2 Nov 2026 02:21:36 -0800 Subject: [PATCH 2/4] soc: apple: sart: Add SARTv4 for T6050 The SART that filters the T6050 (M5 Ultra) ANS's DMA keeps SARTv3's entry format (config, page address and size, 4 KiB units) but moves the address and size arrays from 0x40/0x80 to 0x60/0xc0. Read on J775D before Linux started: the config words at 0x00+4i and seven live entries whose addresses at 0x60+4i and sizes at 0xc0+4i name ANS's MMIO and two DRAM ranges. Let the v3 ops carry the array offsets, so v3 and v4 share one implementation, and match apple,t6050-sart with the v4 offsets. There is no v3 fallback compatible: a v3 driver would read the wrong registers. Log the number of entries the boot loader left (protected) at debug level. v2 and v3 SARTs keep their offsets. Testing: on J775D, every boot since protects the seven entries the boot loader left, and ANS's NVMe runs behind it (Linux boots from and writes to its NVMe partition). Signed-off-by: Jeff Queisser Co-Authored-By: Claude Opus 5.5 Signed-off-by: Erik Bourget --- drivers/soc/apple/sart.c | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/drivers/soc/apple/sart.c b/drivers/soc/apple/sart.c index 6cedcb867c61b..8270a5a731f2c 100644 --- a/drivers/soc/apple/sart.c +++ b/drivers/soc/apple/sart.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -54,15 +55,19 @@ /* SARTv3 registers */ #define APPLE_SART3_CONFIG(idx) (0x00 + 4 * (idx)) -#define APPLE_SART3_PADDR(idx) (0x40 + 4 * (idx)) +#define APPLE_SART3_PADDR 0x40 #define APPLE_SART3_PADDR_SHIFT 12 -#define APPLE_SART3_SIZE(idx) (0x80 + 4 * (idx)) +#define APPLE_SART3_SIZE 0x80 #define APPLE_SART3_SIZE_SHIFT 12 #define APPLE_SART3_SIZE_MAX GENMASK(29, 0) #define APPLE_SART3_FLAGS_ALLOW 0xff +/* SARTv4 (M5): SARTv3's entries, with the address and size arrays moved up */ +#define APPLE_SART4_PADDR 0x60 +#define APPLE_SART4_SIZE 0xc0 + struct apple_sart_ops { void (*get_entry)(struct apple_sart *sart, int index, u8 *flags, phys_addr_t *paddr, size_t *size); @@ -70,6 +75,9 @@ struct apple_sart_ops { phys_addr_t paddr_shifted, size_t size_shifted); /* This is probably a bitfield but the exact meaning of each bit is unknown. */ unsigned int flags_allow; + /* SARTv3/v4: where the address and size arrays start */ + unsigned int paddr_reg; + unsigned int size_reg; unsigned int size_shift; unsigned int paddr_shift; size_t size_max; @@ -179,8 +187,8 @@ static struct apple_sart_ops sart_ops_v2 = { static void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags, phys_addr_t *paddr, size_t *size) { - phys_addr_t paddr_ = readl(sart->regs + APPLE_SART3_PADDR(index)); - size_t size_ = readl(sart->regs + APPLE_SART3_SIZE(index)); + phys_addr_t paddr_ = readl(sart->regs + sart->ops->paddr_reg + 4 * index); + size_t size_ = readl(sart->regs + sart->ops->size_reg + 4 * index); *flags = readl(sart->regs + APPLE_SART3_CONFIG(index)); *size = size_ << APPLE_SART3_SIZE_SHIFT; @@ -190,8 +198,8 @@ static void sart3_get_entry(struct apple_sart *sart, int index, u8 *flags, static void sart3_set_entry(struct apple_sart *sart, int index, u8 flags, phys_addr_t paddr_shifted, size_t size_shifted) { - writel(paddr_shifted, sart->regs + APPLE_SART3_PADDR(index)); - writel(size_shifted, sart->regs + APPLE_SART3_SIZE(index)); + writel(paddr_shifted, sart->regs + sart->ops->paddr_reg + 4 * index); + writel(size_shifted, sart->regs + sart->ops->size_reg + 4 * index); writel(flags, sart->regs + APPLE_SART3_CONFIG(index)); } @@ -199,6 +207,19 @@ static struct apple_sart_ops sart_ops_v3 = { .get_entry = sart3_get_entry, .set_entry = sart3_set_entry, .flags_allow = APPLE_SART3_FLAGS_ALLOW, + .paddr_reg = APPLE_SART3_PADDR, + .size_reg = APPLE_SART3_SIZE, + .size_shift = APPLE_SART3_SIZE_SHIFT, + .paddr_shift = APPLE_SART3_PADDR_SHIFT, + .size_max = APPLE_SART3_SIZE_MAX, +}; + +static struct apple_sart_ops sart_ops_v4 = { + .get_entry = sart3_get_entry, + .set_entry = sart3_set_entry, + .flags_allow = APPLE_SART3_FLAGS_ALLOW, + .paddr_reg = APPLE_SART4_PADDR, + .size_reg = APPLE_SART4_SIZE, .size_shift = APPLE_SART3_SIZE_SHIFT, .paddr_shift = APPLE_SART3_PADDR_SHIFT, .size_max = APPLE_SART3_SIZE_MAX, @@ -236,6 +257,7 @@ static int apple_sart_probe(struct platform_device *pdev) i, flags, &paddr, size); set_bit(i, &sart->protected_entries); } + dev_dbg(dev, "%lu bootloader entries protected\n", hweight_long(sart->protected_entries)); platform_set_drvdata(pdev, sart); return 0; @@ -409,6 +431,10 @@ static void apple_sart_shutdown(struct platform_device *pdev) } static const struct of_device_id apple_sart_of_match[] = { + { + .compatible = "apple,t6050-sart", + .data = &sart_ops_v4, + }, { .compatible = "apple,t6000-sart", .data = &sart_ops_v3, From 1bcd370f7577791426029de73555c243e170f920 Mon Sep 17 00:00:00 2001 From: Jeff Queisser Date: Mon, 2 Nov 2026 02:23:05 -0800 Subject: [PATCH 3/4] nvme: apple: Add the T6050 ANS on bare metal Add apple,t6050-nvme-ans2 for the M5 Ultra's ANS when Linux runs on the hardware. Its layout is T6040's, as the ADT and the registers read before Linux started show: a 64 KiB "nvme" block, a separate NVMMU and linear submission queues. The controller reports 254 queue entries (CAP.MQES 0xfd); the profile uses T6040's 64. FUA writes are not shown durable here, so the profile flushes instead (NVME_QUIRK_BROKEN_FUA). The boot loader leaves ANS's firmware running and the controller shut down (CC.EN 0, CSTS.RDY 0). The driver adopts that session, and, like every post-M4 ANS without cold_reset, never resets it through PMGR, so the profile sets reset_optional, a hardware-profile flag that takes the controller reset as optional, and the binding requires no reset for it. Every other profile still requires its reset. Testing: on J775D, every boot since 2026-10-30 adopts ANS's running firmware and enables the controller with this profile; Linux runs from its NVMe root partition (ext4) through daily builds and reboots without an NVMe error. Signed-off-by: Jeff Queisser Co-Authored-By: Claude Opus 5.5 Signed-off-by: Erik Bourget --- drivers/nvme/host/apple.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index 732d7b63be3d4..2164b13f4e787 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -210,6 +210,8 @@ struct apple_nvme_hw { bool needs_ioq_registers; /* T8132 needs a PMGR reset when starting a stopped coprocessor. */ bool cold_reset; + /* The ANS is never reset through PMGR here, so no reset is named. */ + bool reset_optional; u32 max_queue_depth; unsigned long quirks; }; @@ -2407,7 +2409,9 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev) goto out_detach_genpd; } - anv->reset = devm_reset_control_array_get_exclusive(anv->dev); + anv->reset = anv->hw->reset_optional ? + devm_reset_control_array_get_optional_exclusive(anv->dev) : + devm_reset_control_array_get_exclusive(anv->dev); if (IS_ERR(anv->reset)) { ret = dev_err_probe(dev, PTR_ERR(anv->reset), "Failed to get reset control"); @@ -2776,8 +2780,26 @@ static const struct apple_nvme_hw apple_nvme_t6040_hw = { .quirks = NVME_QUIRK_NO_APST, }; +/* + * T6050 (M5 Ultra) ANS on bare metal: T6040's split layout (a 64 KiB "nvme" + * block, a separate NVMMU, linear submission queues), 64 queue entries. Like + * every post-M4 ANS without cold_reset it is never reset through PMGR, so no + * reset is named. + */ +static const struct apple_nvme_hw apple_nvme_t6050_hw = { + .has_lsq_nvmmu = true, + .has_separate_nvmmu = true, + .needs_ioq_registers = true, + .lsq_in_nvmmu = true, + .reset_optional = true, + .max_queue_depth = 64, + /* FUA is not shown durable on this ANS: flush instead. */ + .quirks = NVME_QUIRK_NO_APST | NVME_QUIRK_BROKEN_FUA, +}; + static const struct of_device_id apple_nvme_of_match[] = { { .compatible = "apple,t6041-nvme-ans2", .data = &apple_nvme_t6040_hw }, + { .compatible = "apple,t6050-nvme-ans2", .data = &apple_nvme_t6050_hw }, { .compatible = "apple,t8015-nvme-ans2", .data = &apple_nvme_t8015_hw }, { .compatible = "apple,t8103-nvme-ans2", .data = &apple_nvme_t8103_hw }, { .compatible = "apple,t8132-nvme-ans2", .data = &apple_nvme_t8132_hw }, From e6e2873d43060788fe591a3e75b92e80c2bb3b79 Mon Sep 17 00:00:00 2001 From: Jeff Queisser Date: Mon, 2 Nov 2026 02:23:19 -0800 Subject: [PATCH 4/4] dt-bindings: nvme: apple: Allow the boot loader's RTKit handoff on T6050 The boot loader leaves the T6050 ANS's firmware running with the controller disabled and, as on T8132 and T8140, adds apple,rtkit-handoff to the node so the kernel adopts that RTKit session instead of starting the co-processor again. The T6050 profile relies on that adoption. Allow the property on apple,t6050-nvme-ans2. Signed-off-by: Jeff Queisser Co-Authored-By: Claude Opus 5.5 Signed-off-by: Erik Bourget --- Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml b/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml index 3d08734f0ee74..7f289a02d4b21 100644 --- a/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml +++ b/Documentation/devicetree/bindings/nvme/apple,nvme-ans.yaml @@ -117,6 +117,7 @@ allOf: not: contains: enum: + - apple,t6050-nvme-ans2 - apple,t8132-nvme-ans2 - apple,t8140-nvme-ans2 then: