Subversion Repositories shark

Rev

Rev 600 | Rev 789 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
582 mauro 1
/*
2
 *      Cyrix MediaGX and NatSemi Geode Suspend Modulation
3
 *      (C) 2002 Zwane Mwaikambo <zwane@commfireservices.com>
4
 *      (C) 2002 Hiroshi Miura   <miura@da-cha.org>
5
 *      All Rights Reserved
6
 *
7
 *      This program is free software; you can redistribute it and/or
8
 *      modify it under the terms of the GNU General Public License
9
 *      version 2 as published by the Free Software Foundation
10
 *
11
 *      The author(s) of this software shall not be held liable for damages
12
 *      of any nature resulting due to the use of this software. This
13
 *      software is provided AS-IS with no warranties.
14
 *     
15
 * Theoritical note:
16
 *
17
 *      (see Geode(tm) CS5530 manual (rev.4.1) page.56)
18
 *
19
 *      CPU frequency control on NatSemi Geode GX1/GXLV processor and CS55x0
20
 *      are based on Suspend Moduration.
21
 *
22
 *      Suspend Modulation works by asserting and de-asserting the SUSP# pin
23
 *      to CPU(GX1/GXLV) for configurable durations. When asserting SUSP#
24
 *      the CPU enters an idle state. GX1 stops its core clock when SUSP# is
25
 *      asserted then power consumption is reduced.
26
 *
27
 *      Suspend Modulation's OFF/ON duration are configurable
28
 *      with 'Suspend Modulation OFF Count Register'
29
 *      and 'Suspend Modulation ON Count Register'.
30
 *      These registers are 8bit counters that represent the number of
31
 *      32us intervals which the SUSP# pin is asserted/de-asserted to the
32
 *      processor.
33
 *
34
 *      These counters define a ratio which is the effective frequency
35
 *      of operation of the system.
36
 *
37
 *                             On Count
38
 *      F_eff = Fgx * ----------------------
39
 *                      On Count + Off Count
40
 *
41
 *      0 <= On Count, Off Count <= 255
42
 *
43
 *      From these limits, we can get register values
44
 *
45
 *      on_duration + off_duration <= MAX_DURATION
46
 *      off_duration = on_duration * (stock_freq - freq) / freq
47
 *
48
 *      on_duration  =  (freq * DURATION) / stock_freq
49
 *      off_duration = DURATION - on_duration
50
 *
51
 *
52
 *---------------------------------------------------------------------------
53
 *
54
 * ChangeLog:
55
 *      Dec. 11, 2002   Hiroshi Miura <miura@da-cha.org>
56
 *              - rewrite for Cyrix MediaGX Cx5510/5520 and
57
 *                NatSemi Geode Cs5530(A).
58
 *
59
 *      Jul. ??, 2002  Zwane Mwaikambo <zwane@commfireservices.com>
60
 *              - cs5530_mod patch for 2.4.19-rc1.
61
 *
62
 *---------------------------------------------------------------------------
63
 *
64
 * Todo
65
 *      Test on machines with 5510, 5530, 5530A
66
 */
67
 
68
/************************************************************************
69
 *                      Suspend Modulation - Definitions                *
70
 ************************************************************************/
71
 
72
#include <linuxcomp.h>
73
 
74
#include <linux/kernel.h>
75
#include <linux/module.h> 
76
#include <linux/sched.h>
77
#include <linux/init.h>
78
#include <linux/smp.h>
79
#include <linux/cpufreq.h>
80
#include <linux/pci.h>
81
#include <asm/processor.h> 
82
#include <asm/errno.h>
83
 
84
extern struct cpuinfo_x86 new_cpu_data;
85
extern unsigned long cpu_khz;
86
 
87
/* PCI config registers, all at F0 */
88
#define PCI_PMER1              0x80    /* power management enable register 1 */
89
#define PCI_PMER2              0x81    /* power management enable register 2 */
90
#define PCI_PMER3              0x82    /* power management enable register 3 */
91
#define PCI_IRQTC              0x8c    /* irq speedup timer counter register:typical 2 to 4ms */
92
#define PCI_VIDTC              0x8d    /* video speedup timer counter register: typical 50 to 100ms */
93
#define PCI_MODOFF             0x94    /* suspend modulation OFF counter register, 1 = 32us */
94
#define PCI_MODON              0x95    /* suspend modulation ON counter register */
95
#define PCI_SUSCFG             0x96    /* suspend configuration register */
96
 
97
/* PMER1 bits */
98
#define GPM                    (1<<0)  /* global power management */
99
#define GIT                    (1<<1)  /* globally enable PM device idle timers */
100
#define GTR                    (1<<2)  /* globally enable IO traps */
101
#define IRQ_SPDUP              (1<<3)  /* disable clock throttle during interrupt handling */
102
#define VID_SPDUP              (1<<4)  /* disable clock throttle during vga video handling */
103
 
104
/* SUSCFG bits */
105
#define SUSMOD                 (1<<0)  /* enable/disable suspend modulation */
106
/* the belows support only with cs5530 (after rev.1.2)/cs5530A */
107
#define SMISPDUP               (1<<1)  /* select how SMI re-enable suspend modulation: */
108
                                       /* IRQTC timer or read SMI speedup disable reg.(F1BAR[08-09h]) */
109
#define SUSCFG                 (1<<2)  /* enable powering down a GXLV processor. "Special 3Volt Suspend" mode */
110
/* the belows support only with cs5530A */
111
#define PWRSVE_ISA             (1<<3)  /* stop ISA clock  */
112
#define PWRSVE                 (1<<4)  /* active idle */
113
 
114
struct gxfreq_params {
115
        u8 on_duration;
116
        u8 off_duration;
117
        u8 pci_suscfg;
118
        u8 pci_pmer1;
119
        u8 pci_pmer2;
120
        u8 pci_rev;
121
        struct pci_dev *cs55x0;
122
};
123
 
124
static struct gxfreq_params *gx_params;
125
static int stock_freq;
126
 
127
/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
128
static int pci_busclk = 0;
129
MODULE_PARM(pci_busclk, "i");
130
 
131
/* maximum duration for which the cpu may be suspended
132
 * (32us * MAX_DURATION). If no parameter is given, this defaults
133
 * to 255.
134
 * Note that this leads to a maximum of 8 ms(!) where the CPU clock
135
 * is suspended -- processing power is just 0.39% of what it used to be,
136
 * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
137
static int max_duration = 255;
138
MODULE_PARM(max_duration, "i");
139
 
140
/* For the default policy, we want at least some processing power
141
 * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
142
 */
143
#define POLICY_MIN_DIV 20
144
 
145
 
146
/* DEBUG
147
 *   Define it if you want verbose debug output
148
 */
149
 
775 mauro 150
//#define SUSPMOD_DEBUG 1
582 mauro 151
 
152
#ifdef SUSPMOD_DEBUG
153
#define dprintk(msg...) printk(KERN_DEBUG "cpufreq:" msg)
154
#else
155
#define dprintk(msg...) do { } while(0)
156
#endif
157
 
158
/**
159
 *      we can detect a core multipiler from dir0_lsb
160
 *      from GX1 datasheet p.56,
161
 *         MULT[3:0]:
162
 *         0000 = SYSCLK multiplied by 4 (test only)
163
 *         0001 = SYSCLK multiplied by 10
164
 *         0010 = SYSCLK multiplied by 4
165
 *         0011 = SYSCLK multiplied by 6
166
 *         0100 = SYSCLK multiplied by 9
167
 *         0101 = SYSCLK multiplied by 5
168
 *         0110 = SYSCLK multiplied by 7
169
 *         0111 = SYSCLK multiplied by 8
170
 *              of 33.3MHz
171
 **/
