Skip to content
Merged
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
23 changes: 22 additions & 1 deletion drivers/hid/dockchannel-hid/dockchannel-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ struct dockchannel_hid {

/* Workqueue to asynchronously create HID devices */
struct workqueue_struct *new_iface_wq;

/* Held while one frame is written to the shared TX FIFO */
struct mutex tx_mutex;
};

static ssize_t apple_layout_id_show(struct device *dev,
Expand Down Expand Up @@ -347,7 +350,7 @@ static bool dchid_verify_checksum(struct dchid_hdr *hdr, void *data, u32 *checks
return *checksum == U32_MAX;
}

static int dchid_send(struct dchid_iface *iface, u32 flags, void *msg, size_t size)
static int dchid_send_frame(struct dchid_iface *iface, u32 flags, void *msg, size_t size)
{
u32 checksum = 0xffffffff;
size_t wsize = round_down(size, 4);
Expand Down Expand Up @@ -398,6 +401,23 @@ static int dchid_send(struct dchid_iface *iface, u32 flags, void *msg, size_t si
return 0;
}

/*
* A frame goes out as several FIFO writes, and each interface has its own
* output lock, so frames for two interfaces could interleave in the shared
* FIFO. The MTP firmware then fails the frame checksum and crashes
* ("[dock ch tl]: xsum err"). Write whole frames only.
*/
static int dchid_send(struct dchid_iface *iface, u32 flags, void *msg, size_t size)
{
struct dockchannel_hid *dchid = iface->dchid;
int ret;

mutex_lock(&dchid->tx_mutex);
ret = dchid_send_frame(iface, flags, msg, size);
mutex_unlock(&dchid->tx_mutex);
return ret;
}

static int dchid_cmd(struct dchid_iface *iface, u32 type, u32 req,
void *data, size_t size, void *resp_buf, size_t resp_size)
{
Expand Down Expand Up @@ -1583,6 +1603,7 @@ static int dockchannel_hid_probe(struct platform_device *pdev)
}

dchid->dev = dev;
mutex_init(&dchid->tx_mutex);
stm = device_get_named_child_node(dev, "stm");
dchid->id_ready = !stm || !fwnode_device_is_available(stm);
fwnode_handle_put(stm);
Expand Down
50 changes: 43 additions & 7 deletions drivers/iommu/apple-dart.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeup.h>
#include <linux/slab.h>
#include <linux/soc/apple/tunable.h>
#include <linux/string.h>
Expand Down Expand Up @@ -356,6 +357,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;
Expand Down Expand Up @@ -5658,13 +5660,9 @@ static __maybe_unused int apple_dart_suspend(struct device *dev)
{
struct apple_dart *dart = dev_get_drvdata(dev);

/*
* The adopted MTP keeps issuing DMA through its firmware bypass.
* Like its RTKit helper, admit only suspend-to-idle: leave the DART
* untouched while that live session and its power domain stay on.
*/
/* Runtime PM may not stop the live MTP; system sleep uses its own callbacks. */
if (dart->live_mtp)
return pm_suspend_target_state == PM_SUSPEND_TO_IDLE ? 0 : -EBUSY;
return -EBUSY;

dart->tunnel_state_restored = false;
if (READ_ONCE(dart->commands_gated))
Expand Down Expand Up @@ -5884,7 +5882,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
24 changes: 23 additions & 1 deletion drivers/soc/apple/rtkit-helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/pm_wakeup.h>
#include <linux/soc/apple/rtkit.h>
#include <linux/soc/apple/rtkit-helper.h>
#include <linux/soc/apple/t8132.h>
Expand Down Expand Up @@ -384,9 +385,29 @@ static int __maybe_unused apple_rtkit_helper_suspend(struct device *dev)
{
struct apple_rtkit_helper *helper = dev_get_drvdata(dev);

/* Only suspend-to-idle has a verified live-session retention contract. */
if (helper->adopted && pm_suspend_target_state != PM_SUSPEND_TO_IDLE)
return -EBUSY;
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))
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 __maybe_unused 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;
}

Expand All @@ -400,6 +421,7 @@ static int __maybe_unused apple_rtkit_helper_freeze(struct device *dev)

static const struct dev_pm_ops apple_rtkit_helper_pm_ops = {
.suspend = pm_sleep_ptr(apple_rtkit_helper_suspend),
.resume = pm_sleep_ptr(apple_rtkit_helper_resume),
.freeze = pm_sleep_ptr(apple_rtkit_helper_freeze),
.poweroff = pm_sleep_ptr(apple_rtkit_helper_freeze),
};
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):
normalized=s.replace('static int __maybe_unused ', 'static int ');a=normalized.index('static int '+n+'(');b=normalized.index('\n}',a)+2;return normalized[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')
Loading
Loading