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
5 changes: 3 additions & 2 deletions arch/arm64/boot/dts/apple/t8140-j700.dts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
sep = &sep;
serial0 = &serial0;
pmp = &pmp;
rtc0 = &smc_rtc;
rtc1 = &pmu_rtc;
/* First the RTC that can wake the system */
rtc0 = &pmu_rtc;
rtc1 = &smc_rtc;
wifi0 = &wifi0;
/* used by the loader to hand over the display */
dcp = &dcp;
Expand Down
2 changes: 1 addition & 1 deletion arch/arm64/configs/asahi.config
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ CONFIG_USB4_APPLE_SOC=m
CONFIG_USB_XHCI_PCI_ASMEDIA=y
CONFIG_TYPEC_SN201202X=m
CONFIG_RTC_DRV_MACSMC=y
CONFIG_RTC_DRV_APPLE_PMU=m
CONFIG_RTC_DRV_APPLE_PMU=y
CONFIG_APPLE_ADMAC=m
CONFIG_APPLE_SIO=m
CONFIG_MFD_MACSMC=y
Expand Down
6 changes: 6 additions & 0 deletions drivers/gpu/drm/asahi/g17/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,12 @@ struct asahi_g17_commit *asahi_g17_commit_prepare(struct device *dev,
return ERR_PTR(ret);
}

/* Whether only the sleep's commit may stop the GPU: then nothing else may. */
bool asahi_g17_commit_required(void)
{
return apple_sleep_commit_stops_coprocs();
}

