diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-12-01 23:35:18 -0500 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-12-01 23:35:18 -0500 |
commit | 54fc5a1ad81d3c7c7553b6d3dff0fc51d8ac2bbd (patch) | |
tree | c08f81b4a947f5cc15e41fe8c3392d6907cb8453 /drivers | |
parent | 9f07f658c45faece684479f2447c21011ca64e9a (diff) | |
parent | f55ec27f48a833febe8c78453bac55bb14e2cc4a (diff) |
Merge remote-tracking branch 'asoc/topic/wm2200' into asoc-next
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/regmap/internal.h | 2 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 50 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 154 |
3 files changed, 154 insertions, 52 deletions
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 80f9ab9c3aa4..ac869d28d5ba 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h | |||
@@ -120,6 +120,8 @@ int _regmap_write(struct regmap *map, unsigned int reg, | |||
120 | 120 | ||
121 | struct regmap_range_node { | 121 | struct regmap_range_node { |
122 | struct rb_node node; | 122 | struct rb_node node; |
123 | const char *name; | ||
124 | struct regmap *map; | ||
123 | 125 | ||
124 | unsigned int range_min; | 126 | unsigned int range_min; |
125 | unsigned int range_max; | 127 | unsigned int range_max; |
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index bb1ff175b962..f4b9dd01c981 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 = { | |||
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 | static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, |
60 | size_t count, loff_t *ppos) | 60 | unsigned int to, char __user *user_buf, |
61 | size_t count, loff_t *ppos) | ||
61 | { | 62 | { |
62 | int reg_len, val_len, tot_len; | 63 | int reg_len, val_len, tot_len; |
63 | size_t buf_pos = 0; | 64 | size_t buf_pos = 0; |
64 | loff_t p = 0; | 65 | loff_t p = 0; |
65 | ssize_t ret; | 66 | ssize_t ret; |
66 | int i; | 67 | int i; |
67 | struct regmap *map = file->private_data; | ||
68 | char *buf; | 68 | char *buf; |
69 | unsigned int val; | 69 | unsigned int val; |
70 | 70 | ||
@@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | |||
80 | val_len = 2 * map->format.val_bytes; | 80 | val_len = 2 * map->format.val_bytes; |
81 | tot_len = reg_len + val_len + 3; /* : \n */ | 81 | tot_len = reg_len + val_len + 3; /* : \n */ |
82 | 82 | ||
83 | for (i = 0; i <= map->max_register; i += map->reg_stride) { | 83 | for (i = from; i <= to; i += map->reg_stride) { |
84 | if (!regmap_readable(map, i)) | 84 | if (!regmap_readable(map, i)) |
85 | continue; | 85 | continue; |
86 | 86 | ||
@@ -95,7 +95,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | |||
95 | 95 | ||
96 | /* Format the register */ | 96 | /* Format the register */ |
97 | snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", | 97 | snprintf(buf + buf_pos, count - buf_pos, "%.*x: ", |
98 | reg_len, i); | 98 | reg_len, i - from); |
99 | buf_pos += reg_len + 2; | 99 | buf_pos += reg_len + 2; |
100 | 100 | ||
101 | /* Format the value, write all X if we can't read */ | 101 | /* Format the value, write all X if we can't read */ |
@@ -126,6 +126,15 @@ out: | |||
126 | return ret; | 126 | return ret; |
127 | } | 127 | } |
128 | 128 | ||
129 | static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf, | ||
130 | size_t count, loff_t *ppos) | ||
131 | { | ||
132 | struct regmap *map = file->private_data; | ||
133 | |||
134 | return regmap_read_debugfs(map, 0, map->max_register, user_buf, | ||
135 | count, ppos); | ||
136 | } | ||
137 | |||
129 | #undef REGMAP_ALLOW_WRITE_DEBUGFS | 138 | #undef REGMAP_ALLOW_WRITE_DEBUGFS |
130 | #ifdef REGMAP_ALLOW_WRITE_DEBUGFS | 139 | #ifdef REGMAP_ALLOW_WRITE_DEBUGFS |
131 | /* | 140 | /* |
@@ -174,6 +183,22 @@ static const struct file_operations regmap_map_fops = { | |||
174 | .llseek = default_llseek, | 183 | .llseek = default_llseek, |
175 | }; | 184 | }; |
176 | 185 | ||
186 | static ssize_t regmap_range_read_file(struct file *file, char __user *user_buf, | ||
187 | size_t count, loff_t *ppos) | ||
188 | { | ||
189 | struct regmap_range_node *range = file->private_data; | ||
190 | struct regmap *map = range->map; | ||
191 | |||
192 | return regmap_read_debugfs(map, range->range_min, range->range_max, | ||
193 | user_buf, count, ppos); | ||
194 | } | ||
195 | |||
196 | static const struct file_operations regmap_range_fops = { | ||
197 | .open = simple_open, | ||
198 | .read = regmap_range_read_file, | ||
199 | .llseek = default_llseek, | ||
200 | }; | ||
201 | |||
177 | static ssize_t regmap_access_read_file(struct file *file, | 202 | static ssize_t regmap_access_read_file(struct file *file, |
178 | char __user *user_buf, size_t count, | 203 | char __user *user_buf, size_t count, |
179 | loff_t *ppos) | 204 | loff_t *ppos) |
@@ -244,6 +269,9 @@ static const struct file_operations regmap_access_fops = { | |||
244 | 269 | ||
245 | void regmap_debugfs_init(struct regmap *map, const char *name) | 270 | void regmap_debugfs_init(struct regmap *map, const char *name) |
246 | { | 271 | { |
272 | struct rb_node *next; | ||
273 | struct regmap_range_node *range_node; | ||
274 | |||
247 | if (name) { | 275 | if (name) { |
248 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", | 276 | map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s", |
249 | dev_name(map->dev), name); | 277 | dev_name(map->dev), name); |
@@ -276,6 +304,18 @@ void regmap_debugfs_init(struct regmap *map, const char *name) | |||
276 | debugfs_create_bool("cache_bypass", 0400, map->debugfs, | 304 | debugfs_create_bool("cache_bypass", 0400, map->debugfs, |
277 | &map->cache_bypass); | 305 | &map->cache_bypass); |
278 | } | 306 | } |
307 | |||
308 | next = rb_first(&map->range_tree); | ||
309 | while (next) { | ||
310 | range_node = rb_entry(next, struct regmap_range_node, node); | ||
311 | |||
312 | if (range_node->name) | ||
313 | debugfs_create_file(range_node->name, 0400, | ||
314 | map->debugfs, range_node, | ||
315 | ®map_range_fops); | ||
316 | |||
317 | next = rb_next(&range_node->node); | ||
318 | } | ||
279 | } | 319 | } |
280 | 320 | ||
281 | void regmap_debugfs_exit(struct regmap *map) | 321 | void regmap_debugfs_exit(struct regmap *map) |
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 52069d29ff12..96253cd949e9 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/%d\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 | ||