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