--- linux-2.6.16.4/arch/i386/kernel/cpu/cpufreq/powernow-k7.c.orig 2006-04-12 12:52:37.780859000 +0200 +++ linux-2.6.16.4/arch/i386/kernel/cpu/cpufreq/powernow-k7.c 2006-04-12 12:53:12.758318000 +0200 @@ -90,6 +90,9 @@ */ static int acpi_force; +static int msr_force; +static int msr_force_latency; +static int msr_force_voltage_scaling; static struct cpufreq_frequency_table *powernow_table; @@ -408,6 +411,116 @@ } #endif +static int powernow_decode_msr (union msr_fidvidstatus *fidvidstatus) +{ + unsigned int i; + unsigned int add_initial, add_max; + + /* FIXME: Is there a way to determine a safe value here + * in the absence of any hints from the BIOS? */ + if (msr_force_latency > 0) { + if (msr_force_latency < 100) { + printk (KERN_INFO PFX "Settling time passed as %d microseconds." + "Should be at least 100. Correcting.\n", msr_force_latency); + msr_force_latency = 100; + } + latency = msr_force_latency; + } else { + latency = 200; + } + dprintk (KERN_INFO PFX "Settling Time: %d microseconds.\n", latency); + + /* We have three possible settings - initial, current + * and maximum. */ + number_scales = 3; + + /* Whatever the BIOS set up initially must work... */ + if (fidvidstatus->bits.CFID != fidvidstatus->bits.SFID) { + dprintk (KERN_INFO PFX "Current frequency differs from initial BIOS setting.\n"); + add_initial = 1; + } else { + add_initial = 0; + } + + /* If the CPU-reported maximum is different to our current + * setting, we want to make it available. */ + if ((fidvidstatus->bits.CFID != fidvidstatus->bits.MFID) && + (fidvidstatus->bits.MFID != fidvidstatus->bits.SFID)) { + + dprintk (KERN_INFO PFX "Current frequency differs from CPU reported maximum.\n"); + add_max = 1; + } else { + add_max = 0; + } + + powernow_table = kmalloc((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 (i = 0; i < number_scales; i++) { + unsigned int speed, valid; + u8 fid, vid; + + switch (i) { + case 0: + /* Current settings */ + fid = fidvidstatus->bits.CFID; + vid = fidvidstatus->bits.CVID; + valid = 1; + break; + case 1: + /* BIOS initial settings */ + fid = fidvidstatus->bits.SFID; + vid = fidvidstatus->bits.SVID; + valid = add_initial; + break; + case 2: + /* CPU-reported maximum settings */ + fid = fidvidstatus->bits.MFID; + vid = fidvidstatus->bits.MVID; + valid = add_max; + break; + default: + vid = 0; + fid = 0; + valid = 0; + } + + if (valid) { + /* Add to frequency table */ + speed = fsb * fid_codes[fid] / 10; + + /* Do not scale voltages unless forced, + * since it could be dangerous. */ + if (!msr_force_voltage_scaling) { + vid = fidvidstatus->bits.CVID; + } + + 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); + + powernow_table[i].frequency = speed; + powernow_table[i].index = fid; /* lower 8 bits */ + powernow_table[i].index |= (vid << 8); /* upper 8 bits */ + if (speed < minimum_speed) + minimum_speed = speed; + if (speed > maximum_speed) + maximum_speed = speed; + } else { + /* Add a dummy entry and carry on */ + powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; + powernow_table[i].index = 0; + } + } + + /* Terminate frequency list */ + powernow_table[number_scales].frequency = CPUFREQ_TABLE_END; + powernow_table[number_scales].index = 0; + + return 0; +} + static int powernow_decode_bios (int maxfid, int startvid) { struct psb_s *psb; @@ -615,6 +728,11 @@ } } + if (result && msr_force) { + printk (KERN_INFO PFX "Building frequency table from MSR info.\n"); + result = powernow_decode_msr(&fidvidstatus); + } + if (result) return result; @@ -678,6 +796,13 @@ module_param(acpi_force, int, 0444); MODULE_PARM_DESC(acpi_force, "Force ACPI to be used."); +module_param(msr_force, int, 0444); +MODULE_PARM_DESC(msr_force, "Force fallback to CPU MSR info"); +module_param(msr_force_latency, int, 0444); +MODULE_PARM_DESC(msr_force_latency, "Set state transition latency in microseconds (default 200us)"); +module_param(msr_force_voltage_scaling, int, 0444); +MODULE_PARM_DESC(msr_force_voltage_scaling, "Enable CPU core voltage scaling"); + MODULE_AUTHOR ("Dave Jones "); MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors."); MODULE_LICENSE ("GPL");