Skip to content

Commit 37c526f

Browse files
groeckWolfram Sang
authored andcommitted
i2c: smbus: Improve handling of stuck alerts
The following messages were observed while testing alert functionality on systems with multiple I2C devices on a single bus if alert was active on more than one chip. smbus_alert 3-000c: SMBALERT# from dev 0x0c, flag 0 smbus_alert 3-000c: no driver alert()! and: smbus_alert 3-000c: SMBALERT# from dev 0x28, flag 0 Once it starts, this message repeats forever at high rate. There is no device at any of the reported addresses. Analysis shows that this is seen if multiple devices have the alert pin active. Apparently some devices do not support SMBus arbitration correctly. They keep sending address bits after detecting an address collision and handle the collision not at all or too late. Specifically, address 0x0c is seen with ADT7461A at address 0x4c and ADM1021 at address 0x18 if alert is active on both chips. Address 0x28 is seen with ADT7483 at address 0x2a and ADT7461 at address 0x4c if alert is active on both chips. Once the system is in bad state (alert is set by more than one chip), it often only recovers by power cycling. To reduce the impact of this problem, abort the endless loop in smbus_alert() if the same address is read more than once and not handled by a driver. Fixes: b5527a7 ("i2c: Add SMBus alert support") Signed-off-by: Guenter Roeck <linux@roeck-us.net> [wsa: it also fixed an interrupt storm in one of my experiments] Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> [wsa: rebased, moved a comment as well, improved the 'invalid' value] Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
1 parent 8400291 commit 37c526f

File tree

1 file changed

+25
-7
lines changed

1 file changed

+25
-7
lines changed

drivers/i2c/i2c-smbus.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ static int smbus_do_alert(struct device *dev, void *addrp)
3434
struct i2c_client *client = i2c_verify_client(dev);
3535
struct alert_data *data = addrp;
3636
struct i2c_driver *driver;
37+
int ret;
3738

3839
if (!client || client->addr != data->addr)
3940
return 0;
@@ -47,16 +48,21 @@ static int smbus_do_alert(struct device *dev, void *addrp)
4748
device_lock(dev);
4849
if (client->dev.driver) {
4950
driver = to_i2c_driver(client->dev.driver);
50-
if (driver->alert)
51+
if (driver->alert) {
52+
/* Stop iterating after we find the device */
5153
driver->alert(client, data->type, data->data);
52-
else
54+
ret = -EBUSY;
55+
} else {
5356
dev_warn(&client->dev, "no driver alert()!\n");
54-
} else
57+
ret = -EOPNOTSUPP;
58+
}
59+
} else {
5560
dev_dbg(&client->dev, "alert with no driver\n");
61+
ret = -ENODEV;
62+
}
5663
device_unlock(dev);
5764

58-
/* Stop iterating after we find the device */
59-
return -EBUSY;
65+
return ret;
6066
}
6167

6268
/*
@@ -67,6 +73,7 @@ static irqreturn_t smbus_alert(int irq, void *d)
6773
{
6874
struct i2c_smbus_alert *alert = d;
6975
struct i2c_client *ara;
76+
unsigned short prev_addr = I2C_CLIENT_END; /* Not a valid address */
7077

7178
ara = alert->ara;
7279

@@ -94,8 +101,19 @@ static irqreturn_t smbus_alert(int irq, void *d)
94101
data.addr, data.data);
95102

96103
/* Notify driver for the device which issued the alert */
97-
device_for_each_child(&ara->adapter->dev, &data,
98-
smbus_do_alert);
104+
status = device_for_each_child(&ara->adapter->dev, &data,
105+
smbus_do_alert);
106+
/*
107+
* If we read the same address more than once, and the alert
108+
* was not handled by a driver, it won't do any good to repeat
109+
* the loop because it will never terminate.
110+
* Bail out in this case.
111+
* Note: This assumes that a driver with alert handler handles
112+
* the alert properly and clears it if necessary.
113+
*/
114+
if (data.addr == prev_addr && status != -EBUSY)
115+
break;
116+
prev_addr = data.addr;
99117
}
100118

101119
return IRQ_HANDLED;

0 commit comments

Comments
 (0)