diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 3df977054781..5c79526245c2 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -110,6 +110,7 @@ struct regmap { /* number of bits to (left) shift the reg value when formatting*/ int reg_shift; int reg_stride; + int reg_stride_order; /* regcache specific members */ const struct regcache_ops *cache_ops; @@ -263,4 +264,19 @@ static inline const char *regmap_name(const struct regmap *map) return map->name; } +static inline unsigned int regmap_get_offset(const struct regmap *map, + unsigned int index) +{ + if (map->reg_stride_order >= 0) + return index << map->reg_stride_order; + else + return index * map->reg_stride; +} + +static inline unsigned int regcache_get_index_by_order(const struct regmap *map, + unsigned int reg) +{ + return reg >> map->reg_stride_order; +} + #endif diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index 686c9e0b930e..3ee72550b1e3 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -16,20 +16,30 @@ #include "internal.h" +static inline unsigned int regcache_flat_get_index(const struct regmap *map, + unsigned int reg) +{ + return regcache_get_index_by_order(map, reg); +} + static int regcache_flat_init(struct regmap *map) { int i; unsigned int *cache; - map->cache = kcalloc(map->max_register + 1, sizeof(unsigned int), - GFP_KERNEL); + if (!map || map->reg_stride_order < 0) + return -EINVAL; + + map->cache = kcalloc(regcache_flat_get_index(map, map->max_register) + + 1, sizeof(unsigned int), GFP_KERNEL); if (!map->cache) return -ENOMEM; cache = map->cache; for (i = 0; i < map->num_reg_defaults; i++) - cache[map->reg_defaults[i].reg] = map->reg_defaults[i].def; + cache[regcache_flat_get_index(map, map->reg_defaults[i].reg)] = + map->reg_defaults[i].def; return 0; } @@ -47,7 +57,7 @@ static int regcache_flat_read(struct regmap *map, { unsigned int *cache = map->cache; - *value = cache[reg]; + *value = cache[regcache_flat_get_index(map, reg)]; return 0; } @@ -57,7 +67,7 @@ static int regcache_flat_write(struct regmap *map, unsigned int reg, { unsigned int *cache = map->cache; - cache[reg] = value; + cache[regcache_flat_get_index(map, reg)] = value; return 0; } diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 9b0d202414d0..26f799e71c82 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -379,6 +379,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq, irq_set_chip_data(virq, data); irq_set_chip(virq, &data->irq_chip); irq_set_nested_thread(virq, 1); + irq_set_parent(virq, data->irq); irq_set_noprobe(virq); return 0; @@ -655,13 +656,34 @@ EXPORT_SYMBOL_GPL(regmap_add_irq_chip); * * @irq: Primary IRQ for the device * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() + * + * This function also dispose all mapped irq on chip. */ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) { + unsigned int virq; + int hwirq; + if (!d) return; free_irq(irq, d); + + /* Dispose all virtual irq from irq domain before removing it */ + for (hwirq = 0; hwirq < d->chip->num_irqs; hwirq++) { + /* Ignore hwirq if holes in the IRQ list */ + if (!d->chip->irqs[hwirq].mask) + continue; + + /* + * Find the virtual irq of hwirq on chip and if it is + * there then dispose it + */ + virq = irq_find_mapping(d->domain, hwirq); + if (virq) + irq_dispose_mapping(virq); + } + irq_domain_remove(d->domain); kfree(d->type_buf); kfree(d->type_buf_def); @@ -674,6 +696,88 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) } EXPORT_SYMBOL_GPL(regmap_del_irq_chip); +static void devm_regmap_irq_chip_release(struct device *dev, void *res) +{ + struct regmap_irq_chip_data *d = *(struct regmap_irq_chip_data **)res; + + regmap_del_irq_chip(d->irq, d); +} + +static int devm_regmap_irq_chip_match(struct device *dev, void *res, void *data) + +{ + struct regmap_irq_chip_data **r = res; + + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +/** + * devm_regmap_add_irq_chip(): Resource manager regmap_add_irq_chip() + * + * @dev: The device pointer on which irq_chip belongs to. + * @map: The regmap for the device. + * @irq: The IRQ the device uses to signal interrupts + * @irq_flags: The IRQF_ flags to use for the primary interrupt. + * @chip: Configuration for the interrupt controller. + * @data: Runtime data structure for the controller, allocated on success + * + * Returns 0 on success or an errno on failure. + * + * The regmap_irq_chip data automatically be released when the device is + * unbound. + */ +int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, + int irq_flags, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data) +{ + struct regmap_irq_chip_data **ptr, *d; + int ret; + + ptr = devres_alloc(devm_regmap_irq_chip_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + ret = regmap_add_irq_chip(map, irq, irq_flags, irq_base, + chip, &d); + if (ret < 0) { + devres_free(ptr); + return ret; + } + + *ptr = d; + devres_add(dev, ptr); + *data = d; + return 0; +} +EXPORT_SYMBOL_GPL(devm_regmap_add_irq_chip); + +/** + * devm_regmap_del_irq_chip(): Resource managed regmap_del_irq_chip() + * + * @dev: Device for which which resource was allocated. + * @irq: Primary IRQ for the device + * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() + */ +void devm_regmap_del_irq_chip(struct device *dev, int irq, + struct regmap_irq_chip_data *data) +{ + int rc; + + WARN_ON(irq != data->irq); + rc = devres_release(dev, devm_regmap_irq_chip_release, + devm_regmap_irq_chip_match, data); + + if (rc != 0) + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regmap_del_irq_chip); + /** * regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip * diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e2f68807d970..8c25635164be 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -19,6 +19,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include "trace.h" @@ -640,6 +641,10 @@ struct regmap *__regmap_init(struct device *dev, map->reg_stride = config->reg_stride; else map->reg_stride = 1; + if (is_power_of_2(map->reg_stride)) + map->reg_stride_order = ilog2(map->reg_stride); + else + map->reg_stride_order = -1; map->use_single_read = config->use_single_rw || !bus || !bus->read; map->use_single_write = config->use_single_rw || !bus || !bus->write; map->can_multi_write = config->can_multi_write && bus && bus->write; @@ -1310,7 +1315,7 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, if (map->writeable_reg) for (i = 0; i < val_len / map->format.val_bytes; i++) if (!map->writeable_reg(map->dev, - reg + (i * map->reg_stride))) + reg + regmap_get_offset(map, i))) return -EINVAL; if (!map->cache_bypass && map->format.parse_val) { @@ -1318,7 +1323,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, int val_bytes = map->format.val_bytes; for (i = 0; i < val_len / val_bytes; i++) { ival = map->format.parse_val(val + (i * val_bytes)); - ret = regcache_write(map, reg + (i * map->reg_stride), + ret = regcache_write(map, + reg + regmap_get_offset(map, i), ival); if (ret) { dev_err(map->dev, @@ -1848,8 +1854,9 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, goto out; } - ret = _regmap_write(map, reg + (i * map->reg_stride), - ival); + ret = _regmap_write(map, + reg + regmap_get_offset(map, i), + ival); if (ret != 0) goto out; } @@ -2421,7 +2428,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, * cost as we expect to hit the cache. */ for (i = 0; i < val_count; i++) { - ret = _regmap_read(map, reg + (i * map->reg_stride), + ret = _regmap_read(map, reg + regmap_get_offset(map, i), &v); if (ret != 0) goto out; @@ -2573,7 +2580,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, } else { for (i = 0; i < val_count; i++) { unsigned int ival; - ret = regmap_read(map, reg + (i * map->reg_stride), + ret = regmap_read(map, reg + regmap_get_offset(map, i), &ival); if (ret != 0) return ret; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 18394343f489..182602bca923 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -162,7 +162,7 @@ typedef void (*regmap_unlock)(void *); * This field is a duplicate of a similar file in * 'struct regmap_bus' and serves exact same purpose. * Use it only for "no-bus" cases. - * @max_register: Optional, specifies the maximum valid register index. + * @max_register: Optional, specifies the maximum valid register address. * @wr_table: Optional, points to a struct regmap_access_table specifying * valid ranges for write access. * @rd_table: As above, for read access. @@ -868,6 +868,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int irq_base, const struct regmap_irq_chip *chip, struct regmap_irq_chip_data **data); void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); + +int devm_regmap_add_irq_chip(struct device *dev, struct regmap *map, int irq, + int irq_flags, int irq_base, + const struct regmap_irq_chip *chip, + struct regmap_irq_chip_data **data); +void devm_regmap_del_irq_chip(struct device *dev, int irq, + struct regmap_irq_chip_data *data); + int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq); struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data);