#include <linux/sched.h>
#include <linux/cpufreq.h>
#include <linux/compiler.h>
-#include <linux/sched.h> /* current */
#include <linux/dmi.h>
#include <linux/acpi.h>
};
#define INTEL_MSR_RANGE (0xffff)
+#define CPUID_6_ECX_APERFMPERF_CAPABILITY (0x1)
struct acpi_cpufreq_data {
struct acpi_processor_performance *acpi_data;
struct cpufreq_frequency_table *freq_table;
+ unsigned int max_freq;
unsigned int resume;
unsigned int cpu_feature;
};
perf = data->acpi_data;
- for (i = 0; i < perf->state_count; i++) {
+ for (i=0; i<perf->state_count; i++) {
if (value == perf->states[i].status)
return data->freq_table[i].frequency;
}
static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
{
int i;
+ struct acpi_processor_performance *perf;
msr &= INTEL_MSR_RANGE;
- for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
- if (msr == data->freq_table[i].index)
+ perf = data->acpi_data;
+
+ for (i=0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ if (msr == perf->states[data->freq_table[i].index].status)
return data->freq_table[i].frequency;
}
return data->freq_table[0].frequency;
}
}
-static void wrport(u16 port, u8 bit_width, u32 value)
-{
- if (bit_width <= 8) {
- outb(value, port);
- } else if (bit_width <= 16) {
- outw(value, port);
- } else if (bit_width <= 32) {
- outl(value, port);
- }
-}
-
-static void rdport(u16 port, u8 bit_width, u32 * ret)
-{
- *ret = 0;
- if (bit_width <= 8) {
- *ret = inb(port);
- } else if (bit_width <= 16) {
- *ret = inw(port);
- } else if (bit_width <= 32) {
- *ret = inl(port);
- }
-}
-
struct msr_addr {
u32 reg;
};
rdmsr(cmd->addr.msr.reg, cmd->val, h);
break;
case SYSTEM_IO_CAPABLE:
- rdport(cmd->addr.io.port, cmd->addr.io.bit_width, &cmd->val);
+ acpi_os_read_port((acpi_io_address)cmd->addr.io.port,
+ &cmd->val,
+ (u32)cmd->addr.io.bit_width);
break;
default:
break;
wrmsr(cmd->addr.msr.reg, cmd->val, h);
break;
case SYSTEM_IO_CAPABLE:
- wrport(cmd->addr.io.port, cmd->addr.io.bit_width, cmd->val);
+ acpi_os_write_port((acpi_io_address)cmd->addr.io.port,
+ cmd->val,
+ (u32)cmd->addr.io.bit_width);
break;
default:
break;
}
}
-static inline void drv_read(struct drv_cmd *cmd)
+static void drv_read(struct drv_cmd *cmd)
{
cpumask_t saved_mask = current->cpus_allowed;
cmd->val = 0;
set_cpus_allowed(current, cmd->mask);
do_drv_read(cmd);
set_cpus_allowed(current, saved_mask);
-
}
static void drv_write(struct drv_cmd *cmd)
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)
{
struct acpi_cpufreq_data *data = drv_data[cpu];
unsigned int cur_freq;
unsigned int i;
- for (i = 0; i < 100; i++) {
+ for (i=0; i<100; i++) {
cur_freq = extract_freq(get_cur_val(mask), data);
if (cur_freq == freq)
return 1;
cpumask_t online_policy_cpus;
struct drv_cmd cmd;
unsigned int msr;
- unsigned int next_state = 0;
- unsigned int next_perf_state = 0;
+ unsigned int next_state = 0; /* Index into freq_table */
+ unsigned int next_perf_state = 0; /* Index into perf table */
unsigned int i;
int result = 0;
dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu);
if (unlikely(data == NULL ||
- data->acpi_data == NULL || data->freq_table == NULL)) {
+ data->acpi_data == NULL || data->freq_table == NULL)) {
return -ENODEV;
}
online_policy_cpus = policy->cpus;
#endif
- cmd.val = get_cur_val(online_policy_cpus);
- freqs.old = extract_freq(cmd.val, data);
- freqs.new = data->freq_table[next_state].frequency;
next_perf_state = data->freq_table[next_state].index;
- if (freqs.new == freqs.old) {
+ if (perf->state == next_perf_state) {
if (unlikely(data->resume)) {
dprintk("Called after resume, resetting to P%d\n",
next_perf_state);
msr =
(u32) perf->states[next_perf_state].
control & INTEL_MSR_RANGE;
+ cmd.val = get_cur_val(online_policy_cpus);
cmd.val = (cmd.val & ~INTEL_MSR_RANGE) | msr;
break;
case SYSTEM_IO_CAPABLE:
else
cpu_set(policy->cpu, cmd.mask);
+ freqs.old = perf->states[perf->state].core_frequency * 1000;
+ freqs.new = data->freq_table[next_state].frequency;
for_each_cpu_mask(i, cmd.mask) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
unsigned long freq;
unsigned long freqn = perf->states[0].core_frequency * 1000;
- for (i = 0; i < (perf->state_count - 1); i++) {
+ for (i=0; i<(perf->state_count-1); i++) {
freq = freqn;
- freqn = perf->states[i + 1].core_frequency * 1000;
+ freqn = perf->states[i+1].core_frequency * 1000;
if ((2 * cpu_khz) > (freqn + freq)) {
perf->state = i;
return freq;
}
}
- perf->state = perf->state_count - 1;
+ perf->state = perf->state_count-1;
return freqn;
} else {
/* assume CPU is at P0... */
return 0;
}
+#ifdef CONFIG_SMP
/*
* Some BIOSes do SW_ANY coordination internally, either set it up in hw
* or do it in BIOS firmware and won't inform about it to OS. If not
},
{ }
};
+#endif
static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
unsigned int valid_states = 0;
unsigned int cpu = policy->cpu;
struct acpi_cpufreq_data *data;
- unsigned int l, h;
unsigned int result = 0;
struct cpuinfo_x86 *c = &cpu_data[policy->cpu];
struct acpi_processor_performance *perf;
data->acpi_data = acpi_perf_data[cpu];
drv_data[cpu] = data;
- if (cpu_has(c, X86_FEATURE_CONSTANT_TSC)) {
+ if (cpu_has(c, X86_FEATURE_CONSTANT_TSC))
acpi_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS;
- }
result = acpi_processor_register_performance(data->acpi_data, cpu);
if (result)
perf = data->acpi_data;
policy->shared_type = perf->shared_type;
+
/*
- * Will let policy->cpus know about dependency only when software
+ * Will let policy->cpus know about dependency only when software
* coordination is required.
*/
if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL ||
goto err_unreg;
}
- data->freq_table =
- kmalloc(sizeof(struct cpufreq_frequency_table) *
- (perf->state_count + 1), GFP_KERNEL);
+ data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
+ (perf->state_count+1), GFP_KERNEL);
if (!data->freq_table) {
result = -ENOMEM;
goto err_unreg;
/* detect transition latency */
policy->cpuinfo.transition_latency = 0;
- for (i = 0; i < perf->state_count; i++) {
+ for (i=0; i<perf->state_count; i++) {
if ((perf->states[i].transition_latency * 1000) >
policy->cpuinfo.transition_latency)
policy->cpuinfo.transition_latency =
}
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ data->max_freq = perf->states[0].core_frequency * 1000;
/* table init */
- for (i = 0; i < perf->state_count; i++) {
- if (i > 0 && perf->states[i].core_frequency ==
- perf->states[i - 1].core_frequency)
+ for (i=0; i<perf->state_count; i++) {
+ if (i>0 && perf->states[i].core_frequency ==
+ perf->states[i-1].core_frequency)
continue;
data->freq_table[valid_states].index = i;
perf->states[i].core_frequency * 1000;
valid_states++;
}
- data->freq_table[perf->state_count].frequency = CPUFREQ_TABLE_END;
+ data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
+ perf->state = 0;
result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
- if (result) {
+ if (result)
goto err_freqfree;
- }
- switch (data->cpu_feature) {
+ switch (perf->control_register.space_id) {
case ACPI_ADR_SPACE_SYSTEM_IO:
/* Current speed is unknown and not detectable by IO port */
policy->cur = acpi_cpufreq_guess_freq(data, policy->cpu);
break;
case ACPI_ADR_SPACE_FIXED_HARDWARE:
- get_cur_freq_on_cpu(cpu);
+ acpi_cpufreq_driver.get = get_cur_freq_on_cpu;
+ policy->cur = get_cur_freq_on_cpu(cpu);
break;
default:
break;
/* notify BIOS that we exist */
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);
for (i = 0; i < perf->state_count; i++)
dprintk(" %cP%d: %d MHz, %d mW, %d uS\n",
return result;
- err_freqfree:
+err_freqfree:
kfree(data->freq_table);
- err_unreg:
+err_unreg:
acpi_processor_unregister_performance(perf, cpu);
- err_free:
+err_free:
kfree(data);
drv_data[cpu] = NULL;
static struct cpufreq_driver acpi_cpufreq_driver = {
.verify = acpi_cpufreq_verify,
.target = acpi_cpufreq_target,
- .get = get_cur_freq_on_cpu,
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
.resume = acpi_cpufreq_resume,
module_param(acpi_pstate_strict, uint, 0644);
MODULE_PARM_DESC(acpi_pstate_strict,
- "value 0 or non-zero. non-zero -> strict ACPI checks are performed during frequency changes.");
+ "value 0 or non-zero. non-zero -> strict ACPI checks are "
+ "performed during frequency changes.");
late_initcall(acpi_cpufreq_init);
module_exit(acpi_cpufreq_exit);