📄 acpi-cpufreq.c
字号:
/* * acpi-cpufreq.c - ACPI Processor P-States Driver ($Revision: 1.4 $) * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/smp.h>#include <linux/sched.h>#include <linux/cpufreq.h>#include <linux/compiler.h>#include <linux/dmi.h>#include <linux/acpi.h>#include <acpi/processor.h>#include <asm/io.h>#include <asm/msr.h>#include <asm/processor.h>#include <asm/cpufeature.h>#include <asm/delay.h>#include <asm/uaccess.h>#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "acpi-cpufreq", msg)MODULE_AUTHOR("Paul Diefenbaugh, Dominik Brodowski");MODULE_DESCRIPTION("ACPI Processor P-States Driver");MODULE_LICENSE("GPL");enum { UNDEFINED_CAPABLE = 0, SYSTEM_INTEL_MSR_CAPABLE, SYSTEM_IO_CAPABLE,};#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;};static struct acpi_cpufreq_data *drv_data[NR_CPUS];/* acpi_perf_data is a pointer to percpu data. */static struct acpi_processor_performance *acpi_perf_data;static struct cpufreq_driver acpi_cpufreq_driver;static unsigned int acpi_pstate_strict;static int check_est_cpu(unsigned int cpuid){ struct cpuinfo_x86 *cpu = &cpu_data(cpuid); if (cpu->x86_vendor != X86_VENDOR_INTEL || !cpu_has(cpu, X86_FEATURE_EST)) return 0; return 1;}static unsigned extract_io(u32 value, struct acpi_cpufreq_data *data){ struct acpi_processor_performance *perf; int i; perf = data->acpi_data; for (i=0; i<perf->state_count; i++) { if (value == perf->states[i].status) return data->freq_table[i].frequency; } return 0;}static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data){ int i; struct acpi_processor_performance *perf; msr &= INTEL_MSR_RANGE; 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 unsigned extract_freq(u32 val, struct acpi_cpufreq_data *data){ switch (data->cpu_feature) { case SYSTEM_INTEL_MSR_CAPABLE: return extract_msr(val, data); case SYSTEM_IO_CAPABLE: return extract_io(val, data); default: return 0; }}struct msr_addr { u32 reg;};struct io_addr { u16 port; u8 bit_width;};typedef union { struct msr_addr msr; struct io_addr io;} drv_addr_union;struct drv_cmd { unsigned int type; cpumask_t mask; drv_addr_union addr; u32 val;};static void do_drv_read(struct drv_cmd *cmd){ u32 h; switch (cmd->type) { case SYSTEM_INTEL_MSR_CAPABLE: rdmsr(cmd->addr.msr.reg, cmd->val, h); break; case SYSTEM_IO_CAPABLE: acpi_os_read_port((acpi_io_address)cmd->addr.io.port, &cmd->val, (u32)cmd->addr.io.bit_width); break; default: break; }}static void do_drv_write(struct drv_cmd *cmd){ u32 lo, hi; switch (cmd->type) { case SYSTEM_INTEL_MSR_CAPABLE: rdmsr(cmd->addr.msr.reg, lo, hi); lo = (lo & ~INTEL_MSR_RANGE) | (cmd->val & INTEL_MSR_RANGE); wrmsr(cmd->addr.msr.reg, lo, hi); break; case SYSTEM_IO_CAPABLE: acpi_os_write_port((acpi_io_address)cmd->addr.io.port, cmd->val, (u32)cmd->addr.io.bit_width); break; default: break; }}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){ cpumask_t saved_mask = current->cpus_allowed; unsigned int i; for_each_cpu_mask(i, cmd->mask) { set_cpus_allowed(current, cpumask_of_cpu(i)); do_drv_write(cmd); } set_cpus_allowed(current, saved_mask); return;}static u32 get_cur_val(cpumask_t mask){ struct acpi_processor_performance *perf; struct drv_cmd cmd; if (unlikely(cpus_empty(mask))) return 0; switch (drv_data[first_cpu(mask)]->cpu_feature) { case SYSTEM_INTEL_MSR_CAPABLE: cmd.type = SYSTEM_INTEL_MSR_CAPABLE; cmd.addr.msr.reg = MSR_IA32_PERF_STATUS; break; case SYSTEM_IO_CAPABLE: cmd.type = SYSTEM_IO_CAPABLE; perf = drv_data[first_cpu(mask)]->acpi_data; cmd.addr.io.port = perf->control_register.address; cmd.addr.io.bit_width = perf->control_register.bit_width; break; default: return 0; } cmd.mask = mask; drv_read(&cmd); dprintk("get_cur_val = %u\n", 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){ struct acpi_cpufreq_data *data = drv_data[cpu]; unsigned int freq; dprintk("get_cur_freq_on_cpu (%d)\n", cpu); if (unlikely(data == NULL || data->acpi_data == NULL || data->freq_table == NULL)) { return 0; } freq = extract_freq(get_cur_val(cpumask_of_cpu(cpu)), data); dprintk("cur freq = %u\n", freq); return freq;}static unsigned int check_freqs(cpumask_t mask, unsigned int freq, struct acpi_cpufreq_data *data){ unsigned int cur_freq; unsigned int i; for (i=0; i<100; i++) { cur_freq = extract_freq(get_cur_val(mask), data); if (cur_freq == freq) return 1; udelay(10); } return 0;}static int acpi_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation){ struct acpi_cpufreq_data *data = drv_data[policy->cpu]; struct acpi_processor_performance *perf; struct cpufreq_freqs freqs; cpumask_t online_policy_cpus; struct drv_cmd cmd; 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)) { return -ENODEV; } perf = data->acpi_data; result = cpufreq_frequency_table_target(policy, data->freq_table, target_freq, relation, &next_state); if (unlikely(result)) return -ENODEV;#ifdef CONFIG_HOTPLUG_CPU /* cpufreq holds the hotplug lock, so we are safe from here on */ cpus_and(online_policy_cpus, cpu_online_map, policy->cpus);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -