Skip to content

Commit f6c29f7

Browse files
groeckWolfram Sang
authored andcommitted
i2c: smbus: Send alert notifications to all devices if source not found
If a SMBus alert is received and the originating device is not found, the reason may be that the address reported on the SMBus alert address is corrupted, for example because multiple devices asserted alert and do not correctly implement SMBus arbitration. If this happens, call alert handlers on all devices connected to the given I2C bus, in the hope that this cleans up the situation. This change reliably fixed the problem on a system with multiple devices on a single bus. Example log where the device on address 0x18 (ADM1021) and on address 0x4c (ADT7461A) both had the alert line asserted: smbus_alert 3-000c: SMBALERT# from dev 0x0c, flag 0 smbus_alert 3-000c: no driver alert()! smbus_alert 3-000c: SMBALERT# from dev 0x0c, flag 0 smbus_alert 3-000c: no driver alert()! lm90 3-0018: temp1 out of range, please check! lm90 3-0018: Disabling ALERT# lm90 3-0029: Everything OK lm90 3-002a: Everything OK lm90 3-004c: temp1 out of range, please check! lm90 3-004c: temp2 out of range, please check! lm90 3-004c: Disabling ALERT# Fixes: b5527a7 ("i2c: Add SMBus alert support") Signed-off-by: Guenter Roeck <linux@roeck-us.net> [wsa: fixed a typo in the commit message] Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
1 parent 37c526f commit f6c29f7

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

drivers/i2c/i2c-smbus.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,32 @@ static int smbus_do_alert(struct device *dev, void *addrp)
6565
return ret;
6666
}
6767

68+
/* Same as above, but call back all drivers with alert handler */
69+
70+
static int smbus_do_alert_force(struct device *dev, void *addrp)
71+
{
72+
struct i2c_client *client = i2c_verify_client(dev);
73+
struct alert_data *data = addrp;
74+
struct i2c_driver *driver;
75+
76+
if (!client || (client->flags & I2C_CLIENT_TEN))
77+
return 0;
78+
79+
/*
80+
* Drivers should either disable alerts, or provide at least
81+
* a minimal handler. Lock so the driver won't change.
82+
*/
83+
device_lock(dev);
84+
if (client->dev.driver) {
85+
driver = to_i2c_driver(client->dev.driver);
86+
if (driver->alert)
87+
driver->alert(client, data->type, data->data);
88+
}
89+
device_unlock(dev);
90+
91+
return 0;
92+
}
93+
6894
/*
6995
* The alert IRQ handler needs to hand work off to a task which can issue
7096
* SMBus calls, because those sleeping calls can't be made in IRQ context.
@@ -106,13 +132,19 @@ static irqreturn_t smbus_alert(int irq, void *d)
106132
/*
107133
* If we read the same address more than once, and the alert
108134
* 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.
135+
* the loop because it will never terminate. Try again, this
136+
* time calling the alert handlers of all devices connected to
137+
* the bus, and abort the loop afterwards. If this helps, we
138+
* are all set. If it doesn't, there is nothing else we can do,
139+
* so we might as well abort the loop.
111140
* Note: This assumes that a driver with alert handler handles
112141
* the alert properly and clears it if necessary.
113142
*/
114-
if (data.addr == prev_addr && status != -EBUSY)
143+
if (data.addr == prev_addr && status != -EBUSY) {
144+
device_for_each_child(&ara->adapter->dev, &data,
145+
smbus_do_alert_force);
115146
break;
147+
}
116148
prev_addr = data.addr;
117149
}
118150

0 commit comments

Comments
 (0)