📄 cpufreq.c
字号:
/* * 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> * * Feb 2008 - Liu Jinsong <jinsong.liu@intel.com> * porting acpi-cpufreq.c from Linux 2.6.23 to Xen hypervisor * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * 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 <xen/types.h>#include <xen/errno.h>#include <xen/delay.h>#include <xen/cpumask.h>#include <xen/timer.h>#include <xen/xmalloc.h>#include <asm/bug.h>#include <asm/msr.h>#include <asm/io.h>#include <asm/config.h>#include <asm/processor.h>#include <asm/percpu.h>#include <asm/cpufeature.h>#include <acpi/acpi.h>#include <acpi/cpufreq/cpufreq.h>struct processor_pminfo processor_pminfo[NR_CPUS];struct cpufreq_policy xen_px_policy[NR_CPUS];static cpumask_t *cpufreq_dom_pt;static cpumask_t cpufreq_dom_mask;static unsigned int cpufreq_dom_max;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 processor_performance *acpi_data; struct cpufreq_frequency_table *freq_table; unsigned int max_freq; unsigned int cpu_feature;};static struct acpi_cpufreq_data *drv_data[NR_CPUS];static struct cpufreq_driver acpi_cpufreq_driver;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 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 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(void *drvcmd){ struct drv_cmd *cmd; u32 lo, hi; cmd = (struct drv_cmd *)drvcmd; 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){ cmd->val = 0; do_drv_read(cmd);}static void drv_write(struct drv_cmd *cmd){ on_selected_cpus( cmd->mask, do_drv_write, (void *)cmd, 0, 0);}static u32 get_cur_val(cpumask_t mask){ struct 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); 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 void __get_measured_perf(void *perf_percent){ unsigned int *ratio = perf_percent; union { struct { uint32_t lo; uint32_t hi; } split; uint64_t whole; } aperf_cur, mperf_cur; 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); 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) *ratio = (aperf_cur.whole * 100) / mperf_cur.whole; else *ratio = 0;}static unsigned int get_measured_perf(unsigned int cpu){ unsigned int retval, perf_percent; cpumask_t cpumask; if (!cpu_online(cpu)) return 0; cpumask = cpumask_of_cpu(cpu); on_selected_cpus(cpumask, __get_measured_perf, (void *)&perf_percent,0,1); retval = drv_data[cpu]->max_freq * perf_percent / 100; return retval;}static unsigned int get_cur_freq_on_cpu(unsigned int cpu){ struct acpi_cpufreq_data *data = drv_data[cpu]; unsigned int freq; 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); 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 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 */ int result = 0; if (unlikely(data == NULL || data->acpi_data == NULL || data->freq_table == NULL)) { return -ENODEV; } perf = data->acpi_data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -