Skip to content

Commit 0b225a4

Browse files
marcanjannau
authored andcommitted
i2c: pasemi: Improve timeout handling and error recovery
The hardware (supposedly) has a 25ms timeout for clock stretching, but the driver uses a 10ms timeout, which is too low (and actually gets hit with the tipd controllers on Apple Silicon machines sporadically). Increase the timeout to 100ms, which should be plenty, and then add handling for all the missing error condition, and better recovery in pasemi_smb_clear(). Since this needs a bunch more bit defines, take the change to switch to bitfield.h macros, which is much more readable. Signed-off-by: Hector Martin <marcan@marcan.st>
1 parent f5a7060 commit 0b225a4

1 file changed

Lines changed: 78 additions & 27 deletions

File tree

drivers/i2c/busses/i2c-pasemi-core.c

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* SMBus host driver for PA Semi PWRficient
66
*/
77

8+
#include <linux/bitfield.h>
89
#include <linux/module.h>
910
#include <linux/pci.h>
1011
#include <linux/kernel.h>
@@ -26,21 +27,31 @@
2627
#define REG_REV 0x28
2728

2829
/* Register defs */
29-
#define MTXFIFO_READ 0x00000400
30-
#define MTXFIFO_STOP 0x00000200
31-
#define MTXFIFO_START 0x00000100
32-
#define MTXFIFO_DATA_M 0x000000ff
33-
34-
#define MRXFIFO_EMPTY 0x00000100
35-
#define MRXFIFO_DATA_M 0x000000ff
36-
37-
#define SMSTA_XEN 0x08000000
38-
#define SMSTA_MTN 0x00200000
39-
40-
#define CTL_MRR 0x00000400
41-
#define CTL_MTR 0x00000200
42-
#define CTL_EN 0x00000800
43-
#define CTL_CLK_M 0x000000ff
30+
#define MTXFIFO_READ BIT(10)
31+
#define MTXFIFO_STOP BIT(9)
32+
#define MTXFIFO_START BIT(8)
33+
#define MTXFIFO_DATA_M GENMASK(7, 0)
34+
35+
#define MRXFIFO_EMPTY BIT(8)
36+
#define MRXFIFO_DATA_M GENMASK(7, 0)
37+
38+
#define SMSTA_XIP BIT(28)
39+
#define SMSTA_XEN BIT(27)
40+
#define SMSTA_JMD BIT(25)
41+
#define SMSTA_JAM BIT(24)
42+
#define SMSTA_MTO BIT(23)
43+
#define SMSTA_MTA BIT(22)
44+
#define SMSTA_MTN BIT(21)
45+
#define SMSTA_MRNE BIT(19)
46+
#define SMSTA_MTE BIT(16)
47+
#define SMSTA_TOM BIT(6)
48+
49+
#define CTL_EN BIT(11)
50+
#define CTL_MRR BIT(10)
51+
#define CTL_MTR BIT(9)
52+
#define CTL_CLK_M GENMASK(7, 0)
53+
54+
#define TRANSFER_TIMEOUT_MS 100
4455

4556
static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val)
4657
{
@@ -70,23 +81,45 @@ static void pasemi_reset(struct pasemi_smbus *smbus)
7081
reinit_completion(&smbus->irq_completion);
7182
}
7283

73-
static void pasemi_smb_clear(struct pasemi_smbus *smbus)
84+
static int pasemi_smb_clear(struct pasemi_smbus *smbus)
7485
{
7586
unsigned int status;
87+
int timeout = TRANSFER_TIMEOUT_MS;
7688

7789
status = reg_read(smbus, REG_SMSTA);
90+
91+
/* First wait for the bus to go idle */
92+
while ((status & (SMSTA_XIP | SMSTA_JAM)) && timeout--) {
93+
msleep(1);
94+
status = reg_read(smbus, REG_SMSTA);
95+
}
96+
97+
if (timeout < 0) {
98+
dev_warn(smbus->dev, "Bus is still stuck (status 0x%08x)\n", status);
99+
return -EIO;
100+
}
101+
102+
/* If any badness happened or there is data in the FIFOs, reset the FIFOs */
103+
if ((status & (SMSTA_MRNE | SMSTA_JMD | SMSTA_MTO | SMSTA_TOM | SMSTA_MTN | SMSTA_MTA)) ||
104+
!(status & SMSTA_MTE))
105+
pasemi_reset(smbus);
106+
107+
/* Clear the flags */
78108
reg_write(smbus, REG_SMSTA, status);
109+
110+
return 0;
79111
}
80112

81113
static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
82114
{
83-
int timeout = 100;
115+
int timeout = TRANSFER_TIMEOUT_MS;
84116
unsigned int status;
85117

86118
if (smbus->use_irq) {
87119
reinit_completion(&smbus->irq_completion);
88-
reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN);
89-
wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(100));
120+
/* XEN should be set when a transaction terminates, whether due to error or not */
121+
reg_write(smbus, REG_IMASK, SMSTA_XEN);
122+
wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(timeout));
90123
reg_write(smbus, REG_IMASK, 0);
91124
status = reg_read(smbus, REG_SMSTA);
92125
} else {
@@ -97,16 +130,32 @@ static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
97130
}
98131
}
99132

100-
/* Got NACK? */
101-
if (status & SMSTA_MTN)
102-
return -ENXIO;
133+
/* Controller timeout? */
134+
if (status & SMSTA_TOM) {
135+
dev_warn(smbus->dev, "Controller timeout, status 0x%08x\n", status);
136+
return -EIO;
137+
}
103138

104-
if (timeout < 0) {
105-
dev_warn(smbus->dev, "Timeout, status 0x%08x\n", status);
106-
reg_write(smbus, REG_SMSTA, status);
139+
/* Peripheral timeout? */
140+
if (status & SMSTA_MTO) {
141+
dev_warn(smbus->dev, "Peripheral timeout, status 0x%08x\n", status);
107142
return -ETIME;
108143
}
109144

145+
/* Still stuck in a transaction? */
146+
if (status & SMSTA_XIP) {
147+
dev_warn(smbus->dev, "Bus stuck, status 0x%08x\n", status);
148+
return -EIO;
149+
}
150+
151+
/* Arbitration loss? */
152+
if (status & SMSTA_MTA)
153+
return -EBUSY;
154+
155+
/* Got NACK? */
156+
if (status & SMSTA_MTN)
157+
return -ENXIO;
158+
110159
/* Clear XEN */
111160
reg_write(smbus, REG_SMSTA, SMSTA_XEN);
112161

@@ -167,7 +216,8 @@ static int pasemi_i2c_xfer(struct i2c_adapter *adapter,
167216
struct pasemi_smbus *smbus = adapter->algo_data;
168217
int ret, i;
169218

170-
pasemi_smb_clear(smbus);
219+
if (pasemi_smb_clear(smbus))
220+
return -EIO;
171221

172222
ret = 0;
173223

@@ -190,7 +240,8 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter,
190240
addr <<= 1;
191241
read_flag = read_write == I2C_SMBUS_READ;
192242

193-
pasemi_smb_clear(smbus);
243+
if (pasemi_smb_clear(smbus))
244+
return -EIO;
194245

195246
switch (size) {
196247
case I2C_SMBUS_QUICK:

0 commit comments

Comments
 (0)