172
static int gx_freq_mult[16] = {
173
                4, 10, 4, 6, 9, 5, 7, 8,
174
                0, 0, 0, 0, 0, 0, 0, 0
175
};
176
 
177
 
178
/****************************************************************
179
 *      Low Level chipset interface                             *
180
 ****************************************************************/
181
static struct pci_device_id gx_chipset_tbl[] __initdata = {
182
        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, PCI_ANY_ID, PCI_ANY_ID },
183
        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5520, PCI_ANY_ID, PCI_ANY_ID },
184
        { PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5510, PCI_ANY_ID, PCI_ANY_ID },
185
        { 0, },
186
};
187
 
188
/**
189
 *     gx_detect_chipset:
190
 *
191
 **/
192
static __init struct pci_dev *gx_detect_chipset(void)
193
{
194
        struct pci_dev *gx_pci = NULL;
195
 
196
        /* check if CPU is a MediaGX or a Geode. */
197
        if ((new_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
198
            (new_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
199
                dprintk(KERN_INFO "gx-suspmod: error: no MediaGX/Geode processor found!\n");
200
                return NULL;           
201
        }
202
 
203
        /* detect which companion chip is used */
204
        while ((gx_pci = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, gx_pci)) != NULL) {
205
                if ((pci_match_device (gx_chipset_tbl, gx_pci)) != NULL) {
206
                        return gx_pci;
207
                }
208
        }
209
 
210
        dprintk(KERN_INFO "gx-suspmod: error: no supported chipset found!\n");
211
        return NULL;
212
}
213
 
214
/**
215
 *      gx_get_cpuspeed:
216
 *
217
 * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi Geode CPU runs.
218
 */
219
static int gx_get_cpuspeed(void)
220
{
221
        if ((gx_params->pci_suscfg & SUSMOD) == 0)
222
                return stock_freq;
223
 
224
        return (stock_freq * gx_params->on_duration)
225
                / (gx_params->on_duration + gx_params->off_duration);
226
}
227
 
228
/**
229
 *      gx_validate_speed:
230
 *      determine current cpu speed
231
 *      
232
**/
233
 
234
static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration, u8 *off_duration)
235
{
236
        unsigned int i;
237
        u8 tmp_on, tmp_off;
238
        int old_tmp_freq = stock_freq;
239
        int tmp_freq;
240
 
241
        *on_duration=1;
242
        *off_duration=0;
243
 
244
        for (i=max_duration; i>0; i--) {
245
                tmp_on = ((khz * i) / stock_freq) & 0xff;
246
                tmp_off = i - tmp_on;
247
                tmp_freq = (stock_freq * tmp_on) / i;
248
                /* if this relation is closer to khz, use this. If it's equal,
249
                 * prefer it, too - lower latency */
250
                if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) {
251
                        *on_duration = tmp_on;
252
                        *off_duration = tmp_off;
253
                        old_tmp_freq = tmp_freq;
254
                }
255
        }
256
 
257
        return old_tmp_freq;
258
}
259
 
260
 
261
/**
262
 *      gx_set_cpuspeed:
263
 *              set cpu speed in khz.
264
 **/
265
 
266
static void gx_set_cpuspeed(unsigned int khz)
267
{
268
        u8 suscfg, pmer1;
269
        unsigned int new_khz;
270
        unsigned long flags;
271
        struct cpufreq_freqs freqs;
272
 
273
 
274
        freqs.cpu = 0;
275
        freqs.old = gx_get_cpuspeed();
276
 
277
        new_khz = gx_validate_speed(khz, &gx_params->on_duration, &gx_params->off_duration);
278
 
279
        freqs.new = new_khz;
280
 
281
        if (new_khz == stock_freq) {  /* if new khz == 100% of CPU speed, it is special case */
282
                local_irq_save(flags);
600 mauro 283
                cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
582 mauro 284
                pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, (gx_params->pci_suscfg & ~(SUSMOD)));
285
                pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &(gx_params->pci_suscfg));