/*
* After the sleep, refused or not: whether the commit stopped the GPU, and in @saved the
* domain's word from before, if it did. Frees @c.
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/asahi/g17/sleep.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct asahi_g17_commit *asahi_g17_commit_prepare(struct device *dev,
struct apple_rtkit *secondary_rtk,
struct apple_mbox *secondary_mbox, bool bridge);
bool asahi_g17_commit_finish(struct asahi_g17_commit *c, u32 *saved);
bool asahi_g17_commit_required(void);
int asahi_g17_power_auto_off(struct device *dev, u32 *saved);
int asahi_g17_power_lower(struct device *dev);
int asahi_g17_power_raise(struct device *dev, u32 saved);
Expand Down
22 changes: 17 additions & 5 deletions drivers/gpu/drm/asahi/g17/sleep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ unsafe extern "C" {
bridge: bool,
) -> *mut core::ffi::c_void;
fn asahi_g17_commit_finish(commit: *mut core::ffi::c_void, saved: *mut u32) -> bool;
fn asahi_g17_commit_required() -> bool;
}

/// What `.complete` does after a sleep whose stop was left to the commit.
Expand Down Expand Up @@ -427,21 +428,32 @@ impl Shared {
true
}

/// Leaves the stop to the commit. False, with the reason logged, if it can't be.
fn sleep_leave_for_commit(&self, firmware: &mut Firmware) -> bool {
/// Leaves the stop to the commit. False, with the reason logged, if it can't be. Where only
/// the commit may stop it, that refuses the sleep instead: a refusal after an earlier stop
/// would leave it stopped.
fn sleep_leave_for_commit(&self, firmware: &mut Firmware) -> Result<bool> {
match firmware.leave_for_commit() {
Ok(commit) => {
self.sleep_commit.store(commit, Ordering::Release);
dev_dbg!(self.dev.as_ref(), "GPU firmware left running for the sleep's commit\n");
true
Ok(true)
}
// SAFETY: Reads the sleep driver's state.
Err(error) if unsafe { asahi_g17_commit_required() } => {
dev_err!(
self.dev.as_ref(),
"no GPU stop at the commit ({:?}): refusing the sleep\n",
error
);
Err(EBUSY)
}
Err(error) => {
dev_warn!(
self.dev.as_ref(),
"no GPU stop at the commit ({:?}): stopping it now\n",
error
);
false
Ok(false)
}
}
}
Expand Down Expand Up @@ -490,7 +502,7 @@ impl Shared {
if !firmware.sleep_quiet(super::now_ns())? {
return Err(EBUSY);
}
if self.sleep_leave_for_commit(firmware) {
if self.sleep_leave_for_commit(firmware)? {
return Ok(());
}
down(firmware)
Expand Down
87 changes: 82 additions & 5 deletions drivers/phy/apple/atc.c
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ struct apple_atcphy {
bool sleep_dead;
struct device *sleep_darts[ATCPHY_SLEEP_DARTS];
unsigned int sleep_ndarts;
struct device_node *pd_usb, *pd_common;
struct device_node *pd_usb, *pd_common, *pd_aon;
u32 *bridge_gate;
u32 *bridge_saved;
unsigned int bridge_count;
Expand Down Expand Up @@ -6370,6 +6370,10 @@ static int atcphy_probe_sleep_power(struct apple_atcphy *atcphy)
return ret;
if (!atcphy->pd_common)
return 0;
atcphy->pd_aon = of_parse_phandle(atcphy->pd_common, "power-domains", 0);
ret = devm_add_action_or_reset(dev, atcphy_put_node, atcphy->pd_aon);
if (ret)
return ret;

n = of_count_phandle_with_args(ctrl, "iommus", "#iommu-cells");
if (n <= 0 || n > ATCPHY_SLEEP_DARTS)
Expand Down Expand Up @@ -6819,6 +6823,21 @@ static void atcphy_bridge_gate(struct apple_atcphy *atcphy, bool open)
}
}

#define ATCPHY_DOMAIN_TRIES 3

static int atcphy_sleep_domain_up(struct device_node *pd)
{
int i, ret;

for (i = 0; i < ATCPHY_DOMAIN_TRIES; i++) {
ret = apple_sleep_domain_set(pd, true);
if (!ret)
break;
}

return ret;
}

/*
* Back up in macOS's order: ATC0_COMMON, the bridge, ATC0_USB, then the DARTs. -ENODEV if
* a domain didn't come on, when nothing in it may be touched.
Expand All @@ -6829,13 +6848,13 @@ static int atcphy_sleep_power_up(struct apple_atcphy *atcphy)
int ret;

atcphy->sleep_powered_down = false;
ret = apple_sleep_domain_set(atcphy->pd_common, true);
ret = atcphy_sleep_domain_up(atcphy->pd_common);
if (!ret) {
atcphy_bridge_gate(atcphy, true);
ret = apple_sleep_domain_set(atcphy->pd_usb, true);
ret = atcphy_sleep_domain_up(atcphy->pd_usb);
}
if (ret) {
dev_err(atcphy->dev, "USB host's power domains not back on: %d, USB off until a reboot\n",
dev_err(atcphy->dev, "USB host's power domains not back on: %d, USB stays off\n",
ret);
WRITE_ONCE(atcphy->sleep_dead, true);
atcphy->sleep_no_keep = true;
Expand Down Expand Up @@ -6965,6 +6984,45 @@ static int atcphy_sleep_come_back(struct apple_atcphy *atcphy, bool common_lost,
return d0 < 0 ? d0 : ret ?: d0;
}

/*
* macOS takes a USB host that fails its raise Off, then starts it cold. The same here, with
* nothing touching the core: clamped in reset, its domains and the always-on one off, then
* on again. The block comes back reset, as after a sleep that took its power, and the host
* starts over. -ENODEV if the core still may be unpowered.
*/
static int atcphy_sleep_recover(struct apple_atcphy *atcphy)
{
int ret;

lockdep_assert_held(&atcphy->lock);
if (!atcphy->sleep_power || !atcphy->pd_aon)
return -ENODEV;
dev_warn(atcphy->dev, "USB controller didn't come back: power cycling it, as macOS's Off does\n");
/* Domains that didn't come on get another try first. */
if (READ_ONCE(atcphy->sleep_dead)) {
WRITE_ONCE(atcphy->sleep_dead, false);
atcphy->sleep_powered_down = true;
if (atcphy_sleep_power_up(atcphy))
return -ENODEV;
}
_atcphy_dwc3_reset_assert(atcphy);
atcphy_usb2_sleep_enter(atcphy);
ret = atcphy_sleep_power_down(atcphy);
if (!ret) {
ret = apple_sleep_domain_set(atcphy->pd_aon, false);
ret = atcphy_sleep_domain_up(atcphy->pd_aon) ?: ret;
}
if (ret)
dev_warn(atcphy->dev, "USB power cycle incomplete: %d\n", ret);
ret = atcphy_sleep_come_back(atcphy, true, false);
if (ret < 0)
dev_err(atcphy->dev, "USB controller not back after the power cycle: %d\n", ret);
else
dev_info(atcphy->dev, "USB controller back after a power cycle: the host starts over\n");

return ret;
}

