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: Sven Peter <sven@svenpeter.dev>
Link: https://lore.kernel.org/r/20250427-pasemi-fixes-v3-2-af28568296c0@svenpeter.dev
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
This commit is contained in:
Sven Peter
2025-05-19 22:23:57 +02:00
committed by Andi Shyti
parent 88fe3078b5
commit 390b8f5834
+29 -12
View File
@@ -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);