📄 longhaul.c
字号:
lo &= ~(1<<28|1<<29); switch (newfsb) { case 66: lo |= (1<<28|1<<29); /* 11 */ break; case 100: lo |= 1<<28; /* 01 */ break; case 133: break; /* 00*/ } } wrmsr (MSR_VIA_LONGHAUL, lo, hi); __hlt(); rdmsr (MSR_VIA_LONGHAUL, lo, hi); lo &= ~(1<<8); lo |= revkey; wrmsr (MSR_VIA_LONGHAUL, lo, hi); break; } cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);}static void __init longhaul_get_ranges (void){ unsigned long lo, hi, invalue; unsigned int minmult=0, maxmult=0, minfsb=0, maxfsb=0; unsigned int multipliers[32]= { 50,30,40,100,55,35,45,95,90,70,80,60,120,75,85,65, -1,110,120,-1,135,115,125,105,130,150,160,140,-1,155,-1,145 }; unsigned int fsb_table[4] = { 133, 100, -1, 66 }; switch (longhaul) { case 1: /* Ugh, Longhaul v1 didn't have the min/max MSRs. Assume min=3.0x & max = whatever we booted at. */ minmult = 30; maxmult = longhaul_get_cpu_mult(); minfsb = maxfsb = current_fsb; break; case 2 ... 3: rdmsr (MSR_VIA_LONGHAUL, lo, hi); invalue = (hi & (1<<0|1<<1|1<<2|1<<3)); if (hi & (1<<11)) invalue += 16; maxmult=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 = 30; /* 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 = current_fsb; } break; } highest_speed = maxmult * maxfsb * 100; lowest_speed = minmult * minfsb * 100; 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);}static void __init longhaul_setup_voltagescaling (unsigned long lo, unsigned long hi){ int revkey; can_scale_voltage = 1; 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) { dprintk (KERN_INFO "longhaul: VRM 8.5 : "); memcpy (voltage_table, vrm85scales, sizeof(voltage_table)); numvscales = (voltage_table[maxvid]-voltage_table[minvid])/25; } else { dprintk (KERN_INFO "longhaul: Mobile VRM : "); memcpy (voltage_table, mobilevrmscales, sizeof(voltage_table)); numvscales = (voltage_table[maxvid]-voltage_table[minvid])/5; } /* 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 &= 0xfe0fff0f; /* Mask unneeded bits */ lo |= (1<<9); /* EnableSoftVID */ lo |= revkey; /* Reinsert key */ lo |= maxvid << 20; wrmsr (MSR_VIA_LONGHAUL, lo, hi); minvid = voltage_table[minvid]; maxvid = voltage_table[maxvid]; dprintk ("Min VID=%d.%03d Max VID=%d.%03d, %d possible voltage scales\n", maxvid/1000, maxvid%1000, minvid/1000, minvid%1000, numvscales);}static inline unsigned int longhaul_statecount_fsb(struct cpufreq_policy *policy, unsigned int fsb) { unsigned int i, count = 0; for(i=0; i<numscales; i++) { if ((clock_ratio[i] != -1) && ((clock_ratio[i] * fsb * 100) <= policy->max) && ((clock_ratio[i] * fsb * 100) >= policy->min)) count++; } return count;}static int longhaul_verify(struct cpufreq_policy *policy){ unsigned int number_states = 0; unsigned int i; unsigned int fsb_index = 0; unsigned int tmpfreq = 0; unsigned int newmax = -1; if (!policy || !longhaul_driver) return -EINVAL; policy->cpu = 0; cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); if (can_scale_fsb==1) { for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) number_states += longhaul_statecount_fsb(policy, fsb_search_table[fsb_index]); } else number_states = longhaul_statecount_fsb(policy, current_fsb); if (number_states) return 0; /* get frequency closest above current policy->max */ if (can_scale_fsb==1) { for (fsb_index=0; fsb_search_table[fsb_index] != -1; fsb_index++) for(i=0; i<numscales; i++) { if (clock_ratio[i] == -1) continue; tmpfreq = clock_ratio[i] * fsb_search_table[fsb_index]; if ((tmpfreq > policy->max) && (tmpfreq < newmax)) newmax = tmpfreq; } } else { for(i=0; i<numscales; i++) { if (clock_ratio[i] == -1) continue; tmpfreq = clock_ratio[i] * current_fsb; if ((tmpfreq > policy->max) && (tmpfreq < newmax)) newmax = tmpfreq; } } policy->max = newmax; cpufreq_verify_within_limits(policy, lowest_speed, highest_speed); return 0;}static int longhaul_get_best_freq_for_fsb(struct cpufreq_policy *policy, unsigned int min_mult, unsigned int max_mult, unsigned int fsb, unsigned int *new_mult){ unsigned int optimal = 0; unsigned int found_optimal = 0; unsigned int i; switch(policy->policy) { case CPUFREQ_POLICY_POWERSAVE: optimal = max_mult; break; case CPUFREQ_POLICY_PERFORMANCE: optimal = min_mult; } for(i=0; i<numscales; i++) { unsigned int freq = fsb * clock_ratio[i] * 100; if ((freq > policy->max) || (freq < policy->min)) continue; switch(policy->policy) { case CPUFREQ_POLICY_POWERSAVE: if (clock_ratio[i] < clock_ratio[optimal]) { found_optimal = 1; optimal = i; } break; case CPUFREQ_POLICY_PERFORMANCE: if (clock_ratio[i] > clock_ratio[optimal]) { found_optimal = 1; optimal = i; } break; } } if (found_optimal) { *new_mult = optimal; return 1; } return 0;}static int longhaul_setpolicy (struct cpufreq_policy *policy){ unsigned int i; unsigned int fsb_index = 0; unsigned int new_fsb = 0; unsigned int new_clock_ratio = 0; unsigned int min_mult = 0; unsigned int max_mult = 0; if (!longhaul_driver) return -EINVAL; if (policy->policy==CPUFREQ_POLICY_PERFORMANCE) fsb_search_table = perf_fsb_table; else fsb_search_table = power_fsb_table; for(i=0;i<numscales;i++) { if (clock_ratio[max_mult] < clock_ratio[i]) max_mult = i; else if (clock_ratio[min_mult] > clock_ratio[i]) min_mult = i; } if (can_scale_fsb==1) { unsigned int found = 0; for (fsb_index=0; fsb_search_table[fsb_index]!=-1; fsb_index++) { if (longhaul_get_best_freq_for_fsb(policy, min_mult, max_mult, fsb_search_table[fsb_index], &new_clock_ratio)) { new_fsb = fsb_search_table[fsb_index]; break; } } if (!found) return -EINVAL; } else { new_fsb = current_fsb; if (!longhaul_get_best_freq_for_fsb(policy, min_mult, max_mult, new_fsb, &new_clock_ratio)) return -EINVAL; } longhaul_setstate(new_clock_ratio, new_fsb); return 0;}static int __init longhaul_init (void){ struct cpuinfo_x86 *c = cpu_data; unsigned int currentspeed; static int currentmult; unsigned long lo, hi; int ret; struct cpufreq_driver *driver; if ((c->x86_vendor != X86_VENDOR_CENTAUR) || (c->x86 !=6) ) return -ENODEV; switch (c->x86_model) { case 6: /* VIA C3 Samuel C5A */ longhaul=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel1_eblcr, sizeof(samuel1_eblcr)); break; case 7: /* C5B / C5C */ switch (c->x86_mask) { case 0: longhaul=1; memcpy (clock_ratio, longhaul1_clock_ratio, sizeof(longhaul1_clock_ratio)); memcpy (eblcr_table, samuel2_eblcr, sizeof(samuel2_eblcr)); break; case 1 ... 15: longhaul=2; memcpy (clock_ratio, longhaul2_clock_ratio, sizeof(longhaul2_clock_ratio)); memcpy (eblcr_table, ezra_eblcr, sizeof(ezra_eblcr)); break; } break; case 8: /* C5M/C5N */ return -ENODEV; // Waiting on updated docs from VIA before this is usable longhaul=3; numscales=32; memcpy (clock_ratio, longhaul3_clock_ratio, sizeof(longhaul3_clock_ratio)); memcpy (eblcr_table, c5m_eblcr, sizeof(c5m_eblcr)); break; default: printk (KERN_INFO "longhaul: Unknown VIA CPU. Contact davej@suse.de\n"); return -ENODEV; } printk (KERN_INFO "longhaul: VIA CPU detected. Longhaul version %d supported\n", longhaul); current_fsb = longhaul_get_cpu_fsb(); currentmult = longhaul_get_cpu_mult(); currentspeed = currentmult * current_fsb * 100; dprintk (KERN_INFO "longhaul: CPU currently at %dMHz (%d x %d.%d)\n", (currentspeed/1000), current_fsb, currentmult/10, currentmult%10); if (longhaul==2 || longhaul==3) { rdmsr (MSR_VIA_LONGHAUL, lo, hi); if ((lo & (1<<0)) && (dont_scale_voltage==0)) longhaul_setup_voltagescaling (lo, hi); if ((lo & (1<<1)) && (dont_scale_fsb==0) && (current_fsb==0)) can_scale_fsb = 1; } longhaul_get_ranges(); driver = kmalloc(sizeof(struct cpufreq_driver) + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL); if (!driver) return -ENOMEM; driver->policy = (struct cpufreq_policy *) (driver + 1);#ifdef CONFIG_CPU_FREQ_24_API driver->cpu_cur_freq[0] = currentspeed;#endif driver->verify = &longhaul_verify; driver->setpolicy = &longhaul_setpolicy; driver->policy[0].cpu = 0; driver->policy[0].min = (unsigned int) lowest_speed; driver->policy[0].max = (unsigned int) highest_speed; driver->policy[0].policy = CPUFREQ_POLICY_PERFORMANCE; driver->policy[0].cpuinfo.min_freq = (unsigned int) lowest_speed; driver->policy[0].cpuinfo.max_freq = (unsigned int) highest_speed; driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL; longhaul_driver = driver; ret = cpufreq_register(driver); if (ret) { longhaul_driver = NULL; kfree(driver); } return ret;}static void __exit longhaul_exit (void){ if (longhaul_driver) { cpufreq_unregister(); kfree(longhaul_driver); }}MODULE_PARM (dont_scale_fsb, "i");MODULE_PARM (dont_scale_voltage, "i");MODULE_PARM (current_fsb, "i");MODULE_AUTHOR ("Dave Jones <davej@suse.de>");MODULE_DESCRIPTION ("Longhaul driver for VIA Cyrix processors.");MODULE_LICENSE ("GPL");module_init(longhaul_init);module_exit(longhaul_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -