Skip to content
Merged
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
24 changes: 21 additions & 3 deletions drivers/mfd/macsmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/iopoll.h>
#include <linux/ktime.h>
#include <linux/math.h>
#include <linux/mfd/core.h>
#include <linux/mfd/macsmc.h>
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down