Subversion Repositories shark

Rev

Rev 790 | 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
 
789 giacomo 336
 
337
void gx_force_values(unsigned char on_duration, unsigned char off_duration) {
338
 
339
        u8 suscfg, pmer1;
340
        unsigned long flags;
797 giacomo 341
        u8 val;
789 giacomo 342
 
343
        if (off_duration == 0) {  /* 100% of CPU speed, it is special case */
344
                local_irq_save(flags);
797 giacomo 345
 
346
                val = getCx86(0xc2);
347
                val &= 0x7F;
348
                setCx86(0xc2,val);             
349
 
789 giacomo 350
                pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, (gx_params->pci_suscfg & ~(SUSMOD)));
351
                pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &(gx_params->pci_suscfg));
352
                local_irq_restore(flags);
353
                dprintk("suspend modulation disabled: cpu runs 100 percent speed.\n");
354
                return;
355
        }
356
 
357
        local_irq_save(flags);
797 giacomo 358
 
359
        val = getCx86(0xc2);
360
        val |= 0x80;
361
        setCx86(0xc2,val);
362
 
789 giacomo 363
        switch (gx_params->cs55x0->device) {
364
        case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
365
                pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
366
                /* FIXME: need to test other values -- Zwane,Miura */
790 giacomo 367
                pci_write_config_byte(gx_params->cs55x0, PCI_IRQTC, 0); /* typical 2 to 4ms */
368
                pci_write_config_byte(gx_params->cs55x0, PCI_VIDTC, 0); /* typical 50 to 100ms */
789 giacomo 369
                pci_write_config_byte(gx_params->cs55x0, PCI_PMER1, pmer1);
370
 
371
                if (gx_params->pci_rev < 0x10) {   /* CS5530(rev 1.2, 1.3) */
372
                        suscfg = gx_params->pci_suscfg | SUSMOD;
373
                } else {                           /* CS5530A,B.. */
374
                        suscfg = gx_params->pci_suscfg | SUSMOD | PWRSVE;
375
                }
376
                break;
377
        case PCI_DEVICE_ID_CYRIX_5520:
378
        case PCI_DEVICE_ID_CYRIX_5510:
379
                suscfg = gx_params->pci_suscfg | SUSMOD;
380
                break;
381
        default:
382
                local_irq_restore(flags);
383
                dprintk("fatal: try to set unknown chipset.\n");
384
                return;
385
        }
386
 
387
        gx_params->off_duration = off_duration;
388
        gx_params->on_duration = on_duration;
389
 
390
        pci_write_config_byte(gx_params->cs55x0, PCI_MODOFF, gx_params->off_duration);
391
        pci_write_config_byte(gx_params->cs55x0, PCI_MODON, gx_params->on_duration);
392
 
393
        pci_write_config_byte(gx_params->cs55x0, PCI_SUSCFG, suscfg);
394
        pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
395
 
396
        local_irq_restore(flags);
397
 
398
        gx_params->pci_suscfg = suscfg;
399
 
400
        dprintk("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
401
                gx_params->on_duration * 32, gx_params->off_duration * 32);
402
 
403
}
404
 
582 mauro 405
/****************************************************************
406
 *             High level functions                             *
407
 ****************************************************************/
408
 
409
/*
410
 *      cpufreq_gx_verify: test if frequency range is valid
411
 *
412
 *      This function checks if a given frequency range in kHz is valid
413
 *      for the hardware supported by the driver.
414
 */
415
 
416
static int cpufreq_gx_verify(struct cpufreq_policy *policy)
417
{
418
        unsigned int tmp_freq = 0;
419
        u8 tmp1, tmp2;
420
 
421
        if (!stock_freq || !policy)
422
                return -EINVAL;
423
 
424
        policy->cpu = 0;
425
        cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
426
 
427
        /* it needs to be assured that at least one supported frequency is
428
         * within policy->min and policy->max. If it is not, policy->max
429
         * needs to be increased until one freuqency is supported.
430
         * policy->min may not be decreased, though. This way we guarantee a
431
         * specific processing capacity.
432
         */
433
        tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
434
        if (tmp_freq < policy->min)
435
                tmp_freq += stock_freq / max_duration;
436
        policy->min = tmp_freq;
437
        if (policy->min > policy->max)
438
                policy->max = tmp_freq;
439
        tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
440
        if (tmp_freq > policy->max)
441
                tmp_freq -= stock_freq / max_duration;
442
        policy->max = tmp_freq;
443
        if (policy->max < policy->min)
444
                policy->max = policy->min;
445
        cpufreq_verify_within_limits(policy, (stock_freq / max_duration), stock_freq);
446
 
447
        return 0;
448
}
449
 
