📄 powernow-k7.c
字号:
/* * 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 + -