X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fi386%2Fkernel%2Fcpu%2Fcpufreq%2Fpowernow-k7.c;h=a748409b7f819217174dc5506d06c54dfac77c03;hb=4e76c8a9fa413ccc09d3f7f664183dcce3555d57;hp=65246256fd077474a9ebd8a9ed4ee48307ee23db;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c index 65246256f..a748409b7 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k7.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k7.c @@ -6,8 +6,6 @@ * Licensed under the terms of the GNU GPL License version 2. * Based upon datasheets & sample CPUs kindly provided by AMD. * - * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* - * * 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. @@ -16,33 +14,27 @@ #include #include -#include +#include #include #include #include #include #include +#include #include +#include #include #include #include -#ifdef CONFIG_ACPI_PROCESSOR +#ifdef CONFIG_X86_POWERNOW_K7_ACPI #include #include #endif #include "powernow-k7.h" -#define DEBUG - -#ifdef DEBUG -#define dprintk(msg...) printk(msg) -#else -#define dprintk(msg...) do { } while(0) -#endif - #define PFX "powernow: " @@ -63,7 +55,7 @@ struct pst_s { u8 numpstates; }; -#ifdef CONFIG_ACPI_PROCESSOR +#ifdef CONFIG_X86_POWERNOW_K7_ACPI union powernow_acpi_control_t { struct { unsigned long fid:5, @@ -75,18 +67,20 @@ union powernow_acpi_control_t { }; #endif -/* divide by 1000 to get VID. */ +#ifdef CONFIG_CPU_FREQ_DEBUG +/* divide by 1000 to get VCore voltage in V. */ static 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, 1024, 1000, 975, 950, 925, 0, + 1075, 1050, 1025, 1000, 975, 950, 925, 0, }; +#endif /* divide by 10 to get FID. */ static int fid_codes[32] = { 110, 115, 120, 125, 50, 55, 60, 65, - 70, 75, 80, 85, 90, 95, 100, 105, + 70, 75, 80, 85, 90, 95, 100, 105, 30, 190, 40, 200, 130, 135, 140, 210, 150, 225, 160, 165, 170, 180, -1, -1, }; @@ -95,7 +89,7 @@ static int fid_codes[32] = { * configuration purpose. */ -static int powernow_acpi_force; +static int acpi_force; static struct cpufreq_frequency_table *powernow_table; @@ -108,6 +102,8 @@ 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; @@ -144,6 +140,11 @@ static int check_powernow(void) } 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) { @@ -159,11 +160,6 @@ static int check_powernow(void) can_scale_vid=1; } - if (!(edx & (1 << 1 | 1 << 2))) { - printk ("nothing.\n"); - return 0; - } - printk (".\n"); return 1; } @@ -175,10 +171,9 @@ static int get_ranges (unsigned char *pst) unsigned int speed; u8 fid, vid; - powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL); + powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL); if (!powernow_table) return -ENOMEM; - memset(powernow_table, 0, (sizeof(struct cpufreq_frequency_table) * (number_scales + 1))); for (j=0 ; j < number_scales; j++) { fid = *pst++; @@ -189,15 +184,12 @@ static int get_ranges (unsigned char *pst) speed = powernow_table[j].frequency; if ((fid_codes[fid] % 10)==5) { -#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE) +#ifdef CONFIG_X86_POWERNOW_K7_ACPI if (have_a0 == 1) powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID; #endif } - dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid, - fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000); - if (speed < minimum_speed) minimum_speed = speed; if (speed > maximum_speed) @@ -205,8 +197,12 @@ static int get_ranges (unsigned char *pst) vid = *pst++; powernow_table[j].index |= (vid << 8); /* upper 8 bits */ - dprintk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000, - mobile_vid_table[vid]%1000); + + 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; @@ -284,7 +280,7 @@ static void change_speed (unsigned int index) change_VID(vid); change_FID(fid); } - + if (have_a0 == 1) local_irq_enable(); @@ -293,9 +289,9 @@ static void change_speed (unsigned int index) } -#ifdef CONFIG_ACPI_PROCESSOR +#ifdef CONFIG_X86_POWERNOW_K7_ACPI -struct acpi_processor_performance *acpi_processor_perf; +static struct acpi_processor_performance *acpi_processor_perf; static int powernow_acpi_init(void) { @@ -308,16 +304,13 @@ static int powernow_acpi_init(void) goto err0; } - acpi_processor_perf = kmalloc(sizeof(struct acpi_processor_performance), + acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL); - if (!acpi_processor_perf) { retval = -ENOMEM; goto err0; } - memset(acpi_processor_perf, 0, sizeof(struct acpi_processor_performance)); - if (acpi_processor_register_performance(acpi_processor_perf, 0)) { retval = -EIO; goto err1; @@ -340,21 +333,19 @@ static int powernow_acpi_init(void) goto err2; } - powernow_table = kmalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL); + powernow_table = kzalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL); if (!powernow_table) { retval = -ENOMEM; goto err2; } - memset(powernow_table, 0, ((number_scales + 1) * sizeof(struct cpufreq_frequency_table))); - pc.val = (unsigned long) acpi_processor_perf->states[0].control; for (i = 0; i < number_scales; i++) { u8 fid, vid; unsigned int speed; pc.val = (unsigned long) acpi_processor_perf->states[i].control; - dprintk (KERN_INFO PFX "acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n", + dprintk ("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n", i, (u32) acpi_processor_perf->states[i].core_frequency, (u32) acpi_processor_perf->states[i].power, @@ -376,10 +367,11 @@ static int powernow_acpi_init(void) powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; } - dprintk (KERN_INFO PFX " FID: 0x%x (%d.%dx [%dMHz])\t", fid, - fid_codes[fid] / 10, fid_codes[fid] % 10, speed/1000); - dprintk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000, - mobile_vid_table[vid]%1000); + 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); if (latency < pc.bits.sgtc) latency = pc.bits.sgtc; @@ -393,6 +385,9 @@ static int powernow_acpi_init(void) 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: @@ -429,21 +424,20 @@ static int powernow_decode_bios (int maxfid, int startvid) p = phys_to_virt(i); if (memcmp(p, "AMDK7PNOW!", 10) == 0){ - dprintk (KERN_INFO PFX "Found PSB header at %p\n", p); + dprintk ("Found PSB header at %p\n", p); psb = (struct psb_s *) p; - dprintk (KERN_INFO PFX "Table version: 0x%x\n", psb->tableversion); + 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 (KERN_INFO PFX "Flags: 0x%x (", psb->flags); + dprintk ("Flags: 0x%x\n", psb->flags); if ((psb->flags & 1)==0) { - dprintk ("Mobile"); + dprintk ("Mobile voltage regulator\n"); } else { - dprintk ("Desktop"); + dprintk ("Desktop voltage regulator\n"); } - dprintk (" voltage regulator)\n"); latency = psb->settlingtime; if (latency < 100) { @@ -451,8 +445,8 @@ static int powernow_decode_bios (int maxfid, int startvid) "Should be at least 100. Correcting.\n", latency); latency = 100; } - dprintk (KERN_INFO PFX "Settling Time: %d microseconds.\n", psb->settlingtime); - dprintk (KERN_INFO PFX "Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst); + 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); @@ -465,11 +459,9 @@ static int powernow_decode_bios (int maxfid, int startvid) if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) && (maxfid==pst->maxfid) && (startvid==pst->startvid)) { - dprintk (KERN_INFO PFX "PST:%d (@%p)\n", i, pst); - dprintk (KERN_INFO PFX " cpuid: 0x%x\t", pst->cpuid); - dprintk ("fsb: %d\t", pst->fsbspeed); - dprintk ("maxFID: 0x%x\t", pst->maxfid); - dprintk ("startvid: 0x%x\n", pst->startvid); + dprintk ("PST:%d (@%p)\n", i, 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; @@ -540,6 +532,45 @@ static int __init fixup_sgtc(void) 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(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; @@ -550,15 +581,16 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy) rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); - /* A K7 with powernow technology is set to max frequency by BIOS */ + 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(KERN_INFO PFX "FSB: %3d.%03d MHz\n", fsb/1000, fsb%1000); + dprintk("FSB: %3dMHz\n", fsb/1000); - if ((dmi_broken & BROKEN_CPUFREQ) || powernow_acpi_force) { + 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 { @@ -588,9 +620,9 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy) policy->governor = CPUFREQ_DEFAULT_GOVERNOR; - policy->cpuinfo.transition_latency = 20 * latency / fsb; + policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency); - policy->cur = maximum_speed; + policy->cur = powernow_get(0); cpufreq_frequency_table_get_attr(powernow_table, policy->cpu); @@ -599,6 +631,15 @@ static int __init powernow_cpu_init (struct cpufreq_policy *policy) 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; } @@ -608,13 +649,14 @@ static struct freq_attr* powernow_table_attr[] = { }; static struct cpufreq_driver powernow_driver = { - .verify = powernow_verify, - .target = powernow_target, - .init = powernow_cpu_init, - .exit = powernow_cpu_exit, - .name = "powernow-k7", - .owner = THIS_MODULE, - .attr = powernow_table_attr, + .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) @@ -627,20 +669,11 @@ static int __init powernow_init (void) static void __exit powernow_exit (void) { -#ifdef CONFIG_ACPI_PROCESSOR - if (acpi_processor_perf) { - acpi_processor_unregister_performance(acpi_processor_perf, 0); - kfree(acpi_processor_perf); - } -#endif cpufreq_unregister_driver(&powernow_driver); - if (powernow_table) - kfree(powernow_table); } -module_param(powernow_acpi_force, int, 0444); - -MODULE_PARM_DESC(acpi_force, "Force ACPI to be used"); +module_param(acpi_force, int, 0444); +MODULE_PARM_DESC(acpi_force, "Force ACPI to be used."); MODULE_AUTHOR ("Dave Jones "); MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");