162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD K7 Powernow driver. 462306a36Sopenharmony_ci * (C) 2003 Dave Jones on behalf of SuSE Labs. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Based upon datasheets & sample CPUs kindly provided by AMD. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Errata 5: 962306a36Sopenharmony_ci * CPU may fail to execute a FID/VID change in presence of interrupt. 1062306a36Sopenharmony_ci * - We cli/sti on stepping A0 CPUs around the FID/VID transition. 1162306a36Sopenharmony_ci * Errata 15: 1262306a36Sopenharmony_ci * CPU with half frequency multipliers may hang upon wakeup from disconnect. 1362306a36Sopenharmony_ci * - We disable half multipliers if ACPI is used on A0 stepping CPUs. 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/moduleparam.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/cpufreq.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci#include <linux/dmi.h> 2662306a36Sopenharmony_ci#include <linux/timex.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */ 3062306a36Sopenharmony_ci#include <asm/msr.h> 3162306a36Sopenharmony_ci#include <asm/cpu_device_id.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 3462306a36Sopenharmony_ci#include <linux/acpi.h> 3562306a36Sopenharmony_ci#include <acpi/processor.h> 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "powernow-k7.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct psb_s { 4162306a36Sopenharmony_ci u8 signature[10]; 4262306a36Sopenharmony_ci u8 tableversion; 4362306a36Sopenharmony_ci u8 flags; 4462306a36Sopenharmony_ci u16 settlingtime; 4562306a36Sopenharmony_ci u8 reserved1; 4662306a36Sopenharmony_ci u8 numpst; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct pst_s { 5062306a36Sopenharmony_ci u32 cpuid; 5162306a36Sopenharmony_ci u8 fsbspeed; 5262306a36Sopenharmony_ci u8 maxfid; 5362306a36Sopenharmony_ci u8 startvid; 5462306a36Sopenharmony_ci u8 numpstates; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 5862306a36Sopenharmony_ciunion powernow_acpi_control_t { 5962306a36Sopenharmony_ci struct { 6062306a36Sopenharmony_ci unsigned long fid:5, 6162306a36Sopenharmony_ci vid:5, 6262306a36Sopenharmony_ci sgtc:20, 6362306a36Sopenharmony_ci res1:2; 6462306a36Sopenharmony_ci } bits; 6562306a36Sopenharmony_ci unsigned long val; 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci#endif 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* divide by 1000 to get VCore voltage in V. */ 7062306a36Sopenharmony_cistatic const int mobile_vid_table[32] = { 7162306a36Sopenharmony_ci 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650, 7262306a36Sopenharmony_ci 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0, 7362306a36Sopenharmony_ci 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100, 7462306a36Sopenharmony_ci 1075, 1050, 1025, 1000, 975, 950, 925, 0, 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci/* divide by 10 to get FID. */ 7862306a36Sopenharmony_cistatic const int fid_codes[32] = { 7962306a36Sopenharmony_ci 110, 115, 120, 125, 50, 55, 60, 65, 8062306a36Sopenharmony_ci 70, 75, 80, 85, 90, 95, 100, 105, 8162306a36Sopenharmony_ci 30, 190, 40, 200, 130, 135, 140, 210, 8262306a36Sopenharmony_ci 150, 225, 160, 165, 170, 180, -1, -1, 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* This parameter is used in order to force ACPI instead of legacy method for 8662306a36Sopenharmony_ci * configuration purpose. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int acpi_force; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct cpufreq_frequency_table *powernow_table; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic unsigned int can_scale_bus; 9462306a36Sopenharmony_cistatic unsigned int can_scale_vid; 9562306a36Sopenharmony_cistatic unsigned int minimum_speed = -1; 9662306a36Sopenharmony_cistatic unsigned int maximum_speed; 9762306a36Sopenharmony_cistatic unsigned int number_scales; 9862306a36Sopenharmony_cistatic unsigned int fsb; 9962306a36Sopenharmony_cistatic unsigned int latency; 10062306a36Sopenharmony_cistatic char have_a0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int check_fsb(unsigned int fsbspeed) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci int delta; 10562306a36Sopenharmony_ci unsigned int f = fsb / 1000; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed; 10862306a36Sopenharmony_ci return delta < 5; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic const struct x86_cpu_id powernow_k7_cpuids[] = { 11262306a36Sopenharmony_ci X86_MATCH_VENDOR_FAM(AMD, 6, NULL), 11362306a36Sopenharmony_ci {} 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int check_powernow(void) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct cpuinfo_x86 *c = &cpu_data(0); 12062306a36Sopenharmony_ci unsigned int maxei, eax, ebx, ecx, edx; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!x86_match_cpu(powernow_k7_cpuids)) 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Get maximum capabilities */ 12662306a36Sopenharmony_ci maxei = cpuid_eax(0x80000000); 12762306a36Sopenharmony_ci if (maxei < 0x80000007) { /* Any powernow info ? */ 12862306a36Sopenharmony_ci#ifdef MODULE 12962306a36Sopenharmony_ci pr_info("No powernow capabilities detected\n"); 13062306a36Sopenharmony_ci#endif 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if ((c->x86_model == 6) && (c->x86_stepping == 0)) { 13562306a36Sopenharmony_ci pr_info("K7 660[A0] core detected, enabling errata workarounds\n"); 13662306a36Sopenharmony_ci have_a0 = 1; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci cpuid(0x80000007, &eax, &ebx, &ecx, &edx); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Check we can actually do something before we say anything.*/ 14262306a36Sopenharmony_ci if (!(edx & (1 << 1 | 1 << 2))) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci pr_info("PowerNOW! Technology present. Can scale: "); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (edx & 1 << 1) { 14862306a36Sopenharmony_ci pr_cont("frequency"); 14962306a36Sopenharmony_ci can_scale_bus = 1; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if ((edx & (1 << 1 | 1 << 2)) == 0x6) 15362306a36Sopenharmony_ci pr_cont(" and "); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (edx & 1 << 2) { 15662306a36Sopenharmony_ci pr_cont("voltage"); 15762306a36Sopenharmony_ci can_scale_vid = 1; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci pr_cont("\n"); 16162306a36Sopenharmony_ci return 1; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 16562306a36Sopenharmony_cistatic void invalidate_entry(unsigned int entry) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci#endif 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int get_ranges(unsigned char *pst) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci unsigned int j; 17462306a36Sopenharmony_ci unsigned int speed; 17562306a36Sopenharmony_ci u8 fid, vid; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci powernow_table = kzalloc((sizeof(*powernow_table) * 17862306a36Sopenharmony_ci (number_scales + 1)), GFP_KERNEL); 17962306a36Sopenharmony_ci if (!powernow_table) 18062306a36Sopenharmony_ci return -ENOMEM; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci for (j = 0 ; j < number_scales; j++) { 18362306a36Sopenharmony_ci fid = *pst++; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10; 18662306a36Sopenharmony_ci powernow_table[j].driver_data = fid; /* lower 8 bits */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci speed = powernow_table[j].frequency; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if ((fid_codes[fid] % 10) == 5) { 19162306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 19262306a36Sopenharmony_ci if (have_a0 == 1) 19362306a36Sopenharmony_ci invalidate_entry(j); 19462306a36Sopenharmony_ci#endif 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (speed < minimum_speed) 19862306a36Sopenharmony_ci minimum_speed = speed; 19962306a36Sopenharmony_ci if (speed > maximum_speed) 20062306a36Sopenharmony_ci maximum_speed = speed; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci vid = *pst++; 20362306a36Sopenharmony_ci powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */ 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) " 20662306a36Sopenharmony_ci "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, 20762306a36Sopenharmony_ci fid_codes[fid] % 10, speed/1000, vid, 20862306a36Sopenharmony_ci mobile_vid_table[vid]/1000, 20962306a36Sopenharmony_ci mobile_vid_table[vid]%1000); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci powernow_table[number_scales].frequency = CPUFREQ_TABLE_END; 21262306a36Sopenharmony_ci powernow_table[number_scales].driver_data = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void change_FID(int fid) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci union msr_fidvidctl fidvidctl; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val); 22362306a36Sopenharmony_ci if (fidvidctl.bits.FID != fid) { 22462306a36Sopenharmony_ci fidvidctl.bits.SGTC = latency; 22562306a36Sopenharmony_ci fidvidctl.bits.FID = fid; 22662306a36Sopenharmony_ci fidvidctl.bits.VIDC = 0; 22762306a36Sopenharmony_ci fidvidctl.bits.FIDC = 1; 22862306a36Sopenharmony_ci wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic void change_VID(int vid) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci union msr_fidvidctl fidvidctl; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val); 23862306a36Sopenharmony_ci if (fidvidctl.bits.VID != vid) { 23962306a36Sopenharmony_ci fidvidctl.bits.SGTC = latency; 24062306a36Sopenharmony_ci fidvidctl.bits.VID = vid; 24162306a36Sopenharmony_ci fidvidctl.bits.FIDC = 0; 24262306a36Sopenharmony_ci fidvidctl.bits.VIDC = 1; 24362306a36Sopenharmony_ci wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int powernow_target(struct cpufreq_policy *policy, unsigned int index) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci u8 fid, vid; 25162306a36Sopenharmony_ci struct cpufreq_freqs freqs; 25262306a36Sopenharmony_ci union msr_fidvidstatus fidvidstatus; 25362306a36Sopenharmony_ci int cfid; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* fid are the lower 8 bits of the index we stored into 25662306a36Sopenharmony_ci * the cpufreq frequency table in powernow_decode_bios, 25762306a36Sopenharmony_ci * vid are the upper 8 bits. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci fid = powernow_table[index].driver_data & 0xFF; 26162306a36Sopenharmony_ci vid = (powernow_table[index].driver_data & 0xFF00) >> 8; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val); 26462306a36Sopenharmony_ci cfid = fidvidstatus.bits.CFID; 26562306a36Sopenharmony_ci freqs.old = fsb * fid_codes[cfid] / 10; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci freqs.new = powernow_table[index].frequency; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Now do the magic poking into the MSRs. */ 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (have_a0 == 1) /* A0 errata 5 */ 27262306a36Sopenharmony_ci local_irq_disable(); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (freqs.old > freqs.new) { 27562306a36Sopenharmony_ci /* Going down, so change FID first */ 27662306a36Sopenharmony_ci change_FID(fid); 27762306a36Sopenharmony_ci change_VID(vid); 27862306a36Sopenharmony_ci } else { 27962306a36Sopenharmony_ci /* Going up, so change VID first */ 28062306a36Sopenharmony_ci change_VID(vid); 28162306a36Sopenharmony_ci change_FID(fid); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (have_a0 == 1) 28662306a36Sopenharmony_ci local_irq_enable(); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return 0; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic struct acpi_processor_performance *acpi_processor_perf; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic int powernow_acpi_init(void) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci int i; 29962306a36Sopenharmony_ci int retval = 0; 30062306a36Sopenharmony_ci union powernow_acpi_control_t pc; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (acpi_processor_perf != NULL && powernow_table != NULL) { 30362306a36Sopenharmony_ci retval = -EINVAL; 30462306a36Sopenharmony_ci goto err0; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL); 30862306a36Sopenharmony_ci if (!acpi_processor_perf) { 30962306a36Sopenharmony_ci retval = -ENOMEM; 31062306a36Sopenharmony_ci goto err0; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map, 31462306a36Sopenharmony_ci GFP_KERNEL)) { 31562306a36Sopenharmony_ci retval = -ENOMEM; 31662306a36Sopenharmony_ci goto err05; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci if (acpi_processor_register_performance(acpi_processor_perf, 0)) { 32062306a36Sopenharmony_ci retval = -EIO; 32162306a36Sopenharmony_ci goto err1; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (acpi_processor_perf->control_register.space_id != 32562306a36Sopenharmony_ci ACPI_ADR_SPACE_FIXED_HARDWARE) { 32662306a36Sopenharmony_ci retval = -ENODEV; 32762306a36Sopenharmony_ci goto err2; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (acpi_processor_perf->status_register.space_id != 33162306a36Sopenharmony_ci ACPI_ADR_SPACE_FIXED_HARDWARE) { 33262306a36Sopenharmony_ci retval = -ENODEV; 33362306a36Sopenharmony_ci goto err2; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci number_scales = acpi_processor_perf->state_count; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (number_scales < 2) { 33962306a36Sopenharmony_ci retval = -ENODEV; 34062306a36Sopenharmony_ci goto err2; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci powernow_table = kzalloc((sizeof(*powernow_table) * 34462306a36Sopenharmony_ci (number_scales + 1)), GFP_KERNEL); 34562306a36Sopenharmony_ci if (!powernow_table) { 34662306a36Sopenharmony_ci retval = -ENOMEM; 34762306a36Sopenharmony_ci goto err2; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci pc.val = (unsigned long) acpi_processor_perf->states[0].control; 35162306a36Sopenharmony_ci for (i = 0; i < number_scales; i++) { 35262306a36Sopenharmony_ci u8 fid, vid; 35362306a36Sopenharmony_ci struct acpi_processor_px *state = 35462306a36Sopenharmony_ci &acpi_processor_perf->states[i]; 35562306a36Sopenharmony_ci unsigned int speed, speed_mhz; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci pc.val = (unsigned long) state->control; 35862306a36Sopenharmony_ci pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n", 35962306a36Sopenharmony_ci i, 36062306a36Sopenharmony_ci (u32) state->core_frequency, 36162306a36Sopenharmony_ci (u32) state->power, 36262306a36Sopenharmony_ci (u32) state->transition_latency, 36362306a36Sopenharmony_ci (u32) state->control, 36462306a36Sopenharmony_ci pc.bits.sgtc); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci vid = pc.bits.vid; 36762306a36Sopenharmony_ci fid = pc.bits.fid; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci powernow_table[i].frequency = fsb * fid_codes[fid] / 10; 37062306a36Sopenharmony_ci powernow_table[i].driver_data = fid; /* lower 8 bits */ 37162306a36Sopenharmony_ci powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */ 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci speed = powernow_table[i].frequency; 37462306a36Sopenharmony_ci speed_mhz = speed / 1000; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* processor_perflib will multiply the MHz value by 1000 to 37762306a36Sopenharmony_ci * get a KHz value (e.g. 1266000). However, powernow-k7 works 37862306a36Sopenharmony_ci * with true KHz values (e.g. 1266768). To ensure that all 37962306a36Sopenharmony_ci * powernow frequencies are available, we must ensure that 38062306a36Sopenharmony_ci * ACPI doesn't restrict them, so we round up the MHz value 38162306a36Sopenharmony_ci * to ensure that perflib's computed KHz value is greater than 38262306a36Sopenharmony_ci * or equal to powernow's KHz value. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci if (speed % 1000 > 0) 38562306a36Sopenharmony_ci speed_mhz++; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if ((fid_codes[fid] % 10) == 5) { 38862306a36Sopenharmony_ci if (have_a0 == 1) 38962306a36Sopenharmony_ci invalidate_entry(i); 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) " 39362306a36Sopenharmony_ci "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10, 39462306a36Sopenharmony_ci fid_codes[fid] % 10, speed_mhz, vid, 39562306a36Sopenharmony_ci mobile_vid_table[vid]/1000, 39662306a36Sopenharmony_ci mobile_vid_table[vid]%1000); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (state->core_frequency != speed_mhz) { 39962306a36Sopenharmony_ci state->core_frequency = speed_mhz; 40062306a36Sopenharmony_ci pr_debug(" Corrected ACPI frequency to %d\n", 40162306a36Sopenharmony_ci speed_mhz); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (latency < pc.bits.sgtc) 40562306a36Sopenharmony_ci latency = pc.bits.sgtc; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (speed < minimum_speed) 40862306a36Sopenharmony_ci minimum_speed = speed; 40962306a36Sopenharmony_ci if (speed > maximum_speed) 41062306a36Sopenharmony_ci maximum_speed = speed; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci powernow_table[i].frequency = CPUFREQ_TABLE_END; 41462306a36Sopenharmony_ci powernow_table[i].driver_data = 0; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* notify BIOS that we exist */ 41762306a36Sopenharmony_ci acpi_processor_notify_smm(THIS_MODULE); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cierr2: 42262306a36Sopenharmony_ci acpi_processor_unregister_performance(0); 42362306a36Sopenharmony_cierr1: 42462306a36Sopenharmony_ci free_cpumask_var(acpi_processor_perf->shared_cpu_map); 42562306a36Sopenharmony_cierr05: 42662306a36Sopenharmony_ci kfree(acpi_processor_perf); 42762306a36Sopenharmony_cierr0: 42862306a36Sopenharmony_ci pr_warn("ACPI perflib can not be used on this platform\n"); 42962306a36Sopenharmony_ci acpi_processor_perf = NULL; 43062306a36Sopenharmony_ci return retval; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci#else 43362306a36Sopenharmony_cistatic int powernow_acpi_init(void) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci pr_info("no support for ACPI processor found - please recompile your kernel with ACPI processor\n"); 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci#endif 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void print_pst_entry(struct pst_s *pst, unsigned int j) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci pr_debug("PST:%d (@%p)\n", j, pst); 44362306a36Sopenharmony_ci pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n", 44462306a36Sopenharmony_ci pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int powernow_decode_bios(int maxfid, int startvid) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct psb_s *psb; 45062306a36Sopenharmony_ci struct pst_s *pst; 45162306a36Sopenharmony_ci unsigned int i, j; 45262306a36Sopenharmony_ci unsigned char *p; 45362306a36Sopenharmony_ci unsigned int etuple; 45462306a36Sopenharmony_ci unsigned int ret; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci etuple = cpuid_eax(0x80000001); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci for (i = 0xC0000; i < 0xffff0 ; i += 16) { 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci p = phys_to_virt(i); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (memcmp(p, "AMDK7PNOW!", 10) == 0) { 46362306a36Sopenharmony_ci pr_debug("Found PSB header at %p\n", p); 46462306a36Sopenharmony_ci psb = (struct psb_s *) p; 46562306a36Sopenharmony_ci pr_debug("Table version: 0x%x\n", psb->tableversion); 46662306a36Sopenharmony_ci if (psb->tableversion != 0x12) { 46762306a36Sopenharmony_ci pr_info("Sorry, only v1.2 tables supported right now\n"); 46862306a36Sopenharmony_ci return -ENODEV; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci pr_debug("Flags: 0x%x\n", psb->flags); 47262306a36Sopenharmony_ci if ((psb->flags & 1) == 0) 47362306a36Sopenharmony_ci pr_debug("Mobile voltage regulator\n"); 47462306a36Sopenharmony_ci else 47562306a36Sopenharmony_ci pr_debug("Desktop voltage regulator\n"); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci latency = psb->settlingtime; 47862306a36Sopenharmony_ci if (latency < 100) { 47962306a36Sopenharmony_ci pr_info("BIOS set settling time to %d microseconds. Should be at least 100. Correcting.\n", 48062306a36Sopenharmony_ci latency); 48162306a36Sopenharmony_ci latency = 100; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci pr_debug("Settling Time: %d microseconds.\n", 48462306a36Sopenharmony_ci psb->settlingtime); 48562306a36Sopenharmony_ci pr_debug("Has %d PST tables. (Only dumping ones " 48662306a36Sopenharmony_ci "relevant to this CPU).\n", 48762306a36Sopenharmony_ci psb->numpst); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci p += sizeof(*psb); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci pst = (struct pst_s *) p; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci for (j = 0; j < psb->numpst; j++) { 49462306a36Sopenharmony_ci pst = (struct pst_s *) p; 49562306a36Sopenharmony_ci number_scales = pst->numpstates; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if ((etuple == pst->cpuid) && 49862306a36Sopenharmony_ci check_fsb(pst->fsbspeed) && 49962306a36Sopenharmony_ci (maxfid == pst->maxfid) && 50062306a36Sopenharmony_ci (startvid == pst->startvid)) { 50162306a36Sopenharmony_ci print_pst_entry(pst, j); 50262306a36Sopenharmony_ci p = (char *)pst + sizeof(*pst); 50362306a36Sopenharmony_ci ret = get_ranges(p); 50462306a36Sopenharmony_ci return ret; 50562306a36Sopenharmony_ci } else { 50662306a36Sopenharmony_ci unsigned int k; 50762306a36Sopenharmony_ci p = (char *)pst + sizeof(*pst); 50862306a36Sopenharmony_ci for (k = 0; k < number_scales; k++) 50962306a36Sopenharmony_ci p += 2; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci pr_info("No PST tables match this cpuid (0x%x)\n", 51362306a36Sopenharmony_ci etuple); 51462306a36Sopenharmony_ci pr_info("This is indicative of a broken BIOS\n"); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci return -EINVAL; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci p++; 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci return -ENODEV; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci/* 52662306a36Sopenharmony_ci * We use the fact that the bus frequency is somehow 52762306a36Sopenharmony_ci * a multiple of 100000/3 khz, then we compute sgtc according 52862306a36Sopenharmony_ci * to this multiple. 52962306a36Sopenharmony_ci * That way, we match more how AMD thinks all of that work. 53062306a36Sopenharmony_ci * We will then get the same kind of behaviour already tested under 53162306a36Sopenharmony_ci * the "well-known" other OS. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_cistatic int fixup_sgtc(void) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci unsigned int sgtc; 53662306a36Sopenharmony_ci unsigned int m; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci m = fsb / 3333; 53962306a36Sopenharmony_ci if ((m % 10) >= 5) 54062306a36Sopenharmony_ci m += 5; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci m /= 10; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci sgtc = 100 * m * latency; 54562306a36Sopenharmony_ci sgtc = sgtc / 3; 54662306a36Sopenharmony_ci if (sgtc > 0xfffff) { 54762306a36Sopenharmony_ci pr_warn("SGTC too large %d\n", sgtc); 54862306a36Sopenharmony_ci sgtc = 0xfffff; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci return sgtc; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic unsigned int powernow_get(unsigned int cpu) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci union msr_fidvidstatus fidvidstatus; 55662306a36Sopenharmony_ci unsigned int cfid; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (cpu) 55962306a36Sopenharmony_ci return 0; 56062306a36Sopenharmony_ci rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val); 56162306a36Sopenharmony_ci cfid = fidvidstatus.bits.CFID; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci return fsb * fid_codes[cfid] / 10; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int acer_cpufreq_pst(const struct dmi_system_id *d) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci pr_warn("%s laptop with broken PST tables in BIOS detected\n", 57062306a36Sopenharmony_ci d->ident); 57162306a36Sopenharmony_ci pr_warn("You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n"); 57262306a36Sopenharmony_ci pr_warn("cpufreq scaling has been disabled as a result of this\n"); 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci/* 57762306a36Sopenharmony_ci * Some Athlon laptops have really fucked PST tables. 57862306a36Sopenharmony_ci * A BIOS update is all that can save them. 57962306a36Sopenharmony_ci * Mention this, and disable cpufreq. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_cistatic const struct dmi_system_id powernow_dmi_table[] = { 58262306a36Sopenharmony_ci { 58362306a36Sopenharmony_ci .callback = acer_cpufreq_pst, 58462306a36Sopenharmony_ci .ident = "Acer Aspire", 58562306a36Sopenharmony_ci .matches = { 58662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"), 58762306a36Sopenharmony_ci DMI_MATCH(DMI_BIOS_VERSION, "3A71"), 58862306a36Sopenharmony_ci }, 58962306a36Sopenharmony_ci }, 59062306a36Sopenharmony_ci { } 59162306a36Sopenharmony_ci}; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic int powernow_cpu_init(struct cpufreq_policy *policy) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci union msr_fidvidstatus fidvidstatus; 59662306a36Sopenharmony_ci int result; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (policy->cpu != 0) 59962306a36Sopenharmony_ci return -ENODEV; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci recalibrate_cpu_khz(); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID]; 60662306a36Sopenharmony_ci if (!fsb) { 60762306a36Sopenharmony_ci pr_warn("can not determine bus frequency\n"); 60862306a36Sopenharmony_ci return -EINVAL; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci pr_debug("FSB: %3dMHz\n", fsb/1000); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (dmi_check_system(powernow_dmi_table) || acpi_force) { 61362306a36Sopenharmony_ci pr_info("PSB/PST known to be broken - trying ACPI instead\n"); 61462306a36Sopenharmony_ci result = powernow_acpi_init(); 61562306a36Sopenharmony_ci } else { 61662306a36Sopenharmony_ci result = powernow_decode_bios(fidvidstatus.bits.MFID, 61762306a36Sopenharmony_ci fidvidstatus.bits.SVID); 61862306a36Sopenharmony_ci if (result) { 61962306a36Sopenharmony_ci pr_info("Trying ACPI perflib\n"); 62062306a36Sopenharmony_ci maximum_speed = 0; 62162306a36Sopenharmony_ci minimum_speed = -1; 62262306a36Sopenharmony_ci latency = 0; 62362306a36Sopenharmony_ci result = powernow_acpi_init(); 62462306a36Sopenharmony_ci if (result) { 62562306a36Sopenharmony_ci pr_info("ACPI and legacy methods failed\n"); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci } else { 62862306a36Sopenharmony_ci /* SGTC use the bus clock as timer */ 62962306a36Sopenharmony_ci latency = fixup_sgtc(); 63062306a36Sopenharmony_ci pr_info("SGTC: %d\n", latency); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (result) 63562306a36Sopenharmony_ci return result; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci pr_info("Minimum speed %d MHz - Maximum speed %d MHz\n", 63862306a36Sopenharmony_ci minimum_speed/1000, maximum_speed/1000); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci policy->cpuinfo.transition_latency = 64162306a36Sopenharmony_ci cpufreq_scale(2000000UL, fsb, latency); 64262306a36Sopenharmony_ci policy->freq_table = powernow_table; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return 0; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic int powernow_cpu_exit(struct cpufreq_policy *policy) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 65062306a36Sopenharmony_ci if (acpi_processor_perf) { 65162306a36Sopenharmony_ci acpi_processor_unregister_performance(0); 65262306a36Sopenharmony_ci free_cpumask_var(acpi_processor_perf->shared_cpu_map); 65362306a36Sopenharmony_ci kfree(acpi_processor_perf); 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci#endif 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci kfree(powernow_table); 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic struct cpufreq_driver powernow_driver = { 66262306a36Sopenharmony_ci .verify = cpufreq_generic_frequency_table_verify, 66362306a36Sopenharmony_ci .target_index = powernow_target, 66462306a36Sopenharmony_ci .get = powernow_get, 66562306a36Sopenharmony_ci#ifdef CONFIG_X86_POWERNOW_K7_ACPI 66662306a36Sopenharmony_ci .bios_limit = acpi_processor_get_bios_limit, 66762306a36Sopenharmony_ci#endif 66862306a36Sopenharmony_ci .init = powernow_cpu_init, 66962306a36Sopenharmony_ci .exit = powernow_cpu_exit, 67062306a36Sopenharmony_ci .name = "powernow-k7", 67162306a36Sopenharmony_ci .attr = cpufreq_generic_attr, 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int __init powernow_init(void) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci if (check_powernow() == 0) 67762306a36Sopenharmony_ci return -ENODEV; 67862306a36Sopenharmony_ci return cpufreq_register_driver(&powernow_driver); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic void __exit powernow_exit(void) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci cpufreq_unregister_driver(&powernow_driver); 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_cimodule_param(acpi_force, int, 0444); 68862306a36Sopenharmony_ciMODULE_PARM_DESC(acpi_force, "Force ACPI to be used."); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ciMODULE_AUTHOR("Dave Jones"); 69162306a36Sopenharmony_ciMODULE_DESCRIPTION("Powernow driver for AMD K7 processors."); 69262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cilate_initcall(powernow_init); 69562306a36Sopenharmony_cimodule_exit(powernow_exit); 69662306a36Sopenharmony_ci 697