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