diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c index f2e7aee54e5978..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 @@ -286,6 +288,9 @@ 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; + bool live_mtp_sleep; u32 four_level : 1; u32 locked : 1; u32 tunneled : 1; @@ -2032,6 +2037,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 +2110,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; @@ -2528,6 +2541,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) @@ -3189,6 +3204,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; @@ -3204,7 +3238,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; @@ -3354,7 +3388,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); @@ -3362,7 +3396,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 = { @@ -3478,6 +3513,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; @@ -3627,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 5f03bed772f264..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,8 +195,44 @@ 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). */ + { .compatible = "apple,t8122-rtk-helper-asc4" }, {}, }; MODULE_DEVICE_TABLE(of, apple_rtkit_helper_of_match); @@ -202,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": {