mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
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:
parent
818bd489f1
commit
24960bd0a1
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user