mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-07-06 09:25:22 +02:00
i2c: core: Lock address during client device instantiation
Krzysztof reported an issue [0] which is caused by parallel attempts to
instantiate the same I2C client device. This can happen if driver
supports auto-detection, but certain devices are also instantiated
explicitly.
The original change isn't actually wrong, it just revealed that I2C core
isn't prepared yet to handle this scenario.
Calls to i2c_new_client_device() can be nested, therefore we can't use a
simple mutex here. Parallel instantiation of devices at different addresses
is ok, so we just have to prevent parallel instantiation at the same address.
We can use a bitmap with one bit per 7-bit I2C client address, and atomic
bit operations to set/check/clear bits.
Now a parallel attempt to instantiate a device at the same address will
result in -EBUSY being returned, avoiding the "sysfs: cannot create duplicate
filename" splash.
Note: This patch version includes small cosmetic changes to the Tested-by
version, only functional change is that address locking is supported
for slave addresses too.
[0] https://lore.kernel.org/linux-i2c/9479fe4e-eb0c-407e-84c0-bd60c15baf74@ans.pl/T/#m12706546e8e2414d8f1a0dc61c53393f731685cc
Fixes: caba40ec35
("eeprom: at24: Probe for DDR3 thermal sensor in the SPD case")
Cc: stable@vger.kernel.org
Tested-by: Krzysztof Piotr Oledzki <ole@ans.pl>
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
This commit is contained in:
parent
3d16973f77
commit
8d3cefaf65
|
@ -915,6 +915,27 @@ int i2c_dev_irq_from_resources(const struct resource *resources,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Serialize device instantiation in case it can be instantiated explicitly
|
||||
* and by auto-detection
|
||||
*/
|
||||
static int i2c_lock_addr(struct i2c_adapter *adap, unsigned short addr,
|
||||
unsigned short flags)
|
||||
{
|
||||
if (!(flags & I2C_CLIENT_TEN) &&
|
||||
test_and_set_bit(addr, adap->addrs_in_instantiation))
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_unlock_addr(struct i2c_adapter *adap, unsigned short addr,
|
||||
unsigned short flags)
|
||||
{
|
||||
if (!(flags & I2C_CLIENT_TEN))
|
||||
clear_bit(addr, adap->addrs_in_instantiation);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_new_client_device - instantiate an i2c device
|
||||
* @adap: the adapter managing the device
|
||||
|
@ -962,6 +983,10 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
|||
goto out_err_silent;
|
||||
}
|
||||
|
||||
status = i2c_lock_addr(adap, client->addr, client->flags);
|
||||
if (status)
|
||||
goto out_err_silent;
|
||||
|
||||
/* Check for address business */
|
||||
status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
|
||||
if (status)
|
||||
|
@ -993,6 +1018,8 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
|
|||
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
|
||||
client->name, dev_name(&client->dev));
|
||||
|
||||
i2c_unlock_addr(adap, client->addr, client->flags);
|
||||
|
||||
return client;
|
||||
|
||||
out_remove_swnode:
|
||||
|
@ -1004,6 +1031,7 @@ out_err:
|
|||
dev_err(&adap->dev,
|
||||
"Failed to register i2c client %s at 0x%02x (%d)\n",
|
||||
client->name, client->addr, status);
|
||||
i2c_unlock_addr(adap, client->addr, client->flags);
|
||||
out_err_silent:
|
||||
if (need_put)
|
||||
put_device(&client->dev);
|
||||
|
|
|
@ -761,6 +761,9 @@ struct i2c_adapter {
|
|||
struct regulator *bus_regulator;
|
||||
|
||||
struct dentry *debugfs;
|
||||
|
||||
/* 7bit address space */
|
||||
DECLARE_BITMAP(addrs_in_instantiation, 1 << 7);
|
||||
};
|
||||
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user