|
@@ -58,10 +58,12 @@ enum {
|
|
};
|
|
};
|
|
|
|
|
|
#define INTEL_MSR_RANGE (0xffff)
|
|
#define INTEL_MSR_RANGE (0xffff)
|
|
|
|
+#define CPUID_6_ECX_APERFMPERF_CAPABILITY (0x1)
|
|
|
|
|
|
struct acpi_cpufreq_data {
|
|
struct acpi_cpufreq_data {
|
|
struct acpi_processor_performance *acpi_data;
|
|
struct acpi_processor_performance *acpi_data;
|
|
struct cpufreq_frequency_table *freq_table;
|
|
struct cpufreq_frequency_table *freq_table;
|
|
|
|
+ unsigned int max_freq;
|
|
unsigned int resume;
|
|
unsigned int resume;
|
|
unsigned int cpu_feature;
|
|
unsigned int cpu_feature;
|
|
};
|
|
};
|
|
@@ -258,6 +260,100 @@ static u32 get_cur_val(cpumask_t mask)
|
|
return cmd.val;
|
|
return cmd.val;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Return the measured active (C0) frequency on this CPU since last call
|
|
|
|
+ * to this function.
|
|
|
|
+ * Input: cpu number
|
|
|
|
+ * Return: Average CPU frequency in terms of max frequency (zero on error)
|
|
|
|
+ *
|
|
|
|
+ * We use IA32_MPERF and IA32_APERF MSRs to get the measured performance
|
|
|
|
+ * over a period of time, while CPU is in C0 state.
|
|
|
|
+ * IA32_MPERF counts at the rate of max advertised frequency
|
|
|
|
+ * IA32_APERF counts at the rate of actual CPU frequency
|
|
|
|
+ * Only IA32_APERF/IA32_MPERF ratio is architecturally defined and
|
|
|
|
+ * no meaning should be associated with absolute values of these MSRs.
|
|
|
|
+ */
|
|
|
|
+static unsigned int get_measured_perf(unsigned int cpu)
|
|
|
|
+{
|
|
|
|
+ union {
|
|
|
|
+ struct {
|
|
|
|
+ u32 lo;
|
|
|
|
+ u32 hi;
|
|
|
|
+ } split;
|
|
|
|
+ u64 whole;
|
|
|
|
+ } aperf_cur, mperf_cur;
|
|
|
|
+
|
|
|
|
+ cpumask_t saved_mask;
|
|
|
|
+ unsigned int perf_percent;
|
|
|
|
+ unsigned int retval;
|
|
|
|
+
|
|
|
|
+ saved_mask = current->cpus_allowed;
|
|
|
|
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));
|
|
|
|
+ if (get_cpu() != cpu) {
|
|
|
|
+ /* We were not able to run on requested processor */
|
|
|
|
+ put_cpu();
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rdmsr(MSR_IA32_APERF, aperf_cur.split.lo, aperf_cur.split.hi);
|
|
|
|
+ rdmsr(MSR_IA32_MPERF, mperf_cur.split.lo, mperf_cur.split.hi);
|
|
|
|
+
|
|
|
|
+ wrmsr(MSR_IA32_APERF, 0,0);
|
|
|
|
+ wrmsr(MSR_IA32_MPERF, 0,0);
|
|
|
|
+
|
|
|
|
+#ifdef __i386__
|
|
|
|
+ /*
|
|
|
|
+ * We dont want to do 64 bit divide with 32 bit kernel
|
|
|
|
+ * Get an approximate value. Return failure in case we cannot get
|
|
|
|
+ * an approximate value.
|
|
|
|
+ */
|
|
|
|
+ if (unlikely(aperf_cur.split.hi || mperf_cur.split.hi)) {
|
|
|
|
+ int shift_count;
|
|
|
|
+ u32 h;
|
|
|
|
+
|
|
|
|
+ h = max_t(u32, aperf_cur.split.hi, mperf_cur.split.hi);
|
|
|
|
+ shift_count = fls(h);
|
|
|
|
+
|
|
|
|
+ aperf_cur.whole >>= shift_count;
|
|
|
|
+ mperf_cur.whole >>= shift_count;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (((unsigned long)(-1) / 100) < aperf_cur.split.lo) {
|
|
|
|
+ int shift_count = 7;
|
|
|
|
+ aperf_cur.split.lo >>= shift_count;
|
|
|
|
+ mperf_cur.split.lo >>= shift_count;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (aperf_cur.split.lo && mperf_cur.split.lo) {
|
|
|
|
+ perf_percent = (aperf_cur.split.lo * 100) / mperf_cur.split.lo;
|
|
|
|
+ } else {
|
|
|
|
+ perf_percent = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+#else
|
|
|
|
+ if (unlikely(((unsigned long)(-1) / 100) < aperf_cur.whole)) {
|
|
|
|
+ int shift_count = 7;
|
|
|
|
+ aperf_cur.whole >>= shift_count;
|
|
|
|
+ mperf_cur.whole >>= shift_count;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (aperf_cur.whole && mperf_cur.whole) {
|
|
|
|
+ perf_percent = (aperf_cur.whole * 100) / mperf_cur.whole;
|
|
|
|
+ } else {
|
|
|
|
+ perf_percent = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ retval = drv_data[cpu]->max_freq * perf_percent / 100;
|
|
|
|
+
|
|
|
|
+ put_cpu();
|
|
|
|
+ set_cpus_allowed(current, saved_mask);
|
|
|
|
+
|
|
|
|
+ dprintk("cpu %d: performance percent %d\n", cpu, perf_percent);
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
|
|
static unsigned int get_cur_freq_on_cpu(unsigned int cpu)
|
|
{
|
|
{
|
|
struct acpi_cpufreq_data *data = drv_data[cpu];
|
|
struct acpi_cpufreq_data *data = drv_data[cpu];
|
|
@@ -497,7 +593,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|
unsigned int valid_states = 0;
|
|
unsigned int valid_states = 0;
|
|
unsigned int cpu = policy->cpu;
|
|
unsigned int cpu = policy->cpu;
|
|
struct acpi_cpufreq_data *data;
|
|
struct acpi_cpufreq_data *data;
|
|
- unsigned int l, h;
|
|
|
|
unsigned int result = 0;
|
|
unsigned int result = 0;
|
|
struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
|
|
struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
|
|
struct acpi_processor_performance *perf;
|
|
struct acpi_processor_performance *perf;
|
|
@@ -591,6 +686,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|
}
|
|
}
|
|
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
|
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
|
|
|
|
|
|
|
+ data->max_freq = perf->states[0].core_frequency * 1000;
|
|
/* table init */
|
|
/* table init */
|
|
for (i = 0; i < perf->state_count; i++) {
|
|
for (i = 0; i < perf->state_count; i++) {
|
|
if (i > 0 && perf->states[i].core_frequency ==
|
|
if (i > 0 && perf->states[i].core_frequency ==
|
|
@@ -625,6 +721,15 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|
/* notify BIOS that we exist */
|
|
/* notify BIOS that we exist */
|
|
acpi_processor_notify_smm(THIS_MODULE);
|
|
acpi_processor_notify_smm(THIS_MODULE);
|
|
|
|
|
|
|
|
+ /* Check for APERF/MPERF support in hardware */
|
|
|
|
+ if (c->x86_vendor == X86_VENDOR_INTEL && c->cpuid_level >= 6) {
|
|
|
|
+ unsigned int ecx;
|
|
|
|
+ ecx = cpuid_ecx(6);
|
|
|
|
+ if (ecx & CPUID_6_ECX_APERFMPERF_CAPABILITY) {
|
|
|
|
+ acpi_cpufreq_driver.getavg = get_measured_perf;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
dprintk("CPU%u - ACPI performance management activated.\n", cpu);
|
|
dprintk("CPU%u - ACPI performance management activated.\n", cpu);
|
|
for (i = 0; i < perf->state_count; i++)
|
|
for (i = 0; i < perf->state_count; i++)
|
|
dprintk(" %cP%d: %d MHz, %d mW, %d uS\n",
|
|
dprintk(" %cP%d: %d MHz, %d mW, %d uS\n",
|