From 29bb45f25ff3051354ed330c0d0f10418a2b8c7c Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Thu, 29 Oct 2015 19:58:47 +0000 Subject: [PATCH 01/18] regmap-mmio: Use native endianness for read/write The regmap API has an endianness setting for formatting reads and writes. This can be set by the usual DT "little-endian" and "big-endian" properties. To work properly the associated regmap_bus needs to read/write in native endian. The "syscon" DT device binding creates an mmio-based regmap_bus which performs all reads/writes as little-endian. These values are then converted again by regmap, which means that all of the MIPS BCM boards (which are big-endian) have been declared as "little-endian" to get regmap to convert them back to big-endian. Modify regmap-mmio to use the native-endian functions __raw_read*() and __raw_write*() instead of the little-endian functions read*() and write*(). Modify the big-endian MIPS BCM boards to use what will now be the correct endianness instead of pretending that the devices are little-endian. Signed-off-by: Simon Arlott Signed-off-by: Mark Brown --- arch/mips/boot/dts/brcm/bcm6328.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7125.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7346.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7358.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7360.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7362.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7420.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7425.dtsi | 1 - arch/mips/boot/dts/brcm/bcm7435.dtsi | 1 - drivers/base/regmap/regmap-mmio.c | 16 ++++++++-------- 10 files changed, 8 insertions(+), 17 deletions(-) diff --git a/arch/mips/boot/dts/brcm/bcm6328.dtsi b/arch/mips/boot/dts/brcm/bcm6328.dtsi index 41891c1e58bd..d52ce3d07f16 100644 --- a/arch/mips/boot/dts/brcm/bcm6328.dtsi +++ b/arch/mips/boot/dts/brcm/bcm6328.dtsi @@ -73,7 +73,6 @@ timer: timer@10000040 { compatible = "syscon"; reg = <0x10000040 0x2c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7125.dtsi b/arch/mips/boot/dts/brcm/bcm7125.dtsi index 1a7efa883c5e..4fc7ecee273c 100644 --- a/arch/mips/boot/dts/brcm/bcm7125.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7125.dtsi @@ -98,7 +98,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7125-sun-top-ctrl", "syscon"; reg = <0x404000 0x60c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7346.dtsi b/arch/mips/boot/dts/brcm/bcm7346.dtsi index d4bf52cfcf17..a3039bb53477 100644 --- a/arch/mips/boot/dts/brcm/bcm7346.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7346.dtsi @@ -118,7 +118,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7346-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7358.dtsi b/arch/mips/boot/dts/brcm/bcm7358.dtsi index 8e2501694d03..4274ff41ec21 100644 --- a/arch/mips/boot/dts/brcm/bcm7358.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7358.dtsi @@ -112,7 +112,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7358-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7360.dtsi b/arch/mips/boot/dts/brcm/bcm7360.dtsi index 7e5f76040fb8..0dcc9163c27b 100644 --- a/arch/mips/boot/dts/brcm/bcm7360.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7360.dtsi @@ -112,7 +112,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7360-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7362.dtsi b/arch/mips/boot/dts/brcm/bcm7362.dtsi index c739ea77acb0..2f3f9fc2c478 100644 --- a/arch/mips/boot/dts/brcm/bcm7362.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7362.dtsi @@ -118,7 +118,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7362-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7420.dtsi b/arch/mips/boot/dts/brcm/bcm7420.dtsi index 5f55d0a50a28..bee221b3b568 100644 --- a/arch/mips/boot/dts/brcm/bcm7420.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7420.dtsi @@ -99,7 +99,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7420-sun-top-ctrl", "syscon"; reg = <0x404000 0x60c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7425.dtsi b/arch/mips/boot/dts/brcm/bcm7425.dtsi index e24d41ab4e30..571f30f52e3f 100644 --- a/arch/mips/boot/dts/brcm/bcm7425.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7425.dtsi @@ -100,7 +100,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7425-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; - little-endian; }; reboot { diff --git a/arch/mips/boot/dts/brcm/bcm7435.dtsi b/arch/mips/boot/dts/brcm/bcm7435.dtsi index 8b9432cc062b..614ee211f71a 100644 --- a/arch/mips/boot/dts/brcm/bcm7435.dtsi +++ b/arch/mips/boot/dts/brcm/bcm7435.dtsi @@ -114,7 +114,6 @@ sun_top_ctrl: syscon@404000 { compatible = "brcm,bcm7425-sun-top-ctrl", "syscon"; reg = <0x404000 0x51c>; - little-endian; }; reboot { diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 426a57e41ac7..8a77876d439a 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -106,17 +106,17 @@ static int regmap_mmio_gather_write(void *context, while (val_size) { switch (ctx->val_bytes) { case 1: - writeb(*(u8 *)val, ctx->regs + offset); + __raw_writeb(*(u8 *)val, ctx->regs + offset); break; case 2: - writew(*(u16 *)val, ctx->regs + offset); + __raw_writew(*(u16 *)val, ctx->regs + offset); break; case 4: - writel(*(u32 *)val, ctx->regs + offset); + __raw_writel(*(u32 *)val, ctx->regs + offset); break; #ifdef CONFIG_64BIT case 8: - writeq(*(u64 *)val, ctx->regs + offset); + __raw_writeq(*(u64 *)val, ctx->regs + offset); break; #endif default: @@ -166,17 +166,17 @@ static int regmap_mmio_read(void *context, while (val_size) { switch (ctx->val_bytes) { case 1: - *(u8 *)val = readb(ctx->regs + offset); + *(u8 *)val = __raw_readb(ctx->regs + offset); break; case 2: - *(u16 *)val = readw(ctx->regs + offset); + *(u16 *)val = __raw_readw(ctx->regs + offset); break; case 4: - *(u32 *)val = readl(ctx->regs + offset); + *(u32 *)val = __raw_readl(ctx->regs + offset); break; #ifdef CONFIG_64BIT case 8: - *(u64 *)val = readq(ctx->regs + offset); + *(u64 *)val = __raw_readq(ctx->regs + offset); break; #endif default: From 6399aea629b02a23364efcb6eea1319b8e9d1abf Mon Sep 17 00:00:00 2001 From: Nikesh Oswal Date: Wed, 21 Oct 2015 14:16:14 +0100 Subject: [PATCH 02/18] regmap: rbtree: When adding a reg do a bsearch for target node A binary search is much more efficient rather than iterating over the rbtree in ascending order which the current code is doing. During initialisation the reg defaults are written to the cache in a large chunk and these are always sorted in the ascending order so for this situation ideally we should have iterated the rbtree in descending order. But at runtime the drivers may write into the cache in any random order so this patch selects to use a bsearch to give an optimal runtime performance and also at initialisation time when reg defaults are written the performance of binary search would be much better than iterating in ascending order which the current code was doing. Signed-off-by: Nikesh Oswal Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 56486d92c4e7..353f60236ce0 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -413,8 +413,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, max = reg + max_dist; /* look for an adjacent register to the one we are about to add */ - for (node = rb_first(&rbtree_ctx->root); node; - node = rb_next(node)) { + node = rbtree_ctx->root.rb_node; + while (node) { rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node); @@ -425,6 +425,11 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg, new_base_reg = min(reg, base_reg); new_top_reg = max(reg, top_reg); } else { + if (max < base_reg) + node = node->rb_left; + else + node = node->rb_right; + continue; } From eeda1bd69d5d8a020ce191f717b94ca99707daad Mon Sep 17 00:00:00 2001 From: lixiubo Date: Fri, 20 Nov 2015 18:06:29 +0800 Subject: [PATCH 03/18] regmap: replace kzalloc with kcalloc Replace kzalloc with specialized function kcalloc when the size is a multiplication of : number * sizeof Signed-off-by: lixiubo Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-flat.c | 2 +- drivers/base/regmap/regcache-lzo.c | 2 +- drivers/base/regmap/regcache-rbtree.c | 5 +++-- drivers/base/regmap/regmap-irq.c | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index 0246f44ded74..686c9e0b930e 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -21,7 +21,7 @@ static int regcache_flat_init(struct regmap *map) int i; unsigned int *cache; - map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1), + map->cache = kcalloc(map->max_register + 1, sizeof(unsigned int), GFP_KERNEL); if (!map->cache) return -ENOMEM; diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 736e0d378567..52f69381c070 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -139,7 +139,7 @@ static int regcache_lzo_init(struct regmap *map) ret = 0; blkcount = regcache_lzo_block_count(map); - map->cache = kzalloc(blkcount * sizeof *lzo_blocks, + map->cache = kcalloc(blkcount, sizeof(*lzo_blocks), GFP_KERNEL); if (!map->cache) return -ENOMEM; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 56486d92c4e7..3b6cfede2fd9 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -366,8 +366,9 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) if (!rbnode->block) goto err_free; - rbnode->cache_present = kzalloc(BITS_TO_LONGS(rbnode->blklen) * - sizeof(*rbnode->cache_present), GFP_KERNEL); + rbnode->cache_present = kcalloc(BITS_TO_LONGS(rbnode->blklen), + sizeof(*rbnode->cache_present), + GFP_KERNEL); if (!rbnode->cache_present) goto err_free_block; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 8d16db533527..4ebbe21ded82 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -386,23 +386,23 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!d) return -ENOMEM; - d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, + d->status_buf = kcalloc(chip->num_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->status_buf) goto err_alloc; - d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, + d->mask_buf = kcalloc(chip->num_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->mask_buf) goto err_alloc; - d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs, + d->mask_buf_def = kcalloc(chip->num_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->mask_buf_def) goto err_alloc; if (chip->wake_base) { - d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, + d->wake_buf = kcalloc(chip->num_regs, sizeof(unsigned int), GFP_KERNEL); if (!d->wake_buf) goto err_alloc; From 549e08a0a93442ab62e0aee8aeb8ae6a7f2b5273 Mon Sep 17 00:00:00 2001 From: lixiubo Date: Fri, 20 Nov 2015 18:06:30 +0800 Subject: [PATCH 04/18] regmap: replace kmalloc with kmalloc_array Replace kmalloc with specialized function kmalloc_array when the size is a multiplication of : number * size Signed-off-by: lixiubo Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-lzo.c | 4 ++-- drivers/base/regmap/regcache-rbtree.c | 4 ++-- drivers/base/regmap/regmap-irq.c | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 52f69381c070..6f77d7319fc6 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -152,8 +152,8 @@ static int regcache_lzo_init(struct regmap *map) * that register. */ bmp_size = map->num_reg_defaults_raw; - sync_bmp = kmalloc(BITS_TO_LONGS(bmp_size) * sizeof(long), - GFP_KERNEL); + sync_bmp = kmalloc_array(BITS_TO_LONGS(bmp_size), sizeof(long), + GFP_KERNEL); if (!sync_bmp) { ret = -ENOMEM; goto err; diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 3b6cfede2fd9..9d7ced559cba 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -361,8 +361,8 @@ regcache_rbtree_node_alloc(struct regmap *map, unsigned int reg) rbnode->base_reg = reg; } - rbnode->block = kmalloc(rbnode->blklen * map->cache_word_size, - GFP_KERNEL); + rbnode->block = kmalloc_array(rbnode->blklen, map->cache_word_size, + GFP_KERNEL); if (!rbnode->block) goto err_free; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 4ebbe21ded82..4d2cb21254aa 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -422,8 +422,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (!map->use_single_read && map->reg_stride == 1 && d->irq_reg_stride == 1) { - d->status_reg_buf = kmalloc(map->format.val_bytes * - chip->num_regs, GFP_KERNEL); + d->status_reg_buf = kmalloc_array(chip->num_regs, + map->format.val_bytes, + GFP_KERNEL); if (!d->status_reg_buf) goto err_alloc; } From a6baa3deaf7aaec5f1c5233128b5285c4c7074e0 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 30 Nov 2015 16:20:15 +0100 Subject: [PATCH 05/18] regmap: Fix leftover from struct reg_default to struct reg_sequence change In 8019ff6cfc04 ("regmap: Use reg_sequence for multi_reg_write / register_patch") struct reg_default was renamed to struct reg_secquence, which missed one place to fix up. Reported-by: kbuild test robot Signed-off-by: Daniel Wagner 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 d68bb402120e..4d9a1a04647b 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1021,7 +1021,7 @@ static inline void regmap_async_complete(struct regmap *map) } static inline int regmap_register_patch(struct regmap *map, - const struct reg_default *regs, + const struct reg_sequence *regs, int num_regs) { WARN_ONCE(1, "regmap API is disabled"); From 9d98fa712920a393591f446fa76cb277309dd37b Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 3 Dec 2015 13:27:20 +0800 Subject: [PATCH 06/18] regmap: mmio: remove the useless code Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 8a77876d439a..dc991c3bec25 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -247,7 +247,6 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, min_stride = 8; break; #endif - break; default: return ERR_PTR(-EINVAL); } From 75fb0aaea18dbcde60a740c5d62af705366d6357 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 3 Dec 2015 13:27:21 +0800 Subject: [PATCH 07/18] regmap: mmio: Add regmap_mmio_get_min_stride Splite the minimal stride parsing into one signal function. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 49 +++++++++++++++++++------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index dc991c3bec25..8812bfb9e3b8 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -61,6 +61,33 @@ static int regmap_mmio_regbits_check(size_t reg_bits) } } +static int regmap_mmio_get_min_stride(size_t val_bits) +{ + int min_stride; + + switch (val_bits) { + case 8: + /* The core treats 0 as 1 */ + min_stride = 0; + return 0; + case 16: + min_stride = 2; + break; + case 32: + min_stride = 4; + break; +#ifdef CONFIG_64BIT + case 64: + min_stride = 8; + break; +#endif + default: + return -EINVAL; + } + + return min_stride; +} + static inline void regmap_mmio_count_check(size_t count, u32 offset) { BUG_ON(count <= offset); @@ -231,25 +258,9 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, if (config->pad_bits) return ERR_PTR(-EINVAL); - switch (config->val_bits) { - case 8: - /* The core treats 0 as 1 */ - min_stride = 0; - break; - case 16: - min_stride = 2; - break; - case 32: - min_stride = 4; - break; -#ifdef CONFIG_64BIT - case 64: - min_stride = 8; - break; -#endif - default: - return ERR_PTR(-EINVAL); - } + min_stride = regmap_mmio_get_min_stride(config->val_bits); + if (min_stride < 0) + return ERR_PTR(min_stride); if (config->reg_stride < min_stride) return ERR_PTR(-EINVAL); From afcc00b91f1865f6d0bbdb687dd642ce8a3c3c9e Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Thu, 3 Dec 2015 17:31:52 +0800 Subject: [PATCH 08/18] regmap: add 64-bit mode support Since the mmio has support the 64-bit has been supported for the 64-bit platform, so should the regmap core too. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 99 ++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4ac63c0e50c7..1791180dca02 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -245,6 +245,28 @@ static void regmap_format_32_native(void *buf, unsigned int val, *(u32 *)buf = val << shift; } +#ifdef CONFIG_64BIT +static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift) +{ + __be64 *b = buf; + + b[0] = cpu_to_be64(val << shift); +} + +static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift) +{ + __le64 *b = buf; + + b[0] = cpu_to_le64(val << shift); +} + +static void regmap_format_64_native(void *buf, unsigned int val, + unsigned int shift) +{ + *(u64 *)buf = val << shift; +} +#endif + static void regmap_parse_inplace_noop(void *buf) { } @@ -332,6 +354,41 @@ static unsigned int regmap_parse_32_native(const void *buf) return *(u32 *)buf; } +#ifdef CONFIG_64BIT +static unsigned int regmap_parse_64_be(const void *buf) +{ + const __be64 *b = buf; + + return be64_to_cpu(b[0]); +} + +static unsigned int regmap_parse_64_le(const void *buf) +{ + const __le64 *b = buf; + + return le64_to_cpu(b[0]); +} + +static void regmap_parse_64_be_inplace(void *buf) +{ + __be64 *b = buf; + + b[0] = be64_to_cpu(b[0]); +} + +static void regmap_parse_64_le_inplace(void *buf) +{ + __le64 *b = buf; + + b[0] = le64_to_cpu(b[0]); +} + +static unsigned int regmap_parse_64_native(const void *buf) +{ + return *(u64 *)buf; +} +#endif + static void regmap_lock_mutex(void *__map) { struct regmap *map = __map; @@ -712,6 +769,21 @@ struct regmap *__regmap_init(struct device *dev, } break; +#ifdef CONFIG_64BIT + case 64: + switch (reg_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_reg = regmap_format_64_be; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_reg = regmap_format_64_native; + break; + default: + goto err_map; + } + break; +#endif + default: goto err_map; } @@ -771,6 +843,27 @@ struct regmap *__regmap_init(struct device *dev, goto err_map; } break; +#ifdef CONFIG_64BIT + switch (val_endian) { + case REGMAP_ENDIAN_BIG: + map->format.format_val = regmap_format_64_be; + map->format.parse_val = regmap_parse_64_be; + map->format.parse_inplace = regmap_parse_64_be_inplace; + break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_val = regmap_format_64_le; + map->format.parse_val = regmap_parse_64_le; + map->format.parse_inplace = regmap_parse_64_le_inplace; + break; + case REGMAP_ENDIAN_NATIVE: + map->format.format_val = regmap_format_64_native; + map->format.parse_val = regmap_parse_64_native; + break; + default: + goto err_map; + } + break; +#endif } if (map->format.format_write) { @@ -2488,11 +2581,17 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, * we assume that the values are native * endian. */ + u64 *u64 = val; u32 *u32 = val; u16 *u16 = val; u8 *u8 = val; switch (map->format.val_bytes) { +#ifdef CONFIG_64BIT + case 8: + u64[i] = ival; + break; +#endif case 4: u32[i] = ival; break; From 19c04788f0e0ef778df39cdeaf8e9889d3020894 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 9 Dec 2015 17:11:53 +0800 Subject: [PATCH 09/18] regmap: fix the warning about unused variable The variable 'u64 *u64' should be only visible on 64-BIT platform. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 1791180dca02..a0d30a0fd8cf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2581,7 +2581,9 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, * we assume that the values are native * endian. */ +#ifdef CONFIG_64BIT u64 *u64 = val; +#endif u32 *u32 = val; u16 *u16 = val; u8 *u8 = val; From 2fd6902ed7587c302611906b5151f6a7efeb2c63 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 9 Dec 2015 13:09:06 +0800 Subject: [PATCH 10/18] regmap: cache: To suppress the noise of checkpatch There will be some warning like the following when checking new patches near this code: "WARNING: Missing a blank line after declarations" This patch will suppress this warning. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 4c07802986b2..3cb8c59a5ff6 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -535,16 +535,19 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, switch (map->cache_word_size) { case 1: { u8 *cache = base; + cache[idx] = val; break; } case 2: { u16 *cache = base; + cache[idx] = val; break; } case 4: { u32 *cache = base; + cache[idx] = val; break; } @@ -568,14 +571,17 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, switch (map->cache_word_size) { case 1: { const u8 *cache = base; + return cache[idx]; } case 2: { const u16 *cache = base; + return cache[idx]; } case 4: { const u32 *cache = base; + return cache[idx]; } default: From 8b7663de6e2bfe3c40e1846e1c4625f33d138757 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 9 Dec 2015 13:09:07 +0800 Subject: [PATCH 11/18] regmap: cache: Add 64-bit mode support Since the mmio has support the 64-bit has been supported for the 64-bit platform, so should the regcache core too. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 3cb8c59a5ff6..1c0210aa2573 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -551,6 +551,14 @@ bool regcache_set_val(struct regmap *map, void *base, unsigned int idx, cache[idx] = val; break; } +#ifdef CONFIG_64BIT + case 8: { + u64 *cache = base; + + cache[idx] = val; + break; + } +#endif default: BUG(); } @@ -584,6 +592,13 @@ unsigned int regcache_get_val(struct regmap *map, const void *base, return cache[idx]; } +#ifdef CONFIG_64BIT + case 8: { + const u64 *cache = base; + + return cache[idx]; + } +#endif default: BUG(); } From 01c377bfe21a1050fd7cfd33699f7d8049dc2723 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Dec 2015 15:59:14 +0300 Subject: [PATCH 12/18] regmap: shift wrapping bugs in 64 bit code We should cast these to 64bit so that we don't truncate away the high bits. Fixes: afcc00b91f18 ('regmap: add 64-bit mode support') Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index a0d30a0fd8cf..9c167e73fdcd 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -250,20 +250,20 @@ static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift) { __be64 *b = buf; - b[0] = cpu_to_be64(val << shift); + b[0] = cpu_to_be64((u64)val << shift); } static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift) { __le64 *b = buf; - b[0] = cpu_to_le64(val << shift); + b[0] = cpu_to_le64((u64)val << shift); } static void regmap_format_64_native(void *buf, unsigned int val, unsigned int shift) { - *(u64 *)buf = val << shift; + *(u64 *)buf = (u64)val << shift; } #endif From 782035ea94dc4028c19e9f39857ce9756119ae94 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Dec 2015 15:59:43 +0300 Subject: [PATCH 13/18] regmap: missing case statement This new code is unreachable. Presumably there was supposed to be a case statement there similar to the earlier code. Fixes: afcc00b91f18 ('regmap: add 64-bit mode support') Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 9c167e73fdcd..d27fe2f0182e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -844,6 +844,7 @@ struct regmap *__regmap_init(struct device *dev, } break; #ifdef CONFIG_64BIT + case 64: switch (val_endian) { case REGMAP_ENDIAN_BIG: map->format.format_val = regmap_format_64_be; From 8cfe2fd3562ba673435bb2d7a4bb451aabd47809 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Dec 2015 11:23:19 +0800 Subject: [PATCH 14/18] regmap: cache: Add warning info for the cache check If there is no cache used for the drivers, the register defaults or the register defaults raw are not need any more. This patch will check this and print a warning. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 4c07802986b2..9436a0d4cf71 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -100,15 +100,19 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) int i; void *tmp_buf; - for (i = 0; i < config->num_reg_defaults; i++) - if (config->reg_defaults[i].reg % map->reg_stride) - return -EINVAL; - if (map->cache_type == REGCACHE_NONE) { + if (config->reg_defaults || config->num_reg_defaults_raw) + dev_warn(map->dev, + "No cache used with register defaults set!\n"); + map->cache_bypass = true; return 0; } + for (i = 0; i < config->num_reg_defaults; i++) + if (config->reg_defaults[i].reg % map->reg_stride) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cache_types); i++) if (cache_types[i]->type == map->cache_type) break; From 167f7066a637332b463adf3b87b2af1c1031591a Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Fri, 11 Dec 2015 11:23:20 +0800 Subject: [PATCH 15/18] regmap: cache: Move the num_reg_defaults check as early as possible If the register defaults are provided by the driver without the number by mistake, it should just return an error with one promotion. This should be as early as possible, then there is no need to verify the register defaults' stride and the other code followed. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regcache.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 9436a0d4cf71..60aeaad7fb69 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -109,6 +109,12 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) return 0; } + if (config->reg_defaults && !config->num_reg_defaults) { + dev_err(map->dev, + "Register defaults are set without the number!\n"); + return -EINVAL; + } + for (i = 0; i < config->num_reg_defaults; i++) if (config->reg_defaults[i].reg % map->reg_stride) return -EINVAL; @@ -142,8 +148,6 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) * a copy of it. */ if (config->reg_defaults) { - if (!map->num_reg_defaults) - return -EINVAL; tmp_buf = kmemdup(config->reg_defaults, map->num_reg_defaults * sizeof(struct reg_default), GFP_KERNEL); if (!tmp_buf) From fcac0233d89ddce72945f3e0afaf4680572d8525 Mon Sep 17 00:00:00 2001 From: Xiubo Li Date: Wed, 16 Dec 2015 17:45:32 +0800 Subject: [PATCH 16/18] regmap: use IS_ALIGNED instead of % to improve the performance The stride value should always equal to 2^n, so we can use bit rotation instead of % to improve the performance. Signed-off-by: Xiubo Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4ac63c0e50c7..a8f6dd9457be 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1513,7 +1513,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; map->lock(map->lock_arg); @@ -1540,7 +1540,7 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val) { int ret; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; map->lock(map->lock_arg); @@ -1714,7 +1714,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (map->bus && !map->format.parse_inplace) return -EINVAL; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; /* @@ -1983,7 +1983,7 @@ static int _regmap_multi_reg_write(struct regmap *map, int reg = regs[i].reg; if (!map->writeable_reg(map->dev, reg)) return -EINVAL; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; } @@ -2133,7 +2133,7 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg, if (val_len % map->format.val_bytes) return -EINVAL; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; map->lock(map->lock_arg); @@ -2260,7 +2260,7 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; map->lock(map->lock_arg); @@ -2296,7 +2296,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; if (val_count == 0) return -EINVAL; @@ -2414,7 +2414,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_bytes = map->format.val_bytes; bool vol = regmap_volatile_range(map, reg, val_count); - if (reg % map->reg_stride) + if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) { From 7a78479fd2acd25db7ecd1744d76f6841ec8a257 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 22 Dec 2015 18:25:26 +0530 Subject: [PATCH 17/18] regmap: irq: add support for configuration of trigger type Some of devices supports the trigger level for interrupt like rising/falling edge specially for GPIOs. The interrupt support of such devices may have uses the generic regmap irq framework for implementation. Add support to configure the trigger type device interrupt register via regmap-irq framework. The regmap-irq framework configures the trigger register only if the details of trigger type registers are provided. [Fixed use of terery operator for legibility -- broonie] Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 100 +++++++++++++++++++++++++++++++ include/linux/regmap.h | 16 +++++ 2 files changed, 116 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 8d16db533527..dbe7a0d15a26 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -39,8 +39,11 @@ struct regmap_irq_chip_data { unsigned int *mask_buf; unsigned int *mask_buf_def; unsigned int *wake_buf; + unsigned int *type_buf; + unsigned int *type_buf_def; unsigned int irq_reg_stride; + unsigned int type_reg_stride; }; static inline const @@ -144,6 +147,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data) } } + for (i = 0; i < d->chip->num_type_reg; i++) { + if (!d->type_buf_def[i]) + continue; + reg = d->chip->type_base + + (i * map->reg_stride * d->type_reg_stride); + if (d->chip->type_invert) + ret = regmap_update_bits(d->map, reg, + d->type_buf_def[i], ~d->type_buf[i]); + else + ret = regmap_update_bits(d->map, reg, + d->type_buf_def[i], d->type_buf[i]); + if (ret != 0) + dev_err(d->map->dev, "Failed to sync type in %x\n", + reg); + } + if (d->chip->runtime_pm) pm_runtime_put(map->dev); @@ -178,6 +197,38 @@ static void regmap_irq_disable(struct irq_data *data) d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } +static int regmap_irq_set_type(struct irq_data *data, unsigned int type) +{ + struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); + struct regmap *map = d->map; + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); + int reg = irq_data->type_reg_offset / map->reg_stride; + + if (!(irq_data->type_rising_mask | irq_data->type_falling_mask)) + return 0; + + d->type_buf[reg] &= ~(irq_data->type_falling_mask | + irq_data->type_rising_mask); + switch (type) { + case IRQ_TYPE_EDGE_FALLING: + d->type_buf[reg] |= irq_data->type_falling_mask; + break; + + case IRQ_TYPE_EDGE_RISING: + d->type_buf[reg] |= irq_data->type_rising_mask; + break; + + case IRQ_TYPE_EDGE_BOTH: + d->type_buf[reg] |= (irq_data->type_falling_mask | + irq_data->type_rising_mask); + break; + + default: + return -EINVAL; + } + return 0; +} + static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); @@ -204,6 +255,7 @@ static const struct irq_chip regmap_irq_chip = { .irq_bus_sync_unlock = regmap_irq_sync_unlock, .irq_disable = regmap_irq_disable, .irq_enable = regmap_irq_enable, + .irq_set_type = regmap_irq_set_type, .irq_set_wake = regmap_irq_set_wake, }; @@ -408,6 +460,18 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_alloc; } + if (chip->num_type_reg) { + d->type_buf_def = kcalloc(chip->num_type_reg, + sizeof(unsigned int), GFP_KERNEL); + if (!d->type_buf_def) + goto err_alloc; + + d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int), + GFP_KERNEL); + if (!d->type_buf) + goto err_alloc; + } + d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; d->irq = irq; @@ -420,6 +484,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, else d->irq_reg_stride = 1; + if (chip->type_reg_stride) + d->type_reg_stride = chip->type_reg_stride; + else + d->type_reg_stride = 1; + if (!map->use_single_read && map->reg_stride == 1 && d->irq_reg_stride == 1) { d->status_reg_buf = kmalloc(map->format.val_bytes * @@ -511,6 +580,33 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } } + if (chip->num_type_reg) { + for (i = 0; i < chip->num_irqs; i++) { + reg = chip->irqs[i].type_reg_offset / map->reg_stride; + d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | + chip->irqs[i].type_falling_mask; + } + for (i = 0; i < chip->num_type_reg; ++i) { + if (!d->type_buf_def[i]) + continue; + + reg = chip->type_base + + (i * map->reg_stride * d->type_reg_stride); + if (chip->type_invert) + ret = regmap_update_bits(map, reg, + d->type_buf_def[i], 0xFF); + else + ret = regmap_update_bits(map, reg, + d->type_buf_def[i], 0x0); + if (ret != 0) { + dev_err(map->dev, + "Failed to set type in 0x%x: %x\n", + reg, ret); + goto err_alloc; + } + } + } + if (irq_base) d->domain = irq_domain_add_legacy(map->dev->of_node, chip->num_irqs, irq_base, 0, @@ -541,6 +637,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, err_domain: /* Should really dispose of the domain but... */ err_alloc: + kfree(d->type_buf); + kfree(d->type_buf_def); kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); @@ -564,6 +662,8 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) free_irq(irq, d); irq_domain_remove(d->domain); + kfree(d->type_buf); + kfree(d->type_buf_def); kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index d68bb402120e..95b6636d3a59 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -788,10 +788,16 @@ int regmap_fields_update_bits(struct regmap_field *field, unsigned int id, * * @reg_offset: Offset of the status/mask register within the bank * @mask: Mask used to flag/control the register. + * @type_reg_offset: Offset register for the irq type setting. + * @type_rising_mask: Mask bit to configure RISING type irq. + * @type_falling_mask: Mask bit to configure FALLING type irq. */ struct regmap_irq { unsigned int reg_offset; unsigned int mask; + unsigned int type_reg_offset; + unsigned int type_rising_mask; + unsigned int type_falling_mask; }; #define REGMAP_IRQ_REG(_irq, _off, _mask) \ @@ -811,18 +817,23 @@ struct regmap_irq { * @ack_base: Base ack address. If zero then the chip is clear on read. * Using zero value is possible with @use_ack bit. * @wake_base: Base address for wake enables. If zero unsupported. + * @type_base: Base address for irq type. If zero unsupported. * @irq_reg_stride: Stride to use for chips where registers are not contiguous. * @init_ack_masked: Ack all masked interrupts once during initalization. * @mask_invert: Inverted mask register: cleared bits are masked out. * @use_ack: Use @ack register even if it is zero. * @ack_invert: Inverted ack register: cleared bits for ack. * @wake_invert: Inverted wake register: cleared bits are wake enabled. + * @type_invert: Invert the type flags. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. * @irqs: Descriptors for individual IRQs. Interrupt numbers are * assigned based on the index in the array of the interrupt. * @num_irqs: Number of descriptors. + * @num_type_reg: Number of type registers. + * @type_reg_stride: Stride to use for chips where type registers are not + * contiguous. */ struct regmap_irq_chip { const char *name; @@ -832,6 +843,7 @@ struct regmap_irq_chip { unsigned int unmask_base; unsigned int ack_base; unsigned int wake_base; + unsigned int type_base; unsigned int irq_reg_stride; bool init_ack_masked:1; bool mask_invert:1; @@ -839,11 +851,15 @@ struct regmap_irq_chip { bool ack_invert:1; bool wake_invert:1; bool runtime_pm:1; + bool type_invert:1; int num_regs; const struct regmap_irq *irqs; int num_irqs; + + int num_type_reg; + unsigned int type_reg_stride; }; struct regmap_irq_chip_data; From 8da61f24cc4a4a66ecae69aa9b9aa8fd989a8969 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 20 Oct 2015 15:40:59 +0100 Subject: [PATCH 18/18] regmap: debugfs: Use seq_file for the access map Unlike the registers file we don't have any substantial performance concerns rendering the entire file (it involves no device accesses) so just use seq_printf() to simplify the code. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 69 ++++++++-------------------- 1 file changed, 18 insertions(+), 51 deletions(-) diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 3f0a7e262d69..1ee3d40861c7 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -397,72 +397,39 @@ static const struct file_operations regmap_reg_ranges_fops = { .llseek = default_llseek, }; -static ssize_t regmap_access_read_file(struct file *file, - char __user *user_buf, size_t count, - loff_t *ppos) +static int regmap_access_show(struct seq_file *s, void *ignored) { - int reg_len, tot_len; - size_t buf_pos = 0; - loff_t p = 0; - ssize_t ret; - int i; - struct regmap *map = file->private_data; - char *buf; + struct regmap *map = s->private; + int i, reg_len; - if (*ppos < 0 || !count) - return -EINVAL; - - buf = kmalloc(count, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* Calculate the length of a fixed format */ reg_len = regmap_calc_reg_len(map->max_register); - tot_len = reg_len + 10; /* ': R W V P\n' */ for (i = 0; i <= map->max_register; i += map->reg_stride) { /* Ignore registers which are neither readable nor writable */ if (!regmap_readable(map, i) && !regmap_writeable(map, i)) continue; - /* If we're in the region the user is trying to read */ - if (p >= *ppos) { - /* ...but not beyond it */ - if (buf_pos + tot_len + 1 >= count) - break; - - /* Format the register */ - snprintf(buf + buf_pos, count - buf_pos, - "%.*x: %c %c %c %c\n", - reg_len, i, - regmap_readable(map, i) ? 'y' : 'n', - regmap_writeable(map, i) ? 'y' : 'n', - regmap_volatile(map, i) ? 'y' : 'n', - regmap_precious(map, i) ? 'y' : 'n'); - - buf_pos += tot_len; - } - p += tot_len; + /* Format the register */ + seq_printf(s, "%.*x: %c %c %c %c\n", reg_len, i, + regmap_readable(map, i) ? 'y' : 'n', + regmap_writeable(map, i) ? 'y' : 'n', + regmap_volatile(map, i) ? 'y' : 'n', + regmap_precious(map, i) ? 'y' : 'n'); } - ret = buf_pos; + return 0; +} - if (copy_to_user(user_buf, buf, buf_pos)) { - ret = -EFAULT; - goto out; - } - - *ppos += buf_pos; - -out: - kfree(buf); - return ret; +static int access_open(struct inode *inode, struct file *file) +{ + return single_open(file, regmap_access_show, inode->i_private); } static const struct file_operations regmap_access_fops = { - .open = simple_open, - .read = regmap_access_read_file, - .llseek = default_llseek, + .open = access_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, }; static ssize_t regmap_cache_only_write_file(struct file *file,