diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-12-10 22:39:20 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-12-10 22:39:20 -0500 |
commit | bcf86687d676fa478c71201294b296126212f06c (patch) | |
tree | fe44b787a21b4d474c5417ce378fe3eec91da837 /drivers/base/regmap | |
parent | d3816c1a0e9ea8e1a04fdf7601837e8c3a0e190e (diff) | |
parent | 5166b7c006eeb4f6becc0822974d8da259484ba1 (diff) |
Merge remote-tracking branch 'regmap/topic/debugfs' into regmap-next
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r-- | drivers/base/regmap/internal.h | 16 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 148 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 154 |
3 files changed, 255 insertions, 63 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9c3aa4..9c3b0e7a6c7d 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h | |||
@@ -15,10 +15,18 @@ | |||
15 | 15 | ||
16 | #include <linux/regmap.h> | 16 | #include <linux/regmap.h> |
17 | #include <linux/fs.h> | 17 | #include <linux/fs.h> |
18 | #include <linux/list.h> | ||
18 | 19 | ||
19 | struct regmap; | 20 | struct regmap; |
20 | struct regcache_ops; | 21 | struct regcache_ops; |
21 | 22 | ||
23 | struct regmap_debugfs_off_cache { | ||
24 | struct list_head list; | ||
25 | off_t min; | ||
26 | off_t max; | ||
27 | unsigned int base_reg; | ||
28 | }; | ||
29 | |||
22 | struct regmap_format { | 30 | struct regmap_format { |
23 | size_t buf_size; | 31 | size_t buf_size; |
24 | size_t reg_bytes; | 32 | size_t reg_bytes; |
@@ -50,6 +58,12 @@ struct regmap { | |||
50 | #ifdef CONFIG_DEBUG_FS | 58 | #ifdef CONFIG_DEBUG_FS |
51 | struct dentry *debugfs; | 59 | struct dentry *debugfs; |
52 | const char *debugfs_name; | 60 | const char *debugfs_name; |
61 | |||
62 | unsigned int debugfs_reg_len; | ||
63 | unsigned int debugfs_val_len; | ||
64 | unsigned int debugfs_tot_len; | ||
65 | |||
66 | struct list_head debugfs_off_cache; | ||
53 | #endif | 67 | #endif |
54 | 68 | ||
55 | unsigned int max_register; | 69 | unsigned int max_register; |
@@ -120,6 +134,8 @@ int _regmap_write(struct regmap *map, unsigned int reg, | |||
120 | 134 | ||
121 | struct regmap_range_node { | 135 | struct regmap_range_node { |
122 | struct rb_node node; | 136 | struct rb_node node; |
137 | const char *name; | ||
138 | struct regmap *map; | ||
123 | 139 | ||
124 | unsigned int range_min; | 140 | unsigned int range_min; |
125 | unsigned int range_max; | 141 | unsigned int range_max; |
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index bb1ff175b962..07aad786f817 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c | |||
@@ -56,17 +56,74 @@ static const struct file_operations regmap_name_fops = { | |||
56 | .llseek = default_llseek, | 56 | .llseek = default_llseek, |
57 | }; | 57 | }; |
58 | 58 | ||
59 | static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | 59 | /* |
60 | size_t count, loff_t *ppos) | 60 | * Work out where the start offset maps into register numbers, bearing |
61 | * in mind that we suppress hidden registers. | ||
62 | */ | ||
63 | static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, | ||
64 | unsigned int base, | ||
65 | loff_t from, | ||
66 | loff_t *pos) | ||
61 | { | 67 | { |
62 | int reg_len, val_len, tot_len; | 68 | struct regmap_debugfs_off_cache *c = NULL; |
63 | size_t buf_pos = 0; | ||
64 | loff_t p = 0; | 69 | loff_t p = 0; |
70 | unsigned int i, ret; | ||
71 | |||
72 | /* | ||
73 | * If we don't have a cache build one so we don't have to do a | ||
74 | * linear scan each time. | ||
75 | */ | ||
76 | if (list_empty(&map->debugfs_off_cache)) { | ||
77 | for (i = base; i <= map->max_register; i += map->reg_stride) { | ||
78 | /* Skip unprinted registers, closing off cache entry */ | ||
79 | if (!regmap_readable(map, i) || | ||
80 | regmap_precious(map, i)) { | ||
81 | if (c) { | ||
82 | c->max = p - 1; | ||
83 | list_add_tail(&c->list, | ||
84 | &map->debugfs_off_cache); | ||
85 | c = NULL; | ||
86 | } | ||
87 | |||
88 | continue; | ||
89 | } | ||
90 | |||
91 | /* No cache entry? Start a new one */ | ||
92 | if (!c) { | ||
93 | c = kzalloc(sizeof(*c), GFP_KERNEL); | ||
94 | if (!c) | ||
95 | break; | ||
96 | c->min = p; | ||
97 | c->base_reg = i; | ||
98 | } | ||
99 | |||
100 | p += map->debugfs_tot_len; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | /* Find the relevant block */ | ||
105 | list_for_each_entry(c, &map->debugfs_off_cache, list) { | ||
106 | if (*pos >= c->min && *pos <= c->max) { | ||
107 | *pos = c->min; | ||
108 | return c->base_reg; | ||
109 | } | ||
110 | |||
111 | ret = c->max; | ||
112 | } | ||
113 | |||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, | ||
118 | unsigned int to, char __user *user_buf, | ||
119 | size_t count, loff_t *ppos) | ||
120 | { | ||
121 | size_t buf_pos = 0; | ||
122 | loff_t p = *ppos; | ||
65 | ssize_t ret; | 123 | ssize_t ret; |
66 | int i; | 124 | int i; |
67 | struct regmap *map = file->private_data; | ||
68 | char *buf; | 125 | char *buf; |
69 | unsigned int val; | 126 | unsigned int val, start_reg; |
70 | 127 | ||
71 | if (*ppos < 0 || !count) | 128 | if (*ppos < 0 || !count) |
72 | return -EINVAL; | 129 | return -EINVAL; |
@@ -76,11 +133,18 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | |||
76 | return -ENOMEM; | 133 | return -ENOMEM; |
77 | 134 | ||
78 | /* Calculate the length of a fixed format */ | 135 | /* Calculate the length of a fixed format */ |
79 | reg_len = regmap_calc_reg_len(map->max_register, buf, count); | 136 | if (!map->debugfs_tot_len) { |
80 | val_len = 2 * map->format.val_bytes; | 137 | map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, |
81 | tot_len = reg_len + val_len + 3; /* : \n */ | 138 | buf, count); |
139 | map->debugfs_val_len = 2 * map->format.val_bytes; | ||
140 | map->debugfs_tot_len = map->debugfs_reg_len + | ||
141 | map->debugfs_val_len + 3; /* : \n */ | ||
142 | } | ||
82 | 143 | ||
83 | for (i = 0; i <= map->max_register; i += map->reg_stride) { | 144 | /* Work out which register we're starting at */ |
145 | start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); | ||
146 | |||
147 | for (i = start_reg; i <= to; i += map->reg_stride) { | ||
84 | if (!regmap_readable(map, i)) | 148 | if (!regmap_readable(map, i)) |
85 | continue; | 149 | continue; |
86 | 150 | ||
@@ -90,26 +154,27 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | |||
90 | /* If we're in the region the user is trying to read */ | 154 | /* If we're in the region the user is trying to read */ |
91 | if (p >= *ppos) { | 155 | if (p >= *ppos) { |
92 | /* ...but not beyond it */ | 156 | /* ...but not beyond it */ |
93 | if (buf_pos >= count - 1 - tot_len) | 157 | if (buf_pos + 1 + map->debugfs_tot_len >= count) |
94 | break; | 158 | break; |
95 | 159 | ||
96 | /* Format the register */ | 160 | /* Format the register */ |
97 | snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", | 161 | snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", |
98 | reg_len, i); | 162 | map->debugfs_reg_len, i - from); |
99 | buf_pos += reg_len + 2; | 163 | buf_pos += map->debugfs_reg_len + 2; |
100 | 164 | ||
101 | /* Format the value, write all X if we can't read */ | 165 | /* Format the value, write all X if we can't read */ |
102 | ret = regmap_read(map, i, &val); | 166 | ret = regmap_read(map, i, &val); |
103 | if (ret == 0) | 167 | if (ret == 0) |
104 | snprintf(buf + buf_pos, count - buf_pos, | 168 | snprintf(buf + buf_pos, count - buf_pos, |
105 | "%.*x", val_len, val); | 169 | "%.*x", map->debugfs_val_len, val); |
106 | else | 170 | else |
107 | memset(buf + buf_pos, 'X', val_len); | 171 | memset(buf + buf_pos, 'X', |
172 | map->debugfs_val_len); | ||
108 | buf_pos += 2 * map->format.val_bytes; | 173 | buf_pos += 2 * map->format.val_bytes; |
109 | 174 | ||
110 | buf[buf_pos++] = '\n'; | 175 | buf[buf_pos++] = '\n'; |
111 | } | 176 | } |
112 | p += tot_len; | 177 | p += map->debugfs_tot_len; |
113 | } | 178 | } |
114 | 179 | ||
115 | ret = buf_pos; | 180 | ret = buf_pos; |
@@ -126,6 +191,15 @@ out: | |||
126 | return ret; | 191 | return ret; |
127 | } | 192 | } |
128 | 193 | ||
194 | static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | ||
195 | size_t count, loff_t *ppos) | ||
196 | { | ||
197 | struct regmap *map = file->private_data; | ||
198 | |||
199 | return regmap_read_debugfs(map, 0, map->max_register, user_buf, | ||
200 | count, ppos); | ||
201 | } | ||
202 | |||
129 | #undef REGMAP_ALLOW_WRITE_DEBUGFS | 203 | #undef REGMAP_ALLOW_WRITE_DEBUGFS |
130 | #ifdef REGMAP_ALLOW_WRITE_DEBUGFS | 204 | #ifdef REGMAP_ALLOW_WRITE_DEBUGFS |
131 | /* | 205 | /* |
@@ -174,6 +248,22 @@ static const struct file_operations regmap_map_fops = { | |||
174 | .llseek = default_llseek, | 248 | .llseek = default_llseek, |
175 | }; | 249 | }; |
176 | 250 | ||
251 | static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, | ||
252 | size_t count, loff_t *ppos) | ||
253 | { | ||
254 | struct regmap_range_node *range = file->private_data; | ||
255 | struct regmap *map = range->map; | ||
256 | |||
257 | return regmap_read_debugfs(map, range->range_min, range->range_max, | ||
258 | user_buf, count, ppos); | ||
259 | } | ||
260 | |||
261 | static const struct file_operations regmap_range_fops = { | ||
262 | .open = simple_open, | ||
263 | .read = regmap_range_read_file, | ||
264 | .llseek = default_llseek, | ||
265 | }; | ||
266 | |||
177 | static ssize_t regmap_access_read_file(struct file *file, | 267 | static ssize_t regmap_access_read_file(struct file *file, |
178 | char __user *user_buf, size_t count, | 268 | char __user *user_buf, size_t count, |
179 | loff_t *ppos) | 269 | loff_t *ppos) |
@@ -244,6 +334,11 @@ static const struct file_operations regmap_access_fops = { | |||
244 | 334 | ||
245 | void regmap_debugfs_init(struct regmap *map, const char *name) | 335 | void regmap_debugfs_init(struct regmap *map, const char *name) |
246 | { | 336 | { |
337 | struct rb_node *next; | ||
338 | struct regmap_range_node *range_node; | ||
339 | |||
340 | INIT_LIST_HEAD(&map->debugfs_off_cache); | ||
341 | |||
247 | if (name) { | 342 | if (name) { |
248 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", | 343 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", |
249 | dev_name(map->dev), name); | 344 | dev_name(map->dev), name); |
@@ -276,11 +371,32 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
276 | debugfs_create_bool("cache_bypass", 0400, map->debugfs, | 371 | debugfs_create_bool("cache_bypass", 0400, map->debugfs, |
277 | &map->cache_bypass); | 372 | &map->cache_bypass); |
278 | } | 373 | } |
374 | |||
375 | next = rb_first(&map->range_tree); | ||
376 | while (next) { | ||
377 | range_node = rb_entry(next, struct regmap_range_node, node); | ||
378 | |||
379 | if (range_node->name) | ||
380 | debugfs_create_file(range_node->name, 0400, | ||
381 | map->debugfs, range_node, | ||
382 | ®map_range_fops); | ||
383 | |||
384 | next = rb_next(&range_node->node); | ||
385 | } | ||
279 | } | 386 | } |
280 | 387 | ||
281 | void regmap_debugfs_exit(struct regmap *map) | 388 | void regmap_debugfs_exit(struct regmap *map) |
282 | { | 389 | { |
390 | struct regmap_debugfs_off_cache *c; | ||
391 | |||
283 | debugfs_remove_recursive(map->debugfs); | 392 | debugfs_remove_recursive(map->debugfs); |
393 | while (!list_empty(&map->debugfs_off_cache)) { | ||
394 | c = list_first_entry(&map->debugfs_off_cache, | ||
395 | struct regmap_debugfs_off_cache, | ||
396 | list); | ||
397 | list_del(&c->list); | ||
398 | kfree(c); | ||
399 | } | ||
284 | kfree(map->debugfs_name); | 400 | kfree(map->debugfs_name); |
285 | } | 401 | } |
286 | 402 | ||
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 810f5094a9a8..c7465f19f674 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c | |||
@@ -519,20 +519,38 @@ struct regmap *regmap_init(struct device *dev, | |||
519 | } | 519 | } |
520 | 520 | ||
521 | map->range_tree = RB_ROOT; | 521 | map->range_tree = RB_ROOT; |
522 | for (i = 0; i < config->n_ranges; i++) { | 522 | for (i = 0; i < config->num_ranges; i++) { |
523 | const struct regmap_range_cfg *range_cfg = &config->ranges[i]; | 523 | const struct regmap_range_cfg *range_cfg = &config->ranges[i]; |
524 | struct regmap_range_node *new; | 524 | struct regmap_range_node *new; |
525 | 525 | ||
526 | /* Sanity check */ | 526 | /* Sanity check */ |
527 | if (range_cfg->range_max < range_cfg->range_min || | 527 | if (range_cfg->range_max < range_cfg->range_min) { |
528 | range_cfg->range_max > map->max_register || | 528 | dev_err(map->dev, "Invalid range %d: %d < %d\n", i, |
529 | range_cfg->selector_reg > map->max_register || | 529 | range_cfg->range_max, range_cfg->range_min); |
530 | range_cfg->window_len == 0) | ||
531 | goto err_range; | 530 | goto err_range; |
531 | } | ||
532 | |||
533 | if (range_cfg->range_max > map->max_register) { | ||
534 | dev_err(map->dev, "Invalid range %d: %d > %d\n", i, | ||
535 | range_cfg->range_max, map->max_register); | ||
536 | goto err_range; | ||
537 | } | ||
538 | |||
539 | if (range_cfg->selector_reg > map->max_register) { | ||
540 | dev_err(map->dev, | ||
541 | "Invalid range %d: selector out of map\n", i); | ||
542 | goto err_range; | ||
543 | } | ||
544 | |||
545 | if (range_cfg->window_len == 0) { | ||
546 | dev_err(map->dev, "Invalid range %d: window_len 0\n", | ||
547 | i); | ||
548 | goto err_range; | ||
549 | } | ||
532 | 550 | ||
533 | /* Make sure, that this register range has no selector | 551 | /* Make sure, that this register range has no selector |
534 | or data window within its boundary */ | 552 | or data window within its boundary */ |
535 | for (j = 0; j < config->n_ranges; j++) { | 553 | for (j = 0; j < config->num_ranges; j++) { |
536 | unsigned sel_reg = config->ranges[j].selector_reg; | 554 | unsigned sel_reg = config->ranges[j].selector_reg; |
537 | unsigned win_min = config->ranges[j].window_start; | 555 | unsigned win_min = config->ranges[j].window_start; |
538 | unsigned win_max = win_min + | 556 | unsigned win_max = win_min + |
@@ -540,11 +558,17 @@ struct regmap *regmap_init(struct device *dev, | |||
540 | 558 | ||
541 | if (range_cfg->range_min <= sel_reg && | 559 | if (range_cfg->range_min <= sel_reg && |
542 | sel_reg <= range_cfg->range_max) { | 560 | sel_reg <= range_cfg->range_max) { |
561 | dev_err(map->dev, | ||
562 | "Range %d: selector for %d in window\n", | ||
563 | i, j); | ||
543 | goto err_range; | 564 | goto err_range; |
544 | } | 565 | } |
545 | 566 | ||
546 | if (!(win_max < range_cfg->range_min || | 567 | if (!(win_max < range_cfg->range_min || |
547 | win_min > range_cfg->range_max)) { | 568 | win_min > range_cfg->range_max)) { |
569 | dev_err(map->dev, | ||
570 | "Range %d: window for %d in window\n", | ||
571 | i, j); | ||
548 | goto err_range; | 572 | goto err_range; |
549 | } | 573 | } |
550 | } | 574 | } |
@@ -555,6 +579,8 @@ struct regmap *regmap_init(struct device *dev, | |||
555 | goto err_range; | 579 | goto err_range; |
556 | } | 580 | } |
557 | 581 | ||
582 | new->map = map; | ||
583 | new->name = range_cfg->name; | ||
558 | new->range_min = range_cfg->range_min; | 584 | new->range_min = range_cfg->range_min; |
559 | new->range_max = range_cfg->range_max; | 585 | new->range_max = range_cfg->range_max; |
560 | new->selector_reg = range_cfg->selector_reg; | 586 | new->selector_reg = range_cfg->selector_reg; |
@@ -564,6 +590,7 @@ struct regmap *regmap_init(struct device *dev, | |||
564 | new->window_len = range_cfg->window_len; | 590 | new->window_len = range_cfg->window_len; |
565 | 591 | ||
566 | if (_regmap_range_add(map, new) == false) { | 592 | if (_regmap_range_add(map, new) == false) { |
593 | dev_err(map->dev, "Failed to add range %d\n", i); | ||
567 | kfree(new); | 594 | kfree(new); |
568 | goto err_range; | 595 | goto err_range; |
569 | } | 596 | } |
@@ -579,7 +606,7 @@ struct regmap *regmap_init(struct device *dev, | |||
579 | } | 606 | } |
580 | 607 | ||
581 | ret = regcache_init(map, config); | 608 | ret = regcache_init(map, config); |
582 | if (ret < 0) | 609 | if (ret != 0) |
583 | goto err_range; | 610 | goto err_range; |
584 | 611 | ||
585 | regmap_debugfs_init(map, config->name); | 612 | regmap_debugfs_init(map, config->name); |
@@ -738,59 +765,57 @@ struct regmap *dev_get_regmap(struct device *dev, const char *name) | |||
738 | EXPORT_SYMBOL_GPL(dev_get_regmap); | 765 | EXPORT_SYMBOL_GPL(dev_get_regmap); |
739 | 766 | ||
740 | static int _regmap_select_page(struct regmap *map, unsigned int *reg, | 767 | static int _regmap_select_page(struct regmap *map, unsigned int *reg, |
768 | struct regmap_range_node *range, | ||
741 | unsigned int val_num) | 769 | unsigned int val_num) |
742 | { | 770 | { |
743 | struct regmap_range_node *range; | ||
744 | void *orig_work_buf; | 771 | void *orig_work_buf; |
745 | unsigned int win_offset; | 772 | unsigned int win_offset; |
746 | unsigned int win_page; | 773 | unsigned int win_page; |
747 | bool page_chg; | 774 | bool page_chg; |
748 | int ret; | 775 | int ret; |
749 | 776 | ||
750 | range = _regmap_range_lookup(map, *reg); | 777 | win_offset = (*reg - range->range_min) % range->window_len; |
751 | if (range) { | 778 | win_page = (*reg - range->range_min) / range->window_len; |
752 | win_offset = (*reg - range->range_min) % range->window_len; | ||
753 | win_page = (*reg - range->range_min) / range->window_len; | ||
754 | |||
755 | if (val_num > 1) { | ||
756 | /* Bulk write shouldn't cross range boundary */ | ||
757 | if (*reg + val_num - 1 > range->range_max) | ||
758 | return -EINVAL; | ||
759 | 779 | ||
760 | /* ... or single page boundary */ | 780 | if (val_num > 1) { |
761 | if (val_num > range->window_len - win_offset) | 781 | /* Bulk write shouldn't cross range boundary */ |
762 | return -EINVAL; | 782 | if (*reg + val_num - 1 > range->range_max) |
763 | } | 783 | return -EINVAL; |
764 | 784 | ||
765 | /* It is possible to have selector register inside data window. | 785 | /* ... or single page boundary */ |
766 | In that case, selector register is located on every page and | 786 | if (val_num > range->window_len - win_offset) |
767 | it needs no page switching, when accessed alone. */ | 787 | return -EINVAL; |
768 | if (val_num > 1 || | 788 | } |
769 | range->window_start + win_offset != range->selector_reg) { | ||
770 | /* Use separate work_buf during page switching */ | ||
771 | orig_work_buf = map->work_buf; | ||
772 | map->work_buf = map->selector_work_buf; | ||
773 | 789 | ||
774 | ret = _regmap_update_bits(map, range->selector_reg, | 790 | /* It is possible to have selector register inside data window. |
775 | range->selector_mask, | 791 | In that case, selector register is located on every page and |
776 | win_page << range->selector_shift, | 792 | it needs no page switching, when accessed alone. */ |
777 | &page_chg); | 793 | if (val_num > 1 || |
794 | range->window_start + win_offset != range->selector_reg) { | ||
795 | /* Use separate work_buf during page switching */ | ||
796 | orig_work_buf = map->work_buf; | ||
797 | map->work_buf = map->selector_work_buf; | ||
778 | 798 | ||
779 | map->work_buf = orig_work_buf; | 799 | ret = _regmap_update_bits(map, range->selector_reg, |
800 | range->selector_mask, | ||
801 | win_page << range->selector_shift, | ||
802 | &page_chg); | ||
780 | 803 | ||
781 | if (ret < 0) | 804 | map->work_buf = orig_work_buf; |
782 | return ret; | ||
783 | } | ||
784 | 805 | ||
785 | *reg = range->window_start + win_offset; | 806 | if (ret != 0) |
807 | return ret; | ||
786 | } | 808 | } |
787 | 809 | ||
810 | *reg = range->window_start + win_offset; | ||
811 | |||
788 | return 0; | 812 | return 0; |
789 | } | 813 | } |
790 | 814 | ||
791 | static int _regmap_raw_write(struct regmap *map, unsigned int reg, | 815 | static int _regmap_raw_write(struct regmap *map, unsigned int reg, |
792 | const void *val, size_t val_len) | 816 | const void *val, size_t val_len) |
793 | { | 817 | { |
818 | struct regmap_range_node *range; | ||
794 | u8 *u8 = map->work_buf; | 819 | u8 *u8 = map->work_buf; |
795 | void *buf; | 820 | void *buf; |
796 | int ret = -ENOTSUPP; | 821 | int ret = -ENOTSUPP; |
@@ -825,9 +850,35 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, | |||
825 | } | 850 | } |
826 | } | 851 | } |
827 | 852 | ||
828 | ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); | 853 | range = _regmap_range_lookup(map, reg); |
829 | if (ret < 0) | 854 | if (range) { |
830 | return ret; | 855 | int val_num = val_len / map->format.val_bytes; |
856 | int win_offset = (reg - range->range_min) % range->window_len; | ||
857 | int win_residue = range->window_len - win_offset; | ||
858 | |||
859 | /* If the write goes beyond the end of the window split it */ | ||
860 | while (val_num > win_residue) { | ||
861 | dev_dbg(map->dev, "Writing window %d/%zu\n", | ||
862 | win_residue, val_len / map->format.val_bytes); | ||
863 | ret = _regmap_raw_write(map, reg, val, win_residue * | ||
864 | map->format.val_bytes); | ||
865 | if (ret != 0) | ||
866 | return ret; | ||
867 | |||
868 | reg += win_residue; | ||
869 | val_num -= win_residue; | ||
870 | val += win_residue * map->format.val_bytes; | ||
871 | val_len -= win_residue * map->format.val_bytes; | ||
872 | |||
873 | win_offset = (reg - range->range_min) % | ||
874 | range->window_len; | ||
875 | win_residue = range->window_len - win_offset; | ||
876 | } | ||
877 | |||
878 | ret = _regmap_select_page(map, ®, range, val_num); | ||
879 | if (ret != 0) | ||
880 | return ret; | ||
881 | } | ||
831 | 882 | ||
832 | map->format.format_reg(map->work_buf, reg, map->reg_shift); | 883 | map->format.format_reg(map->work_buf, reg, map->reg_shift); |
833 | 884 | ||
@@ -876,6 +927,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, | |||
876 | int _regmap_write(struct regmap *map, unsigned int reg, | 927 | int _regmap_write(struct regmap *map, unsigned int reg, |
877 | unsigned int val) | 928 | unsigned int val) |
878 | { | 929 | { |
930 | struct regmap_range_node *range; | ||
879 | int ret; | 931 | int ret; |
880 | BUG_ON(!map->format.format_write && !map->format.format_val); | 932 | BUG_ON(!map->format.format_write && !map->format.format_val); |
881 | 933 | ||
@@ -897,9 +949,12 @@ int _regmap_write(struct regmap *map, unsigned int reg, | |||
897 | trace_regmap_reg_write(map->dev, reg, val); | 949 | trace_regmap_reg_write(map->dev, reg, val); |
898 | 950 | ||
899 | if (map->format.format_write) { | 951 | if (map->format.format_write) { |
900 | ret = _regmap_select_page(map, ®, 1); | 952 | range = _regmap_range_lookup(map, reg); |
901 | if (ret < 0) | 953 | if (range) { |
902 | return ret; | 954 | ret = _regmap_select_page(map, ®, range, 1); |
955 | if (ret != 0) | ||
956 | return ret; | ||
957 | } | ||
903 | 958 | ||
904 | map->format.format_write(map, reg, val); | 959 | map->format.format_write(map, reg, val); |
905 | 960 | ||
@@ -1055,12 +1110,17 @@ EXPORT_SYMBOL_GPL(regmap_bulk_write); | |||
1055 | static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, | 1110 | static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, |
1056 | unsigned int val_len) | 1111 | unsigned int val_len) |
1057 | { | 1112 | { |
1113 | struct regmap_range_node *range; | ||
1058 | u8 *u8 = map->work_buf; | 1114 | u8 *u8 = map->work_buf; |
1059 | int ret; | 1115 | int ret; |
1060 | 1116 | ||
1061 | ret = _regmap_select_page(map, ®, val_len / map->format.val_bytes); | 1117 | range = _regmap_range_lookup(map, reg); |
1062 | if (ret < 0) | 1118 | if (range) { |
1063 | return ret; | 1119 | ret = _regmap_select_page(map, ®, range, |
1120 | val_len / map->format.val_bytes); | ||
1121 | if (ret != 0) | ||
1122 | return ret; | ||
1123 | } | ||
1064 | 1124 | ||
1065 | map->format.format_reg(map->work_buf, reg, map->reg_shift); | 1125 | map->format.format_reg(map->work_buf, reg, map->reg_shift); |
1066 | 1126 | ||