📄 powernow-k7.c
字号:
(u32) state->power, (u32) state->transition_latency, (u32) state->control, pc.bits.sgtc); vid = pc.bits.vid; fid = pc.bits.fid; powernow_table[i].frequency = fsb * fid_codes[fid] / 10; powernow_table[i].index = fid; /* lower 8 bits */ powernow_table[i].index |= (vid << 8); /* upper 8 bits */ speed = powernow_table[i].frequency; speed_mhz = speed / 1000; /* processor_perflib will multiply the MHz value by 1000 to * get a KHz value (e.g. 1266000). However, powernow-k7 works * with true KHz values (e.g. 1266768). To ensure that all * powernow frequencies are available, we must ensure that * ACPI doesn't restrict them, so we round up the MHz value * to ensure that perflib's computed KHz value is greater than * or equal to powernow's KHz value. */ if (speed % 1000 > 0) speed_mhz++; if ((fid_codes[fid] % 10)==5) { if (have_a0 == 1) powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; } dprintk (" FID: 0x%x (%d.%dx [%dMHz]) " "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, fid_codes[fid] % 10, speed_mhz, vid, mobile_vid_table[vid]/1000, mobile_vid_table[vid]%1000); if (state->core_frequency != speed_mhz) { state->core_frequency = speed_mhz; dprintk(" Corrected ACPI frequency to %d\n", speed_mhz); } if (latency < pc.bits.sgtc) latency = pc.bits.sgtc; if (speed < minimum_speed) minimum_speed = speed; if (speed > maximum_speed) maximum_speed = speed; } powernow_table[i].frequency = CPUFREQ_TABLE_END; powernow_table[i].index = 0; /* notify BIOS that we exist */ acpi_processor_notify_smm(THIS_MODULE); return 0;err2: acpi_processor_unregister_performance(acpi_processor_perf, 0);err1: kfree(acpi_processor_perf);err0: printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n"); acpi_processor_perf = NULL; return retval;}#elsestatic int powernow_acpi_init(void){ printk(KERN_INFO PFX "no support for ACPI processor found." " Please recompile your kernel with ACPI processor\n"); return -EINVAL;}#endifstatic int powernow_decode_bios (int maxfid, int startvid){ struct psb_s *psb; struct pst_s *pst; unsigned int i, j; unsigned char *p; unsigned int etuple; unsigned int ret; etuple = cpuid_eax(0x80000001); for (i=0xC0000; i < 0xffff0 ; i+=16) { p = phys_to_virt(i); if (memcmp(p, "AMDK7PNOW!", 10) == 0){ dprintk ("Found PSB header at %p\n", p); psb = (struct psb_s *) p; dprintk ("Table version: 0x%x\n", psb->tableversion); if (psb->tableversion != 0x12) { printk (KERN_INFO PFX "Sorry, only v1.2 tables supported right now\n"); return -ENODEV; } dprintk ("Flags: 0x%x\n", psb->flags); if ((psb->flags & 1)==0) { dprintk ("Mobile voltage regulator\n"); } else { dprintk ("Desktop voltage regulator\n"); } latency = psb->settlingtime; if (latency < 100) { printk (KERN_INFO PFX "BIOS set settling time to %d microseconds." "Should be at least 100. Correcting.\n", latency); latency = 100; } dprintk ("Settling Time: %d microseconds.\n", psb->settlingtime); dprintk ("Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst); p += sizeof (struct psb_s); pst = (struct pst_s *) p; for (j=0; j<psb->numpst; j++) { pst = (struct pst_s *) p; number_scales = pst->numpstates; if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) && (maxfid==pst->maxfid) && (startvid==pst->startvid)) { dprintk ("PST:%d (@%p)\n", j, pst); dprintk (" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n", pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid); ret = get_ranges ((char *) pst + sizeof (struct pst_s)); return ret; } else { unsigned int k; p = (char *) pst + sizeof (struct pst_s); for (k=0; k<number_scales; k++) p+=2; } } printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple); printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n"); return -EINVAL; } p++; } return -ENODEV;}static int powernow_target (struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation){ unsigned int newstate; if (cpufreq_frequency_table_target(policy, powernow_table, target_freq, relation, &newstate)) return -EINVAL; change_speed(newstate); return 0;}static int powernow_verify (struct cpufreq_policy *policy){ return cpufreq_frequency_table_verify(policy, powernow_table);}/* * We use the fact that the bus frequency is somehow * a multiple of 100000/3 khz, then we compute sgtc according * to this multiple. * That way, we match more how AMD thinks all of that work. * We will then get the same kind of behaviour already tested under * the "well-known" other OS. */static int __init fixup_sgtc(void){ unsigned int sgtc; unsigned int m; m = fsb / 3333; if ((m % 10) >= 5) m += 5; m /= 10; sgtc = 100 * m * latency; sgtc = sgtc / 3; if (sgtc > 0xfffff) { printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc); sgtc = 0xfffff; } return sgtc;}static unsigned int powernow_get(unsigned int cpu){ union msr_fidvidstatus fidvidstatus; unsigned int cfid; if (cpu) return 0; rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); cfid = fidvidstatus.bits.CFID; return (fsb * fid_codes[cfid] / 10);}static int __init acer_cpufreq_pst(const struct dmi_system_id *d){ printk(KERN_WARNING "%s laptop with broken PST tables in BIOS detected.\n", d->ident); printk(KERN_WARNING "You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n"); printk(KERN_WARNING "cpufreq scaling has been disabled as a result of this.\n"); return 0;}/* * Some Athlon laptops have really fucked PST tables. * A BIOS update is all that can save them. * Mention this, and disable cpufreq. */static struct dmi_system_id __initdata powernow_dmi_table[] = { { .callback = acer_cpufreq_pst, .ident = "Acer Aspire", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"), DMI_MATCH(DMI_BIOS_VERSION, "3A71"), }, }, { }};static int __init powernow_cpu_init (struct cpufreq_policy *policy){ union msr_fidvidstatus fidvidstatus; int result; if (policy->cpu != 0) return -ENODEV; rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); recalibrate_cpu_khz(); fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID]; if (!fsb) { printk(KERN_WARNING PFX "can not determine bus frequency\n"); return -EINVAL; } dprintk("FSB: %3dMHz\n", fsb/1000); if (dmi_check_system(powernow_dmi_table) || acpi_force) { printk (KERN_INFO PFX "PSB/PST known to be broken. Trying ACPI instead\n"); result = powernow_acpi_init(); } else { result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID); if (result) { printk (KERN_INFO PFX "Trying ACPI perflib\n"); maximum_speed = 0; minimum_speed = -1; latency = 0; result = powernow_acpi_init(); if (result) { printk (KERN_INFO PFX "ACPI and legacy methods failed\n"); printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.html\n"); } } else { /* SGTC use the bus clock as timer */ latency = fixup_sgtc(); printk(KERN_INFO PFX "SGTC: %d\n", latency); } } if (result) return result; printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n", minimum_speed/1000, maximum_speed/1000); policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency); policy->cur = powernow_get(0); cpufreq_frequency_table_get_attr(powernow_table, policy->cpu); return cpufreq_frequency_table_cpuinfo(policy, powernow_table);}static int powernow_cpu_exit (struct cpufreq_policy *policy) { cpufreq_frequency_table_put_attr(policy->cpu);#ifdef CONFIG_X86_POWERNOW_K7_ACPI if (acpi_processor_perf) { acpi_processor_unregister_performance(acpi_processor_perf, 0); kfree(acpi_processor_perf); }#endif kfree(powernow_table); return 0;}static struct freq_attr* powernow_table_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, NULL,};static struct cpufreq_driver powernow_driver = { .verify = powernow_verify, .target = powernow_target, .get = powernow_get, .init = powernow_cpu_init, .exit = powernow_cpu_exit, .name = "powernow-k7", .owner = THIS_MODULE, .attr = powernow_table_attr,};static int __init powernow_init (void){ if (check_powernow()==0) return -ENODEV; return cpufreq_register_driver(&powernow_driver);}static void __exit powernow_exit (void){ cpufreq_unregister_driver(&powernow_driver);}module_param(acpi_force, int, 0444);MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");MODULE_AUTHOR ("Dave Jones <davej@codemonkey.org.uk>");MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");MODULE_LICENSE ("GPL");late_initcall(powernow_init);module_exit(powernow_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -