1 /* 2 * Based on documentation provided by Dave Jones. Thanks! 3 * 4 * Licensed under the terms of the GNU GPL License version 2. 5 * 6 * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/module.h> 11 #include <linux/init.h> 12 #include <linux/cpufreq.h> 13 #include <linux/ioport.h> 14 #include <linux/slab.h> 15 #include <linux/timex.h> 16 #include <linux/io.h> 17 #include <linux/delay.h> 18 19 #include <asm/msr.h> 20 #include <asm/tsc.h> 21 22 #define EPS_BRAND_C7M 0 23 #define EPS_BRAND_C7 1 24 #define EPS_BRAND_EDEN 2 25 #define EPS_BRAND_C3 3 26 #define EPS_BRAND_C7D 4 27 28 struct eps_cpu_data { 29 u32 fsb; 30 struct cpufreq_frequency_table freq_table[]; 31 }; 32 33 static struct eps_cpu_data *eps_cpu[NR_CPUS]; 34 35 36 static unsigned int eps_get(unsigned int cpu) 37 { 38 struct eps_cpu_data *centaur; 39 u32 lo, hi; 40 41 if (cpu) 42 return 0; 43 centaur = eps_cpu[cpu]; 44 if (centaur == NULL) 45 return 0; 46 47 /* Return current frequency */ 48 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 49 return centaur->fsb * ((lo >> 8) & 0xff); 50 } 51 52 static int eps_set_state(struct eps_cpu_data *centaur, 53 unsigned int cpu, 54 u32 dest_state) 55 { 56 struct cpufreq_freqs freqs; 57 u32 lo, hi; 58 int err = 0; 59 int i; 60 61 freqs.old = eps_get(cpu); 62 freqs.new = centaur->fsb * ((dest_state >> 8) & 0xff); 63 freqs.cpu = cpu; 64 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 65 66 /* Wait while CPU is busy */ 67 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 68 i = 0; 69 while (lo & ((1 << 16) | (1 << 17))) { 70 udelay(16); 71 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 72 i++; 73 if (unlikely(i > 64)) { 74 err = -ENODEV; 75 goto postchange; 76 } 77 } 78 /* Set new multiplier and voltage */ 79 wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0); 80 /* Wait until transition end */ 81 i = 0; 82 do { 83 udelay(16); 84 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 85 i++; 86 if (unlikely(i > 64)) { 87 err = -ENODEV; 88 goto postchange; 89 } 90 } while (lo & ((1 << 16) | (1 << 17))); 91 92 /* Return current frequency */ 93 postchange: 94 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 95 freqs.new = centaur->fsb * ((lo >> 8) & 0xff); 96 97 #ifdef DEBUG 98 { 99 u8 current_multiplier, current_voltage; 100 101 /* Print voltage and multiplier */ 102 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 103 current_voltage = lo & 0xff; 104 printk(KERN_INFO "eps: Current voltage = %dmV\n", 105 current_voltage * 16 + 700); 106 current_multiplier = (lo >> 8) & 0xff; 107 printk(KERN_INFO "eps: Current multiplier = %d\n", 108 current_multiplier); 109 } 110 #endif 111 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 112 return err; 113 } 114 115 static int eps_target(struct cpufreq_policy *policy, 116 unsigned int target_freq, 117 unsigned int relation) 118 { 119 struct eps_cpu_data *centaur; 120 unsigned int newstate = 0; 121 unsigned int cpu = policy->cpu; 122 unsigned int dest_state; 123 int ret; 124 125 if (unlikely(eps_cpu[cpu] == NULL)) 126 return -ENODEV; 127 centaur = eps_cpu[cpu]; 128 129 if (unlikely(cpufreq_frequency_table_target(policy, 130 &eps_cpu[cpu]->freq_table[0], 131 target_freq, 132 relation, 133 &newstate))) { 134 return -EINVAL; 135 } 136 137 /* Make frequency transition */ 138 dest_state = centaur->freq_table[newstate].index & 0xffff; 139 ret = eps_set_state(centaur, cpu, dest_state); 140 if (ret) 141 printk(KERN_ERR "eps: Timeout!\n"); 142 return ret; 143 } 144 145 static int eps_verify(struct cpufreq_policy *policy) 146 { 147 return cpufreq_frequency_table_verify(policy, 148 &eps_cpu[policy->cpu]->freq_table[0]); 149 } 150 151 static int eps_cpu_init(struct cpufreq_policy *policy) 152 { 153 unsigned int i; 154 u32 lo, hi; 155 u64 val; 156 u8 current_multiplier, current_voltage; 157 u8 max_multiplier, max_voltage; 158 u8 min_multiplier, min_voltage; 159 u8 brand = 0; 160 u32 fsb; 161 struct eps_cpu_data *centaur; 162 struct cpuinfo_x86 *c = &cpu_data(0); 163 struct cpufreq_frequency_table *f_table; 164 int k, step, voltage; 165 int ret; 166 int states; 167 168 if (policy->cpu != 0) 169 return -ENODEV; 170 171 /* Check brand */ 172 printk(KERN_INFO "eps: Detected VIA "); 173 174 switch (c->x86_model) { 175 case 10: 176 rdmsr(0x1153, lo, hi); 177 brand = (((lo >> 2) ^ lo) >> 18) & 3; 178 printk(KERN_CONT "Model A "); 179 break; 180 case 13: 181 rdmsr(0x1154, lo, hi); 182 brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff; 183 printk(KERN_CONT "Model D "); 184 break; 185 } 186 187 switch (brand) { 188 case EPS_BRAND_C7M: 189 printk(KERN_CONT "C7-M\n"); 190 break; 191 case EPS_BRAND_C7: 192 printk(KERN_CONT "C7\n"); 193 break; 194 case EPS_BRAND_EDEN: 195 printk(KERN_CONT "Eden\n"); 196 break; 197 case EPS_BRAND_C7D: 198 printk(KERN_CONT "C7-D\n"); 199 break; 200 case EPS_BRAND_C3: 201 printk(KERN_CONT "C3\n"); 202 return -ENODEV; 203 break; 204 } 205 /* Enable Enhanced PowerSaver */ 206 rdmsrl(MSR_IA32_MISC_ENABLE, val); 207 if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) { 208 val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP; 209 wrmsrl(MSR_IA32_MISC_ENABLE, val); 210 /* Can be locked at 0 */ 211 rdmsrl(MSR_IA32_MISC_ENABLE, val); 212 if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) { 213 printk(KERN_INFO "eps: Can't enable Enhanced PowerSaver\n"); 214 return -ENODEV; 215 } 216 } 217 218 /* Print voltage and multiplier */ 219 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 220 current_voltage = lo & 0xff; 221 printk(KERN_INFO "eps: Current voltage = %dmV\n", 222 current_voltage * 16 + 700); 223 current_multiplier = (lo >> 8) & 0xff; 224 printk(KERN_INFO "eps: Current multiplier = %d\n", current_multiplier); 225 226 /* Print limits */ 227 max_voltage = hi & 0xff; 228 printk(KERN_INFO "eps: Highest voltage = %dmV\n", 229 max_voltage * 16 + 700); 230 max_multiplier = (hi >> 8) & 0xff; 231 printk(KERN_INFO "eps: Highest multiplier = %d\n", max_multiplier); 232 min_voltage = (hi >> 16) & 0xff; 233 printk(KERN_INFO "eps: Lowest voltage = %dmV\n", 234 min_voltage * 16 + 700); 235 min_multiplier = (hi >> 24) & 0xff; 236 printk(KERN_INFO "eps: Lowest multiplier = %d\n", min_multiplier); 237 238 /* Sanity checks */ 239 if (current_multiplier == 0 || max_multiplier == 0 240 || min_multiplier == 0) 241 return -EINVAL; 242 if (current_multiplier > max_multiplier 243 || max_multiplier <= min_multiplier) 244 return -EINVAL; 245 if (current_voltage > 0x1f || max_voltage > 0x1f) 246 return -EINVAL; 247 if (max_voltage < min_voltage) 248 return -EINVAL; 249 250 /* Calc FSB speed */ 251 fsb = cpu_khz / current_multiplier; 252 /* Calc number of p-states supported */ 253 if (brand == EPS_BRAND_C7M) 254 states = max_multiplier - min_multiplier + 1; 255 else 256 states = 2; 257 258 /* Allocate private data and frequency table for current cpu */ 259 centaur = kzalloc(sizeof(struct eps_cpu_data) 260 + (states + 1) * sizeof(struct cpufreq_frequency_table), 261 GFP_KERNEL); 262 if (!centaur) 263 return -ENOMEM; 264 eps_cpu[0] = centaur; 265 266 /* Copy basic values */ 267 centaur->fsb = fsb; 268 269 /* Fill frequency and MSR value table */ 270 f_table = ¢aur->freq_table[0]; 271 if (brand != EPS_BRAND_C7M) { 272 f_table[0].frequency = fsb * min_multiplier; 273 f_table[0].index = (min_multiplier << 8) | min_voltage; 274 f_table[1].frequency = fsb * max_multiplier; 275 f_table[1].index = (max_multiplier << 8) | max_voltage; 276 f_table[2].frequency = CPUFREQ_TABLE_END; 277 } else { 278 k = 0; 279 step = ((max_voltage - min_voltage) * 256) 280 / (max_multiplier - min_multiplier); 281 for (i = min_multiplier; i <= max_multiplier; i++) { 282 voltage = (k * step) / 256 + min_voltage; 283 f_table[k].frequency = fsb * i; 284 f_table[k].index = (i << 8) | voltage; 285 k++; 286 } 287 f_table[k].frequency = CPUFREQ_TABLE_END; 288 } 289 290 policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */ 291 policy->cur = fsb * current_multiplier; 292 293 ret = cpufreq_frequency_table_cpuinfo(policy, ¢aur->freq_table[0]); 294 if (ret) { 295 kfree(centaur); 296 return ret; 297 } 298 299 cpufreq_frequency_table_get_attr(¢aur->freq_table[0], policy->cpu); 300 return 0; 301 } 302 303 static int eps_cpu_exit(struct cpufreq_policy *policy) 304 { 305 unsigned int cpu = policy->cpu; 306 struct eps_cpu_data *centaur; 307 u32 lo, hi; 308 309 if (eps_cpu[cpu] == NULL) 310 return -ENODEV; 311 centaur = eps_cpu[cpu]; 312 313 /* Get max frequency */ 314 rdmsr(MSR_IA32_PERF_STATUS, lo, hi); 315 /* Set max frequency */ 316 eps_set_state(centaur, cpu, hi & 0xffff); 317 /* Bye */ 318 cpufreq_frequency_table_put_attr(policy->cpu); 319 kfree(eps_cpu[cpu]); 320 eps_cpu[cpu] = NULL; 321 return 0; 322 } 323 324 static struct freq_attr *eps_attr[] = { 325 &cpufreq_freq_attr_scaling_available_freqs, 326 NULL, 327 }; 328 329 static struct cpufreq_driver eps_driver = { 330 .verify = eps_verify, 331 .target = eps_target, 332 .init = eps_cpu_init, 333 .exit = eps_cpu_exit, 334 .get = eps_get, 335 .name = "e_powersaver", 336 .owner = THIS_MODULE, 337 .attr = eps_attr, 338 }; 339 340 static int __init eps_init(void) 341 { 342 struct cpuinfo_x86 *c = &cpu_data(0); 343 344 /* This driver will work only on Centaur C7 processors with 345 * Enhanced SpeedStep/PowerSaver registers */ 346 if (c->x86_vendor != X86_VENDOR_CENTAUR 347 || c->x86 != 6 || c->x86_model < 10) 348 return -ENODEV; 349 if (!cpu_has(c, X86_FEATURE_EST)) 350 return -ENODEV; 351 352 if (cpufreq_register_driver(&eps_driver)) 353 return -EINVAL; 354 return 0; 355 } 356 357 static void __exit eps_exit(void) 358 { 359 cpufreq_unregister_driver(&eps_driver); 360 } 361 362 MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>"); 363 MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's."); 364 MODULE_LICENSE("GPL"); 365 366 module_init(eps_init); 367 module_exit(eps_exit); 368