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

📄 powernow-k8.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *   (c) 2003, 2004, 2005 Advanced Micro Devices, Inc. *  Your use of this code is subject to the terms and conditions of the *  GNU general public license version 2. See "COPYING" or *  http://www.gnu.org/licenses/gpl.html * *  Support : mark.langsdorf@amd.com * *  Based on the powernow-k7.c module written by Dave Jones. *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs *  (C) 2004 Dominik Brodowski <linux@brodo.de> *  (C) 2004 Pavel Machek <pavel@suse.cz> *  Licensed under the terms of the GNU GPL License version 2. *  Based upon datasheets & sample CPUs kindly provided by AMD. * *  Valuable input gratefully received from Dave Jones, Pavel Machek, *  Dominik Brodowski, and others. *  Originally developed by Paul Devriendt. *  Processor information obtained from Chapter 9 (Power and Thermal Management) *  of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD *  Opteron Processors" available for download from www.amd.com * *  Tables for specific CPUs can be infrerred from *     http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf */#include <linux/kernel.h>#include <linux/smp.h>#include <linux/module.h>#include <linux/init.h>#include <linux/cpufreq.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/cpumask.h>#include <linux/sched.h>	/* for current / set_cpus_allowed() */#include <asm/msr.h>#include <asm/io.h>#include <asm/delay.h>#ifdef CONFIG_X86_POWERNOW_K8_ACPI#include <linux/acpi.h>#include <acpi/processor.h>#endif#define PFX "powernow-k8: "#define BFX PFX "BIOS error: "#define VERSION "version 1.50.4"#include "powernow-k8.h"/* serialize freq changes  */static DECLARE_MUTEX(fidvid_sem);static struct powernow_k8_data *powernow_data[NR_CPUS];#ifndef CONFIG_SMPstatic cpumask_t cpu_core_map[1];#endif/* Return a frequency in MHz, given an input fid */static u32 find_freq_from_fid(u32 fid){	return 800 + (fid * 100);}/* Return a frequency in KHz, given an input fid */static u32 find_khz_freq_from_fid(u32 fid){	return 1000 * find_freq_from_fid(fid);}/* Return a voltage in miliVolts, given an input vid */static u32 find_millivolts_from_vid(struct powernow_k8_data *data, u32 vid){	return 1550-vid*25;}/* Return the vco fid for an input fid * * Each "low" fid has corresponding "high" fid, and you can get to "low" fids * only from corresponding high fids. This returns "high" fid corresponding to * "low" one. */static u32 convert_fid_to_vco_fid(u32 fid){	if (fid < HI_FID_TABLE_BOTTOM) {		return 8 + (2 * fid);	} else {		return fid;	}}/* * Return 1 if the pending bit is set. Unless we just instructed the processor * to transition to a new state, seeing this bit set is really bad news. */static int pending_bit_stuck(void){	u32 lo, hi;	rdmsr(MSR_FIDVID_STATUS, lo, hi);	return lo & MSR_S_LO_CHANGE_PENDING ? 1 : 0;}/* * Update the global current fid / vid values from the status msr. * Returns 1 on error. */static int query_current_values_with_pending_wait(struct powernow_k8_data *data){	u32 lo, hi;	u32 i = 0;	do {		if (i++ > 10000) {			dprintk("detected change pending stuck\n");			return 1;		}		rdmsr(MSR_FIDVID_STATUS, lo, hi);	} while (lo & MSR_S_LO_CHANGE_PENDING);	data->currvid = hi & MSR_S_HI_CURRENT_VID;	data->currfid = lo & MSR_S_LO_CURRENT_FID;	return 0;}/* the isochronous relief time */static void count_off_irt(struct powernow_k8_data *data){	udelay((1 << data->irt) * 10);	return;}/* the voltage stabalization time */static void count_off_vst(struct powernow_k8_data *data){	udelay(data->vstable * VST_UNITS_20US);	return;}/* need to init the control msr to a safe value (for each cpu) */static void fidvid_msr_init(void){	u32 lo, hi;	u8 fid, vid;	rdmsr(MSR_FIDVID_STATUS, lo, hi);	vid = hi & MSR_S_HI_CURRENT_VID;	fid = lo & MSR_S_LO_CURRENT_FID;	lo = fid | (vid << MSR_C_LO_VID_SHIFT);	hi = MSR_C_HI_STP_GNT_BENIGN;	dprintk("cpu%d, init lo 0x%x, hi 0x%x\n", smp_processor_id(), lo, hi);	wrmsr(MSR_FIDVID_CTL, lo, hi);}/* write the new fid value along with the other control fields to the msr */static int write_new_fid(struct powernow_k8_data *data, u32 fid){	u32 lo;	u32 savevid = data->currvid;	u32 i = 0;	if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {		printk(KERN_ERR PFX "internal error - overflow on fid write\n");		return 1;	}	lo = fid | (data->currvid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;	dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",		fid, lo, data->plllock * PLL_LOCK_CONVERSION);	do {		wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);		if (i++ > 100) {			printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");			return 1;		}				} while (query_current_values_with_pending_wait(data));	count_off_irt(data);	if (savevid != data->currvid) {		printk(KERN_ERR PFX "vid change on fid trans, old 0x%x, new 0x%x\n",		       savevid, data->currvid);		return 1;	}	if (fid != data->currfid) {		printk(KERN_ERR PFX "fid trans failed, fid 0x%x, curr 0x%x\n", fid,		        data->currfid);		return 1;	}	return 0;}/* Write a new vid to the hardware */static int write_new_vid(struct powernow_k8_data *data, u32 vid){	u32 lo;	u32 savefid = data->currfid;	int i = 0;	if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {		printk(KERN_ERR PFX "internal error - overflow on vid write\n");		return 1;	}	lo = data->currfid | (vid << MSR_C_LO_VID_SHIFT) | MSR_C_LO_INIT_FID_VID;	dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",		vid, lo, STOP_GRANT_5NS);	do {		wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);                if (i++ > 100) {                        printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");                        return 1;                }	} while (query_current_values_with_pending_wait(data));	if (savefid != data->currfid) {		printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",		       savefid, data->currfid);		return 1;	}	if (vid != data->currvid) {		printk(KERN_ERR PFX "vid trans failed, vid 0x%x, curr 0x%x\n", vid,				data->currvid);		return 1;	}	return 0;}/* * Reduce the vid by the max of step or reqvid. * Decreasing vid codes represent increasing voltages: * vid of 0 is 1.550V, vid of 0x1e is 0.800V, vid of VID_OFF is off. */static int decrease_vid_code_by_step(struct powernow_k8_data *data, u32 reqvid, u32 step){	if ((data->currvid - reqvid) > step)		reqvid = data->currvid - step;	if (write_new_vid(data, reqvid))		return 1;	count_off_vst(data);	return 0;}/* Change the fid and vid, by the 3 phases. */static int transition_fid_vid(struct powernow_k8_data *data, u32 reqfid, u32 reqvid){	if (core_voltage_pre_transition(data, reqvid))		return 1;	if (core_frequency_transition(data, reqfid))		return 1;	if (core_voltage_post_transition(data, reqvid))		return 1;	if (query_current_values_with_pending_wait(data))		return 1;	if ((reqfid != data->currfid) || (reqvid != data->currvid)) {		printk(KERN_ERR PFX "failed (cpu%d): req 0x%x 0x%x, curr 0x%x 0x%x\n",				smp_processor_id(),				reqfid, reqvid, data->currfid, data->currvid);		return 1;	}	dprintk("transitioned (cpu%d): new fid 0x%x, vid 0x%x\n",		smp_processor_id(), data->currfid, data->currvid);	return 0;}/* Phase 1 - core voltage transition ... setup voltage */static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid){	u32 rvosteps = data->rvo;	u32 savefid = data->currfid;	u32 maxvid, lo;	dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n",		smp_processor_id(),		data->currfid, data->currvid, reqvid, data->rvo);	rdmsr(MSR_FIDVID_STATUS, lo, maxvid);	maxvid = 0x1f & (maxvid >> 16);	dprintk("ph1 maxvid=0x%x\n", maxvid);	if (reqvid < maxvid) /* lower numbers are higher voltages */		reqvid = maxvid;	while (data->currvid > reqvid) {		dprintk("ph1: curr 0x%x, req vid 0x%x\n",			data->currvid, reqvid);		if (decrease_vid_code_by_step(data, reqvid, data->vidmvs))			return 1;	}	while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) {		if (data->currvid == maxvid) {			rvosteps = 0;		} else {			dprintk("ph1: changing vid for rvo, req 0x%x\n",				data->currvid - 1);			if (decrease_vid_code_by_step(data, data->currvid - 1, 1))				return 1;			rvosteps--;		}	}	if (query_current_values_with_pending_wait(data))		return 1;	if (savefid != data->currfid) {		printk(KERN_ERR PFX "ph1 err, currfid changed 0x%x\n", data->currfid);		return 1;	}	dprintk("ph1 complete, currfid 0x%x, currvid 0x%x\n",		data->currfid, data->currvid);	return 0;}/* Phase 2 - core frequency transition */static int core_frequency_transition(struct powernow_k8_data *data, u32 reqfid){	u32 vcoreqfid, vcocurrfid, vcofiddiff, savevid = data->currvid;	if ((reqfid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) {		printk(KERN_ERR PFX "ph2: illegal lo-lo transition 0x%x 0x%x\n",			reqfid, data->currfid);		return 1;	}	if (data->currfid == reqfid) {		printk(KERN_ERR PFX "ph2 null fid transition 0x%x\n", data->currfid);		return 0;	}	dprintk("ph2 (cpu%d): starting, currfid 0x%x, currvid 0x%x, reqfid 0x%x\n",		smp_processor_id(),		data->currfid, data->currvid, reqfid);	vcoreqfid = convert_fid_to_vco_fid(reqfid);	vcocurrfid = convert_fid_to_vco_fid(data->currfid);	vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid	    : vcoreqfid - vcocurrfid;	while (vcofiddiff > 2) {		if (reqfid > data->currfid) {			if (data->currfid > LO_FID_TABLE_TOP) {				if (write_new_fid(data, data->currfid + 2)) {					return 1;				}			} else {				if (write_new_fid				    (data, 2 + convert_fid_to_vco_fid(data->currfid))) {					return 1;				}			}		} else {			if (write_new_fid(data, data->currfid - 2))				return 1;		}		vcocurrfid = convert_fid_to_vco_fid(data->currfid);		vcofiddiff = vcocurrfid > vcoreqfid ? vcocurrfid - vcoreqfid		    : vcoreqfid - vcocurrfid;	}	if (write_new_fid(data, reqfid))		return 1;	if (query_current_values_with_pending_wait(data))		return 1;	if (data->currfid != reqfid) {		printk(KERN_ERR PFX			"ph2: mismatch, failed fid transition, curr 0x%x, req 0x%x\n",

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -