Commit f4f64fa8 authored by Hector Martin's avatar Hector Martin Committed by Andi Shyti
Browse files

i2c: pasemi: Improve error recovery



Add handling for all the missing error condition, and better recovery in
pasemi_smb_clear().

Reviewed-by: default avatarAlyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: default avatarNeal Gompa <neal@gompa.dev>
Signed-off-by: default avatarHector Martin <marcan@marcan.st>
Co-developed-by: default avatarSven Peter <sven@svenpeter.dev>
Signed-off-by: default avatarSven Peter <sven@svenpeter.dev>
Link: https://lore.kernel.org/r/20250427-pasemi-fixes-v3-3-af28568296c0@svenpeter.dev


Signed-off-by: default avatarAndi Shyti <andi.shyti@kernel.org>
parent 390b8f58
Loading
Loading
Loading
Loading
+54 −7
Original line number Diff line number Diff line
@@ -87,12 +87,31 @@ static void pasemi_reset(struct pasemi_smbus *smbus)
	reinit_completion(&smbus->irq_completion);
}

static void pasemi_smb_clear(struct pasemi_smbus *smbus)
static int pasemi_smb_clear(struct pasemi_smbus *smbus)
{
	unsigned int status;
	int ret;

	status = reg_read(smbus, REG_SMSTA);
	/* First wait for the bus to go idle */
	ret = readx_poll_timeout(ioread32, smbus->ioaddr + REG_SMSTA,
				 status, !(status & (SMSTA_XIP | SMSTA_JAM)),
				 USEC_PER_MSEC,
				 USEC_PER_MSEC * PASEMI_TRANSFER_TIMEOUT_MS);

	if (ret < 0) {
		dev_err(smbus->dev, "Bus is still stuck (status 0x%08x)\n", status);
		return -EIO;
	}

	/* If any badness happened or there is data in the FIFOs, reset the FIFOs */
	if ((status & (SMSTA_MRNE | SMSTA_JMD | SMSTA_MTO | SMSTA_TOM | SMSTA_MTN | SMSTA_MTA)) ||
	    !(status & SMSTA_MTE))
		pasemi_reset(smbus);

	/* Clear the flags */
	reg_write(smbus, REG_SMSTA, status);

	return 0;
}

static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
@@ -130,9 +149,35 @@ static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
		}
	}

	/* Controller timeout? */
	if (status & SMSTA_TOM) {
		dev_err(smbus->dev, "Controller timeout, status 0x%08x\n", status);
		return -EIO;
	}

	/* Peripheral timeout? */
	if (status & SMSTA_MTO) {
		dev_err(smbus->dev, "Peripheral timeout, status 0x%08x\n", status);
		return -ETIME;
	}

	/* Still stuck in a transaction? */
	if (status & SMSTA_XIP) {
		dev_err(smbus->dev, "Bus stuck, status 0x%08x\n", status);
		return -EIO;
	}

	/* Arbitration loss? */
	if (status & SMSTA_MTA) {
		dev_err(smbus->dev, "Arbitration loss, status 0x%08x\n", status);
		return -EBUSY;
	}

	/* Got NACK? */
	if (status & SMSTA_MTN)
	if (status & SMSTA_MTN) {
		dev_err(smbus->dev, "NACK, status 0x%08x\n", status);
		return -ENXIO;
	}

	/* Clear XEN */
	reg_write(smbus, REG_SMSTA, SMSTA_XEN);
@@ -194,9 +239,9 @@ static int pasemi_i2c_xfer(struct i2c_adapter *adapter,
	struct pasemi_smbus *smbus = adapter->algo_data;
	int ret, i;

	pasemi_smb_clear(smbus);

	ret = 0;
	ret = pasemi_smb_clear(smbus);
	if (ret)
		return ret;

	for (i = 0; i < num && !ret; i++)
		ret = pasemi_i2c_xfer_msg(adapter, &msgs[i], (i == (num - 1)));
@@ -217,7 +262,9 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
	addr <<= 1;
	read_flag = read_write == I2C_SMBUS_READ;

	pasemi_smb_clear(smbus);
	err = pasemi_smb_clear(smbus);
	if (err)
		return err;

	switch (size) {
	case I2C_SMBUS_QUICK: