⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 acpi-cpufreq.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -