diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2016-06-27 06:34:07 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-07-06 18:13:20 -0400 |
commit | da0c6dc00c69d0adaae99c3b3d2ea0c869a3fb35 (patch) | |
tree | 857ed9f41fafe743720003520ecc94d4e53114d8 | |
parent | 8d540ea79211dd272403ed79b2033bbded19ac42 (diff) |
cpufreq: Handle sorted frequency tables more efficiently
cpufreq drivers aren't required to provide a sorted frequency table
today, and even the ones which provide a sorted table aren't handled
efficiently by cpufreq core.
This patch adds infrastructure to verify if the freq-table provided by
the drivers is sorted or not, and use efficient helpers if they are
sorted.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r-- | drivers/cpufreq/freq_table.c | 73 | ||||
-rw-r--r-- | include/linux/cpufreq.h | 234 |
2 files changed, 296 insertions, 11 deletions
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index eac8bcbdaad1..3bbbf9e6960c 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c | |||
@@ -113,9 +113,9 @@ int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy) | |||
113 | } | 113 | } |
114 | EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify); | 114 | EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify); |
115 | 115 | ||
116 | int cpufreq_frequency_table_target(struct cpufreq_policy *policy, | 116 | int cpufreq_table_index_unsorted(struct cpufreq_policy *policy, |
117 | unsigned int target_freq, | 117 | unsigned int target_freq, |
118 | unsigned int relation) | 118 | unsigned int relation) |
119 | { | 119 | { |
120 | struct cpufreq_frequency_table optimal = { | 120 | struct cpufreq_frequency_table optimal = { |
121 | .driver_data = ~0, | 121 | .driver_data = ~0, |
@@ -205,7 +205,7 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, | |||
205 | table[index].frequency); | 205 | table[index].frequency); |
206 | return index; | 206 | return index; |
207 | } | 207 | } |
208 | EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); | 208 | EXPORT_SYMBOL_GPL(cpufreq_table_index_unsorted); |
209 | 209 | ||
210 | int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, | 210 | int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, |
211 | unsigned int freq) | 211 | unsigned int freq) |
@@ -297,15 +297,72 @@ struct freq_attr *cpufreq_generic_attr[] = { | |||
297 | }; | 297 | }; |
298 | EXPORT_SYMBOL_GPL(cpufreq_generic_attr); | 298 | EXPORT_SYMBOL_GPL(cpufreq_generic_attr); |
299 | 299 | ||
300 | static int set_freq_table_sorted(struct cpufreq_policy *policy) | ||
301 | { | ||
302 | struct cpufreq_frequency_table *pos, *table = policy->freq_table; | ||
303 | struct cpufreq_frequency_table *prev = NULL; | ||
304 | int ascending = 0; | ||
305 | |||
306 | policy->freq_table_sorted = CPUFREQ_TABLE_UNSORTED; | ||
307 | |||
308 | cpufreq_for_each_valid_entry(pos, table) { | ||
309 | if (!prev) { | ||
310 | prev = pos; | ||
311 | continue; | ||
312 | } | ||
313 | |||
314 | if (pos->frequency == prev->frequency) { | ||
315 | pr_warn("Duplicate freq-table entries: %u\n", | ||
316 | pos->frequency); | ||
317 | return -EINVAL; | ||
318 | } | ||
319 | |||
320 | /* Frequency increased from prev to pos */ | ||
321 | if (pos->frequency > prev->frequency) { | ||
322 | /* But frequency was decreasing earlier */ | ||
323 | if (ascending < 0) { | ||
324 | pr_debug("Freq table is unsorted\n"); | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | ascending++; | ||
329 | } else { | ||
330 | /* Frequency decreased from prev to pos */ | ||
331 | |||
332 | /* But frequency was increasing earlier */ | ||
333 | if (ascending > 0) { | ||
334 | pr_debug("Freq table is unsorted\n"); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | ascending--; | ||
339 | } | ||
340 | |||
341 | prev = pos; | ||
342 | } | ||
343 | |||
344 | if (ascending > 0) | ||
345 | policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_ASCENDING; | ||
346 | else | ||
347 | policy->freq_table_sorted = CPUFREQ_TABLE_SORTED_DESCENDING; | ||
348 | |||
349 | pr_debug("Freq table is sorted in %s order\n", | ||
350 | ascending > 0 ? "ascending" : "descending"); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
300 | int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, | 355 | int cpufreq_table_validate_and_show(struct cpufreq_policy *policy, |
301 | struct cpufreq_frequency_table *table) | 356 | struct cpufreq_frequency_table *table) |
302 | { | 357 | { |
303 | int ret = cpufreq_frequency_table_cpuinfo(policy, table); | 358 | int ret; |
304 | 359 | ||
305 | if (!ret) | 360 | ret = cpufreq_frequency_table_cpuinfo(policy, table); |
306 | policy->freq_table = table; | 361 | if (ret) |
362 | return ret; | ||
307 | 363 | ||
308 | return ret; | 364 | policy->freq_table = table; |
365 | return set_freq_table_sorted(policy); | ||
309 | } | 366 | } |
310 | EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show); | 367 | EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show); |
311 | 368 | ||
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index c378776628b4..c6410b1b2490 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h | |||
@@ -36,6 +36,12 @@ | |||
36 | 36 | ||
37 | struct cpufreq_governor; | 37 | struct cpufreq_governor; |
38 | 38 | ||
39 | enum cpufreq_table_sorting { | ||
40 | CPUFREQ_TABLE_UNSORTED, | ||
41 | CPUFREQ_TABLE_SORTED_ASCENDING, | ||
42 | CPUFREQ_TABLE_SORTED_DESCENDING | ||
43 | }; | ||
44 | |||
39 | struct cpufreq_freqs { | 45 | struct cpufreq_freqs { |
40 | unsigned int cpu; /* cpu nr */ | 46 | unsigned int cpu; /* cpu nr */ |
41 | unsigned int old; | 47 | unsigned int old; |
@@ -87,6 +93,7 @@ struct cpufreq_policy { | |||
87 | 93 | ||
88 | struct cpufreq_user_policy user_policy; | 94 | struct cpufreq_user_policy user_policy; |
89 | struct cpufreq_frequency_table *freq_table; | 95 | struct cpufreq_frequency_table *freq_table; |
96 | enum cpufreq_table_sorting freq_table_sorted; | ||
90 | 97 | ||
91 | struct list_head policy_list; | 98 | struct list_head policy_list; |
92 | struct kobject kobj; | 99 | struct kobject kobj; |
@@ -597,9 +604,9 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy, | |||
597 | struct cpufreq_frequency_table *table); | 604 | struct cpufreq_frequency_table *table); |
598 | int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy); | 605 | int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy); |
599 | 606 | ||
600 | int cpufreq_frequency_table_target(struct cpufreq_policy *policy, | 607 | int cpufreq_table_index_unsorted(struct cpufreq_policy *policy, |
601 | unsigned int target_freq, | 608 | unsigned int target_freq, |
602 | unsigned int relation); | 609 | unsigned int relation); |
603 | int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, | 610 | int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy, |
604 | unsigned int freq); | 611 | unsigned int freq); |
605 | 612 | ||
@@ -610,6 +617,227 @@ int cpufreq_boost_trigger_state(int state); | |||
610 | int cpufreq_boost_enabled(void); | 617 | int cpufreq_boost_enabled(void); |
611 | int cpufreq_enable_boost_support(void); | 618 | int cpufreq_enable_boost_support(void); |
612 | bool policy_has_boost_freq(struct cpufreq_policy *policy); | 619 | bool policy_has_boost_freq(struct cpufreq_policy *policy); |
620 | |||
621 | /* Find lowest freq at or above target in a table in ascending order */ | ||
622 | static inline int cpufreq_table_find_index_al(struct cpufreq_policy *policy, | ||
623 | unsigned int target_freq) | ||
624 | { | ||
625 | struct cpufreq_frequency_table *table = policy->freq_table; | ||
626 | unsigned int freq; | ||
627 | int i, best = -1; | ||
628 | |||
629 | for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
630 | freq = table[i].frequency; | ||
631 | |||
632 | if (freq >= target_freq) | ||
633 | return i; | ||
634 | |||
635 | best = i; | ||
636 | } | ||
637 | |||
638 | return best; | ||
639 | } | ||
640 | |||
641 | /* Find lowest freq at or above target in a table in descending order */ | ||
642 | static inline int cpufreq_table_find_index_dl(struct cpufreq_policy *policy, | ||
643 | unsigned int target_freq) | ||
644 | { | ||
645 | struct cpufreq_frequency_table *table = policy->freq_table; | ||
646 | unsigned int freq; | ||
647 | int i, best = -1; | ||
648 | |||
649 | for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
650 | freq = table[i].frequency; | ||
651 | |||
652 | if (freq == target_freq) | ||
653 | return i; | ||
654 | |||
655 | if (freq > target_freq) { | ||
656 | best = i; | ||
657 | continue; | ||
658 | } | ||
659 | |||
660 | /* No freq found above target_freq */ | ||
661 | if (best == -1) | ||
662 | return i; | ||
663 | |||
664 | return best; | ||
665 | } | ||
666 | |||
667 | return best; | ||
668 | } | ||
669 | |||
670 | /* Works only on sorted freq-tables */ | ||
671 | static inline int cpufreq_table_find_index_l(struct cpufreq_policy *policy, | ||
672 | unsigned int target_freq) | ||
673 | { | ||
674 | target_freq = clamp_val(target_freq, policy->min, policy->max); | ||
675 | |||
676 | if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) | ||
677 | return cpufreq_table_find_index_al(policy, target_freq); | ||
678 | else | ||
679 | return cpufreq_table_find_index_dl(policy, target_freq); | ||
680 | } | ||
681 | |||
682 | /* Find highest freq at or below target in a table in ascending order */ | ||
683 | static inline int cpufreq_table_find_index_ah(struct cpufreq_policy *policy, | ||
684 | unsigned int target_freq) | ||
685 | { | ||
686 | struct cpufreq_frequency_table *table = policy->freq_table; | ||
687 | unsigned int freq; | ||
688 | int i, best = -1; | ||
689 | |||
690 | for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
691 | freq = table[i].frequency; | ||
692 | |||
693 | if (freq == target_freq) | ||
694 | return i; | ||
695 | |||
696 | if (freq < target_freq) { | ||
697 | best = i; | ||
698 | continue; | ||
699 | } | ||
700 | |||
701 | /* No freq found below target_freq */ | ||
702 | if (best == -1) | ||
703 | return i; | ||
704 | |||
705 | return best; | ||
706 | } | ||
707 | |||
708 | return best; | ||
709 | } | ||
710 | |||
711 | /* Find highest freq at or below target in a table in descending order */ | ||
712 | static inline int cpufreq_table_find_index_dh(struct cpufreq_policy *policy, | ||
713 | unsigned int target_freq) | ||
714 | { | ||
715 | struct cpufreq_frequency_table *table = policy->freq_table; | ||
716 | unsigned int freq; | ||
717 | int i, best = -1; | ||
718 | |||
719 | for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
720 | freq = table[i].frequency; | ||
721 | |||
722 | if (freq <= target_freq) | ||
723 | return i; | ||
724 | |||
725 | best = i; | ||
726 | } | ||
727 | |||
728 | return best; | ||
729 | } | ||
730 | |||
731 | /* Works only on sorted freq-tables */ | ||
732 | static inline int cpufreq_table_find_index_h(struct cpufreq_policy *policy, | ||
733 | unsigned int target_freq) | ||
734 | { | ||
735 | target_freq = clamp_val(target_freq, policy->min, policy->max); | ||
736 | |||
737 | if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) | ||
738 | return cpufreq_table_find_index_ah(policy, target_freq); | ||
739 | else | ||
740 | return cpufreq_table_find_index_dh(policy, target_freq); | ||
741 | } | ||
742 | |||
743 | /* Find closest freq to target in a table in ascending order */ | ||
744 | static inline int cpufreq_table_find_index_ac(struct cpufreq_policy *policy, | ||
745 | unsigned int target_freq) | ||
746 | { | ||
747 | struct cpufreq_frequency_table *table = policy->freq_table; | ||
748 | unsigned int freq; | ||
749 | int i, best = -1; | ||
750 | |||
751 | for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
752 | freq = table[i].frequency; | ||
753 | |||
754 | if (freq == target_freq) | ||
755 | return i; | ||
756 | |||
757 | if (freq < target_freq) { | ||
758 | best = i; | ||
759 | continue; | ||
760 | } | ||
761 | |||
762 | /* No freq found below target_freq */ | ||
763 | if (best == -1) | ||
764 | return i; | ||
765 | |||
766 | /* Choose the closest freq */ | ||
767 | if (target_freq - table[best].frequency > freq - target_freq) | ||
768 | return i; | ||
769 | |||
770 | return best; | ||
771 | } | ||
772 | |||
773 | return best; | ||
774 | } | ||
775 | |||
776 | /* Find closest freq to target in a table in descending order */ | ||
777 | static inline int cpufreq_table_find_index_dc(struct cpufreq_policy *policy, | ||
778 | unsigned int target_freq) | ||
779 | { | ||
780 | struct cpufreq_frequency_table *table = policy->freq_table; | ||
781 | unsigned int freq; | ||
782 | int i, best = -1; | ||
783 | |||
784 | for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { | ||
785 | freq = table[i].frequency; | ||
786 | |||
787 | if (freq == target_freq) | ||
788 | return i; | ||
789 | |||
790 | if (freq > target_freq) { | ||
791 | best = i; | ||
792 | continue; | ||
793 | } | ||
794 | |||
795 | /* No freq found above target_freq */ | ||
796 | if (best == -1) | ||
797 | return i; | ||
798 | |||
799 | /* Choose the closest freq */ | ||
800 | if (table[best].frequency - target_freq > target_freq - freq) | ||
801 | return i; | ||
802 | |||
803 | return best; | ||
804 | } | ||
805 | |||
806 | return best; | ||
807 | } | ||
808 | |||
809 | /* Works only on sorted freq-tables */ | ||
810 | static inline int cpufreq_table_find_index_c(struct cpufreq_policy *policy, | ||
811 | unsigned int target_freq) | ||
812 | { | ||
813 | target_freq = clamp_val(target_freq, policy->min, policy->max); | ||
814 | |||
815 | if (policy->freq_table_sorted == CPUFREQ_TABLE_SORTED_ASCENDING) | ||
816 | return cpufreq_table_find_index_ac(policy, target_freq); | ||
817 | else | ||
818 | return cpufreq_table_find_index_dc(policy, target_freq); | ||
819 | } | ||
820 | |||
821 | static inline int cpufreq_frequency_table_target(struct cpufreq_policy *policy, | ||
822 | unsigned int target_freq, | ||
823 | unsigned int relation) | ||
824 | { | ||
825 | if (unlikely(policy->freq_table_sorted == CPUFREQ_TABLE_UNSORTED)) | ||
826 | return cpufreq_table_index_unsorted(policy, target_freq, | ||
827 | relation); | ||
828 | |||
829 | switch (relation) { | ||
830 | case CPUFREQ_RELATION_L: | ||
831 | return cpufreq_table_find_index_l(policy, target_freq); | ||
832 | case CPUFREQ_RELATION_H: | ||
833 | return cpufreq_table_find_index_h(policy, target_freq); | ||
834 | case CPUFREQ_RELATION_C: | ||
835 | return cpufreq_table_find_index_c(policy, target_freq); | ||
836 | default: | ||
837 | pr_err("%s: Invalid relation: %d\n", __func__, relation); | ||
838 | return -EINVAL; | ||
839 | } | ||
840 | } | ||
613 | #else | 841 | #else |
614 | static inline int cpufreq_boost_trigger_state(int state) | 842 | static inline int cpufreq_boost_trigger_state(int state) |
615 | { | 843 | { |