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/regmap.c | |
parent | d3816c1a0e9ea8e1a04fdf7601837e8c3a0e190e (diff) | |
parent | 5166b7c006eeb4f6becc0822974d8da259484ba1 (diff) |
Merge remote-tracking branch 'regmap/topic/debugfs' into regmap-next
Diffstat (limited to 'drivers/base/regmap/regmap.c')
-rw-r--r-- | drivers/base/regmap/regmap.c | 154 |
1 files changed, 107 insertions, 47 deletions
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 | ||