286
                local_irq_restore(flags);
287
                dprintk("suspend modulation disabled: cpu runs 100 percent speed.\n");
600 mauro 288
                cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
582 mauro 289
                return;
290
        }
291
 
600 mauro 292
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
582 mauro 293
 
294
        local_irq_save(flags);
295
        switch (gx_params->cs55x0->device) {
296
        case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
297
                pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
298
                /* FIXME: need to test other values -- Zwane,Miura */
299
                pci_write_config_byte(gx_params->cs55x0, PCI_IRQTC, 4); /* typical 2 to 4ms */
300
                pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 100);/* typical 50 to 100ms */
301
                pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1);
302
 
303
                if (gx_params->pci_rev < 0x10) {   /* CS5530(rev 1.2, 1.3) */
304
                        suscfg = gx_params->pci_suscfg | SUSMOD;
305
                } else {                           /* CS5530A,B.. */
306
                        suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE;
307
                }
308
                break;
309
        case PCI_DEVICE_ID_CYRIX_5520:
310
        case PCI_DEVICE_ID_CYRIX_5510:
311
                suscfg = gx_params->pci_suscfg | SUSMOD;
312
                break;
313
        default:
314
                local_irq_restore(flags);
315
                dprintk("fatal: try to set unknown chipset.\n");
316
                return;
317
        }
318
 
319
        pci_write_config_byte(gx_params->cs55x0, PCI_MODOFF, gx_params->off_duration);
320
        pci_write_config_byte(gx_params->cs55x0, PCI_MODON, gx_params->on_duration);
321
 
322
        pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, suscfg);
323
        pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
324
 
325
        local_irq_restore(flags);
326
 
327
        gx_params->pci_suscfg = suscfg;
328
 
600 mauro 329
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
582 mauro 330
 
331
        dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
332
                gx_params->on_duration * 32, gx_params->off_duration * 32);
333
        dprintk("suspend modulation w/ clock speed: %d kHz.\n", freqs.new);
334
}
335
 
336
/****************************************************************
337
 *             High level functions                             *
338
 ****************************************************************/
339
 
340
/*
341
 *      cpufreq_gx_verify: test if frequency range is valid
342
 *
343
 *      This function checks if a given frequency range in kHz is valid
344
 *      for the hardware supported by the driver.
345
 */
346
 
347
static int cpufreq_gx_verify(struct cpufreq_policy *policy)
348
{
349
        unsigned int tmp_freq = 0;
350
        u8 tmp1, tmp2;
351
 
352
        if (!stock_freq || !policy)
353
                return -EINVAL;
354
 
355
        policy->cpu = 0;
356
        cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
357
 
358
        /* it needs to be assured that at least one supported frequency is
359
         * within policy->min and policy->max. If it is not, policy->max
360
         * needs to be increased until one freuqency is supported.
361
         * policy->min may not be decreased, though. This way we guarantee a
362
         * specific processing capacity.
363
         */
364
        tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
365
        if (tmp_freq < policy->min)
366
                tmp_freq += stock_freq / max_duration;
367
        policy->min = tmp_freq;
368
        if (policy->min > policy->max)
369
                policy->max = tmp_freq;
370
        tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
371
        if (tmp_freq > policy->max)
372
                tmp_freq -= stock_freq / max_duration;
373
        policy->max = tmp_freq;
374
        if (policy->max < policy->min)
375
                policy->max = policy->min;
376
        cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
377
 
378
        return 0;
379
}
380
 
381
/*
382
 *      cpufreq_gx_target:  
383
 *
384
 */
385
static int cpufreq_gx_target(struct cpufreq_policy *policy,
386
                             unsigned int target_freq,
387
                             unsigned int relation)
