1
2
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76#include <linux/kernel.h>
77#include <linux/module.h>
78#include <linux/init.h>
79#include <linux/smp.h>
80#include <linux/cpufreq.h>
81#include <linux/pci.h>
82#include <linux/errno.h>
83
84#include <asm/processor-cyrix.h>
85
86
87#define PCI_PMER1 0x80
88#define PCI_PMER2 0x81
89#define PCI_PMER3 0x82
90#define PCI_IRQTC 0x8c
91#define PCI_VIDTC 0x8d
92#define PCI_MODOFF 0x94
93#define PCI_MODON 0x95
94#define PCI_SUSCFG 0x96
95
96
97#define GPM (1<<0)
98#define GIT (1<<1)
99#define GTR (1<<2)
100#define IRQ_SPDUP (1<<3)
101#define VID_SPDUP (1<<4)
102
103
104#define SUSMOD (1<<0)
105
106#define SMISPDUP (1<<1)
107
108#define SUSCFG (1<<2)
109
110#define PWRSVE_ISA (1<<3)
111#define PWRSVE (1<<4)
112
113struct gxfreq_params {
114 u8 on_duration;
115 u8 off_duration;
116 u8 pci_suscfg;
117 u8 pci_pmer1;
118 u8 pci_pmer2;
119 struct pci_dev *cs55x0;
120};
121
122static struct gxfreq_params *gx_params;
123static int stock_freq;
124
125
126static int pci_busclk;
127module_param(pci_busclk, int, 0444);
128
129
130
131
132
133
134
135static int max_duration = 255;
136module_param(max_duration, int, 0444);
137
138
139
140
141#define POLICY_MIN_DIV 20
142
143
144#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, \
145 "gx-suspmod", msg)
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161static int gx_freq_mult[16] = {
162 4, 10, 4, 6, 9, 5, 7, 8,
163 0, 0, 0, 0, 0, 0, 0, 0
164};
165
166
167
168
169
170static struct pci_device_id gx_chipset_tbl[] __initdata = {
171 { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY,
172 PCI_ANY_ID, PCI_ANY_ID },
173 { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520,
174 PCI_ANY_ID, PCI_ANY_ID },
175 { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510,
176 PCI_ANY_ID, PCI_ANY_ID },
177 { 0, },
178};
179
180static void gx_write_byte(int reg, int value)
181{
182 pci_write_config_byte(gx_params->cs55x0, reg, value);
183}
184
185
186
187
188
189static __init struct pci_dev *gx_detect_chipset(void)
190{
191 struct pci_dev *gx_pci = NULL;
192
193
194 if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
195 (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
196 dprintk("error: no MediaGX/Geode processor found!\n");
197 return NULL;
198 }
199
200
201 while ((gx_pci = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, gx_pci)) != NULL) {
202 if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
203 return gx_pci;
204 }
205
206 dprintk("error: no supported chipset found!\n");
207 return NULL;
208}
209
210
211
212
213
214
215
216static unsigned int gx_get_cpuspeed(unsigned int cpu)
217{
218 if ((gx_params->pci_suscfg & SUSMOD) == 0)
219 return stock_freq;
220
221 return (stock_freq * gx_params->off_duration)
222 / (gx_params->on_duration + gx_params->off_duration);
223}
224
225
226
227
228
229
230
231static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration,
232 u8 *off_duration)
233{
234 unsigned int i;
235 u8 tmp_on, tmp_off;
236 int old_tmp_freq = stock_freq;
237 int tmp_freq;
238
239 *off_duration = 1;
240 *on_duration = 0;
241
242 for (i = max_duration; i > 0; i--) {
243 tmp_off = ((khz * i) / stock_freq) & 0xff;
244 tmp_on = i - tmp_off;
245 tmp_freq = (stock_freq * tmp_off) / i;
246
247
248 if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) {
249 *on_duration = tmp_on;
250 *off_duration = tmp_off;
251 old_tmp_freq = tmp_freq;
252 }
253 }
254
255 return old_tmp_freq;
256}
257
258
259
260
261
262
263
264static void gx_set_cpuspeed(unsigned int khz)
265{
266 u8 suscfg, pmer1;
267 unsigned int new_khz;
268 unsigned long flags;
269 struct cpufreq_freqs freqs;
270
271 freqs.cpu = 0;
272 freqs.old = gx_get_cpuspeed(0);
273
274 new_khz = gx_validate_speed(khz, &gx_params->on_duration,
275 &gx_params->off_duration);
276
277 freqs.new = new_khz;
278
279 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
280 local_irq_save(flags);
281
282
283
284 if (new_khz != stock_freq) {
285
286 switch (gx_params->cs55x0->device) {
287 case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
288 pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
289
290
291 gx_write_byte(PCI_IRQTC, 4);
292
293 gx_write_byte(PCI_VIDTC, 100);
294 gx_write_byte(PCI_PMER1, pmer1);
295
296 if (gx_params->cs55x0->revision < 0x10) {
297
298 suscfg = gx_params->pci_suscfg|SUSMOD;
299 } else {
300
301 suscfg = gx_params->pci_suscfg|SUSMOD|PWRSVE;
302 }
303 break;
304 case PCI_DEVICE_ID_CYRIX_5520:
305 case PCI_DEVICE_ID_CYRIX_5510:
306 suscfg = gx_params->pci_suscfg | SUSMOD;
307 break;
308 default:
309 local_irq_restore(flags);
310 dprintk("fatal: try to set unknown chipset.\n");
311 return;
312 }
313 } else {
314 suscfg = gx_params->pci_suscfg & ~(SUSMOD);
315 gx_params->off_duration = 0;
316 gx_params->on_duration = 0;
317 dprintk("suspend modulation disabled: cpu runs 100%% speed.\n");
318 }
319
320 gx_write_byte(PCI_MODOFF, gx_params->off_duration);
321 gx_write_byte(PCI_MODON, gx_params->on_duration);
322
323 gx_write_byte(PCI_SUSCFG, suscfg);
324 pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
325
326 local_irq_restore(flags);
327
328 gx_params->pci_suscfg = suscfg;
329
330 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
331
332 dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
333 gx_params->on_duration * 32, gx_params->off_duration * 32);
334 dprintk("suspend modulation w/ clock speed: %d kHz.\n", freqs.new);
335}
336
337
338
339
340
341
342
343
344
345
346
347
348static int cpufreq_gx_verify(struct cpufreq_policy *policy)
349{
350 unsigned int tmp_freq = 0;
351 u8 tmp1, tmp2;
352
353 if (!stock_freq || !policy)
354 return -EINVAL;
355
356 policy->cpu = 0;
357 cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
358 stock_freq);
359
360
361
362
363
364
365
366 tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
367 if (tmp_freq < policy->min)
368 tmp_freq += stock_freq / max_duration;
369 policy->min = tmp_freq;
370 if (policy->min > policy->max)
371 policy->max = tmp_freq;
372 tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
373 if (tmp_freq > policy->max)
374 tmp_freq -= stock_freq / max_duration;
375 policy->max = tmp_freq;
376 if (policy->max < policy->min)
377 policy->max = policy->min;
378 cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
379 stock_freq);
380
381 return 0;
382}
383
384
385
386
387
388static int cpufreq_gx_target(struct cpufreq_policy *policy,
389 unsigned int target_freq,
390 unsigned int relation)
391{
392 u8 tmp1, tmp2;
393 unsigned int tmp_freq;
394
395 if (!stock_freq || !policy)
396 return -EINVAL;
397
398 policy->cpu = 0;
399
400 tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
401 while (tmp_freq < policy->min) {
402 tmp_freq += stock_freq / max_duration;
403 tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
404 }
405 while (tmp_freq > policy->max) {
406 tmp_freq -= stock_freq / max_duration;
407 tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
408 }
409
410 gx_set_cpuspeed(tmp_freq);
411
412 return 0;
413}
414
415static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
416{
417 unsigned int maxfreq, curfreq;
418
419 if (!policy || policy->cpu != 0)
420 return -ENODEV;
421
422
423 if (pci_busclk)
424 maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
425 else if (cpu_khz)
426 maxfreq = cpu_khz;
427 else
428 maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
429
430 stock_freq = maxfreq;
431 curfreq = gx_get_cpuspeed(0);
432
433 dprintk("cpu max frequency is %d.\n", maxfreq);
434 dprintk("cpu current frequency is %dkHz.\n", curfreq);
435
436
437 policy->cpu = 0;
438
439 if (max_duration < POLICY_MIN_DIV)
440 policy->min = maxfreq / max_duration;
441 else
442 policy->min = maxfreq / POLICY_MIN_DIV;
443 policy->max = maxfreq;
444 policy->cur = curfreq;
445 policy->cpuinfo.min_freq = maxfreq / max_duration;
446 policy->cpuinfo.max_freq = maxfreq;
447 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
448
449 return 0;
450}
451
452
453
454
455
456static struct cpufreq_driver gx_suspmod_driver = {
457 .get = gx_get_cpuspeed,
458 .verify = cpufreq_gx_verify,
459 .target = cpufreq_gx_target,
460 .init = cpufreq_gx_cpu_init,
461 .name = "gx-suspmod",
462 .owner = THIS_MODULE,
463};
464
465static int __init cpufreq_gx_init(void)
466{
467 int ret;
468 struct gxfreq_params *params;
469 struct pci_dev *gx_pci;
470
471
472 gx_pci = gx_detect_chipset();
473 if (gx_pci == NULL)
474 return -ENODEV;
475
476
477 if (max_duration > 0xff)
478 max_duration = 0xff;
479
480 dprintk("geode suspend modulation available.\n");
481
482 params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
483 if (params == NULL)
484 return -ENOMEM;
485
486 params->cs55x0 = gx_pci;
487 gx_params = params;
488
489
490 pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg));
491 pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
492 pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
493 pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
494 pci_read_config_byte(params->cs55x0, PCI_MODOFF,
495 &(params->off_duration));
496
497 ret = cpufreq_register_driver(&gx_suspmod_driver);
498 if (ret) {
499 kfree(params);
500 return ret;
501 }
502
503 return 0;
504}
505
506static void __exit cpufreq_gx_exit(void)
507{
508 cpufreq_unregister_driver(&gx_suspmod_driver);
509 pci_dev_put(gx_params->cs55x0);
510 kfree(gx_params);
511}
512
513MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");
514MODULE_DESCRIPTION("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
515MODULE_LICENSE("GPL");
516
517module_init(cpufreq_gx_init);
518module_exit(cpufreq_gx_exit);
519
520