Subversion Repositories shark

Rev

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