--- linux-2.6.16.9/arch/i386/kernel/cpu/cpufreq/powernow-k7.c.orig 2006-04-19 10:30:12.000000000 +0200 +++ linux-2.6.16.9/arch/i386/kernel/cpu/cpufreq/powernow-k7.c 2006-04-19 14:28:50.296543000 +0200 @@ -85,12 +85,35 @@ 150, 225, 160, 165, 170, 180, -1, -1, }; +/* translation table for even multiplier to fid */ +static int even_multiplier[20] = { + 16, 18, 4, 6, 8, 10, 12, 14, // 3, 4, 5, 6, 7 ,8 , 9, 10 + 0, 2, 20, 22, 24, 26, 28, 29, // 11, 12, 13, 14, 15, 16, 17, 18 + 17, 19, 23, 25, // 19, 20, 21, 22 +}; + +/* translation table for odd multiplier to fid*/ +static int odd_multiplier[9] = { + 5, 7, 9, 11, 13, 15, 1, 3, // 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5 + 21, // 13.5 +}; + /* This parameter is used in order to force ACPI instead of legacy method for * configuration purpose. */ static int acpi_force; +/* This parameters can be used to manually overwrite the tables */ +static int overwrite_table = 0; +#define MAX_PST 10 +static int multiplier_arr_size = MAX_PST; +static int voltage_arr_size = MAX_PST; +static int multiplier[MAX_PST] = {[0 ... (MAX_PST - 1)] = 0 }; +static int voltage[MAX_PST] = {[0 ... (MAX_PST - 1)] = 0 }; +static int switch_latency = 0; +static unsigned int fsb = 0; + static struct cpufreq_frequency_table *powernow_table; static unsigned int can_scale_bus; @@ -98,7 +121,6 @@ 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; @@ -408,6 +430,131 @@ } #endif +static int powernow_manual_settings(union msr_fidvidstatus *fidvidstatus) +{ + int i,k, validentry; + unsigned int max_multiplier, max_voltage; + unsigned int speed, cm; + u8 vid, fid; + static struct cpufreq_frequency_table *powernow_table_tmp; + + if (switch_latency > 0) { + if (switch_latency < 100) { + printk (KERN_INFO PFX "Settling time passed as %d microseconds." + "Should be at least 100. Correcting.\n", switch_latency); + switch_latency = 100; + } + latency = switch_latency; + } else { + latency = 200; + } + dprintk ("Settling Time: %d microseconds.\n", latency); + + /* get number of specified multipliers */ + number_scales = multiplier_arr_size; + for (i=0; i < multiplier_arr_size; i++) { + if (multiplier[i] == 0) { + number_scales=i; + break; + } + } + + /* get maximum values */ + max_multiplier = fid_codes[fidvidstatus->bits.MFID]; + max_voltage = mobile_vid_table[fidvidstatus->bits.MVID]; + + /* allocate memory */ + 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))); + + k=0; + for(i=0; i 4) && (cm < 14)) { + fid= odd_multiplier[cm-5]; + validentry = 1; + } + } else { + /* even_multiplier */ + if ((cm < 23) && (cm > 2)) { + fid = even_multiplier[cm-3]; + validentry=1; + } + } + } + } + + if (validentry) { + /* if no voltage specified use CPU default */ + if (voltage[i] == 0) + voltage[i] = max_voltage; + + /* we do not allow higher voltages than the CPU's maximum + 925 mV is the minimum */ + if ((voltage[i] <= max_voltage) && (voltage[i] >= 925)) { + if (voltage[i] >= 1300) { + vid = 40 - (voltage[i]/50); + } else { + vid = 67 - (voltage[i]/25); + } + /* calculate speed */ + speed = fsb * fid_codes[fid] / 10; + powernow_table[k].frequency = speed; + powernow_table[k].index=fid; /*lower 8 bits*/ + powernow_table[k].index|= (vid << 8); /*upper 8 bits*/ + + if (speed < minimum_speed) + minimum_speed = speed; + if (speed > maximum_speed) + maximum_speed = speed; + + dprintk (" FID: 0x%x (%d.%dx [%dMHz])\t", fid, fid_codes[fid] / 10, fid_codes[fid] % 10, (speed + 500)/1000); + printk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000, mobile_vid_table[vid]%1000); + k++; + } + } else { + // invalid entry + dprintk ("Entry %d is invalid\n", i+1); + } + } + + if (k < number_scales) { + /* some entrys were invalid need to realloc table */ + number_scales = k; + powernow_table_tmp = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL); + if (!powernow_table_tmp) { + kfree(powernow_table); + return -ENOMEM; + } + memcpy(powernow_table_tmp,powernow_table,(sizeof(struct cpufreq_frequency_table) * (number_scales + 1))); + kfree(powernow_table); + powernow_table=powernow_table_tmp; + } + + /* 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; @@ -581,11 +728,26 @@ rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val); - /* recalibrate cpu_khz */ - result = recalibrate_cpu_khz(); - if (result) - return result; + if (fsb) { + unsigned int mult; + if (fsb < 1000) + fsb *= 1000; + + if (fsb < 1000 || fsb > 1000000) { + printk(KERN_WARNING PFX "FSB %ukhz out of range\n", fsb); + return -EINVAL; + } + + mult = ((2*cpu_khz + fsb/2) / fsb) * 5; + if (mult != fid_codes[fidvidstatus.bits.MFID]) + dprintk("MSR reported multiplier %u.%u\n", fid_codes[fidvidstatus.bits.MFID]/10, fid_codes[fidvidstatus.bits.MFID]%10); + + dprintk("Current multiplier %u.%u. CPU running at %luMHz\n", mult/10, mult%10, (cpu_khz + 500)/1000); + if (mult < 30 || mult >= 230) + dprintk("Multiplier %u.%u not possible\n", mult/10, mult%10); + } else + fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID]; if (!fsb) { printk(KERN_WARNING PFX "can not determine bus frequency\n"); @@ -596,6 +758,9 @@ 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 if (overwrite_table){ + printk(KERN_INFO PFX "Overwriting PST table with manual settings\n"); + result = powernow_manual_settings(&fidvidstatus); } else { result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID); if (result) { @@ -678,6 +843,17 @@ module_param(acpi_force, int, 0444); MODULE_PARM_DESC(acpi_force, "Force ACPI to be used."); +module_param(overwrite_table,int,0444); +MODULE_PARM_DESC(overwrite_table, "overwrite table with manually settings"); +module_param(fsb, uint, 0444); +MODULE_PARM_DESC(fsb, "(MHz) overrides the calculated FSB"); +module_param_array(multiplier, int, &multiplier_arr_size, 0444); +MODULE_PARM_DESC(multiplier, "Specifiy up to 10 multipliers, multiply them by 10: 5->50, 5.5->55"); +module_param_array(voltage, int, &voltage_arr_size, 0444); +MODULE_PARM_DESC(voltage, "Specify voltages in respect to the given multipliers, specify them in mV: 1.275V -> 1275"); +module_param(switch_latency, int, 0444); +MODULE_PARM_DESC(switch_latency, "Set state transition latency in microseconds (default 200us)"); + MODULE_AUTHOR ("Dave Jones "); MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors."); MODULE_LICENSE ("GPL");