--- longhaul.c Thu May 16 12:50:00 2002 +++ longhaul_pb.c Thu May 16 13:05:26 2002 @@ -2,6 +2,7 @@ * $Id: longhaul.c,v 1.47 2002/05/02 15:47:39 davej Exp $ * * (C) 2001 Dave Jones. + * (C) 2002 Padraig Brady. * * Licensed under the terms of the GNU GPL License version 2. * Based upon datasheets & sample CPUs kindly provided by VIA. @@ -41,7 +42,6 @@ static int numscales=16, numvscales; static int minvid, maxvid; -static int currentfsb; static int can_scale_voltage; static int can_scale_fsb; static int vrmrev; @@ -50,9 +50,17 @@ /* Module parameters */ static int dont_scale_voltage; static int dont_scale_fsb; +static int current_fsb; +static int favour_fast_fsb; #define __hlt() __asm__ __volatile__("hlt": : :"memory") +/* + * Clock ratio tables. + * The eblcr ones specify the ratio read from the CPU. + * The clock_ratio ones specify what to write to the CPU. + */ + /* VIA C3 Samuel 1 & Samuel 2 (stepping 0)*/ static int __initdata longhaul1_clock_ratio[16] = { -1, /* 0000 -> RESERVED */ @@ -223,6 +231,15 @@ 145, /* 1111 -> 14.5x */ }; +/* fsb values as defined in CPU */ +static unsigned int eblcr_fsb_table[] = { 66, 133, 100, -1 }; +/* fsb values to favour low fsb speed (lower power) */ +static unsigned int power_fsb_table[] = { 66, 100, 133, -1 }; +/* fsb values to favour high fsb speed (for e.g. if lowering CPU + freq because of heat, but want to maintain highest performance possible) */ +static unsigned int perf_fsb_table[] = { 133, 100, 66, -1 }; +static unsigned int *fsb_search_table; + /* Voltage scales. Div by 1000 to get actual voltage. */ static int __initdata vrm85scales[32] = { 1250, 1200, 1150, 1100, 1050, 1800, 1750, 1700, @@ -244,14 +261,31 @@ static int voltage_table[32]; static int highest_speed, lowest_speed; static int longhaul; /* version. */ -static unsigned int clock_bogomips[32]; + +/* + * All integer math. + * Input: + * ratio is multiplier*10, for e.g. 15 = 1.5, 12=1.2 + * Output: + * return value is in units of fsb input parameter + */ +static unsigned int get_clock_from_ratio(int ratio, unsigned fsb) +{ + unsigned int clock=0; + + if (ratio > 0 && fsb != 0) { + clock = (ratio/10)*fsb; + if (ratio%10) + clock += (fsb/(10/(ratio%10))); + } + return clock; +} static unsigned int longhaul_validatespeed_fsb(unsigned int freq, unsigned fsb, unsigned int oldbest) { int i; unsigned int newclock; unsigned int best=0; - int bits=-1; best = oldbest; @@ -259,11 +293,8 @@ for (i=0; i best) && (newclock <= (freq+1))) if ((newclock>=lowest_speed) && (newclock<=highest_speed)) @@ -272,6 +303,7 @@ return best; } +/* Determine nearest speed possible for the desired fsb speed */ static unsigned int longhaul_validatespeed (unsigned int freq) { unsigned int best=0; @@ -282,12 +314,12 @@ freq=highest_speed; if (can_scale_fsb==1) { - best = longhaul_validatespeed_fsb (freq, 66, best); - best = longhaul_validatespeed_fsb (freq, 100, best); - best = longhaul_validatespeed_fsb (freq, 133, best); + int fsb_index; + for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) + best = longhaul_validatespeed_fsb (freq, fsb_search_table[fsb_index], best); } else { /* Can only scale multiplier. */ - best = longhaul_validatespeed_fsb (freq, currentfsb, best); + best = longhaul_validatespeed_fsb (freq, current_fsb, best); } return best; @@ -297,6 +329,8 @@ * longhaul_set_cpu_frequency() * This gets passed a MHz value that has been preprocessed * with longhaul_validatespeed(). + * + * Note this function is only called if a new frequency has been selected. */ static void longhaul_set_cpu_frequency (unsigned int Mhz) @@ -310,56 +344,33 @@ /* Find out which mult bit-pattern & FSB we want */ for (index=0; index>18; - return eblcr_fsb_table[invalue]; + if (current_fsb == 0) { + rdmsr (0x2a, lo, hi); + invalue = (lo & (1<<18|1<<19)) >>18; + return eblcr_fsb_table[invalue]; + } else { + return current_fsb; + } } @@ -476,7 +488,7 @@ unsigned long invalue=0,lo, hi; rdmsr (0x2a, lo, hi); - invalue = (lo & (1<<25|1<<24|1<<23|1<<22)) >>22; + invalue = (lo & (1<<22|1<<23|1<<24|1<<25)) >>22; if (longhaul==3) { if (lo & (1<<27)) invalue+=16; @@ -501,36 +513,40 @@ case 2 ... 3: rdmsr (0x110a, lo, hi); + invalue = (hi & (1<<0|1<<1|1<<2|1<<3)); - if (lo & (1<<12)) + if (hi & (1<<11)) invalue += 16; maxmult=multipliers[invalue]; - invalue = (hi & (1<<16|1<<17|1<<18|1<<19)) >> 16; - minmult = multipliers[invalue]; + #if 0 /* This is MaxMhz @ Min Voltage. Ignore for now */ + invalue = (hi & (1<<16|1<<17|1<<18|1<<19)) >> 16; + if (hi & (1<<27)) + invalue += 16; + minmult = multipliers[invalue]; + #else + minmult = 3; /* as per spec */ + #endif if (can_scale_fsb==1) { invalue = (hi & (1<<9|1<<10)) >> 9; maxfsb = fsb_table[invalue]; + invalue = (hi & (1<<25|1<<26)) >> 25; minfsb = fsb_table[invalue]; + dprintk (KERN_INFO "longhaul: Min FSB=%d Max FSB=%d\n", minfsb, maxfsb); } else { - minfsb = maxfsb = currentfsb; + minfsb = maxfsb = current_fsb; } break; } - highest_speed = (maxmult/10) * maxfsb; - if (maxmult%10==5) - highest_speed += maxfsb/2; - - lowest_speed = (minmult/10) * minfsb; - if (minmult%10==5) - lowest_speed += minfsb/2; + highest_speed = get_clock_from_ratio(maxmult, maxfsb); + lowest_speed = get_clock_from_ratio(minmult, minfsb); - dprintk (KERN_INFO "longhaul: MinMult=%d MaxMult=%d\n", + dprintk (KERN_INFO "longhaul: MinMult(x10)=%d MaxMult(x10)=%d\n", minmult, maxmult); dprintk (KERN_INFO "longhaul: Lowestspeed=%d Highestspeed=%d\n", lowest_speed, highest_speed); @@ -543,8 +559,8 @@ can_scale_voltage = 1; - minvid = (hi & (1<<20|1<<21|1<<22|1<<23|1<<24)) >> 20; - maxvid = (hi & (1<<4|1<<5|1<<6|1<<7|1<<8)) >> 4; + minvid = (hi & (1<<20|1<<21|1<<22|1<<23|1<<24)) >> 20; /* 56:52 */ + maxvid = (hi & (1<<4|1<<5|1<<6|1<<7|1<<8)) >> 4; /* 40:36 */ vrmrev = (lo & (1<<15))>>15; if (vrmrev==0) { @@ -560,7 +576,7 @@ /* Current voltage isn't readable at first, so we need to set it to a known value. The spec says to use maxvid */ revkey = (lo & 0xf)<<4; /* Rev key. */ - lo &= 0x0fff0bf0f; /* Mask unneeded bits */ + lo &= 0xfe0fff0f; /* Mask unneeded bits */ lo |= (1<<9); /* EnableSoftVID */ lo |= revkey; /* Reinsert key */ lo |= maxvid << 20; @@ -575,7 +591,8 @@ static int __init longhaul_init (void) { struct cpuinfo_x86 *c = cpu_data; - int currentspeed, halfmult; + unsigned int currentspeed; + static int currentmult; unsigned long lo, hi; if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) ) @@ -583,7 +600,7 @@ switch (c->x86_model) { case 6: /* VIA C3 Samuel C5A */ - return -ENODEV; // See line 499 + return -ENODEV; //see line 499 longhaul=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); @@ -592,7 +609,7 @@ case 7: /* C5B / C5C */ switch (c->x86_mask) { case 0: - return -ENODEV; // See line 499 + return -ENODEV; //see line 499 longhaul=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); @@ -606,7 +623,7 @@ break; case 8: /* C5M/C5N */ - return -ENODEV; // Waiting on updated docs from VIA before this is usable + return -ENODEV; // Waiting on updated docs from VIA before this is useable longhaul=3; numscales=32; memcpy (clock_ratio, longhaul3_clock_ratio, sizeof(longhaul3_clock_ratio)); @@ -620,29 +637,30 @@ printk (KERN_INFO "longhaul: VIA CPU detected. Longhaul version %d supported\n", longhaul); - currentfsb = longhaul_get_cpu_fsb(); - halfmult = longhaul_get_cpu_mult() % 10; - currentspeed = currentfsb * (longhaul_get_cpu_mult() / 10); - if (halfmult==5) - currentspeed += (currentfsb / 2); + if (favour_fast_fsb==1) + fsb_search_table = perf_fsb_table; + else + fsb_search_table = power_fsb_table; + + current_fsb = longhaul_get_cpu_fsb(); + currentmult = longhaul_get_cpu_mult(); + currentspeed = get_clock_from_ratio(currentmult, current_fsb); dprintk (KERN_INFO "longhaul: CPU currently at %dMHz (%d x %d.%d)\n", - currentspeed, currentfsb, longhaul_get_cpu_mult()/10, halfmult); + currentspeed, current_fsb, currentmult/10, currentmult%10); if (longhaul==2 || longhaul==3) { rdmsr (0x110a, lo, hi); if ((lo & (1<<0)) && (dont_scale_voltage==0)) longhaul_setup_voltagescaling (lo, hi); - if ((lo & (1<<1)) && (dont_scale_fsb==0)) + if ((lo & (1<<1)) && (dont_scale_fsb==0) && (current_fsb==0)) can_scale_fsb = 1; } longhaul_get_ranges(); - cpufreq_init (currentspeed*1000, - (unsigned int) lowest_speed, - (unsigned int) highest_speed); + cpufreq_init (currentspeed*1000); cpufreq_setfunctions (&longhaul_validatespeed, &longhaul_set_cpu_frequency); return 0; } @@ -655,6 +673,8 @@ MODULE_PARM (dont_scale_fsb, "i"); MODULE_PARM (dont_scale_voltage, "i"); +MODULE_PARM (current_fsb, "i"); +MODULE_PARM (favour_fast_fsb, "i"); MODULE_AUTHOR ("Dave Jones "); MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");