Future AMD processors, starting with Family 16h, can provide software
with feedback on how the workload may respond to frequency change --
memory-bound workloads will not benefit from higher frequency, where
as compute-bound workloads will. This patch enables this "frequency
sensitivity feedback" to aid the ondemand governor to make better
frequency change decisions by hooking into the powersave bias.

Signed-off-by: Jacob Shin <[email protected]>
---
 arch/x86/include/uapi/asm/msr-index.h  |    1 +
 drivers/cpufreq/Kconfig.x86            |   10 +++
 drivers/cpufreq/Makefile               |    1 +
 drivers/cpufreq/amd_freq_sensitivity.c |  147 ++++++++++++++++++++++++++++++++
 4 files changed, 159 insertions(+)
 create mode 100644 drivers/cpufreq/amd_freq_sensitivity.c

diff --git a/arch/x86/include/uapi/asm/msr-index.h 
b/arch/x86/include/uapi/asm/msr-index.h
index 7a060f4..b2e6c49 100644
--- a/arch/x86/include/uapi/asm/msr-index.h
+++ b/arch/x86/include/uapi/asm/msr-index.h
@@ -173,6 +173,7 @@
 #define MSR_AMD64_TSC_RATIO            0xc0000104
 #define MSR_AMD64_NB_CFG               0xc001001f
 #define MSR_AMD64_PATCH_LOADER         0xc0010020
+#define MSR_AMD64_FREQ_SENSITIVITY     0xc0010080
 #define MSR_AMD64_OSVW_ID_LENGTH       0xc0010140
 #define MSR_AMD64_OSVW_STATUS          0xc0010141
 #define MSR_AMD64_DC_CFG               0xc0011022
diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
index d7dc0ed..6c714b0 100644
--- a/drivers/cpufreq/Kconfig.x86
+++ b/drivers/cpufreq/Kconfig.x86
@@ -129,6 +129,16 @@ config X86_POWERNOW_K8
 
          For details, take a look at <file:Documentation/cpu-freq/>.
 
+config X86_AMD_FREQ_SENSITIVITY
+       tristate "AMD 'frequency sensitivity feedback' powersave bias"
+       depends on CPU_FREQ_GOV_ONDEMAND && X86_ACPI_CPUFREQ
+       help
+         This adds support for 'frequency sensitivity feedback' feature on
+         supported AMD processors, which hooks into the ondemand governor's
+         powersave bias to influence frequency change decisions.
+
+         If in doubt, say N.
+
 config X86_GX_SUSPMOD
        tristate "Cyrix MediaGX/NatSemi Geode Suspend Modulation"
        depends on X86_32 && PCI
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 863fd18..01dfdaf 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_X86_SPEEDSTEP_CENTRINO)  += speedstep-centrino.o
 obj-$(CONFIG_X86_P4_CLOCKMOD)          += p4-clockmod.o
 obj-$(CONFIG_X86_CPUFREQ_NFORCE2)      += cpufreq-nforce2.o
 obj-$(CONFIG_X86_INTEL_PSTATE)         += intel_pstate.o
+obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o
 
 
##################################################################################
 # ARM SoC drivers
diff --git a/drivers/cpufreq/amd_freq_sensitivity.c 
b/drivers/cpufreq/amd_freq_sensitivity.c
new file mode 100644
index 0000000..997feb0
--- /dev/null
+++ b/drivers/cpufreq/amd_freq_sensitivity.c
@@ -0,0 +1,147 @@
+/*
+ * amd_freq_sensitivity.c: AMD "frequency sensitivity feedback" powersave bias
+ *                         for ondemand governor.
+ *
+ * Copyright (C) 2013 Advanced Micro Devices, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/percpu-defs.h>
+#include <linux/init.h>
+
+#include "cpufreq_governor.h"
+
+#define PROC_FEEDBACK_INTERFACE_SHIFT          11
+#define CLASS_CODE_SHIFT                       56
+#define CLASS_CODE_MASK                                0xff
+#define CLASS_CODE_CORE_FREQUENCY_SENSITIVITY  0x01
+
+static u32 msr_addr;
+
+struct cpu_data_t {
+       u64 actual;
+       u64 reference;
+       unsigned int freq_prev;
+};
+
+static DEFINE_PER_CPU(struct cpu_data_t, cpu_data);
+
+static unsigned int amd_powersave_bias_target(struct cpufreq_policy *policy,
+               unsigned int freq_next, unsigned int relation)
+{
+       int sensitivity;
+       long d_actual, d_reference;
+       struct msr actual, reference;
+       struct cpu_data_t *data = &per_cpu(cpu_data, policy->cpu);
+       struct dbs_data *od_data = policy->governor_data;
+       struct od_dbs_tuners *od_tuners = od_data->tuners;
+       struct od_cpu_dbs_info_s *od_info =
+               od_data->cdata->get_cpu_dbs_info_s(policy->cpu);
+
+       rdmsr_on_cpu(policy->cpu, msr_addr, &actual.l, &actual.h);
+       rdmsr_on_cpu(policy->cpu, msr_addr + 1, &reference.l, &reference.h);
+       actual.h &= 0x00ffffff;
+       reference.h &= 0x00ffffff;
+
+       if (!od_info->freq_table)
+               goto out;
+
+       /* counter wrapped around, so push until next check */
+       if (actual.q < data->actual || reference.q < data->reference) {
+               freq_next = policy->cur;
+               goto out;
+       }
+
+       d_actual = actual.q - data->actual;
+       d_reference = reference.q - data->reference;
+
+       /* divide by 0, so push as well */
+       if (d_reference == 0) {
+               freq_next = policy->cur;
+               goto out;
+       }
+
+       sensitivity = 1000 - (1000 * (d_reference - d_actual) / d_reference);
+
+       if (sensitivity > 1000)
+               sensitivity = 1000;
+       else if (sensitivity < 0)
+               sensitivity = 0;
+
+       /* this workload is not CPU bound, so choose a lower freq */
+       if (sensitivity < od_tuners->powersave_bias) {
+               if (data->freq_prev == policy->cur)
+                       freq_next = policy->cur;
+
+               if (freq_next > policy->cur)
+                       freq_next = policy->cur;
+               else if (freq_next < policy->cur)
+                       freq_next = policy->min;
+               else {
+                       unsigned int index = 0;
+
+                       cpufreq_frequency_table_target(policy,
+                               od_info->freq_table, policy->cur - 1,
+                               CPUFREQ_RELATION_H, &index);
+                       freq_next = od_info->freq_table[index].frequency;
+               }
+
+               data->freq_prev = freq_next;
+       } else
+               data->freq_prev = 0;
+
+out:
+       data->actual = actual.q;
+       data->reference = reference.q;
+       return freq_next;
+}
+
+static int __init amd_freq_sensitivity_init(void)
+{
+       int i;
+       u32 eax, edx, dummy;
+
+       if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+               return -ENODEV;
+
+       cpuid(0x80000007, &eax, &dummy, &dummy, &edx);
+
+       if (!(edx & (1 << PROC_FEEDBACK_INTERFACE_SHIFT)))
+               return -ENODEV;
+
+       for (i = 0; i < (eax & 0xf); i++) {
+               u64 val;
+               u32 addr = MSR_AMD64_FREQ_SENSITIVITY + (i * 2);
+
+               rdmsrl(addr, val);
+
+               if (((val >> CLASS_CODE_SHIFT) & CLASS_CODE_MASK)
+                       == CLASS_CODE_CORE_FREQUENCY_SENSITIVITY) {
+                       msr_addr = addr;
+                       break;
+               }
+       }
+
+       if (!msr_addr)
+               return -ENODEV;
+
+       od_register_powersave_bias_function(amd_powersave_bias_target);
+       return 0;
+}
+module_init(amd_freq_sensitivity_init);
+
+static void __exit amd_freq_sensitivity_exit(void)
+{
+       od_unregister_powersave_bias_function();
+}
+module_exit(amd_freq_sensitivity_exit);
+
+MODULE_AUTHOR("Jacob Shin <[email protected]>");
+MODULE_DESCRIPTION("AMD 'frequency sensitivity feedback' powersave bias for "
+               "the ondemand governor.");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to