static int atcphy_suspend_noirq(struct device *dev)
{
struct apple_atcphy *atcphy = dev_get_drvdata(dev);
Expand All @@ -6982,15 +7040,20 @@ static int atcphy_suspend_noirq(struct device *dev)
return -EBUSY;
}
ret = atcphy_controller_hibernate(atcphy);
if (ret)
if (ret) {
if (atcphy->sleep_ret == -ENODEV)
atcphy->sleep_ret = atcphy_sleep_recover(atcphy);
return ret;
}
atcphy->hibernated = true;
ret = atcphy_sleep_power_down(atcphy);
if (ret) {
/* No resume callback follows a failed suspend: back now, and stop from now on. */
atcphy->sleep_no_keep = true;
atcphy->sleep_ret = atcphy_sleep_come_back(atcphy, atcphy->sleep_powered_down,
true);
if (atcphy->sleep_ret == -ENODEV)
atcphy->sleep_ret = atcphy_sleep_recover(atcphy);
atcphy->hibernated = false;
return ret;
}
Expand Down Expand Up @@ -7027,6 +7090,8 @@ static int atcphy_resume_noirq(struct device *dev)
apple_sleep_usb_set_asleep(false);
/* The PHY took ATC0_COMMON down itself, so this goes for a refused sleep too. */
atcphy->sleep_ret = atcphy_sleep_come_back(atcphy, true, false);
if (atcphy->sleep_ret == -ENODEV)
atcphy->sleep_ret = atcphy_sleep_recover(atcphy);
atcphy->hibernated = false;
if (!atcphy->sleep_ret)
dev_dbg(dev, "USB controller back in D0 after the sleep, always-on side kept, PHY in mode %d\n",
Expand Down Expand Up @@ -7114,6 +7179,18 @@ int apple_atcphy_sleep_result(struct phy *usb2_phy)
}
EXPORT_SYMBOL_GPL(apple_atcphy_sleep_result);

int apple_atcphy_sleep_recover(struct phy *usb2_phy)
{
struct apple_atcphy *atcphy = atcphy_from_usb2_phy(usb2_phy);

if (!atcphy)
return -EOPNOTSUPP;
guard(mutex)(&atcphy->lock);

return atcphy_sleep_recover(atcphy);
}
EXPORT_SYMBOL_GPL(apple_atcphy_sleep_recover);

/*
* A controller that was restarted after the sleep is set up as at boot and may hibernate
* again. Only the PHY's own failures stop it for the later sleeps; a sleep refused for a
Expand Down
12 changes: 8 additions & 4 deletions drivers/rtc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2099,10 +2099,14 @@ config RTC_DRV_APPLE_PMU
depends on ARCH_APPLE && NVMEM_APPLE_SPMI && PM_SLEEP
help
Read the PMU clock on M1 Pro/Max/Ultra, M2, M4 Pro and A18 Pro
systems and use its alarm to wake the system
from suspend-to-idle. Alarms are limited to one hour; deeper
suspend states are not supported. Continue setting the shared
clock through the SMC RTC.
systems and use its alarm to wake the system from suspend-to-idle,
and on A18 Pro from a firmware-driven suspend too. A18 Pro alarms
reach as far as the PMU's comparator, and the clock can be set here;
elsewhere alarms are limited to one hour and the shared clock is set
through the SMC RTC.

Say Y where this is the system's first RTC (rtc0), so that it sets
the system time at boot.

This driver can be built as the module rtc-apple-pmu.

Expand Down
49 changes: 46 additions & 3 deletions drivers/rtc/rtc-apple-pmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define APPLE_PMU_RTC_ARM BIT(6)
#define APPLE_PMU_RTC_ALARM BIT(0)
#define APPLE_PMU_RTC_HZ_SHIFT 16
/* The suspend-to-idle limit, where the alarm is all that wakes the system. */
#define APPLE_PMU_RTC_MAX_DELAY 3600

/*
Expand All @@ -33,6 +34,8 @@ struct apple_pmu_rtc_hw {
u8 control_event_bit;
u8 monitor_bit;
bool offset_in_seconds;
/* How far ahead an alarm may be, in seconds */
u32 max_delay;
};

