Commit 390b8f58 authored by Sven Peter's avatar Sven Peter Committed by Andi Shyti
Browse files

i2c: pasemi: Improve timeout handling



The hardware (supposedly) has a 25ms timeout for clock stretching
and the driver uses 100ms which should be plenty.
The interrupt path however misses handling for errors while waiting for
the completion and the polling path uses an open-coded readx_poll_timeout.
Note that we drop reg_write(smbus, REG_SMSTA, status) while fixing those
issues here which will be done anyway whenever the next transaction starts
via pasemi_smb_clear.

Signed-off-by: default avatarSven Peter <sven@svenpeter.dev>
Link: https://lore.kernel.org/r/20250427-pasemi-fixes-v3-2-af28568296c0@svenpeter.dev


Signed-off-by: default avatarAndi Shyti <andi.shyti@kernel.org>
parent 88fe3078
Loading
Loading
Loading
Loading
+29 −12
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -52,6 +53,12 @@
#define CTL_UJM		BIT(8)
#define CTL_CLK_M	GENMASK(7, 0)

/*
 * The hardware (supposedly) has a 25ms timeout for clock stretching, thus
 * use 100ms here which should be plenty.
 */
#define PASEMI_TRANSFER_TIMEOUT_MS	100

static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
{
	dev_dbg(smbus->dev, "smbus write reg %x val %08x\n", reg, val);
@@ -90,20 +97,36 @@ static void pasemi_smb_clear(struct pasemi_smbus *smbus)

static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
{
	int timeout = 100;
	unsigned int status;

	if (smbus->use_irq) {
		reinit_completion(&smbus->irq_completion);
		reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN);
		wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(100));
		int ret = wait_for_completion_timeout(
				&smbus->irq_completion,
				msecs_to_jiffies(PASEMI_TRANSFER_TIMEOUT_MS));
		reg_write(smbus, REG_IMASK, 0);
		status = reg_read(smbus, REG_SMSTA);

		if (ret < 0) {
			dev_err(smbus->dev,
				"Completion wait failed with %d, status 0x%08x\n",
				ret, status);
			return ret;
		} else if (ret == 0) {
			dev_err(smbus->dev, "Timeout, status 0x%08x\n", status);
			return -ETIME;
		}
	} else {
		status = reg_read(smbus, REG_SMSTA);
		while (!(status & SMSTA_XEN) && timeout--) {
			msleep(1);
			status = reg_read(smbus, REG_SMSTA);
		int ret = readx_poll_timeout(
				ioread32, smbus->ioaddr + REG_SMSTA,
				status, status & SMSTA_XEN,
				USEC_PER_MSEC,
				USEC_PER_MSEC * PASEMI_TRANSFER_TIMEOUT_MS);

		if (ret < 0) {
			dev_err(smbus->dev, "Timeout, status 0x%08x\n", status);
			return -ETIME;
		}
	}

@@ -111,12 +134,6 @@ static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
	if (status & SMSTA_MTN)
		return -ENXIO;

	if (timeout < 0) {
		dev_warn(smbus->dev, "Timeout, status 0x%08x\n", status);
		reg_write(smbus, REG_SMSTA, status);
		return -ETIME;
	}

	/* Clear XEN */
	reg_write(smbus, REG_SMSTA, SMSTA_XEN);