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