]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
i2c: move ATR alias pool to a separate struct
authorRomain Gantois <romain.gantois@bootlin.com>
Thu, 6 Mar 2025 16:23:26 +0000 (17:23 +0100)
committerWolfram Sang <wsa+renesas@sang-engineering.com>
Fri, 18 Apr 2025 21:33:53 +0000 (23:33 +0200)
Each I2C address translator (ATR) has a pool of client aliases which can be
used as translation targets. Some ATRs have a single alias pool shared by
all downstream channels, while others have a separate alias pool for each
channel. Currently, this alias pool is represented by the "aliases",
"num_aliases", and "use_mask" fields of struct i2c_atr.

In preparation for adding per-channel alias pool support, move the
"aliases", "num_aliases", "use_mask" and associated lock to a new struct
called "struct alias_pool".

Tested-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Romain Gantois <romain.gantois@bootlin.com>
Acked-by: Andi Shyti <andi.shyti@kernel.org>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
drivers/i2c/i2c-atr.c

index 118fd7d764dce51de587e274ef452e031ccff52d..e9c6b85e3e4a4ff84fdf9ad139f05fdceb441120 100644 (file)
@@ -34,6 +34,23 @@ struct i2c_atr_alias_pair {
        u16 alias;
 };
 
+/**
+ * struct i2c_atr_alias_pool - Pool of client aliases available for an ATR.
+ * @size:     Total number of aliases
+ *
+ * @lock:     Lock protecting @aliases and @use_mask
+ * @aliases:  Array of aliases, must hold exactly @size elements
+ * @use_mask: Mask of used aliases
+ */
+struct i2c_atr_alias_pool {
+       size_t size;
+
+       /* Protects aliases and use_mask */
+       spinlock_t lock;
+       u16 *aliases;
+       unsigned long *use_mask;
+};
+
 /**
  * struct i2c_atr_chan - Data for a channel.
  * @adap:            The &struct i2c_adapter for the channel
@@ -67,10 +84,7 @@ struct i2c_atr_chan {
  * @algo:      The &struct i2c_algorithm for adapters
  * @lock:      Lock for the I2C bus segment (see &struct i2c_lock_operations)
  * @max_adapters: Maximum number of adapters this I2C ATR can have
- * @num_aliases: Number of aliases in the aliases array
- * @aliases:   The aliases array
- * @alias_mask_lock: Lock protecting alias_use_mask
- * @alias_use_mask: Bitmask for used aliases in aliases array
+ * @alias_pool: Pool of available client aliases
  * @i2c_nb:    Notifier for remote client add & del events
  * @adapter:   Array of adapters
  */
@@ -86,17 +100,54 @@ struct i2c_atr {
        struct mutex lock;
        int max_adapters;
 
-       size_t num_aliases;
-       const u16 *aliases;
-       /* Protects alias_use_mask */
-       spinlock_t alias_mask_lock;
-       unsigned long *alias_use_mask;
+       struct i2c_atr_alias_pool *alias_pool;
 
        struct notifier_block i2c_nb;
 
        struct i2c_adapter *adapter[] __counted_by(max_adapters);
 };
 
