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

📄 powernow-k7.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  AMD K7 Powernow driver. *  (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs. *  (C) 2003-2004 Dave Jones <davej@redhat.com> * *  Licensed under the terms of the GNU GPL License version 2. *  Based upon datasheets & sample CPUs kindly provided by AMD. * * Errata 5: Processor may fail to execute a FID/VID change in presence of interrupt. * - We cli/sti on stepping A0 CPUs around the FID/VID transition. * Errata 15: Processors with half frequency multipliers may hang upon wakeup from disconnect. * - We disable half multipliers if ACPI is used on A0 stepping CPUs. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/cpufreq.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/dmi.h>#include <asm/msr.h>#include <asm/timer.h>#include <asm/timex.h>#include <asm/io.h>#include <asm/system.h>#ifdef CONFIG_X86_POWERNOW_K7_ACPI#include <linux/acpi.h>#include <acpi/processor.h>#endif#include "powernow-k7.h"#define PFX "powernow: "struct psb_s {	u8 signature[10];	u8 tableversion;	u8 flags;	u16 settlingtime;	u8 reserved1;	u8 numpst;};struct pst_s {	u32 cpuid;	u8 fsbspeed;	u8 maxfid;	u8 startvid;	u8 numpstates;};#ifdef CONFIG_X86_POWERNOW_K7_ACPIunion powernow_acpi_control_t {	struct {		unsigned long fid:5,		vid:5,		sgtc:20,		res1:2;	} bits;	unsigned long val;};#endif#ifdef CONFIG_CPU_FREQ_DEBUG/* divide by 1000 to get VCore voltage in V. */static const int mobile_vid_table[32] = {    2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,    1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,    1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,    1075, 1050, 1025, 1000, 975, 950, 925, 0,};#endif/* divide by 10 to get FID. */static const int fid_codes[32] = {    110, 115, 120, 125, 50, 55, 60, 65,    70, 75, 80, 85, 90, 95, 100, 105,    30, 190, 40, 200, 130, 135, 140, 210,    150, 225, 160, 165, 170, 180, -1, -1,};/* This parameter is used in order to force ACPI instead of legacy method for * configuration purpose. */static int acpi_force;static struct cpufreq_frequency_table *powernow_table;static unsigned int can_scale_bus;static unsigned int can_scale_vid;static unsigned int minimum_speed=-1;static unsigned int maximum_speed;static unsigned int number_scales;static unsigned int fsb;static unsigned int latency;static char have_a0;#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k7", msg)static int check_fsb(unsigned int fsbspeed){	int delta;	unsigned int f = fsb / 1000;	delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;	return (delta < 5);}static int check_powernow(void){	struct cpuinfo_x86 *c = &cpu_data(0);	unsigned int maxei, eax, ebx, ecx, edx;	if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 !=6)) {#ifdef MODULE		printk (KERN_INFO PFX "This module only works with AMD K7 CPUs\n");#endif		return 0;	}	/* Get maximum capabilities */	maxei = cpuid_eax (0x80000000);	if (maxei < 0x80000007) {	/* Any powernow info ? */#ifdef MODULE		printk (KERN_INFO PFX "No powernow capabilities detected\n");#endif		return 0;	}	if ((c->x86_model == 6) && (c->x86_mask == 0)) {		printk (KERN_INFO PFX "K7 660[A0] core detected, enabling errata workarounds\n");		have_a0 = 1;	}	cpuid(0x80000007, &eax, &ebx, &ecx, &edx);	/* Check we can actually do something before we say anything.*/	if (!(edx & (1 << 1 | 1 << 2)))		return 0;	printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");	if (edx & 1 << 1) {		printk ("frequency");		can_scale_bus=1;	}	if ((edx & (1 << 1 | 1 << 2)) == 0x6)		printk (" and ");	if (edx & 1 << 2) {		printk ("voltage");		can_scale_vid=1;	}	printk (".\n");	return 1;}static int get_ranges (unsigned char *pst){	unsigned int j;	unsigned int speed;	u8 fid, vid;	powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);	if (!powernow_table)		return -ENOMEM;	for (j=0 ; j < number_scales; j++) {		fid = *pst++;		powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;		powernow_table[j].index = fid; /* lower 8 bits */		speed = powernow_table[j].frequency;		if ((fid_codes[fid] % 10)==5) {#ifdef CONFIG_X86_POWERNOW_K7_ACPI			if (have_a0 == 1)				powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;#endif		}		if (speed < minimum_speed)			minimum_speed = speed;		if (speed > maximum_speed)			maximum_speed = speed;		vid = *pst++;		powernow_table[j].index |= (vid << 8); /* upper 8 bits */		dprintk ("   FID: 0x%x (%d.%dx [%dMHz])  "			 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,			 fid_codes[fid] % 10, speed/1000, vid,			 mobile_vid_table[vid]/1000,			 mobile_vid_table[vid]%1000);	}	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;	powernow_table[number_scales].index = 0;	return 0;}static void change_FID(int fid){	union msr_fidvidctl fidvidctl;	rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);	if (fidvidctl.bits.FID != fid) {		fidvidctl.bits.SGTC = latency;		fidvidctl.bits.FID = fid;		fidvidctl.bits.VIDC = 0;		fidvidctl.bits.FIDC = 1;		wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);	}}static void change_VID(int vid){	union msr_fidvidctl fidvidctl;	rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);	if (fidvidctl.bits.VID != vid) {		fidvidctl.bits.SGTC = latency;		fidvidctl.bits.VID = vid;		fidvidctl.bits.FIDC = 0;		fidvidctl.bits.VIDC = 1;		wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);	}}static void change_speed (unsigned int index){	u8 fid, vid;	struct cpufreq_freqs freqs;	union msr_fidvidstatus fidvidstatus;	int cfid;	/* fid are the lower 8 bits of the index we stored into	 * the cpufreq frequency table in powernow_decode_bios,	 * vid are the upper 8 bits.	 */	fid = powernow_table[index].index & 0xFF;	vid = (powernow_table[index].index & 0xFF00) >> 8;	freqs.cpu = 0;	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);	cfid = fidvidstatus.bits.CFID;	freqs.old = fsb * fid_codes[cfid] / 10;	freqs.new = powernow_table[index].frequency;	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);	/* Now do the magic poking into the MSRs.  */	if (have_a0 == 1)	/* A0 errata 5 */		local_irq_disable();	if (freqs.old > freqs.new) {		/* Going down, so change FID first */		change_FID(fid);		change_VID(vid);	} else {		/* Going up, so change VID first */		change_VID(vid);		change_FID(fid);	}	if (have_a0 == 1)		local_irq_enable();	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);}#ifdef CONFIG_X86_POWERNOW_K7_ACPIstatic struct acpi_processor_performance *acpi_processor_perf;static int powernow_acpi_init(void){	int i;	int retval = 0;	union powernow_acpi_control_t pc;	if (acpi_processor_perf != NULL && powernow_table != NULL) {		retval = -EINVAL;		goto err0;	}	acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),				      GFP_KERNEL);	if (!acpi_processor_perf) {		retval = -ENOMEM;		goto err0;	}	if (acpi_processor_register_performance(acpi_processor_perf, 0)) {		retval = -EIO;		goto err1;	}	if (acpi_processor_perf->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {		retval = -ENODEV;		goto err2;	}	if (acpi_processor_perf->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {		retval = -ENODEV;		goto err2;	}	number_scales = acpi_processor_perf->state_count;	if (number_scales < 2) {		retval = -ENODEV;		goto err2;	}	powernow_table = kzalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);	if (!powernow_table) {		retval = -ENOMEM;		goto err2;	}	pc.val = (unsigned long) acpi_processor_perf->states[0].control;	for (i = 0; i < number_scales; i++) {		u8 fid, vid;		struct acpi_processor_px *state =			&acpi_processor_perf->states[i];		unsigned int speed, speed_mhz;		pc.val = (unsigned long) state->control;		dprintk ("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",			 i,			 (u32) state->core_frequency,

⌨️ 快捷键说明

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