From b429fab4467e2320f67c058d7419c03c7221d125 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Tue, 19 Jan 2016 17:14:39 -0800 Subject: regmap: clairify meaning of max_register The exact meaning of max_register is not entierly clear. Follow the common wording and use "address" instead of "index". Signed-off-by: Stefan Agner Signed-off-by: Mark Brown --- include/linux/regmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 18394343f489..27aaac9027c4 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. -- cgit v1.2.3 From 46189518d73080f4e4ea269a3b0f4b8583f486bd Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Feb 2016 17:58:22 +0530 Subject: regmap: irq: dispose all virtual irq before removing domain It is require to dispose all virtual irq of hwirq on chip created on given irq domain before removing this irq domain. Hence dispose all mapped irqs before deleting the irq domains in regmap_del_irq_chip(); Signed-off-by: Laxman Dewangan Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 9b0d202414d0..7e1e9e86c70b 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -655,13 +655,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); -- cgit v1.2.3 From 800c3a0e683601c4ede490e8525852e63b0f6615 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 10 Feb 2016 14:29:50 +0530 Subject: regmap: irq: add devm apis for regmap_{add,del}_irq_chip Add device managed APIs for regmap_add_irq_chip() and regmap_del_irq_chip() so that it can be managed by device framework for freeing it. This helps on following: 1. Maintaining the sequence of resource allocation and deallocation regmap_add_irq_chip(&d); devm_requested_threaded_irq(virq) On free path: regmap_del_irq_chip(d); and then removing the irq registration. On this case, regmap irq is deleted before the irq is free. This force to use normal irq registration. By using devm apis, the sequence can be maintain properly: devm_regmap_add_irq_chip(&d); devm_requested_threaded_irq(virq); and resource deallocation will be done in reverse order by device framework. 2. No need to delete the regmap_irq_chip in error path or remove callback and hence there is less code on this path. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 82 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 8 ++++ 2 files changed, 90 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 7e1e9e86c70b..36d08ca2cbe2 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -695,6 +695,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/include/linux/regmap.h b/include/linux/regmap.h index 18394343f489..de428962bfe6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -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); -- cgit v1.2.3 From ca747be22fa57bbee50e34c220401160e8f2a07f Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 4 Jan 2016 18:00:33 +0800 Subject: regmap: core: Introduce register stride order Since the register stride should always equal to 2^N, and bit rotation is much faster than multiplication and division. So introducing the stride order and using bit rotation to get the offset of the register from the index to improve the performance. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 10 ++++++++++ drivers/base/regmap/regmap.c | 19 +++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 3df977054781..c22b04b2ca17 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,13 @@ 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; +} + #endif diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee54e841de4a..29d526e0fc0f 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" @@ -638,6 +639,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; @@ -1308,7 +1313,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) { @@ -1316,7 +1321,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, @@ -1846,8 +1852,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; } @@ -2416,7 +2423,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; @@ -2568,7 +2575,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; -- cgit v1.2.3 From 8b31ec5572f812acac36f3d02944ade76a8f51b9 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 4 Jan 2016 18:00:34 +0800 Subject: regcache: Introduce the index parsing API by stride order Here introduces regcache_get_index_by_order() for regmap cache, which uses the register stride order and bit rotation, to improve the performance. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index c22b04b2ca17..5c79526245c2 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -273,4 +273,10 @@ static inline unsigned int regmap_get_offset(const struct regmap *map, 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 -- cgit v1.2.3 From ce11020f0e82dbb18eb59b6f6ce92cc0410b353e Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Mon, 4 Jan 2016 18:00:35 +0800 Subject: regcache: flat: Introduce register strider order Here we introduce regcache_flat_get_index(), which using register stride order and bit rotation, will save some memory spaces for flat cache. Though this will also lost some access performance, since the bit rotation is used to get the index of the cache array, and this could be ingored for memory I/O accessing. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-flat.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) 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; } -- cgit v1.2.3 From 58a5336292abb032085d621810c3c41c9cea36ed Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 26 Feb 2016 17:53:57 +0200 Subject: regmap: irq: Enable irq retriggering for nested irqs When nested interrupts are handled with regmap irq framework, we need to mark the interrupts to be resend for pending interrupts on enable_irq. Else the events might be lost for nested irqs. Signed-off-by: Grygorii Strashko Tested-by: Nishanth Menon Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 36d08ca2cbe2..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; -- cgit v1.2.3 From 045b98480cbe9d8dfe80983013591769f8a112cc Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 10 Feb 2016 14:29:50 +0530 Subject: regmap: irq: add devm apis for regmap_{add,del}_irq_chip Add device managed APIs for regmap_add_irq_chip() and regmap_del_irq_chip() so that it can be managed by device framework for freeing it. This helps on following: 1. Maintaining the sequence of resource allocation and deallocation regmap_add_irq_chip(&d); devm_requested_threaded_irq(virq) On free path: regmap_del_irq_chip(d); and then removing the irq registration. On this case, regmap irq is deleted before the irq is free. This force to use normal irq registration. By using devm apis, the sequence can be maintain properly: devm_regmap_add_irq_chip(&d); devm_requested_threaded_irq(virq); and resource deallocation will be done in reverse order by device framework. 2. No need to delete the regmap_irq_chip in error path or remove callback and hence there is less code on this path. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 82 ++++++++++++++++++++++++++++++++++++++++ include/linux/regmap.h | 8 ++++ 2 files changed, 90 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 9b0d202414d0..0b0ea3b88a91 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -674,6 +674,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/include/linux/regmap.h b/include/linux/regmap.h index 18394343f489..de428962bfe6 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -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); -- cgit v1.2.3