i2c: move ATR alias pool to a separate struct

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>
This commit is contained in:
Romain Gantois 2025-03-06 17:23:26 +01:00 committed by Wolfram Sang
parent 818bd489f1
commit 24960bd0a1

View 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);
}