388
{
389
        u8 tmp1, tmp2;
390
        unsigned int tmp_freq;
391
 
392
        if (!stock_freq || !policy)
393
                return -EINVAL;
394
 
395
        policy->cpu = 0;
396
 
397
        tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
398
        while (tmp_freq < policy->min) {
399
                tmp_freq += stock_freq / max_duration;
400
                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
401
        }
402
        while (tmp_freq > policy->max) {
403
                tmp_freq -= stock_freq / max_duration;
404
                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
405
        }
406
 
407
        gx_set_cpuspeed(tmp_freq);
408
 
409
        return 0;
410
}
411
 
412
static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
413
{
414
        int maxfreq, curfreq;
415
 
416
        if (!policy || policy->cpu != 0)
417
                return -ENODEV;
418
 
419
        /* determine maximum frequency */
420
        if (pci_busclk) {
421
                maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
422
        } else if (cpu_khz) {
423
                maxfreq = cpu_khz;
424
        } else {
425
                maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
426
        }
427
        stock_freq = maxfreq;
428
        curfreq = gx_get_cpuspeed();
429
 
430
        dprintk("cpu max frequency is %d.\n", maxfreq);
431
        dprintk("cpu current frequency is %dkHz.\n",curfreq);
432
 
433
        /* setup basic struct for cpufreq API */
434
        policy->cpu = 0;
435
 
436
        if (max_duration < POLICY_MIN_DIV)
437
                policy->min = maxfreq / max_duration;
438
        else
439
                policy->min = maxfreq / POLICY_MIN_DIV;
440
        policy->max = maxfreq;
441
        policy->cur = curfreq;
442
        policy->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR;
443
        policy->cpuinfo.min_freq = maxfreq / max_duration;
444
        policy->cpuinfo.max_freq = maxfreq;
445
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
446
 
447
        return 0;
448
}
449
 
450
/*
451
 * cpufreq_gx_init:
452
 *   MediaGX/Geode GX initialize cpufreq driver
453
 */
454
static struct cpufreq_driver gx_suspmod_driver = {
455
        .verify         = cpufreq_gx_verify,
456
        .target         = cpufreq_gx_target,
457
        .init           = cpufreq_gx_cpu_init,
458
        .name           = "gx-suspmod",
459
        .owner          = THIS_MODULE,
460
};
461
 
462
/*static*/ int __init cpufreq_gx_init(void)
463
{
464
        int ret;
465
        struct gxfreq_params *params;
466
        struct pci_dev *gx_pci;
467
        u32 class_rev;
468
 
469
        /* Test if we have the right hardware */
470
        if ((gx_pci = gx_detect_chipset()) == NULL)
471
                return -ENODEV;
472
 
473
        /* check whether module parameters are sane */
474
        if (max_duration > 0xff)
475
                max_duration = 0xff;
476
 
477
        dprintk("geode suspend modulation available.\n");
478
 
479
        params = kmalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
480
        if (params == NULL)
481
                return -ENOMEM;
482
        memset(params, 0, sizeof(struct gxfreq_params));
483
 
484
        params->cs55x0 = gx_pci;
485
        gx_params = params;
486
 
487
        /* keep cs55x0 configurations */
488
        pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg));
489
        pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
490
        pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
491
        pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
492
        pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration));
493
        pci_read_config_dword(params->cs55x0, PCI_CLASS_REVISION, &class_rev);
494
        params->pci_rev = class_rev && 0xff;
495
 
496
        if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) {
497
                kfree(params);
498
                return ret;                   /* register error! */
499
        }
500
 
501
        return 0;
502
}
503
 
504
/*static*/ void __exit cpufreq_gx_exit(void)
505
{
506
        cpufreq_unregister_driver(&gx_suspmod_driver);
507
        kfree(gx_params);
508
}
509
 
510
MODULE_AUTHOR ("Hiroshi Miura <miura@da-cha.org>");
511
MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
512
MODULE_LICENSE ("GPL");
513
 
514
module_init(cpufreq_gx_init);
515
module_exit(cpufreq_gx_exit);
516