+static struct i2c_atr_alias_pool *i2c_atr_alloc_alias_pool(size_t num_aliases)
+{
+       struct i2c_atr_alias_pool *alias_pool;
+       int ret;
+
+       alias_pool = kzalloc(sizeof(*alias_pool), GFP_KERNEL);
+       if (!alias_pool)
+               return ERR_PTR(-ENOMEM);
+
+       alias_pool->size = num_aliases;
+
+       alias_pool->aliases = kcalloc(num_aliases, sizeof(*alias_pool->aliases), GFP_KERNEL);
+       if (!alias_pool->aliases) {
+               ret = -ENOMEM;
+               goto err_free_alias_pool;
+       }
+
+       alias_pool->use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL);
+       if (!alias_pool->use_mask) {
+               ret = -ENOMEM;
+               goto err_free_aliases;
+       }
+
+       spin_lock_init(&alias_pool->lock);
+
+       return alias_pool;
+
+err_free_aliases:
+       kfree(alias_pool->aliases);
+err_free_alias_pool:
+       kfree(alias_pool);
+       return ERR_PTR(ret);
+}
+
+static void i2c_atr_free_alias_pool(struct i2c_atr_alias_pool *alias_pool)
+{
+       bitmap_free(alias_pool->use_mask);
+       kfree(alias_pool->aliases);
+       kfree(alias_pool);
+}
+
 static struct i2c_atr_alias_pair *
 i2c_atr_find_mapping_by_addr(const struct list_head *list, u16 phys_addr)
 {
@@ -259,44 +310,42 @@ static const struct i2c_lock_operations i2c_atr_lock_ops = {
        .unlock_bus =  i2c_atr_unlock_bus,
 };
 
-static int i2c_atr_reserve_alias(struct i2c_atr *atr)
+static int i2c_atr_reserve_alias(struct i2c_atr_alias_pool *alias_pool)
 {
        unsigned long idx;
+       u16 alias;
 
-       spin_lock(&atr->alias_mask_lock);
+       spin_lock(&alias_pool->lock);
 
-       idx = find_first_zero_bit(atr->alias_use_mask, atr->num_aliases);
-       if (idx >= atr->num_aliases) {
-               spin_unlock(&atr->alias_mask_lock);
-               dev_err(atr->dev, "failed to find a free alias\n");
+       idx = find_first_zero_bit(alias_pool->use_mask, alias_pool->size);
+       if (idx >= alias_pool->size) {
+               spin_unlock(&alias_pool->lock);
                return -EBUSY;
        }
 
-       set_bit(idx, atr->alias_use_mask);
+       set_bit(idx, alias_pool->use_mask);
 
-       spin_unlock(&atr->alias_mask_lock);
+       alias = alias_pool->aliases[idx];
 
-       return atr->aliases[idx];
+       spin_unlock(&alias_pool->lock);
+       return alias;
 }
 
-static void i2c_atr_release_alias(struct i2c_atr *atr, u16 alias)
+static void i2c_atr_release_alias(struct i2c_atr_alias_pool *alias_pool, u16 alias)
 {
        unsigned int idx;
 
-       spin_lock(&atr->alias_mask_lock);
+       spin_lock(&alias_pool->lock);
 
-       for (idx = 0; idx < atr->num_aliases; ++idx) {
-               if (atr->aliases[idx] == alias) {
-                       clear_bit(idx, atr->alias_use_mask);
-                       spin_unlock(&atr->alias_mask_lock);
+       for (idx = 0; idx < alias_pool->size; ++idx) {
+               if (alias_pool->aliases[idx] == alias) {
+                       clear_bit(idx, alias_pool->use_mask);
+                       spin_unlock(&alias_pool->lock);
                        return;
                }
        }
 
-       spin_unlock(&atr->alias_mask_lock);
-
-        /* This should never happen */
-       dev_warn(atr->dev, "Unable to find mapped alias\n");
+       spin_unlock(&alias_pool->lock);
 }
 
 static int i2c_atr_attach_addr(struct i2c_adapter *adapter,
@@ -308,9 +357,11 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter,
        u16 alias;
        int ret;
 
-       ret = i2c_atr_reserve_alias(atr);
-       if (ret < 0)
+       ret = i2c_atr_reserve_alias(atr->alias_pool);
+       if (ret < 0) {
+               dev_err(atr->dev, "failed to find a free alias\n");
                return ret;
+       }
 
        alias = ret;
 
@@ -336,7 +387,7 @@ static int i2c_atr_attach_addr(struct i2c_adapter *adapter,
 err_free:
        kfree(c2a);
 err_release_alias:
-       i2c_atr_release_alias(atr, alias);
+       i2c_atr_release_alias(atr->alias_pool, alias);
 
        return ret;
 }
@@ -357,7 +408,7 @@ static void i2c_atr_detach_addr(struct i2c_adapter *adapter,
                return;
        }
 
-       i2c_atr_release_alias(atr, c2a->alias);
+       i2c_atr_release_alias(atr->alias_pool, c2a->alias);
 
        dev_dbg(atr->dev,
                "chan%u: detached alias 0x%02x from addr 0x%02x\n",
@@ -411,12 +462,11 @@ static int i2c_atr_bus_notifier_call(struct notifier_block *nb,
 
 static int i2c_atr_parse_alias_pool(struct i2c_atr *atr)
 {
+       struct i2c_atr_alias_pool *alias_pool;
        struct device *dev = atr->dev;
-       unsigned long *alias_use_mask;
        size_t num_aliases;
        unsigned int i;
        u32 *aliases32;
-       u16 *aliases16;
        int ret;
 
        ret = fwnode_property_count_u32(dev_fwnode(dev), "i2c-alias-pool");
@@ -428,12 +478,23 @@ static int i2c_atr_parse_alias_pool(struct i2c_atr *atr)
 
        num_aliases = ret;
 
-       if (!num_aliases)
+       alias_pool = i2c_atr_alloc_alias_pool(num_aliases);
+       if (IS_ERR(alias_pool)) {
+               ret = PTR_ERR(alias_pool);
+               dev_err(dev, "Failed to allocate alias pool, err %d\n", ret);
+               return ret;
+       }
+
+       atr->alias_pool = alias_pool;
+
+       if (!alias_pool->size)
                return 0;
 
        aliases32 = kcalloc(num_aliases, sizeof(*aliases32), GFP_KERNEL);
-       if (!aliases32)
-               return -ENOMEM;
+       if (!aliases32) {
+               ret = -ENOMEM;
+               goto err_free_alias_pool;
+       }
 
        ret = fwnode_property_read_u32_array(dev_fwnode(dev), "i2c-alias-pool",
                                             aliases32, num_aliases);
@@ -443,43 +504,27 @@ static int i2c_atr_parse_alias_pool(struct i2c_atr *atr)
                goto err_free_aliases32;
        }
 
-       aliases16 = kcalloc(num_aliases, sizeof(*aliases16), GFP_KERNEL);
-       if (!aliases16) {
-               ret = -ENOMEM;
-               goto err_free_aliases32;
-       }
-
        for (i = 0; i < num_aliases; i++) {
                if (!(aliases32[i] & 0xffff0000)) {
-                       aliases16[i] = aliases32[i];
+                       alias_pool->aliases[i] = aliases32[i];
                        continue;
                }
 
                dev_err(dev, "Failed to parse 'i2c-alias-pool' property: I2C flags are not supported\n");
                ret = -EINVAL;
-               goto err_free_aliases16;
-       }
-
-       alias_use_mask = bitmap_zalloc(num_aliases, GFP_KERNEL);
-       if (!alias_use_mask) {
-               ret = -ENOMEM;
-               goto err_free_aliases16;
+               goto err_free_aliases32;
        }
 
        kfree(aliases32);
 
-       atr->num_aliases = num_aliases;
-       atr->aliases = aliases16;
-       atr->alias_use_mask = alias_use_mask;
-
-       dev_dbg(dev, "i2c-alias-pool has %zu aliases", atr->num_aliases);
+       dev_dbg(dev, "i2c-alias-pool has %zu aliases\n", alias_pool->size);
 
        return 0;
 
-err_free_aliases16:
-       kfree(aliases16);
 err_free_aliases32:
        kfree(aliases32);
+err_free_alias_pool:
+       i2c_atr_free_alias_pool(alias_pool);
        return ret;
 }
 
@@ -500,7 +545,6 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
                return ERR_PTR(-ENOMEM);
 
        mutex_init(&atr->lock);
-       spin_lock_init(&atr->alias_mask_lock);
 
        atr->parent = parent;
        atr->dev = dev;
@@ -520,13 +564,12 @@ struct i2c_atr *i2c_atr_new(struct i2c_adapter *parent, struct device *dev,
        atr->i2c_nb.notifier_call = i2c_atr_bus_notifier_call;
        ret = bus_register_notifier(&i2c_bus_type, &atr->i2c_nb);
        if (ret)
-               goto err_free_aliases;
+               goto err_free_alias_pool;
 
        return atr;
 
-err_free_aliases:
-       bitmap_free(atr->alias_use_mask);
-       kfree(atr->aliases);
+err_free_alias_pool:
+       i2c_atr_free_alias_pool(atr->alias_pool);
 err_destroy_mutex:
        mutex_destroy(&atr->lock);
        kfree(atr);
@@ -543,8 +586,7 @@ void i2c_atr_delete(struct i2c_atr *atr)
                WARN_ON(atr->adapter[i]);
 
        bus_unregister_notifier(&i2c_bus_type, &atr->i2c_nb);
-       bitmap_free(atr->alias_use_mask);
-       kfree(atr->aliases);
+       i2c_atr_free_alias_pool(atr->alias_pool);
        mutex_destroy(&atr->lock);
        kfree(atr);
 }