Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 80 additions & 4 deletions drivers/iommu/apple-dart.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeup.h>
#include <linux/suspend.h>
#include <linux/slab.h>
#include <linux/soc/apple/tunable.h>
#include <linux/swab.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -3354,15 +3388,16 @@ 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);

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 = {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 },
Expand Down
39 changes: 39 additions & 0 deletions drivers/soc/apple/rtkit-helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeup.h>
#include <linux/suspend.h>
#include <linux/soc/apple/rtkit.h>
#include <linux/string.h>

Expand Down Expand Up @@ -193,15 +195,52 @@ 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);

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,
Expand Down
61 changes: 61 additions & 0 deletions tools/asahi/j613-mtp-s2idle-tests.py
Original file line number Diff line number Diff line change
@@ -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 <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <errno.h>
#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')
8 changes: 8 additions & 0 deletions tools/aurora-ci/host-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Loading