1/* 2 * (C) 2004-2006 Sebastian Witt <se.witt@gmx.net> 3 * 4 * Licensed under the terms of the GNU GPL License version 2. 5 * Based upon reverse engineered information 6 * 7 * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous* 8 */ 9 10#include <linux/kernel.h> 11#include <linux/module.h> 12#include <linux/moduleparam.h> 13#include <linux/init.h> 14#include <linux/cpufreq.h> 15#include <linux/pci.h> 16#include <linux/delay.h> 17 18#define NFORCE2_XTAL 25 19#define NFORCE2_BOOTFSB 0x48 20#define NFORCE2_PLLENABLE 0xa8 21#define NFORCE2_PLLREG 0xa4 22#define NFORCE2_PLLADR 0xa0 23#define NFORCE2_PLL(mul, div) (0x100000 | (mul << 8) | div) 24 25#define NFORCE2_MIN_FSB 50 26#define NFORCE2_SAFE_DISTANCE 50 27 28/* Delay in ms between FSB changes */ 29//#define NFORCE2_DELAY 10 30 31/* nforce2_chipset: 32 * FSB is changed using the chipset 33 */ 34static struct pci_dev *nforce2_chipset_dev; 35 36/* fid: 37 * multiplier * 10 38 */ 39static int fid = 0; 40 41/* min_fsb, max_fsb: 42 * minimum and maximum FSB (= FSB at boot time) 43 */ 44static int min_fsb = 0; 45static int max_fsb = 0; 46 47MODULE_AUTHOR("Sebastian Witt <se.witt@gmx.net>"); 48MODULE_DESCRIPTION("nForce2 FSB changing cpufreq driver"); 49MODULE_LICENSE("GPL"); 50 51module_param(fid, int, 0444); 52module_param(min_fsb, int, 0444); 53 54MODULE_PARM_DESC(fid, "CPU multiplier to use (11.5 = 115)"); 55MODULE_PARM_DESC(min_fsb, 56 "Minimum FSB to use, if not defined: current FSB - 50"); 57 58#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-nforce2", msg) 59 60/** 61 * nforce2_calc_fsb - calculate FSB 62 * @pll: PLL value 63 * 64 * Calculates FSB from PLL value 65 */ 66static int nforce2_calc_fsb(int pll) 67{ 68 unsigned char mul, div; 69 70 mul = (pll >> 8) & 0xff; 71 div = pll & 0xff; 72 73 if (div > 0) 74 return NFORCE2_XTAL * mul / div; 75 76 return 0; 77} 78 79/** 80 * nforce2_calc_pll - calculate PLL value 81 * @fsb: FSB 82 * 83 * Calculate PLL value for given FSB 84 */ 85static int nforce2_calc_pll(unsigned int fsb) 86{ 87 unsigned char xmul, xdiv; 88 unsigned char mul = 0, div = 0; 89 int tried = 0; 90 91 /* Try to calculate multiplier and divider up to 4 times */ 92 while (((mul == 0) || (div == 0)) && (tried <= 3)) { 93 for (xdiv = 2; xdiv <= 0x80; xdiv++) 94 for (xmul = 1; xmul <= 0xfe; xmul++) 95 if (nforce2_calc_fsb(NFORCE2_PLL(xmul, xdiv)) == 96 fsb + tried) { 97 mul = xmul; 98 div = xdiv; 99 } 100 tried++; 101 } 102 103 if ((mul == 0) || (div == 0)) 104 return -1; 105 106 return NFORCE2_PLL(mul, div); 107} 108 109/** 110 * nforce2_write_pll - write PLL value to chipset 111 * @pll: PLL value 112 * 113 * Writes new FSB PLL value to chipset 114 */ 115static void nforce2_write_pll(int pll) 116{ 117 int temp; 118 119 /* Set the pll addr. to 0x00 */ 120 pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLADR, 0); 121 122 /* Now write the value in all 64 registers */ 123 for (temp = 0; temp <= 0x3f; temp++) 124 pci_write_config_dword(nforce2_chipset_dev, NFORCE2_PLLREG, pll); 125 126 return; 127} 128 129/** 130 * nforce2_fsb_read - Read FSB 131 * 132 * Read FSB from chipset 133 * If bootfsb != 0, return FSB at boot-time 134 */ 135static unsigned int nforce2_fsb_read(int bootfsb) 136{ 137 struct pci_dev *nforce2_sub5; 138 u32 fsb, temp = 0; 139 140 /* Get chipset boot FSB from subdevice 5 (FSB at boot-time) */ 141 nforce2_sub5 = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 142 0x01EF,PCI_ANY_ID,PCI_ANY_ID,NULL); 143 if (!nforce2_sub5) 144 return 0; 145 146 pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb); 147 fsb /= 1000000; 148 149 /* Check if PLL register is already set */ 150 pci_read_config_byte(nforce2_chipset_dev,NFORCE2_PLLENABLE, (u8 *)&temp); 151 152 if(bootfsb || !temp) 153 return fsb; 154 155 /* Use PLL register FSB value */ 156 pci_read_config_dword(nforce2_chipset_dev,NFORCE2_PLLREG, &temp); 157 fsb = nforce2_calc_fsb(temp); 158 159 return fsb; 160} 161 162/** 163 * nforce2_set_fsb - set new FSB 164 * @fsb: New FSB 165 * 166 * Sets new FSB 167 */ 168static int nforce2_set_fsb(unsigned int fsb) 169{ 170 u32 temp = 0; 171 unsigned int tfsb; 172 int diff; 173 int pll = 0; 174 175 if ((fsb > max_fsb) || (fsb < NFORCE2_MIN_FSB)) { 176 printk(KERN_ERR "cpufreq: FSB %d is out of range!\n", fsb); 177 return -EINVAL; 178 } 179 180 tfsb = nforce2_fsb_read(0); 181 if (!tfsb) { 182 printk(KERN_ERR "cpufreq: Error while reading the FSB\n"); 183 return -EINVAL; 184 } 185 186 /* First write? Then set actual value */ 187 pci_read_config_byte(nforce2_chipset_dev,NFORCE2_PLLENABLE, (u8 *)&temp); 188 if (!temp) { 189 pll = nforce2_calc_pll(tfsb); 190 191 if (pll < 0) 192 return -EINVAL; 193 194 nforce2_write_pll(pll); 195 } 196 197 /* Enable write access */ 198 temp = 0x01; 199 pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLENABLE, (u8)temp); 200 201 diff = tfsb - fsb; 202 203 if (!diff) 204 return 0; 205 206 while ((tfsb != fsb) && (tfsb <= max_fsb) && (tfsb >= min_fsb)) { 207 if (diff < 0) 208 tfsb++; 209 else 210 tfsb--; 211 212 /* Calculate the PLL reg. value */ 213 if ((pll = nforce2_calc_pll(tfsb)) == -1) 214 return -EINVAL; 215 216 nforce2_write_pll(pll); 217#ifdef NFORCE2_DELAY 218 mdelay(NFORCE2_DELAY); 219#endif 220 } 221 222 temp = 0x40; 223 pci_write_config_byte(nforce2_chipset_dev, NFORCE2_PLLADR, (u8)temp); 224 225 return 0; 226} 227 228/** 229 * nforce2_get - get the CPU frequency 230 * @cpu: CPU number 231 * 232 * Returns the CPU frequency 233 */ 234static unsigned int nforce2_get(unsigned int cpu) 235{ 236 if (cpu) 237 return 0; 238 return nforce2_fsb_read(0) * fid * 100; 239} 240 241/** 242 * nforce2_target - set a new CPUFreq policy 243 * @policy: new policy 244 * @target_freq: the target frequency 245 * @relation: how that frequency relates to achieved frequency (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H) 246 * 247 * Sets a new CPUFreq policy. 248 */ 249static int nforce2_target(struct cpufreq_policy *policy, 250 unsigned int target_freq, unsigned int relation) 251{ 252// unsigned long flags; 253 struct cpufreq_freqs freqs; 254 unsigned int target_fsb; 255 256 if ((target_freq > policy->max) || (target_freq < policy->min)) 257 return -EINVAL; 258 259 target_fsb = target_freq / (fid * 100); 260 261 freqs.old = nforce2_get(policy->cpu); 262 freqs.new = target_fsb * fid * 100; 263 freqs.cpu = 0; /* Only one CPU on nForce2 plattforms */ 264 265 if (freqs.old == freqs.new) 266 return 0; 267 268 dprintk("Old CPU frequency %d kHz, new %d kHz\n", 269 freqs.old, freqs.new); 270 271 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 272 273 /* Disable IRQs */ 274 //local_irq_save(flags); 275 276 if (nforce2_set_fsb(target_fsb) < 0) 277 printk(KERN_ERR "cpufreq: Changing FSB to %d failed\n", 278 target_fsb); 279 else 280 dprintk("Changed FSB successfully to %d\n", 281 target_fsb); 282 283 /* Enable IRQs */ 284 //local_irq_restore(flags); 285 286 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 287 288 return 0; 289} 290 291/** 292 * nforce2_verify - verifies a new CPUFreq policy 293 * @policy: new policy 294 */ 295static int nforce2_verify(struct cpufreq_policy *policy) 296{ 297 unsigned int fsb_pol_max; 298 299 fsb_pol_max = policy->max / (fid * 100); 300 301 if (policy->min < (fsb_pol_max * fid * 100)) 302 policy->max = (fsb_pol_max + 1) * fid * 100; 303 304 cpufreq_verify_within_limits(policy, 305 policy->cpuinfo.min_freq, 306 policy->cpuinfo.max_freq); 307 return 0; 308} 309 310static int nforce2_cpu_init(struct cpufreq_policy *policy) 311{ 312 unsigned int fsb; 313 unsigned int rfid; 314 315 /* capability check */ 316 if (policy->cpu != 0) 317 return -ENODEV; 318 319 /* Get current FSB */ 320 fsb = nforce2_fsb_read(0); 321 322 if (!fsb) 323 return -EIO; 324 325 /* FIX: Get FID from CPU */ 326 if (!fid) { 327 if (!cpu_khz) { 328 printk(KERN_WARNING 329 "cpufreq: cpu_khz not set, can't calculate multiplier!\n"); 330 return -ENODEV; 331 } 332 333 fid = cpu_khz / (fsb * 100); 334 rfid = fid % 5; 335 336 if (rfid) { 337 if (rfid > 2) 338 fid += 5 - rfid; 339 else 340 fid -= rfid; 341 } 342 } 343 344 printk(KERN_INFO "cpufreq: FSB currently at %i MHz, FID %d.%d\n", fsb, 345 fid / 10, fid % 10); 346 347 /* Set maximum FSB to FSB at boot time */ 348 max_fsb = nforce2_fsb_read(1); 349 350 if(!max_fsb) 351 return -EIO; 352 353 if (!min_fsb) 354 min_fsb = max_fsb - NFORCE2_SAFE_DISTANCE; 355 356 if (min_fsb < NFORCE2_MIN_FSB) 357 min_fsb = NFORCE2_MIN_FSB; 358 359 /* cpuinfo and default policy values */ 360 policy->cpuinfo.min_freq = min_fsb * fid * 100; 361 policy->cpuinfo.max_freq = max_fsb * fid * 100; 362 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; 363 policy->cur = nforce2_get(policy->cpu); 364 policy->min = policy->cpuinfo.min_freq; 365 policy->max = policy->cpuinfo.max_freq; 366 policy->governor = CPUFREQ_DEFAULT_GOVERNOR; 367 368 return 0; 369} 370 371static int nforce2_cpu_exit(struct cpufreq_policy *policy) 372{ 373 return 0; 374} 375 376static struct cpufreq_driver nforce2_driver = { 377 .name = "nforce2", 378 .verify = nforce2_verify, 379 .target = nforce2_target, 380 .get = nforce2_get, 381 .init = nforce2_cpu_init, 382 .exit = nforce2_cpu_exit, 383 .owner = THIS_MODULE, 384}; 385 386/** 387 * nforce2_detect_chipset - detect the Southbridge which contains FSB PLL logic 388 * 389 * Detects nForce2 A2 and C1 stepping 390 * 391 */ 392static unsigned int nforce2_detect_chipset(void) 393{ 394 u8 revision; 395 396 nforce2_chipset_dev = pci_get_subsys(PCI_VENDOR_ID_NVIDIA, 397 PCI_DEVICE_ID_NVIDIA_NFORCE2, 398 PCI_ANY_ID, PCI_ANY_ID, NULL); 399 400 if (nforce2_chipset_dev == NULL) 401 return -ENODEV; 402 403 pci_read_config_byte(nforce2_chipset_dev, PCI_REVISION_ID, &revision); 404 405 printk(KERN_INFO "cpufreq: Detected nForce2 chipset revision %X\n", 406 revision); 407 printk(KERN_INFO 408 "cpufreq: FSB changing is maybe unstable and can lead to crashes and data loss.\n"); 409 410 return 0; 411} 412 413/** 414 * nforce2_init - initializes the nForce2 CPUFreq driver 415 * 416 * Initializes the nForce2 FSB support. Returns -ENODEV on unsupported 417 * devices, -EINVAL on problems during initiatization, and zero on 418 * success. 419 */ 420static int __init nforce2_init(void) 421{ 422 /* TODO: do we need to detect the processor? */ 423 424 /* detect chipset */ 425 if (nforce2_detect_chipset()) { 426 printk(KERN_ERR "cpufreq: No nForce2 chipset.\n"); 427 return -ENODEV; 428 } 429 430 return cpufreq_register_driver(&nforce2_driver); 431} 432 433/** 434 * nforce2_exit - unregisters cpufreq module 435 * 436 * Unregisters nForce2 FSB change support. 437 */ 438static void __exit nforce2_exit(void) 439{ 440 cpufreq_unregister_driver(&nforce2_driver); 441} 442 443module_init(nforce2_init); 444module_exit(nforce2_exit); 445