From 90f790d2dc96f5a61855ae65b90e30c40c893a20 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 Aug 2012 21:45:05 +0100 Subject: regmap: irq: Allow users to retrieve the irq_domain This is useful for integration with other subsystems, especially MFD, and provides an alternative API for users that request their own IRQs. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5b6b1d8e6cc..5972ad95854 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -458,3 +458,22 @@ int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) return irq_create_mapping(data->domain, irq); } EXPORT_SYMBOL_GPL(regmap_irq_get_virq); + +/** + * regmap_irq_get_domain(): Retrieve the irq_domain for the chip + * + * Useful for drivers to request their own IRQs and for integration + * with subsystems. For ease of integration NULL is accepted as a + * domain, allowing devices to just call this even if no domain is + * allocated. + * + * @data: regmap_irq controller to operate on. + */ +struct irq_domain *regmap_irq_get_domain(struct regmap_irq_chip_data *data) +{ + if (data) + return data->domain; + else + return NULL; +} +EXPORT_SYMBOL_GPL(regmap_irq_get_domain); -- cgit v1.2.3-18-g5258 From e3549cd01347ef211d01353bdf2572b086574007 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 2 Oct 2012 20:17:15 +0100 Subject: regmap: Rename n_ranges to num_ranges This makes things consistent with the rest of the API and is actually what the documentation says. We don't currently have any in tree users so low cost. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d29ff1..ea9d6eb826b 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -519,7 +519,7 @@ struct regmap *regmap_init(struct device *dev, } map->range_tree = RB_ROOT; - for (i = 0; i < config->n_ranges; i++) { + for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; struct regmap_range_node *new; @@ -532,7 +532,7 @@ struct regmap *regmap_init(struct device *dev, /* Make sure, that this register range has no selector or data window within its boundary */ - for (j = 0; j < config->n_ranges; j++) { + for (j = 0; j < config->num_ranges; j++) { unsigned sel_reg = config->ranges[j].selector_reg; unsigned win_min = config->ranges[j].window_start; unsigned win_max = win_min + -- cgit v1.2.3-18-g5258 From 061adc064adbbdd9eb127ab2e86b7a71f4ccaf2e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 12:17:51 +0100 Subject: regmap: When we sanity check during range adds say what errors we find Rather than just returning a single error code for every possible thing we can notice print an error message saying what the problem was. This makes it very much easier to figure out what's wrong and fix it. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ea9d6eb826b..0544f63ecd3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -524,11 +524,29 @@ struct regmap *regmap_init(struct device *dev, struct regmap_range_node *new; /* Sanity check */ - if (range_cfg->range_max < range_cfg->range_min || - range_cfg->range_max > map->max_register || - range_cfg->selector_reg > map->max_register || - range_cfg->window_len == 0) + if (range_cfg->range_max < range_cfg->range_min) { + dev_err(map->dev, "Invalid range %d: %d < %d\n", i, + range_cfg->range_max, range_cfg->range_min); goto err_range; + } + + if (range_cfg->range_max > map->max_register) { + dev_err(map->dev, "Invalid range %d: %d > %d\n", i, + range_cfg->range_max, map->max_register); + goto err_range; + } + + if (range_cfg->selector_reg > map->max_register) { + dev_err(map->dev, + "Invalid range %d: selector out of map\n", i); + goto err_range; + } + + if (range_cfg->window_len == 0) { + dev_err(map->dev, "Invalid range %d: window_len 0\n", + i); + goto err_range; + } /* Make sure, that this register range has no selector or data window within its boundary */ @@ -540,11 +558,17 @@ struct regmap *regmap_init(struct device *dev, if (range_cfg->range_min <= sel_reg && sel_reg <= range_cfg->range_max) { + dev_err(map->dev, + "Range %d: selector for %d in window\n", + i, j); goto err_range; } if (!(win_max < range_cfg->range_min || win_min > range_cfg->range_max)) { + dev_err(map->dev, + "Range %d: window for %d in window\n", + i, j); goto err_range; } } @@ -564,6 +588,7 @@ struct regmap *regmap_init(struct device *dev, new->window_len = range_cfg->window_len; if (_regmap_range_add(map, new) == false) { + dev_err(map->dev, "Failed to add range %d\n", i); kfree(new); goto err_range; } -- cgit v1.2.3-18-g5258 From d058bb49618482f2eff0db57618c9a7352916dd5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 12:40:47 +0100 Subject: regmap: Allow ranges to be named For more useful diagnostics. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9c3aa..27e66c3e7a5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -120,6 +120,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, struct regmap_range_node { struct rb_node node; + const char *name; unsigned int range_min; unsigned int range_max; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0544f63ecd3..ce5129df440 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -579,6 +579,7 @@ struct regmap *regmap_init(struct device *dev, goto err_range; } + new->name = range_cfg->name; new->range_min = range_cfg->range_min; new->range_max = range_cfg->range_max; new->selector_reg = range_cfg->selector_reg; -- cgit v1.2.3-18-g5258 From bd9cc12f4a7e7389432bba0cae6970dfc28f423c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 12:45:37 +0100 Subject: regmap: Factor out debugfs register read This will allow the use of the same code for reading register ranges. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index bb1ff175b96..25b6843d6a4 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,15 +56,15 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; -static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, + unsigned int to, char __user *user_buf, + size_t count, loff_t *ppos) { int reg_len, val_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; unsigned int val; @@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, val_len = 2 * map->format.val_bytes; tot_len = reg_len + val_len + 3; /* : \n */ - for (i = 0; i <= map->max_register; i += map->reg_stride) { + for (i = from; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; @@ -95,7 +95,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, /* Format the register */ snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", - reg_len, i); + reg_len, i - from); buf_pos += reg_len + 2; /* Format the value, write all X if we can't read */ @@ -126,6 +126,15 @@ out: return ret; } +static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap *map = file->private_data; + + return regmap_read_debugfs(map, 0, map->max_register, user_buf, + count, ppos); +} + #undef REGMAP_ALLOW_WRITE_DEBUGFS #ifdef REGMAP_ALLOW_WRITE_DEBUGFS /* -- cgit v1.2.3-18-g5258 From 4b020b3f9ba2af8031c5c7d759fbafd234d1c390 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 3 Oct 2012 13:13:16 +0100 Subject: regmap: Provide debugfs read of register ranges If a register range is named then provide a debugfs file showing the contents of the range separately. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-debugfs.c | 31 +++++++++++++++++++++++++++++++ drivers/base/regmap/regmap.c | 1 + 3 files changed, 33 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 27e66c3e7a5..ac869d28d5b 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -121,6 +121,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, struct regmap_range_node { struct rb_node node; const char *name; + struct regmap *map; unsigned int range_min; unsigned int range_max; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 25b6843d6a4..f4b9dd01c98 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -183,6 +183,22 @@ static const struct file_operations regmap_map_fops = { .llseek = default_llseek, }; +static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct regmap_range_node *range = file->private_data; + struct regmap *map = range->map; + + return regmap_read_debugfs(map, range->range_min, range->range_max, + user_buf, count, ppos); +} + +static const struct file_operations regmap_range_fops = { + .open = simple_open, + .read = regmap_range_read_file, + .llseek = default_llseek, +}; + static ssize_t regmap_access_read_file(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -253,6 +269,9 @@ static const struct file_operations regmap_access_fops = { void regmap_debugfs_init(struct regmap *map, const char *name) { + struct rb_node *next; + struct regmap_range_node *range_node; + if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", dev_name(map->dev), name); @@ -285,6 +304,18 @@ void regmap_debugfs_init(struct regmap *map, const char *name) debugfs_create_bool("cache_bypass", 0400, map->debugfs, &map->cache_bypass); } + + next = rb_first(&map->range_tree); + while (next) { + range_node = rb_entry(next, struct regmap_range_node, node); + + if (range_node->name) + debugfs_create_file(range_node->name, 0400, + map->debugfs, range_node, + ®map_range_fops); + + next = rb_next(&range_node->node); + } } void regmap_debugfs_exit(struct regmap *map) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ce5129df440..366b629f4b1 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -579,6 +579,7 @@ struct regmap *regmap_init(struct device *dev, goto err_range; } + new->map = map; new->name = range_cfg->name; new->range_min = range_cfg->range_min; new->range_max = range_cfg->range_max; -- cgit v1.2.3-18-g5258 From 98bc7dfd76407eaa0964ecb4d5319c957a3b9df9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Oct 2012 17:31:11 +0100 Subject: regmap: Factor range lookup out of page selection This will support a subsequent update to allow bulk writes to cross window boundaries. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 91 +++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 40 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 366b629f4b1..4bb926cd7bf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -765,59 +765,57 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name) EXPORT_SYMBOL_GPL(dev_get_regmap); static int _regmap_select_page(struct regmap *map, unsigned int *reg, + struct regmap_range_node *range, unsigned int val_num) { - struct regmap_range_node *range; void *orig_work_buf; unsigned int win_offset; unsigned int win_page; bool page_chg; int ret; - range = _regmap_range_lookup(map, *reg); - if (range) { - win_offset = (*reg - range->range_min) % range->window_len; - win_page = (*reg - range->range_min) / range->window_len; + win_offset = (*reg - range->range_min) % range->window_len; + win_page = (*reg - range->range_min) / range->window_len; - if (val_num > 1) { - /* Bulk write shouldn't cross range boundary */ - if (*reg + val_num - 1 > range->range_max) - return -EINVAL; + if (val_num > 1) { + /* Bulk write shouldn't cross range boundary */ + if (*reg + val_num - 1 > range->range_max) + return -EINVAL; - /* ... or single page boundary */ - if (val_num > range->window_len - win_offset) - return -EINVAL; - } + /* ... or single page boundary */ + if (val_num > range->window_len - win_offset) + return -EINVAL; + } - /* It is possible to have selector register inside data window. - In that case, selector register is located on every page and - it needs no page switching, when accessed alone. */ - if (val_num > 1 || - range->window_start + win_offset != range->selector_reg) { - /* Use separate work_buf during page switching */ - orig_work_buf = map->work_buf; - map->work_buf = map->selector_work_buf; + /* It is possible to have selector register inside data window. + In that case, selector register is located on every page and + it needs no page switching, when accessed alone. */ + if (val_num > 1 || + range->window_start + win_offset != range->selector_reg) { + /* Use separate work_buf during page switching */ + orig_work_buf = map->work_buf; + map->work_buf = map->selector_work_buf; - ret = _regmap_update_bits(map, range->selector_reg, - range->selector_mask, - win_page << range->selector_shift, - &page_chg); + ret = _regmap_update_bits(map, range->selector_reg, + range->selector_mask, + win_page << range->selector_shift, + &page_chg); - map->work_buf = orig_work_buf; + map->work_buf = orig_work_buf; - if (ret < 0) - return ret; - } - - *reg = range->window_start + win_offset; + if (ret < 0) + return ret; } + *reg = range->window_start + win_offset; + return 0; } static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { + struct regmap_range_node *range; u8 *u8 = map->work_buf; void *buf; int ret = -ENOTSUPP; @@ -852,9 +850,13 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, } } - ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); - if (ret < 0) - return ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, + val_len / map->format.val_bytes); + if (ret < 0) + return ret; + } map->format.format_reg(map->work_buf, reg, map->reg_shift); @@ -903,6 +905,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { + struct regmap_range_node *range; int ret; BUG_ON(!map->format.format_write && !map->format.format_val); @@ -924,9 +927,12 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); if (map->format.format_write) { - ret = _regmap_select_page(map, ®, 1); - if (ret < 0) - return ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret < 0) + return ret; + } map->format.format_write(map, reg, val); @@ -1082,12 +1088,17 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { + struct regmap_range_node *range; u8 *u8 = map->work_buf; int ret; - ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); - if (ret < 0) - return ret; + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, + val_len / map->format.val_bytes); + if (ret < 0) + return ret; + } map->format.format_reg(map->work_buf, reg, map->reg_shift); -- cgit v1.2.3-18-g5258 From 0ff3e62ff119f2b65b0a8ad48fcb669f609fd904 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Oct 2012 17:39:13 +0100 Subject: regmap: Make return code checks consistent The range code was written to check for return codes less than zero as errors but throughout the rest of the API return codes not equal to zero are errors. Change all these checks to match the house style. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4bb926cd7bf..baf9586b4fd 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -606,7 +606,7 @@ struct regmap *regmap_init(struct device *dev, } ret = regcache_init(map, config); - if (ret < 0) + if (ret != 0) goto err_range; regmap_debugfs_init(map, config->name); @@ -803,7 +803,7 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, map->work_buf = orig_work_buf; - if (ret < 0) + if (ret != 0) return ret; } @@ -854,7 +854,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, if (range) { ret = _regmap_select_page(map, ®, range, val_len / map->format.val_bytes); - if (ret < 0) + if (ret != 0) return ret; } @@ -930,7 +930,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, 1); - if (ret < 0) + if (ret != 0) return ret; } @@ -1096,7 +1096,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (range) { ret = _regmap_select_page(map, ®, range, val_len / map->format.val_bytes); - if (ret < 0) + if (ret != 0) return ret; } -- cgit v1.2.3-18-g5258 From 8a2ceac6617a67d8a1ee4bd255743d577bde311a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 4 Oct 2012 18:20:18 +0100 Subject: regmap: Split raw writes that cross window boundaries If a block write covers a paged memory region and crosses a window boundary then rather than failing the write split the transfer up into multiple writes, making the whole process more transparent for drivers. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index baf9586b4fd..96253cd949e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -852,8 +852,30 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, range = _regmap_range_lookup(map, reg); if (range) { - ret = _regmap_select_page(map, ®, range, - val_len / map->format.val_bytes); + int val_num = val_len / map->format.val_bytes; + int win_offset = (reg - range->range_min) % range->window_len; + int win_residue = range->window_len - win_offset; + + /* If the write goes beyond the end of the window split it */ + while (val_num > win_residue) { + dev_dbg(map->dev, "Writing window %d/%d\n", + win_residue, val_len / map->format.val_bytes); + ret = _regmap_raw_write(map, reg, val, win_residue * + map->format.val_bytes); + if (ret != 0) + return ret; + + reg += win_residue; + val_num -= win_residue; + val += win_residue * map->format.val_bytes; + val_len -= win_residue * map->format.val_bytes; + + win_offset = (reg - range->range_min) % + range->window_len; + win_residue = range->window_len - win_offset; + } + + ret = _regmap_select_page(map, ®, range, val_num); if (ret != 0) return ret; } -- cgit v1.2.3-18-g5258 From a8f28cfad8cd44d7c34b166d0e5ace1125dbee1f Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 8 Oct 2012 22:06:30 +0200 Subject: regmap: silence GCC warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building regmap.o triggers this GCC warning: drivers/base/regmap/regmap.c: In function ‘regmap_raw_read’: drivers/base/regmap/regmap.c:1172:6: warning: ‘ret’ may be used uninitialized in this function [-Wmaybe-uninitialized] Long story short: Jakub Jelinek pointed out that there is a type mismatch between 'num' in regmap_volatile_range() and 'val_count' in regmap_raw_read(). And indeed, converting 'num' to the type of 'val_count' (ie, size_t) makes this warning go away. Signed-off-by: Paul Bolle Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d29ff1..94555951b6e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -82,7 +82,7 @@ bool regmap_precious(struct regmap *map, unsigned int reg) } static bool regmap_volatile_range(struct regmap *map, unsigned int reg, - unsigned int num) + size_t num) { unsigned int i; -- cgit v1.2.3-18-g5258 From 0d4529c534c1c664f25088eb5f5b4d7ce0ee2510 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Tue, 16 Oct 2012 15:56:59 +0200 Subject: regmap: make lock/unlock functions customizable It is sometimes convenient for a regmap user to override the standard regmap lock/unlock functions with custom functions. For instance this can be useful in case an already existing spinlock or mutex has to be used for locking a set of registers instead of the internal regmap spinlock/mutex. Note that the fast_io field of struct regmap_bus is ignored in case custom locking functions are used. Signed-off-by: Davide Ciminaghi Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 +-- drivers/base/regmap/regmap.c | 65 ++++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 30 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9c3aa..b1ee824e2a5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -31,14 +31,12 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; -typedef void (*regmap_lock)(struct regmap *map); -typedef void (*regmap_unlock)(struct regmap *map); - struct regmap { struct mutex mutex; spinlock_t spinlock; regmap_lock lock; regmap_unlock unlock; + void *lock_arg; /* This is passed to lock/unlock functions */ struct device *dev; /* Device we do I/O on */ void *work_buf; /* Scratch buffer used to format I/O */ diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d29ff1..5d8b75255b5 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -214,23 +214,27 @@ static unsigned int regmap_parse_32_native(void *buf) return *(u32 *)buf; } -static void regmap_lock_mutex(struct regmap *map) +static void regmap_lock_mutex(void *__map) { + struct regmap *map = __map; mutex_lock(&map->mutex); } -static void regmap_unlock_mutex(struct regmap *map) +static void regmap_unlock_mutex(void *__map) { + struct regmap *map = __map; mutex_unlock(&map->mutex); } -static void regmap_lock_spinlock(struct regmap *map) +static void regmap_lock_spinlock(void *__map) { + struct regmap *map = __map; spin_lock(&map->spinlock); } -static void regmap_unlock_spinlock(struct regmap *map) +static void regmap_unlock_spinlock(void *__map) { + struct regmap *map = __map; spin_unlock(&map->spinlock); } @@ -335,14 +339,21 @@ struct regmap *regmap_init(struct device *dev, goto err; } - if (bus->fast_io) { - spin_lock_init(&map->spinlock); - map->lock = regmap_lock_spinlock; - map->unlock = regmap_unlock_spinlock; + if (config->lock && config->unlock) { + map->lock = config->lock; + map->unlock = config->unlock; + map->lock_arg = config->lock_arg; } else { - mutex_init(&map->mutex); - map->lock = regmap_lock_mutex; - map->unlock = regmap_unlock_mutex; + if (bus->fast_io) { + spin_lock_init(&map->spinlock); + map->lock = regmap_lock_spinlock; + map->unlock = regmap_unlock_spinlock; + } else { + mutex_init(&map->mutex); + map->lock = regmap_lock_mutex; + map->unlock = regmap_unlock_mutex; + } + map->lock_arg = map; } map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->format.pad_bytes = config->pad_bits / 8; @@ -939,11 +950,11 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_write(map, reg, val); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -975,11 +986,11 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_raw_write(map, reg, val, val_len); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1011,7 +1022,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); /* No formatting is require if val_byte is 1 */ if (val_bytes == 1) { @@ -1047,7 +1058,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, kfree(wval); out: - map->unlock(map); + map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_write); @@ -1137,11 +1148,11 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_read(map, reg, val); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1171,7 +1182,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (reg % map->reg_stride) return -EINVAL; - map->lock(map); + map->lock(map->lock_arg); if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { @@ -1193,7 +1204,7 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } out: - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1300,9 +1311,9 @@ int regmap_update_bits(struct regmap *map, unsigned int reg, bool change; int ret; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_update_bits(map, reg, mask, val, &change); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } @@ -1326,9 +1337,9 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, { int ret; - map->lock(map); + map->lock(map->lock_arg); ret = _regmap_update_bits(map, reg, mask, val, change); - map->unlock(map); + map->unlock(map->lock_arg); return ret; } EXPORT_SYMBOL_GPL(regmap_update_bits_check); @@ -1357,7 +1368,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, if (map->patch) return -EBUSY; - map->lock(map); + map->lock(map->lock_arg); bypass = map->cache_bypass; @@ -1385,7 +1396,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs, out: map->cache_bypass = bypass; - map->unlock(map); + map->unlock(map->lock_arg); return ret; } -- cgit v1.2.3-18-g5258 From 1a61cfe3445218637f38b355c76fc3132865a0a6 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 25 Oct 2012 14:07:18 -0200 Subject: regmap: Fix printing of size_t variable val_bytes is of 'size_t', so it should be printed as '%zu'. Fixes the following build warning on x86: drivers/base/regmap/regmap.c:872:4: warning: format '%d' expects argument of type 'int', but argument 5 has type 'size_t' [-Wformat] Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 96253cd949e..336bedb04ba 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -858,7 +858,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, /* If the write goes beyond the end of the window split it */ while (val_num > win_residue) { - dev_dbg(map->dev, "Writing window %d/%d\n", + dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * map->format.val_bytes); -- cgit v1.2.3-18-g5258 From 6d04b8ac575c38d94515b4e8f3b800c5c61ef611 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 26 Oct 2012 19:05:32 +0100 Subject: regmap: core: Report registers in hex when we can't cache This seems to be the most common way of reporting register numbers, it's certainly what we do for trace. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d29ff1..810f5094a9a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -814,7 +814,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, ival); if (ret) { dev_err(map->dev, - "Error in caching of register: %u ret: %d\n", + "Error in caching of register: %x ret: %d\n", reg + i, ret); return ret; } -- cgit v1.2.3-18-g5258 From 76aad392f75e6ce5be3f106554e16f7ff96543e5 Mon Sep 17 00:00:00 2001 From: Davide Ciminaghi Date: Tue, 20 Nov 2012 15:20:30 +0100 Subject: regmap: introduce tables for readable/writeable/volatile/precious checks Many of the regmap enabled drivers implementing one or more of the readable, writeable, volatile and precious methods use the same code pattern: return ((reg >= X && reg <= Y) || (reg >= W && reg <= Z) || ...) Switch to a data driven approach, using tables to describe readable/writeable/volatile and precious registers ranges instead. The table based check can still be overridden by passing the usual function pointers via struct regmap_config. Signed-off-by: Davide Ciminaghi Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++++ drivers/base/regmap/regmap.c | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 2cd01b57b1c..288e135fd5f 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -55,6 +55,10 @@ struct regmap { bool (*readable_reg)(struct device *dev, unsigned int reg); bool (*volatile_reg)(struct device *dev, unsigned int reg); bool (*precious_reg)(struct device *dev, unsigned int reg); + const struct regmap_access_table *wr_table; + const struct regmap_access_table *rd_table; + const struct regmap_access_table *volatile_table; + const struct regmap_access_table *precious_table; u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 64eb8350a07..96f7e8523ee 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -34,6 +34,36 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +bool regmap_reg_in_ranges(unsigned int reg, + const struct regmap_range *ranges, + unsigned int nranges) +{ + const struct regmap_range *r; + int i; + + for (i = 0, r = ranges; i < nranges; i++, r++) + if (regmap_reg_in_range(reg, r)) + return true; + return false; +} +EXPORT_SYMBOL_GPL(regmap_reg_in_ranges); + +static bool _regmap_check_range_table(struct regmap *map, + unsigned int reg, + const struct regmap_access_table *table) +{ + /* Check "no ranges" first */ + if (regmap_reg_in_ranges(reg, table->no_ranges, table->n_no_ranges)) + return false; + + /* In case zero "yes ranges" are supplied, any reg is OK */ + if (!table->n_yes_ranges) + return true; + + return regmap_reg_in_ranges(reg, table->yes_ranges, + table->n_yes_ranges); +} + bool regmap_writeable(struct regmap *map, unsigned int reg) { if (map->max_register && reg > map->max_register) @@ -42,6 +72,9 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) if (map->writeable_reg) return map->writeable_reg(map->dev, reg); + if (map->wr_table) + return _regmap_check_range_table(map, reg, map->wr_table); + return true; } @@ -56,6 +89,9 @@ bool regmap_readable(struct regmap *map, unsigned int reg) if (map->readable_reg) return map->readable_reg(map->dev, reg); + if (map->rd_table) + return _regmap_check_range_table(map, reg, map->rd_table); + return true; } @@ -67,6 +103,9 @@ bool regmap_volatile(struct regmap *map, unsigned int reg) if (map->volatile_reg) return map->volatile_reg(map->dev, reg); + if (map->volatile_table) + return _regmap_check_range_table(map, reg, map->volatile_table); + return true; } @@ -78,6 +117,9 @@ bool regmap_precious(struct regmap *map, unsigned int reg) if (map->precious_reg) return map->precious_reg(map->dev, reg); + if (map->precious_table) + return _regmap_check_range_table(map, reg, map->precious_table); + return false; } @@ -370,6 +412,10 @@ struct regmap *regmap_init(struct device *dev, map->bus = bus; map->bus_context = bus_context; map->max_register = config->max_register; + map->wr_table = config->wr_table; + map->rd_table = config->rd_table; + map->volatile_table = config->volatile_table; + map->precious_table = config->precious_table; map->writeable_reg = config->writeable_reg; map->readable_reg = config->readable_reg; map->volatile_reg = config->volatile_reg; -- cgit v1.2.3-18-g5258 From cbc1938badc31f43ab77e92a9b1a51c4fe8b4113 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 6 Dec 2012 13:29:05 +0900 Subject: regmap: Cache register and value sizes for debugfs No point in calculating them every time. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 4 ++++ drivers/base/regmap/regmap-debugfs.c | 24 ++++++++++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index ac869d28d5b..1abcd27e2d0 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -50,6 +50,10 @@ struct regmap { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; + + unsigned int debugfs_reg_len; + unsigned int debugfs_val_len; + unsigned int debugfs_tot_len; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index f4b9dd01c98..00fbd58706b 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -60,7 +60,6 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) { - int reg_len, val_len, tot_len; size_t buf_pos = 0; loff_t p = 0; ssize_t ret; @@ -76,9 +75,13 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, return -ENOMEM; /* Calculate the length of a fixed format */ - reg_len = regmap_calc_reg_len(map->max_register, buf, count); - val_len = 2 * map->format.val_bytes; - tot_len = reg_len + val_len + 3; /* : \n */ + if (!map->debugfs_tot_len) { + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, + buf, count); + map->debugfs_val_len = 2 * map->format.val_bytes; + map->debugfs_tot_len = map->debugfs_reg_len + + map->debugfs_val_len + 3; /* : \n */ + } for (i = from; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) @@ -90,26 +93,27 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos >= count - 1 - tot_len) + if (buf_pos >= count - 1 - map->debugfs_tot_len) break; /* Format the register */ snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", - reg_len, i - from); - buf_pos += reg_len + 2; + map->debugfs_reg_len, i - from); + buf_pos += map->debugfs_reg_len + 2; /* Format the value, write all X if we can't read */ ret = regmap_read(map, i, &val); if (ret == 0) snprintf(buf + buf_pos, count - buf_pos, - "%.*x", val_len, val); + "%.*x", map->debugfs_val_len, val); else - memset(buf + buf_pos, 'X', val_len); + memset(buf + buf_pos, 'X', + map->debugfs_val_len); buf_pos += 2 * map->format.val_bytes; buf[buf_pos++] = '\n'; } - p += tot_len; + p += map->debugfs_tot_len; } ret = buf_pos; -- cgit v1.2.3-18-g5258 From db04328c167ff8e7c57f4a3532214aeada3a82fd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Dec 2012 01:14:11 +0900 Subject: regmap: debugfs: Avoid overflows for very small reads If count is less than the size of a register then we may hit integer wraparound when trying to move backwards to check if we're still in the buffer. Instead move the position forwards to check if it's still in the buffer, we are unlikely to be able to allocate a buffer sufficiently big to overflow here. Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/base/regmap/regmap-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 00fbd58706b..3df274e0606 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -93,7 +93,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos >= count - 1 - map->debugfs_tot_len) + if (buf_pos + 1 + map->debugfs_tot_len >= count) break; /* Format the register */ -- cgit v1.2.3-18-g5258 From afab2f7b21b042bcbffb1e82f78243382a122d70 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 9 Dec 2012 17:20:10 +0900 Subject: regmap: debugfs: Factor out initial seek In preparation for doing things a bit more quickly than a linear scan factor out the initial seek from the debugfs register dump. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 39 +++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 3df274e0606..749a1dc5bbf 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -56,16 +56,46 @@ static const struct file_operations regmap_name_fops = { .llseek = default_llseek, }; +/* + * Work out where the start offset maps into register numbers, bearing + * in mind that we suppress hidden registers. + */ +static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, + unsigned int base, + loff_t from, + loff_t *pos) +{ + loff_t p = *pos; + unsigned int i; + + for (i = base; i <= map->max_register; i += map->reg_stride) { + if (!regmap_readable(map, i)) + continue; + + if (regmap_precious(map, i)) + continue; + + if (i >= from) { + *pos = p; + return i; + } + + p += map->debugfs_tot_len; + } + + return base; +} + static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) { size_t buf_pos = 0; - loff_t p = 0; + loff_t p = *ppos; ssize_t ret; int i; char *buf; - unsigned int val; + unsigned int val, start_reg; if (*ppos < 0 || !count) return -EINVAL; @@ -83,7 +113,10 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, map->debugfs_val_len + 3; /* : \n */ } - for (i = from; i <= to; i += map->reg_stride) { + /* Work out which register we're starting at */ + start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); + + for (i = start_reg; i <= to; i += map->reg_stride) { if (!regmap_readable(map, i)) continue; -- cgit v1.2.3-18-g5258 From 5166b7c006eeb4f6becc0822974d8da259484ba1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Dec 2012 01:24:29 +0900 Subject: regmap: debugfs: Cache offsets of valid regions for dump Avoid doing a linear scan of the entire register map for each read() of the debugfs register dump by recording the offsets where valid registers exist when we first read the registers file. This assumes the set of valid registers never changes, if this is not the case invalidation of the cache will be required. This could be further improved for large blocks of contiguous registers by calculating the register we will read from within the block - currently we do a linear scan of the block. An rbtree may also be worthwhile. Signed-off-by: Mark Brown --- drivers/base/regmap/internal.h | 10 ++++++ drivers/base/regmap/regmap-debugfs.c | 67 ++++++++++++++++++++++++++++-------- 2 files changed, 63 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 1abcd27e2d0..9c3b0e7a6c7 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -15,10 +15,18 @@ #include #include +#include struct regmap; struct regcache_ops; +struct regmap_debugfs_off_cache { + struct list_head list; + off_t min; + off_t max; + unsigned int base_reg; +}; + struct regmap_format { size_t buf_size; size_t reg_bytes; @@ -54,6 +62,8 @@ struct regmap { unsigned int debugfs_reg_len; unsigned int debugfs_val_len; unsigned int debugfs_tot_len; + + struct list_head debugfs_off_cache; #endif unsigned int max_register; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 749a1dc5bbf..07aad786f81 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -65,25 +65,53 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, loff_t from, loff_t *pos) { - loff_t p = *pos; - unsigned int i; - - for (i = base; i <= map->max_register; i += map->reg_stride) { - if (!regmap_readable(map, i)) - continue; - - if (regmap_precious(map, i)) - continue; + struct regmap_debugfs_off_cache *c = NULL; + loff_t p = 0; + unsigned int i, ret; + + /* + * If we don't have a cache build one so we don't have to do a + * linear scan each time. + */ + if (list_empty(&map->debugfs_off_cache)) { + for (i = base; i <= map->max_register; i += map->reg_stride) { + /* Skip unprinted registers, closing off cache entry */ + if (!regmap_readable(map, i) || + regmap_precious(map, i)) { + if (c) { + c->max = p - 1; + list_add_tail(&c->list, + &map->debugfs_off_cache); + c = NULL; + } + + continue; + } + + /* No cache entry? Start a new one */ + if (!c) { + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + break; + c->min = p; + c->base_reg = i; + } + + p += map->debugfs_tot_len; + } + } - if (i >= from) { - *pos = p; - return i; + /* Find the relevant block */ + list_for_each_entry(c, &map->debugfs_off_cache, list) { + if (*pos >= c->min && *pos <= c->max) { + *pos = c->min; + return c->base_reg; } - p += map->debugfs_tot_len; + ret = c->max; } - return base; + return ret; } static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, @@ -309,6 +337,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) struct rb_node *next; struct regmap_range_node *range_node; + INIT_LIST_HEAD(&map->debugfs_off_cache); + if (name) { map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", dev_name(map->dev), name); @@ -357,7 +387,16 @@ void regmap_debugfs_init(struct regmap *map, const char *name) void regmap_debugfs_exit(struct regmap *map) { + struct regmap_debugfs_off_cache *c; + debugfs_remove_recursive(map->debugfs); + while (!list_empty(&map->debugfs_off_cache)) { + c = list_first_entry(&map->debugfs_off_cache, + struct regmap_debugfs_off_cache, + list); + list_del(&c->list); + kfree(c); + } kfree(map->debugfs_name); } -- cgit v1.2.3-18-g5258