diff options
| author | Prashanth Prakash <pprakash@codeaurora.org> | 2018-04-04 14:14:52 -0400 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2018-04-24 06:33:28 -0400 |
| commit | 256f19d212f260c955a90a0efc7753e11b18e34c (patch) | |
| tree | 6c9203ba7e37647e92a451a48085d9704569af42 | |
| parent | 6fa12d584dcba18f67425ce17e9317311a624f81 (diff) | |
cpufreq / CPPC: Support for CPPC v3
Use CPPC v3 entries to convert the abstract processor performance to
processor frequency in KHz.
Signed-off-by: Prashanth Prakash <pprakash@codeaurora.org>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
| -rw-r--r-- | drivers/cpufreq/cppc_cpufreq.c | 80 |
1 files changed, 68 insertions, 12 deletions
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index bc5fc1630876..e67e94b0ec14 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c | |||
| @@ -42,9 +42,6 @@ | |||
| 42 | */ | 42 | */ |
| 43 | static struct cppc_cpudata **all_cpu_data; | 43 | static struct cppc_cpudata **all_cpu_data; |
| 44 | 44 | ||
| 45 | /* Capture the max KHz from DMI */ | ||
| 46 | static u64 cppc_dmi_max_khz; | ||
| 47 | |||
| 48 | /* Callback function used to retrieve the max frequency from DMI */ | 45 | /* Callback function used to retrieve the max frequency from DMI */ |
| 49 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) | 46 | static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private) |
| 50 | { | 47 | { |
| @@ -75,6 +72,64 @@ static u64 cppc_get_dmi_max_khz(void) | |||
| 75 | return (1000 * mhz); | 72 | return (1000 * mhz); |
| 76 | } | 73 | } |
| 77 | 74 | ||
| 75 | /* | ||
| 76 | * If CPPC lowest_freq and nominal_freq registers are exposed then we can | ||
| 77 | * use them to convert perf to freq and vice versa | ||
| 78 | * | ||
| 79 | * If the perf/freq point lies between Nominal and Lowest, we can treat | ||
| 80 | * (Low perf, Low freq) and (Nom Perf, Nom freq) as 2D co-ordinates of a line | ||
| 81 | * and extrapolate the rest | ||
| 82 | * For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion | ||
| 83 | */ | ||
| 84 | static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu, | ||
| 85 | unsigned int perf) | ||
| 86 | { | ||
| 87 | static u64 max_khz; | ||
| 88 | struct cppc_perf_caps *caps = &cpu->perf_caps; | ||
| 89 | u64 mul, div; | ||
| 90 | |||
| 91 | if (caps->lowest_freq && caps->nominal_freq) { | ||
| 92 | if (perf >= caps->nominal_perf) { | ||
| 93 | mul = caps->nominal_freq; | ||
| 94 | div = caps->nominal_perf; | ||
| 95 | } else { | ||
| 96 | mul = caps->nominal_freq - caps->lowest_freq; | ||
| 97 | div = caps->nominal_perf - caps->lowest_perf; | ||
| 98 | } | ||
| 99 | } else { | ||
| 100 | if (!max_khz) | ||
| 101 | max_khz = cppc_get_dmi_max_khz(); | ||
| 102 | mul = max_khz; | ||
| 103 | div = cpu->perf_caps.highest_perf; | ||
| 104 | } | ||
| 105 | return (u64)perf * mul / div; | ||
| 106 | } | ||
| 107 | |||
| 108 | static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu, | ||
| 109 | unsigned int freq) | ||
| 110 | { | ||
| 111 | static u64 max_khz; | ||
| 112 | struct cppc_perf_caps *caps = &cpu->perf_caps; | ||
| 113 | u64 mul, div; | ||
| 114 | |||
| 115 | if (caps->lowest_freq && caps->nominal_freq) { | ||
| 116 | if (freq >= caps->nominal_freq) { | ||
| 117 | mul = caps->nominal_perf; | ||
| 118 | div = caps->nominal_freq; | ||
| 119 | } else { | ||
| 120 | mul = caps->lowest_perf; | ||
| 121 | div = caps->lowest_freq; | ||
| 122 | } | ||
| 123 | } else { | ||
| 124 | if (!max_khz) | ||
| 125 | max_khz = cppc_get_dmi_max_khz(); | ||
| 126 | mul = cpu->perf_caps.highest_perf; | ||
| 127 | div = max_khz; | ||
| 128 | } | ||
| 129 | |||
| 130 | return (u64)freq * mul / div; | ||
| 131 | } | ||
| 132 | |||
| 78 | static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, | 133 | static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, |
| 79 | unsigned int target_freq, | 134 | unsigned int target_freq, |
| 80 | unsigned int relation) | 135 | unsigned int relation) |
| @@ -86,7 +141,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, | |||
| 86 | 141 | ||
| 87 | cpu = all_cpu_data[policy->cpu]; | 142 | cpu = all_cpu_data[policy->cpu]; |
| 88 | 143 | ||
| 89 | desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz; | 144 | desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq); |
| 90 | /* Return if it is exactly the same perf */ | 145 | /* Return if it is exactly the same perf */ |
| 91 | if (desired_perf == cpu->perf_ctrls.desired_perf) | 146 | if (desired_perf == cpu->perf_ctrls.desired_perf) |
| 92 | return ret; | 147 | return ret; |
| @@ -143,24 +198,24 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
| 143 | return ret; | 198 | return ret; |
| 144 | } | 199 | } |
| 145 | 200 | ||
| 146 | cppc_dmi_max_khz = cppc_get_dmi_max_khz(); | 201 | /* Convert the lowest and nominal freq from MHz to KHz */ |
| 202 | cpu->perf_caps.lowest_freq *= 1000; | ||
| 203 | cpu->perf_caps.nominal_freq *= 1000; | ||
| 147 | 204 | ||
| 148 | /* | 205 | /* |
| 149 | * Set min to lowest nonlinear perf to avoid any efficiency penalty (see | 206 | * Set min to lowest nonlinear perf to avoid any efficiency penalty (see |
| 150 | * Section 8.4.7.1.1.5 of ACPI 6.1 spec) | 207 | * Section 8.4.7.1.1.5 of ACPI 6.1 spec) |
| 151 | */ | 208 | */ |
| 152 | policy->min = cpu->perf_caps.lowest_nonlinear_perf * cppc_dmi_max_khz / | 209 | policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf); |
| 153 | cpu->perf_caps.highest_perf; | 210 | policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf); |
| 154 | policy->max = cppc_dmi_max_khz; | ||
| 155 | 211 | ||
| 156 | /* | 212 | /* |
| 157 | * Set cpuinfo.min_freq to Lowest to make the full range of performance | 213 | * Set cpuinfo.min_freq to Lowest to make the full range of performance |
| 158 | * available if userspace wants to use any perf between lowest & lowest | 214 | * available if userspace wants to use any perf between lowest & lowest |
| 159 | * nonlinear perf | 215 | * nonlinear perf |
| 160 | */ | 216 | */ |
| 161 | policy->cpuinfo.min_freq = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz / | 217 | policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf); |
| 162 | cpu->perf_caps.highest_perf; | 218 | policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf); |
| 163 | policy->cpuinfo.max_freq = cppc_dmi_max_khz; | ||
| 164 | 219 | ||
| 165 | policy->transition_delay_us = cppc_get_transition_latency(cpu_num) / | 220 | policy->transition_delay_us = cppc_get_transition_latency(cpu_num) / |
| 166 | NSEC_PER_USEC; | 221 | NSEC_PER_USEC; |
| @@ -187,7 +242,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
| 187 | cpu->cur_policy = policy; | 242 | cpu->cur_policy = policy; |
| 188 | 243 | ||
| 189 | /* Set policy->cur to max now. The governors will adjust later. */ | 244 | /* Set policy->cur to max now. The governors will adjust later. */ |
| 190 | policy->cur = cppc_dmi_max_khz; | 245 | policy->cur = cppc_cpufreq_perf_to_khz(cpu, |
| 246 | cpu->perf_caps.highest_perf); | ||
| 191 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; | 247 | cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; |
| 192 | 248 | ||
| 193 | ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); | 249 | ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); |
