From cbfdfdb77db26a48dd0114f42dbe07252cef288f Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Sun, 1 Nov 2026 19:04:19 -0500 Subject: [PATCH 1/3] iommu: apple-dart: preserve the live J613 MTP bypass stream Keep the boot loader DMA context for a running J613 MTP session. Admit only the T8122 MTP DART at 0x2fa808000, exactly stream 1, and verify that its control register still selects bypass before skipping reset. Select an identity domain and reject paging, blocked-domain attachment and runtime suspend while the inherited session runs. Retain the clock and bypass state at removal. This permits the existing mtp-live-v1 RTKit adoption path to keep serving input without another INIT. The C-diag11 implementation passed owner input testing on J613 with firmware 26.6.2. The rebased series requires a separate owner boot test. Signed-off-by: Satya Benson --- drivers/iommu/apple-dart.c | 41 +++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index abbd4e86af4935..52ceb25b3fd6c9 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -286,6 +286,8 @@ struct apple_dart { u32 pgsize; u32 num_streams; u32 supports_bypass : 1; + /* J613 MTP keeps issuing DMA in the boot loader identity domain. */ + bool live_mtp; u32 four_level : 1; u32 locked : 1; u32 tunneled : 1; @@ -2032,6 +2034,10 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain, struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); struct apple_dart_domain *dart_domain = to_dart_domain(domain); + for_each_stream_map(i, cfg, stream_map) + if (stream_map->dart->live_mtp) + return -EBUSY; + for_each_stream_map(i, cfg, stream_map) WARN_ON(pm_runtime_get_sync(stream_map->dart->dev) < 0); @@ -2101,6 +2107,10 @@ static int apple_dart_attach_dev_blocked(struct iommu_domain *domain, struct apple_dart_stream_map *stream_map; int i; + for_each_stream_map(i, cfg, stream_map) + if (stream_map->dart->live_mtp) + return -EBUSY; + if (cfg->locked) return -EINVAL; @@ -2511,6 +2521,8 @@ static int apple_dart_def_domain_type(struct device *dev) { struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); + if (cfg->stream_maps[0].dart->live_mtp) + return IOMMU_DOMAIN_IDENTITY; if (cfg->stream_maps[0].dart->pgsize > PAGE_SIZE) return IOMMU_DOMAIN_IDENTITY; if (!cfg->supports_bypass) @@ -3172,6 +3184,25 @@ static int apple_dart_probe(struct platform_device *pdev) goto err_clk_disable; } + /* A live MTP epoch must never lose its firmware DMA addresses. */ + if (of_property_present(dev->of_node, "apple,bypass-handoff-streams")) { + u32 stream; + + if (!of_machine_is_compatible("apple,j613") || + !of_device_is_compatible(dev->of_node, "apple,t8122-dart") || + res->start != 0x2fa808000ULL || !dart->supports_bypass || + of_property_count_u32_elems(dev->of_node, + "apple,bypass-handoff-streams") != 1 || + of_property_read_u32(dev->of_node, "apple,bypass-handoff-streams", + &stream) || + stream != 1 || stream >= dart->num_streams || + apple_dart_readl(dart, DART_TCR(dart, stream)) != dart->hw->tcr_bypass) { + ret = -EINVAL; + goto err_clk_disable; + } + dart->live_mtp = true; + } + ret = apple_dart_read_remaps(dart); if (ret) goto err_clk_disable; @@ -3187,7 +3218,7 @@ static int apple_dart_probe(struct platform_device *pdev) dart->fw_mirror = dart->locked && !dart->fw_handoff && (of_device_is_compatible(dev->of_node, "apple,t8140-dart") || of_device_is_compatible(dev->of_node, "apple,t8142-dart")); - if (!dart->locked) { + if (!dart->locked && !dart->live_mtp) { ret = apple_dart_hw_reset(dart); if (ret) goto err_clk_disable; @@ -3337,7 +3368,7 @@ static void apple_dart_remove(struct platform_device *pdev) * A command issued then never completes. The next probe resets the * block after that clock is running again. */ - if (!dart->locked && !READ_ONCE(dart->commands_gated)) + if (!dart->locked && !dart->live_mtp && !READ_ONCE(dart->commands_gated)) apple_dart_hw_reset(dart); free_irq(dart->irq, dart); @@ -3345,7 +3376,8 @@ static void apple_dart_remove(struct platform_device *pdev) iommu_device_unregister(&dart->iommu); iommu_device_sysfs_remove(&dart->iommu); - clk_bulk_disable_unprepare(dart->num_clks, dart->clks); + if (!dart->live_mtp) + clk_bulk_disable_unprepare(dart->num_clks, dart->clks); } static const struct apple_dart_hw apple_dart_hw_t8103 = { @@ -3461,6 +3493,9 @@ static __maybe_unused int apple_dart_suspend(struct device *dev) struct apple_dart *dart = dev_get_drvdata(dev); unsigned int sid, idx; + if (dart->live_mtp) + return -EBUSY; + dart->tunnel_state_restored = false; if (READ_ONCE(dart->commands_gated)) return 0; From 5392f2cfc083db5743d345e764adc4d5c896b789 Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Mon, 2 Nov 2026 12:56:58 -0500 Subject: [PATCH 2/3] soc: apple: rtkit-helper: match the T8122 helper compatible The J613 and J615 25G83 device trees list the MTP as "apple,t8122-mtp", "apple,t8122-rtk-helper-asc4", "apple,mtp" without the generic "apple,rtk-helper-asc4" fallback, and say that Linux binds the SoC-specific compatible. The helper only matches the generic one, so on those boards it never probes, DockChannel HID defers forever and the built-in keyboard and trackpad stay absent. Match "apple,t8122-rtk-helper-asc4" as well. Signed-off-by: Satya Benson --- drivers/soc/apple/rtkit-helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/apple/rtkit-helper.c b/drivers/soc/apple/rtkit-helper.c index 5f03bed772f264..be4f7dc8f1ffe1 100644 --- a/drivers/soc/apple/rtkit-helper.c +++ b/drivers/soc/apple/rtkit-helper.c @@ -195,6 +195,8 @@ static void apple_rtkit_helper_remove(struct platform_device *pdev) static const struct of_device_id apple_rtkit_helper_of_match[] = { { .compatible = "apple,rtk-helper-asc4" }, + /* J613/J615 25G83 DTs omit the generic fallback (see t8122-j613-25g83.dts). */ + { .compatible = "apple,t8122-rtk-helper-asc4" }, {}, }; MODULE_DEVICE_TABLE(of, apple_rtkit_helper_of_match); From bdcbd5e3c9be00e36a02d6df15dbeb4a45c0417d Mon Sep 17 00:00:00 2001 From: Satya Benson Date: Tue, 3 Nov 2026 11:27:59 -0500 Subject: [PATCH 3/3] soc: apple: retain the live J613 MTP and DART suppliers through s2idle Keep the adopted MTP firmware session and identity DMA stream powered through s2idle. Check ASC RUN and bypass before and after sleep, reject deeper sleep, and refuse to reset inherited DMA on loss. Leave non-J613 helpers and ordinary DART runtime PM unchanged. Exercise the real PM callbacks with host fixtures, including abort unwinding and non-J613 peers. Keep successful retention messages at debug level. Signed-off-by: Satya Benson --- drivers/iommu/apple-dart.c | 43 +++++++++++++++++++- drivers/soc/apple/rtkit-helper.c | 37 +++++++++++++++++ tools/asahi/j613-mtp-s2idle-tests.py | 61 ++++++++++++++++++++++++++++ tools/aurora-ci/host-tests.json | 8 ++++ 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 tools/asahi/j613-mtp-s2idle-tests.py diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index fc2b29a3216a50..3f4fd0604b51ef 100644 --- a/drivers/iommu/apple-dart.c +++ b/drivers/iommu/apple-dart.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include #include @@ -288,6 +290,7 @@ struct apple_dart { u32 supports_bypass : 1; /* J613 MTP keeps issuing DMA in the boot loader identity domain. */ bool live_mtp; + bool live_mtp_sleep; u32 four_level : 1; u32 locked : 1; u32 tunneled : 1; @@ -3662,7 +3665,45 @@ static __maybe_unused int apple_dart_resume(struct device *dev) return ret; } -static DEFINE_RUNTIME_DEV_PM_OPS(apple_dart_pm_ops, apple_dart_suspend, apple_dart_resume, NULL); +/* The adopted MTP stays live: system s2idle must retain, never reset it. */ +static int apple_dart_system_suspend(struct device *dev) +{ + struct apple_dart *dart = dev_get_drvdata(dev); + + if (!dart->live_mtp) + return pm_runtime_force_suspend(dev); + if (pm_suspend_target_state != PM_SUSPEND_TO_IDLE) + return -EBUSY; + if (apple_dart_readl(dart, DART_TCR(dart, 1)) != dart->hw->tcr_bypass) + return -EIO; + /* PMGR's ACTIVE_WAKEUP domains and their ancestors must stay powered. */ + device_set_awake_path(dev); + dart->live_mtp_sleep = true; + dev_dbg(dev, "live MTP s2idle: retaining clocks, supplier power and bypass stream 1\n"); + return 0; +} + +static int apple_dart_system_resume(struct device *dev) +{ + struct apple_dart *dart = dev_get_drvdata(dev); + + if (!dart->live_mtp) + return pm_runtime_force_resume(dev); + if (!dart->live_mtp_sleep) + return 0; + dart->live_mtp_sleep = false; + if (apple_dart_readl(dart, DART_TCR(dart, 1)) != dart->hw->tcr_bypass) { + dev_err(dev, "live MTP bypass lost across s2idle; refusing reset of inherited DMA\n"); + return -EIO; + } + dev_dbg(dev, "live MTP s2idle: bypass retained; no reset or invalidate\n"); + return 0; +} + +static const struct dev_pm_ops apple_dart_pm_ops = { + RUNTIME_PM_OPS(apple_dart_suspend, apple_dart_resume, NULL) + SYSTEM_SLEEP_PM_OPS(apple_dart_system_suspend, apple_dart_system_resume) +}; static const struct of_device_id apple_dart_of_match[] = { { .compatible = "apple,t8103-dart", .data = &apple_dart_hw_t8103 }, diff --git a/drivers/soc/apple/rtkit-helper.c b/drivers/soc/apple/rtkit-helper.c index be4f7dc8f1ffe1..a582eafedd29d2 100644 --- a/drivers/soc/apple/rtkit-helper.c +++ b/drivers/soc/apple/rtkit-helper.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include @@ -193,6 +195,40 @@ static void apple_rtkit_helper_remove(struct platform_device *pdev) writel_relaxed(0, helper->asc_base + APPLE_ASC_CPU_CONTROL); } +static int apple_rtkit_helper_suspend(struct device *dev) +{ + struct apple_rtkit_helper *helper = dev_get_drvdata(dev); + + if (!helper->adopted || !of_machine_is_compatible("apple,j613")) + return 0; + if (pm_suspend_target_state != PM_SUSPEND_TO_IDLE) + return -EBUSY; + if (!(readl(helper->asc_base + APPLE_ASC_CPU_CONTROL) & APPLE_ASC_CPU_CONTROL_RUN)) + return -EHOSTDOWN; + /* mtp-live-v1 cannot be restarted: keep its ASC suppliers awake. */ + device_set_awake_path(dev); + dev_dbg(dev, "adopted MTP s2idle: retaining ASC RUN and firmware session\n"); + return 0; +} + +static int apple_rtkit_helper_resume(struct device *dev) +{ + struct apple_rtkit_helper *helper = dev_get_drvdata(dev); + + if (!helper->adopted || !of_machine_is_compatible("apple,j613")) + return 0; + if (!(readl(helper->asc_base + APPLE_ASC_CPU_CONTROL) & APPLE_ASC_CPU_CONTROL_RUN)) { + dev_err(dev, "adopted MTP lost ASC RUN; refusing firmware restart\n"); + return -EHOSTDOWN; + } + dev_dbg(dev, "adopted MTP s2idle: ASC RUN retained, no INIT\n"); + return 0; +} + +static const struct dev_pm_ops apple_rtkit_helper_pm_ops = { + SYSTEM_SLEEP_PM_OPS(apple_rtkit_helper_suspend, apple_rtkit_helper_resume) +}; + static const struct of_device_id apple_rtkit_helper_of_match[] = { { .compatible = "apple,rtk-helper-asc4" }, /* J613/J615 25G83 DTs omit the generic fallback (see t8122-j613-25g83.dts). */ @@ -204,6 +240,7 @@ MODULE_DEVICE_TABLE(of, apple_rtkit_helper_of_match); static struct platform_driver apple_rtkit_helper_driver = { .driver = { .name = "rtkit-helper", + .pm = pm_sleep_ptr(&apple_rtkit_helper_pm_ops), .of_match_table = apple_rtkit_helper_of_match, }, .probe = apple_rtkit_helper_probe, diff --git a/tools/asahi/j613-mtp-s2idle-tests.py b/tools/asahi/j613-mtp-s2idle-tests.py new file mode 100644 index 00000000000000..a235677ca6efc8 --- /dev/null +++ b/tools/asahi/j613-mtp-s2idle-tests.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Actual system PM callbacks, synthetic register/PM boundaries; no device access.""" +from pathlib import Path +import subprocess,tempfile +R=Path(__file__).resolve().parents[2] +def fn(s,n): + a=s.index('static int '+n+'(');b=s.index('\n}',a)+2;return s[a:b] +s=(R/'drivers/iommu/apple-dart.c').read_text();h=(R/'drivers/soc/apple/rtkit-helper.c').read_text() +assert 'if (dart->live_mtp)\n\t\treturn -EBUSY;' in s +pre=r''' +#include +#include +#include +#include +#define dev_dbg(...) ((void)0) +#define dev_err(...) ((void)0) +#define DART_TCR(d,s) (s) +#define APPLE_ASC_CPU_CONTROL 0x44 +#define APPLE_ASC_CPU_CONTROL_RUN 16 +#define PM_SUSPEND_TO_IDLE 1 +static int pm_suspend_target_state=1; +struct device {void *data;bool awake;}; +struct hw {unsigned tcr_bypass;}; +struct apple_dart {bool live_mtp,live_mtp_sleep;struct hw *hw;unsigned tcr;}; +struct apple_rtkit_helper {bool adopted;unsigned char *asc_base;}; +static void *dev_get_drvdata(struct device *d){return d->data;} +static unsigned apple_dart_readl(struct apple_dart *d,unsigned sid){assert(sid==1);return d->tcr;} +static void device_set_awake_path(struct device *d){d->awake=true;} +static unsigned forced_suspend,forced_resume; +static int pm_runtime_force_suspend(struct device *d){forced_suspend++;return 7;} +static int pm_runtime_force_resume(struct device *d){forced_resume++;return 8;} +static bool of_machine_is_compatible(const char *s){return true;} +static unsigned readl(void *p){return *(unsigned *)p;} +''' +body=fn(s,'apple_dart_system_suspend')+fn(s,'apple_dart_system_resume')+fn(h,'apple_rtkit_helper_suspend')+fn(h,'apple_rtkit_helper_resume') +for forbidden in ['writel','hw_reset','invalidate_tlb','apple_rtkit_wake','apple_rtkit_boot','apple_rtkit_quiesce']: + assert forbidden not in body,forbidden +checks=r''' +int main(void){ + struct hw hw={.tcr_bypass=3};struct apple_dart a={.live_mtp=true,.hw=&hw,.tcr=3};struct device d={.data=&a}; + assert(!apple_dart_system_suspend(&d)&&d.awake&&a.live_mtp_sleep); + assert(!apple_dart_system_resume(&d)&&!a.live_mtp_sleep&&a.tcr==3&&!forced_suspend&&!forced_resume); + // Abort unwind is the same retained transaction, and can be repeated. + assert(!apple_dart_system_suspend(&d));assert(!apple_dart_system_resume(&d)); + pm_suspend_target_state=2;assert(apple_dart_system_suspend(&d)==-EBUSY);pm_suspend_target_state=1; + a.tcr=0;assert(apple_dart_system_suspend(&d)==-EIO);a.tcr=3; + assert(!apple_dart_system_suspend(&d));a.tcr=0;assert(apple_dart_system_resume(&d)==-EIO&&a.tcr==0); + a.live_mtp=false;assert(apple_dart_system_suspend(&d)==7&&apple_dart_system_resume(&d)==8); + unsigned regs[32]={0};struct apple_rtkit_helper h={.adopted=true,.asc_base=(void *)regs};d.data=&h;d.awake=false; + regs[0x44/4]=16;assert(!apple_rtkit_helper_suspend(&d)&&d.awake);assert(!apple_rtkit_helper_resume(&d)); + regs[0x44/4]=0;assert(apple_rtkit_helper_resume(&d)==-EHOSTDOWN&&!regs[0x44/4]); + assert(apple_rtkit_helper_suspend(&d)==-EHOSTDOWN); + h.adopted=false;assert(!apple_rtkit_helper_suspend(&d)&&!apple_rtkit_helper_resume(&d)); +} +''' +with tempfile.TemporaryDirectory() as t: + p=Path(t)/'pm.c';p.write_text(pre+body+checks) + subprocess.run(['clang','-std=gnu11','-Wall','-Werror','-fsanitize=address,undefined',str(p),'-o',str(p.with_suffix(''))],check=True) + subprocess.run([str(p.with_suffix(''))],check=True) +print('PASS actual MTP/DART system PM: retained normal/abort cycles, runtime delegation, non-s2idle refusal, lost bypass/RUN refuses without reset/INIT') diff --git a/tools/aurora-ci/host-tests.json b/tools/aurora-ci/host-tests.json index a1b93d695dfd6e..02c4d0d9743de5 100644 --- a/tools/aurora-ci/host-tests.json +++ b/tools/aurora-ci/host-tests.json @@ -2282,6 +2282,14 @@ "{output}" ], "note": "Build only the host Kconfig tool in an isolated output directory; check all built-in/module dependency combinations." + }, + { + "name": "tools/asahi/j613-mtp-s2idle-tests.py", + "group": "tools", + "argv": [ + "python3", + "tools/asahi/j613-mtp-s2idle-tests.py" + ] } ], "helpers": {