From 67c3100c8234762accf2ee70affcb4a76e0fc855 Mon Sep 17 00:00:00 2001 From: Sean Dolbec Date: Tue, 3 Nov 2026 14:23:34 -0500 Subject: [PATCH] mfd: macsmc: give atomic writes time for the SMC to answer Restart and power-off are issued as atomic SMC writes (MBSE = 'phra' / 'offw'). Since c243a5f85a ("mfd: macsmc: bound the atomic write poll"), apple_smc_write_atomic() waits SMC_TIMEOUT_MS (500 ms) for the reply and, with a command still pending, polls RTKit once and returns -EBUSY. On the M4 Mac mini (J773g, T8132) that is sometimes too short for the SMC: macsmc_restart() logs "Failed to issue MBSE = phra", the restart chain falls through to apple_wdt_restart(), and iBoot takes the watchdog reset as a crash (panicmedic-telemetry is set) and boots macOS instead of the configured Linux startup volume. Keep the bound that c243a5f85a introduced, but make it 5 s in the atomic paths: wait that long for a pending earlier command before refusing, and that long for the reply. Log replies slower than 100 ms, and failures, so slow SMCs show up in the log. Observed on the J773g: with the 500 ms bound a reboot from a test initramfs landed in macOS (2026-11-02 23:12); with this change the same path returned to Linux through the SMC (2026-11-03 10:59). Fixes: c243a5f85a ("mfd: macsmc: bound the atomic write poll") Signed-off-by: Sean Dolbec --- drivers/mfd/macsmc.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c index ac9d821bf865c..e88af94489c05 100644 --- a/drivers/mfd/macsmc.c +++ b/drivers/mfd/macsmc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,8 @@ #define SMC_RESULT SMC_MSG #define SMC_TIMEOUT_MS 500 +/* Restart and power-off go through atomic writes; give the SMC time. */ +#define SMC_ATOMIC_TIMEOUT_MS 5000 static const struct mfd_cell apple_smc_devs[] = { MFD_CELL_NAME("macsmc-input"), @@ -283,6 +286,8 @@ int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, u8 result; int ret, poll_ret; u64 msg; + ktime_t t0; + s64 us; if (size > SMC_MAX_SIZE || size == 0) return -EINVAL; @@ -296,9 +301,17 @@ int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, * release its SRAM. Its late reply does: take it if it has arrived. */ if (smc->atomic_pending) { - ret = apple_rtkit_poll(smc->rtk); - if (ret < 0 || smc->atomic_pending) + /* Wait for the earlier command's late reply instead of giving up. */ + ret = read_poll_timeout_atomic(apple_rtkit_poll, poll_ret, + poll_ret < 0 || !smc->atomic_pending, + 100, SMC_ATOMIC_TIMEOUT_MS * USEC_PER_MSEC, + false, smc->rtk); + if (poll_ret < 0 || smc->atomic_pending) { + dev_err(smc->dev, "atomic: earlier command still pending (%d/%d)\n", + ret, poll_ret); return -EBUSY; + } + dev_warn(smc->dev, "atomic: earlier command's reply arrived late\n"); } memcpy_toio(smc->shmem.iomem, buf, size); @@ -317,10 +330,15 @@ int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, return ret; } + t0 = ktime_get(); ret = read_poll_timeout_atomic(apple_rtkit_poll, poll_ret, poll_ret < 0 || !smc->atomic_pending, - 100, SMC_TIMEOUT_MS * USEC_PER_MSEC, + 100, SMC_ATOMIC_TIMEOUT_MS * USEC_PER_MSEC, false, smc->rtk); + us = ktime_us_delta(ktime_get(), t0); + if (us > 100 * USEC_PER_MSEC || ret < 0 || smc->atomic_pending) + dev_warn(smc->dev, "atomic: key %p4ch reply after %lld us (ret %d)\n", + &key, us, ret); if (poll_ret < 0) { dev_err(smc->dev, "RTKit poll failed (%llx)", msg); return poll_ret;