Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cpufreq: amd-pstate: Add guided mode control support via sysfs

amd_pstate driver's `status` sysfs entry helps to control the driver's
mode dynamically by user. After the addition of guided mode the
combinations of mode transitions have been increased (16 combinations).
Therefore optimise the amd_pstate_update_status function by implementing
a state transition table.

There are 4 states amd_pstate supports, namely: 'disable', 'passive',
'active', and 'guided'. The transition from any state to any other
state is possible after this change.

Sysfs interface:

To disable amd_pstate driver:
# echo disable > /sys/devices/system/cpu/amd_pstate/status

To enable passive mode:
# echo passive > /sys/devices/system/cpu/amd_pstate/status

To change mode to active:
# echo active > /sys/devices/system/cpu/amd_pstate/status

To change mode to guided:
# echo guided > /sys/devices/system/cpu/amd_pstate/status

Acked-by: Huang Rui <ray.huang@amd.com>
Reviewed-by: Mario Limonciello <mario.limonciello@amd.com>
Tested-by: Oleksandr Natalenko <oleksandr@natalenko.name>
Signed-off-by: Wyes Karny <wyes.karny@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Wyes Karny and committed by
Rafael J. Wysocki
3ca7bc81 2dd6d0eb

+101 -42
+101 -42
drivers/cpufreq/amd-pstate.c
··· 106 106 [EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE, 107 107 }; 108 108 109 + typedef int (*cppc_mode_transition_fn)(int); 110 + 109 111 static inline int get_mode_idx_from_str(const char *str, size_t size) 110 112 { 111 113 int i; ··· 840 838 return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); 841 839 } 842 840 841 + static void amd_pstate_driver_cleanup(void) 842 + { 843 + amd_pstate_enable(false); 844 + cppc_state = AMD_PSTATE_DISABLE; 845 + current_pstate_driver = NULL; 846 + } 847 + 848 + static int amd_pstate_register_driver(int mode) 849 + { 850 + int ret; 851 + 852 + if (mode == AMD_PSTATE_PASSIVE || mode == AMD_PSTATE_GUIDED) 853 + current_pstate_driver = &amd_pstate_driver; 854 + else if (mode == AMD_PSTATE_ACTIVE) 855 + current_pstate_driver = &amd_pstate_epp_driver; 856 + else 857 + return -EINVAL; 858 + 859 + cppc_state = mode; 860 + ret = cpufreq_register_driver(current_pstate_driver); 861 + if (ret) { 862 + amd_pstate_driver_cleanup(); 863 + return ret; 864 + } 865 + return 0; 866 + } 867 + 868 + static int amd_pstate_unregister_driver(int dummy) 869 + { 870 + cpufreq_unregister_driver(current_pstate_driver); 871 + amd_pstate_driver_cleanup(); 872 + return 0; 873 + } 874 + 875 + static int amd_pstate_change_mode_without_dvr_change(int mode) 876 + { 877 + int cpu = 0; 878 + 879 + cppc_state = mode; 880 + 881 + if (boot_cpu_has(X86_FEATURE_CPPC) || cppc_state == AMD_PSTATE_ACTIVE) 882 + return 0; 883 + 884 + for_each_present_cpu(cpu) { 885 + cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1); 886 + } 887 + 888 + return 0; 889 + } 890 + 891 + static int amd_pstate_change_driver_mode(int mode) 892 + { 893 + int ret; 894 + 895 + ret = amd_pstate_unregister_driver(0); 896 + if (ret) 897 + return ret; 898 + 899 + ret = amd_pstate_register_driver(mode); 900 + if (ret) 901 + return ret; 902 + 903 + return 0; 904 + } 905 + 906 + cppc_mode_transition_fn mode_state_machine[AMD_PSTATE_MAX][AMD_PSTATE_MAX] = { 907 + [AMD_PSTATE_DISABLE] = { 908 + [AMD_PSTATE_DISABLE] = NULL, 909 + [AMD_PSTATE_PASSIVE] = amd_pstate_register_driver, 910 + [AMD_PSTATE_ACTIVE] = amd_pstate_register_driver, 911 + [AMD_PSTATE_GUIDED] = amd_pstate_register_driver, 912 + }, 913 + [AMD_PSTATE_PASSIVE] = { 914 + [AMD_PSTATE_DISABLE] = amd_pstate_unregister_driver, 915 + [AMD_PSTATE_PASSIVE] = NULL, 916 + [AMD_PSTATE_ACTIVE] = amd_pstate_change_driver_mode, 917 + [AMD_PSTATE_GUIDED] = amd_pstate_change_mode_without_dvr_change, 918 + }, 919 + [AMD_PSTATE_ACTIVE] = { 920 + [AMD_PSTATE_DISABLE] = amd_pstate_unregister_driver, 921 + [AMD_PSTATE_PASSIVE] = amd_pstate_change_driver_mode, 922 + [AMD_PSTATE_ACTIVE] = NULL, 923 + [AMD_PSTATE_GUIDED] = amd_pstate_change_driver_mode, 924 + }, 925 + [AMD_PSTATE_GUIDED] = { 926 + [AMD_PSTATE_DISABLE] = amd_pstate_unregister_driver, 927 + [AMD_PSTATE_PASSIVE] = amd_pstate_change_mode_without_dvr_change, 928 + [AMD_PSTATE_ACTIVE] = amd_pstate_change_driver_mode, 929 + [AMD_PSTATE_GUIDED] = NULL, 930 + }, 931 + }; 932 + 843 933 static ssize_t amd_pstate_show_status(char *buf) 844 934 { 845 935 if (!current_pstate_driver) ··· 940 846 return sysfs_emit(buf, "%s\n", amd_pstate_mode_string[cppc_state]); 941 847 } 942 848 943 - static void amd_pstate_driver_cleanup(void) 944 - { 945 - current_pstate_driver = NULL; 946 - } 947 - 948 849 static int amd_pstate_update_status(const char *buf, size_t size) 949 850 { 950 - int ret = 0; 951 851 int mode_idx; 952 852 953 - if (size > 7 || size < 6) 853 + if (size > strlen("passive") || size < strlen("active")) 954 854 return -EINVAL; 855 + 955 856 mode_idx = get_mode_idx_from_str(buf, size); 956 857 957 - switch(mode_idx) { 958 - case AMD_PSTATE_DISABLE: 959 - if (!current_pstate_driver) 960 - return -EINVAL; 961 - if (cppc_state == AMD_PSTATE_ACTIVE) 962 - return -EBUSY; 963 - cpufreq_unregister_driver(current_pstate_driver); 964 - amd_pstate_driver_cleanup(); 965 - break; 966 - case AMD_PSTATE_PASSIVE: 967 - if (current_pstate_driver) { 968 - if (current_pstate_driver == &amd_pstate_driver) 969 - return 0; 970 - cpufreq_unregister_driver(current_pstate_driver); 971 - cppc_state = AMD_PSTATE_PASSIVE; 972 - current_pstate_driver = &amd_pstate_driver; 973 - } 858 + if (mode_idx < 0 || mode_idx >= AMD_PSTATE_MAX) 859 + return -EINVAL; 974 860 975 - ret = cpufreq_register_driver(current_pstate_driver); 976 - break; 977 - case AMD_PSTATE_ACTIVE: 978 - if (current_pstate_driver) { 979 - if (current_pstate_driver == &amd_pstate_epp_driver) 980 - return 0; 981 - cpufreq_unregister_driver(current_pstate_driver); 982 - current_pstate_driver = &amd_pstate_epp_driver; 983 - cppc_state = AMD_PSTATE_ACTIVE; 984 - } 861 + if (mode_state_machine[cppc_state][mode_idx]) 862 + return mode_state_machine[cppc_state][mode_idx](mode_idx); 985 863 986 - ret = cpufreq_register_driver(current_pstate_driver); 987 - break; 988 - default: 989 - ret = -EINVAL; 990 - break; 991 - } 992 - 993 - return ret; 864 + return 0; 994 865 } 995 866 996 867 static ssize_t show_status(struct kobject *kobj,