static const struct apple_pmu_rtc_hw apple_pmu_rtc_baku = {
Expand All @@ -43,6 +46,8 @@ static const struct apple_pmu_rtc_hw apple_pmu_rtc_baku = {
.event = 0xf80c,
.event_bit = BIT(0),
.offset_in_seconds = true,
/* As far as the comparator reaches: macOS's own deep-sleep wakes use it. */
.max_delay = U32_MAX,
};

static const struct apple_pmu_rtc_hw apple_pmu_rtc_abbey = {
Expand All @@ -53,6 +58,7 @@ static const struct apple_pmu_rtc_hw apple_pmu_rtc_abbey = {
.event = 0x2000,
.event_bit = BIT(1),
.monitor_bit = BIT(0),
.max_delay = APPLE_PMU_RTC_MAX_DELAY,
};

/* M2 (t8112): Baku's register block with the offset in 1/32768 s. */
Expand All @@ -64,6 +70,7 @@ static const struct apple_pmu_rtc_hw apple_pmu_rtc_stowe = {
.event = 0xf80c,
.event_bit = BIT(0),
.monitor_bit = BIT(0),
.max_delay = APPLE_PMU_RTC_MAX_DELAY,
};

/* M1 Pro, Max and Ultra (t600x), offset in 1/32768 s */
Expand All @@ -76,6 +83,7 @@ static const struct apple_pmu_rtc_hw apple_pmu_rtc_maverick = {
.event_bit = BIT(0),
.control_event_bit = BIT(1),
.monitor_bit = BIT(0),
.max_delay = APPLE_PMU_RTC_MAX_DELAY,
};

struct apple_pmu_rtc {
Expand Down Expand Up @@ -149,6 +157,31 @@ static int apple_pmu_rtc_read_time(struct device *dev, struct rtc_time *time)
return 0;
}

/*
* Baku's offset is in whole seconds, and this sets it as the SMC RTC does: the
* counter rounded to the nearest second, so the clock is at most half a second off.
*/
static int apple_pmu_rtc_set_time(struct device *dev, struct rtc_time *time)
{
struct apple_pmu_rtc *rtc = dev_get_drvdata(dev);
__le64 value;
u64 counter;
s64 offset;
int error;

guard(mutex)(&rtc->transaction);
error = apple_pmu_rtc_counter(rtc, &counter);
if (error)
return error;
offset = rtc_tm_to_time64(time) -
(s64)((counter + BIT(APPLE_PMU_RTC_HZ_SHIFT - 1)) >> APPLE_PMU_RTC_HZ_SHIFT);
if (offset < 0 || offset > RTC_TIMESTAMP_END_9999 - U32_MAX)
return -ERANGE;
value = cpu_to_le64(offset);
error = nvmem_cell_write(rtc->offset, &value, sizeof(value));
return error < 0 ? error : 0;
}

/* All comparator/control accesses are serialized with the IRQ thread. */
static int apple_pmu_rtc_ack_alarm(struct apple_pmu_rtc *rtc)
{
Expand Down Expand Up @@ -228,7 +261,7 @@ static int apple_pmu_rtc_validate_alarm(struct apple_pmu_rtc *rtc, u64 comparato
counter >>= APPLE_PMU_RTC_HZ_SHIFT;
if (comparator <= counter)
return -ETIME;
if (comparator - counter > APPLE_PMU_RTC_MAX_DELAY)
if (comparator - counter > rtc->hw->max_delay)
return -ERANGE;
return 0;
}
Expand Down Expand Up @@ -375,13 +408,22 @@ static int apple_pmu_rtc_init_alarm(struct apple_pmu_rtc *rtc)
return apple_pmu_rtc_stop_alarm(rtc);
}

/* The others' offsets are set through the SMC RTC. */
static const struct rtc_class_ops apple_pmu_rtc_ops = {
.read_time = apple_pmu_rtc_read_time,
.read_alarm = apple_pmu_rtc_read_alarm,
.set_alarm = apple_pmu_rtc_set_alarm,
.alarm_irq_enable = apple_pmu_rtc_alarm_enable,
};

static const struct rtc_class_ops apple_pmu_rtc_seconds_ops = {
.read_time = apple_pmu_rtc_read_time,
.set_time = apple_pmu_rtc_set_time,
.read_alarm = apple_pmu_rtc_read_alarm,
.set_alarm = apple_pmu_rtc_set_alarm,
.alarm_irq_enable = apple_pmu_rtc_alarm_enable,
};

static int apple_pmu_rtc_prepare(struct device *dev)
{
struct apple_pmu_rtc *rtc = dev_get_drvdata(dev);
Expand Down Expand Up @@ -441,10 +483,11 @@ static int apple_pmu_rtc_probe(struct platform_device *pdev)
rtc->clock = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc->clock))
return PTR_ERR(rtc->clock);
rtc->clock->ops = &apple_pmu_rtc_ops;
rtc->clock->ops = rtc->hw->offset_in_seconds ? &apple_pmu_rtc_seconds_ops :
&apple_pmu_rtc_ops;
rtc->clock->range_min = 0;
rtc->clock->range_max = RTC_TIMESTAMP_END_9999;
rtc->clock->alarm_offset_max = APPLE_PMU_RTC_MAX_DELAY;
rtc->clock->alarm_offset_max = rtc->hw->max_delay;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->clock->features);
error = device_init_wakeup(dev, true);
if (error)
Expand Down
Loading
Loading