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;