summaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-06-05 19:08:27 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2018-06-05 19:08:27 +0200
commitf4fe74cc909bf811cd9cc7fd84f5a7514e06a7e1 (patch)
tree858648091006b4b4795170560132ff6073759af6 /drivers/cpufreq
parentMerge tag 'pm-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafa... (diff)
parentMerge branches 'acpi-soc' and 'acpi-tables' (diff)
downloadlinux-f4fe74cc909bf811cd9cc7fd84f5a7514e06a7e1.tar.xz
linux-f4fe74cc909bf811cd9cc7fd84f5a7514e06a7e1.zip
Merge tag 'acpi-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull ACPI updates from Rafael Wysocki: "These update the ACPICA code in the kernel to the 20180508 upstream revision and make it support the RT patch, add CPPC v3 support to the ACPI CPPC library, add a WDAT-based watchdog quirk to prevent clashes with the RTC, add quirks to the ACPI AC and battery drivers, and update the ACPI SoC drivers. Specifics: - Update the ACPICA code in the kernel to the 20180508 upstream revision including: * iASL -tc option enhancement (Bob Moore). * Debugger improvements (Bob Moore). * Support for tables larger than 1 MB in acpidump/acpixtract (Bob Moore). * Minor fixes and cleanups (Colin Ian King, Toomas Soome). - Make the ACPICA code in the kernel support the RT patch (Sebastian Andrzej Siewior, Steven Rostedt). - Add a kmemleak annotation to the ACPICA code (Larry Finger). - Add CPPC v3 support to the ACPI CPPC library and fix two issues related to CPPC (Prashanth Prakash, Al Stone). - Add an ACPI WDAT-based watchdog quirk to prefer iTCO_wdt on systems where WDAT clashes with the RTC SRAM (Mika Westerberg). - Add some quirks to the ACPI AC and battery drivers (Carlo Caione, Hans de Goede). - Update the ACPI SoC drivers for Intel (LPSS) and AMD (APD) platforms (Akshu Agrawal, Hans de Goede). - Fix up some assorted minor issues (Al Stone, Laszlo Toth, Mathieu Malaterre)" * tag 'acpi-4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (32 commits) ACPICA: Mark acpi_ut_create_internal_object_dbg() memory allocations as non-leaks ACPI / watchdog: Prefer iTCO_wdt always when WDAT table uses RTC SRAM mailbox: PCC: erroneous error message when parsing ACPI PCCT ACPICA: Update version to 20180508 ACPICA: acpidump/acpixtract: Support for tables larger than 1MB ACPI: APD: Add AMD misc clock handler support clk: x86: Add ST oscout platform clock ACPICA: Update version to 20180427 ACPICA: Debugger: Removed direct support for EC address space in "Test Objects" ACPICA: Debugger: Add Package support for "test objects" command ACPICA: Improve error messages for the namespace root node ACPICA: Fix potential infinite loop in acpi_rs_dump_byte_list ACPICA: vsnprintf: this statement may fall through ACPICA: Tables: Fix spelling mistake in comment ACPICA: iASL: Enhance the -tc option (create AML hex file in C) ACPI: Add missing prototype_for arch_post_acpi_subsys_init() ACPI / tables: improve comments regarding acpi_parse_entries_array() ACPICA: Convert acpi_gbl_hardware lock back to an acpi_raw_spinlock ACPICA: provide abstraction for raw_spinlock_t ACPI / CPPC: Fix invalid PCC channel status errors ...
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c80
1 files changed, 68 insertions, 12 deletions
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index b15115a48775..3464580ac3ca 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -42,9 +42,6 @@
*/
static struct cppc_cpudata **all_cpu_data;
-/* Capture the max KHz from DMI */
-static u64 cppc_dmi_max_khz;
-
/* Callback function used to retrieve the max frequency from DMI */
static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
{
@@ -75,6 +72,64 @@ static u64 cppc_get_dmi_max_khz(void)
return (1000 * mhz);
}
+/*
+ * If CPPC lowest_freq and nominal_freq registers are exposed then we can
+ * use them to convert perf to freq and vice versa
+ *
+ * If the perf/freq point lies between Nominal and Lowest, we can treat
+ * (Low perf, Low freq) and (Nom Perf, Nom freq) as 2D co-ordinates of a line
+ * and extrapolate the rest
+ * For perf/freq > Nominal, we use the ratio perf:freq at Nominal for conversion
+ */
+static unsigned int cppc_cpufreq_perf_to_khz(struct cppc_cpudata *cpu,
+ unsigned int perf)
+{
+ static u64 max_khz;
+ struct cppc_perf_caps *caps = &cpu->perf_caps;
+ u64 mul, div;
+
+ if (caps->lowest_freq && caps->nominal_freq) {
+ if (perf >= caps->nominal_perf) {
+ mul = caps->nominal_freq;
+ div = caps->nominal_perf;
+ } else {
+ mul = caps->nominal_freq - caps->lowest_freq;
+ div = caps->nominal_perf - caps->lowest_perf;
+ }
+ } else {
+ if (!max_khz)
+ max_khz = cppc_get_dmi_max_khz();
+ mul = max_khz;
+ div = cpu->perf_caps.highest_perf;
+ }
+ return (u64)perf * mul / div;
+}
+
+static unsigned int cppc_cpufreq_khz_to_perf(struct cppc_cpudata *cpu,
+ unsigned int freq)
+{
+ static u64 max_khz;
+ struct cppc_perf_caps *caps = &cpu->perf_caps;
+ u64 mul, div;
+
+ if (caps->lowest_freq && caps->nominal_freq) {
+ if (freq >= caps->nominal_freq) {
+ mul = caps->nominal_perf;
+ div = caps->nominal_freq;
+ } else {
+ mul = caps->lowest_perf;
+ div = caps->lowest_freq;
+ }
+ } else {
+ if (!max_khz)
+ max_khz = cppc_get_dmi_max_khz();
+ mul = cpu->perf_caps.highest_perf;
+ div = max_khz;
+ }
+
+ return (u64)freq * mul / div;
+}
+
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
@@ -86,7 +141,7 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
cpu = all_cpu_data[policy->cpu];
- desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz;
+ desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq);
/* Return if it is exactly the same perf */
if (desired_perf == cpu->perf_ctrls.desired_perf)
return ret;
@@ -186,24 +241,24 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret;
}
- cppc_dmi_max_khz = cppc_get_dmi_max_khz();
+ /* Convert the lowest and nominal freq from MHz to KHz */
+ cpu->perf_caps.lowest_freq *= 1000;
+ cpu->perf_caps.nominal_freq *= 1000;
/*
* Set min to lowest nonlinear perf to avoid any efficiency penalty (see
* Section 8.4.7.1.1.5 of ACPI 6.1 spec)
*/
- policy->min = cpu->perf_caps.lowest_nonlinear_perf * cppc_dmi_max_khz /
- cpu->perf_caps.highest_perf;
- policy->max = cppc_dmi_max_khz;
+ policy->min = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_nonlinear_perf);
+ policy->max = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf);
/*
* Set cpuinfo.min_freq to Lowest to make the full range of performance
* available if userspace wants to use any perf between lowest & lowest
* nonlinear perf
*/
- policy->cpuinfo.min_freq = cpu->perf_caps.lowest_perf * cppc_dmi_max_khz /
- cpu->perf_caps.highest_perf;
- policy->cpuinfo.max_freq = cppc_dmi_max_khz;
+ policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.lowest_perf);
+ policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu, cpu->perf_caps.highest_perf);
policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu_num);
policy->shared_type = cpu->shared_type;
@@ -229,7 +284,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpu->cur_policy = policy;
/* Set policy->cur to max now. The governors will adjust later. */
- policy->cur = cppc_dmi_max_khz;
+ policy->cur = cppc_cpufreq_perf_to_khz(cpu,
+ cpu->perf_caps.highest_perf);
cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf;
ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls);