From 98c5a9e018c188064dea2debae7651e8b1e6d258 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 30 Jun 2024 22:56:13 +0200 Subject: [PATCH 2/2] i2c: Serialize client device instantiation Signed-off-by: Heiner Kallweit --- drivers/i2c/i2c-core-base.c | 45 ++++++++++++++++++++++++++++++------- include/linux/i2c.h | 3 +++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index a6c2974b9..2fd3a5432 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -928,7 +928,7 @@ int i2c_dev_irq_from_resources(const struct resource *resources, } /** - * i2c_new_client_device - instantiate an i2c device + * __i2c_new_client_device - instantiate an i2c device * @adap: the adapter managing the device * @info: describes one I2C device; bus_num is ignored * Context: can sleep @@ -944,7 +944,7 @@ int i2c_dev_irq_from_resources(const struct resource *resources, * i2c_unregister_device(); or an ERR_PTR to describe the error. */ struct i2c_client * -i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +__i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; bool need_put = false; @@ -1025,6 +1025,20 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf kfree(client); return ERR_PTR(status); } +EXPORT_SYMBOL_GPL(__i2c_new_client_device); + + +struct i2c_client * +i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info) +{ + struct i2c_client *cl; + + rt_mutex_lock_nested(&adap->client_lock, i2c_adapter_depth(adap)); + cl = __i2c_new_client_device(adap, info); + rt_mutex_unlock(&adap->client_lock); + + return cl; +} EXPORT_SYMBOL_GPL(i2c_new_client_device); /** @@ -1516,6 +1530,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) adap->locked_flags = 0; rt_mutex_init(&adap->bus_lock); + rt_mutex_init(&adap->client_lock); rt_mutex_init(&adap->mux_lock); mutex_init(&adap->userspace_clients_lock); INIT_LIST_HEAD(&adap->userspace_clients); @@ -2457,13 +2472,15 @@ static int i2c_detect_address(struct i2c_client *temp_client, return err; } + rt_mutex_lock_nested(&adapter->client_lock, i2c_adapter_depth(adapter)); + /* Skip if already in use (7 bit, no need to encode flags) */ if (i2c_check_addr_busy(adapter, addr, NULL)) - return 0; + goto out; /* Make sure there is something at this address */ if (!i2c_default_probe(adapter, addr)) - return 0; + goto out; /* Finally call the custom detection function */ memset(&info, 0, sizeof(struct i2c_board_info)); @@ -2472,7 +2489,9 @@ static int i2c_detect_address(struct i2c_client *temp_client, if (err) { /* -ENODEV is returned if the detection fails. We catch it here as this isn't an error. */ - return err == -ENODEV ? 0 : err; + if (err == -ENODEV) + err = 0; + goto out; } /* Consistency check */ @@ -2500,7 +2519,9 @@ static int i2c_detect_address(struct i2c_client *temp_client, dev_err(&adapter->dev, "Failed creating %s at 0x%02x\n", info.type, info.addr); } - return 0; +out: + rt_mutex_unlock(&adapter->client_lock); + return err; } static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver) @@ -2559,11 +2580,14 @@ i2c_new_scanned_device(struct i2c_adapter *adap, unsigned short const *addr_list, int (*probe)(struct i2c_adapter *adap, unsigned short addr)) { + struct i2c_client *cl; int i; if (!probe) probe = i2c_default_probe; + rt_mutex_lock_nested(&adap->client_lock, i2c_adapter_depth(adap)); + for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) { /* Check address validity */ if (i2c_check_7bit_addr_validity_strict(addr_list[i]) < 0) { @@ -2587,11 +2611,16 @@ i2c_new_scanned_device(struct i2c_adapter *adap, if (addr_list[i] == I2C_CLIENT_END) { dev_dbg(&adap->dev, "Probing failed, no device found\n"); - return ERR_PTR(-ENODEV); + cl = ERR_PTR(-ENODEV); + goto out; } info->addr = addr_list[i]; - return i2c_new_client_device(adap, info); + cl = __i2c_new_client_device(adap, info); + +out: + rt_mutex_unlock(&adap->client_lock); + return cl; } EXPORT_SYMBOL_GPL(i2c_new_scanned_device); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 424acb98c..981b16147 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -457,6 +457,8 @@ struct i2c_board_info { */ struct i2c_client * i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info); +struct i2c_client * +__i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info); /* If you don't know the exact address of an I2C device, use this variant * instead, which can probe for device presence in a list of possible @@ -725,6 +727,7 @@ struct i2c_adapter { /* data fields that are valid for all devices */ const struct i2c_lock_operations *lock_ops; struct rt_mutex bus_lock; + struct rt_mutex client_lock; struct rt_mutex mux_lock; int timeout; /* in jiffies */ -- 2.45.2