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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d2..ea9d6eb 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 + diff --git a/include/linux/regmap.h b/include/linux/regmap.h index e3bcc3f..afc8e1a 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -133,7 +133,7 @@ struct regmap_config { enum regmap_endian val_format_endian; const struct regmap_range_cfg *ranges; - unsigned int n_ranges; + unsigned int num_ranges; }; /** -- cgit v0.10.2 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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ea9d6eb..0544f63 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 v0.10.2 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 diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9..27e66c3 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 0544f63..ce5129d 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; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index afc8e1a..9f228d7 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -142,6 +142,8 @@ struct regmap_config { * 1. page selector register update; * 2. access through data window registers. * + * @name: Descriptive name for diagnostics + * * @range_min: Address of the lowest register address in virtual range. * @range_max: Address of the highest register in virtual range. * @@ -153,6 +155,8 @@ struct regmap_config { * @window_len: Number of registers in data window. */ struct regmap_range_cfg { + const char *name; + /* Registers of virtual address range */ unsigned int range_min; unsigned int range_max; -- cgit v0.10.2 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 diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index bb1ff17..25b6843 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 v0.10.2 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 diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 27e66c3..ac869d2 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 25b6843..f4b9dd0 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 ce5129d..366b629 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 v0.10.2 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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 366b629..4bb926c 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 v0.10.2 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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4bb926c..baf9586 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 v0.10.2 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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index baf9586..96253cd 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 v0.10.2 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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 96253cd..336bedb 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 v0.10.2 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 diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d2..810f509 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 v0.10.2