450
/*
451
 *      cpufreq_gx_target:  
452
 *
453
 */
454
static int cpufreq_gx_target(struct cpufreq_policy *policy,
455
                             unsigned int target_freq,
456
                             unsigned int relation)
457
{
458
        u8 tmp1, tmp2;
459
        unsigned int tmp_freq;
460
 
461
        if (!stock_freq || !policy)
462
                return -EINVAL;
463
 
464
        policy->cpu = 0;
465
 
466
        tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
467
        while (tmp_freq < policy->min) {
468
                tmp_freq += stock_freq / max_duration;
469
                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
470
        }
471
        while (tmp_freq > policy->max) {
472
                tmp_freq -= stock_freq / max_duration;
473
                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
474
        }
475
 
476
        gx_set_cpuspeed(tmp_freq);
477
 
478
        return 0;
479
}
480
 
481
static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
482
{
483
        int maxfreq, curfreq;
484
 
485
        if (!policy || policy->cpu != 0)
486
                return -ENODEV;
487
 
488
        /* determine maximum frequency */
489
        if (pci_busclk) {
490
                maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
491
        } else if (cpu_khz) {
492
                maxfreq = cpu_khz;
493
        } else {
494
                maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
495
        }
496
        stock_freq = maxfreq;
497
        curfreq = gx_get_cpuspeed();
498
 
499
        dprintk("cpu max frequency is %d.\n", maxfreq);
500
        dprintk("cpu current frequency is %dkHz.\n",curfreq);
501
 
502
        /* setup basic struct for cpufreq API */
503
        policy->cpu = 0;
504
 
505
        if (max_duration < POLICY_MIN_DIV)
506
                policy->min = maxfreq / max_duration;
507
        else
508
                policy->min = maxfreq / POLICY_MIN_DIV;
509
        policy->max = maxfreq;
510
        policy->cur = curfreq;
511
        policy->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR;
512
        policy->cpuinfo.min_freq = maxfreq / max_duration;
513
        policy->cpuinfo.max_freq = maxfreq;
514
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
515
 
516
        return 0;
517
}
518
 
519
/*
520
 * cpufreq_gx_init:
521
 *   MediaGX/Geode GX initialize cpufreq driver
522
 */
523
static struct cpufreq_driver gx_suspmod_driver = {
524
        .verify         = cpufreq_gx_verify,
525
        .target         = cpufreq_gx_target,
526
        .init           = cpufreq_gx_cpu_init,
527
        .name           = "gx-suspmod",
528
        .owner          = THIS_MODULE,
529
};
530
 
531
/*static*/ int __init cpufreq_gx_init(void)
532
{
533
        int ret;
534
        struct gxfreq_params *params;
535
        struct pci_dev *gx_pci;
536
        u32 class_rev;
537
 
538
        /* Test if we have the right hardware */
539
        if ((gx_pci = gx_detect_chipset()) == NULL)
540
                return -ENODEV;
541
 
542
        /* check whether module parameters are sane */
543
        if (max_duration > 0xff)
544
                max_duration = 0xff;
545
 
546
        dprintk("geode suspend modulation available.\n");
547
 
548
        params = kmalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
549
        if (params == NULL)
550
                return -ENOMEM;
551
        memset(params, 0, sizeof(struct gxfreq_params));
552
 
553
        params->cs55x0 = gx_pci;
554
        gx_params = params;
555
 
556
        /* keep cs55x0 configurations */
557
        pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg));
558
        pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
559
        pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
560
        pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
561
        pci_read_config_byte(params->cs55x0, PCI_MODOFF, &(params->off_duration));
562
        pci_read_config_dword(params->cs55x0, PCI_CLASS_REVISION, &class_rev);
563
        params->pci_rev = class_rev && 0xff;
564
 
565
        if ((ret = cpufreq_register_driver(&gx_suspmod_driver))) {
566
                kfree(params);
567
                return ret;                   /* register error! */
568
        }
569
 
570
        return 0;
571
}
572
 
573
/*static*/ void __exit cpufreq_gx_exit(void)
574
{
575
        cpufreq_unregister_driver(&gx_suspmod_driver);
576
        kfree(gx_params);
577
}
578
 
579
MODULE_AUTHOR ("Hiroshi Miura <miura@da-cha.org>");
580
MODULE_DESCRIPTION ("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
581
MODULE_LICENSE ("GPL");
582
 
583
module_init(cpufreq_gx_init);
584
module_exit(cpufreq_gx_exit);
585