~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/cpufreq/gx-suspmod.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 * Theoretical 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 Modulation.
 
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(ON)/de-asserted(OFF)
 
32
 *      to the processor.
 
33
 *
 
34
 *      These counters define a ratio which is the effective frequency
 
35
 *      of operation of the system.
 
36
 *
 
37
 *                             OFF Count
 
38
 *      F_eff = Fgx * ----------------------
 
39
 *                      OFF Count + ON Count
 
40
 *
 
41
 *      0 <= On Count, Off Count <= 255
 
42
 *
 
43
 *      From these limits, we can get register values
 
44
 *
 
45
 *      off_duration + on_duration <= MAX_DURATION
 
46
 *      on_duration = off_duration * (stock_freq - freq) / freq
 
47
 *
 
48
 *      off_duration  =  (freq * DURATION) / stock_freq
 
49
 *      on_duration = DURATION - off_duration
 
50
 *
 
51
 *
 
52
 *---------------------------------------------------------------------------
 
53
 *
 
54
 * ChangeLog:
 
55
 *      Dec. 12, 2003   Hiroshi Miura <miura@da-cha.org>
 
56
 *              - fix on/off register mistake
 
57
 *              - fix cpu_khz calc when it stops cpu modulation.
 
58
 *
 
59
 *      Dec. 11, 2002   Hiroshi Miura <miura@da-cha.org>
 
60
 *              - rewrite for Cyrix MediaGX Cx5510/5520 and
 
61
 *                NatSemi Geode Cs5530(A).
 
62
 *
 
63
 *      Jul. ??, 2002  Zwane Mwaikambo <zwane@commfireservices.com>
 
64
 *              - cs5530_mod patch for 2.4.19-rc1.
 
65
 *
 
66
 *---------------------------------------------------------------------------
 
67
 *
 
68
 * Todo
 
69
 *      Test on machines with 5510, 5530, 5530A
 
70
 */
 
71
 
 
72
/************************************************************************
 
73
 *                      Suspend Modulation - Definitions                *
 
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
#include <linux/slab.h>
 
84
 
 
85
#include <asm/processor-cyrix.h>
 
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 below is supported 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 below is supported 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
        struct pci_dev *cs55x0;
 
121
};
 
122
 
 
123
static struct gxfreq_params *gx_params;
 
124
static int stock_freq;
 
125
 
 
126
/* PCI bus clock - defaults to 30.000 if cpu_khz is not available */
 
127
static int pci_busclk;
 
128
module_param(pci_busclk, int, 0444);
 
129
 
 
130
/* maximum duration for which the cpu may be suspended
 
131
 * (32us * MAX_DURATION). If no parameter is given, this defaults
 
132
 * to 255.
 
133
 * Note that this leads to a maximum of 8 ms(!) where the CPU clock
 
134
 * is suspended -- processing power is just 0.39% of what it used to be,
 
135
 * though. 781.25 kHz(!) for a 200 MHz processor -- wow. */
 
136
static int max_duration = 255;
 
137
module_param(max_duration, int, 0444);
 
138
 
 
139
/* For the default policy, we want at least some processing power
 
140
 * - let's say 5%. (min = maxfreq / POLICY_MIN_DIV)
 
141
 */
 
142
#define POLICY_MIN_DIV 20
 
143
 
 
144
 
 
145
/**
 
146
 * we can detect a core multipiler from dir0_lsb
 
147
 * from GX1 datasheet p.56,
 
148
 *      MULT[3:0]:
 
149
 *      0000 = SYSCLK multiplied by 4 (test only)
 
150
 *      0001 = SYSCLK multiplied by 10
 
151
 *      0010 = SYSCLK multiplied by 4
 
152
 *      0011 = SYSCLK multiplied by 6
 
153
 *      0100 = SYSCLK multiplied by 9
 
154
 *      0101 = SYSCLK multiplied by 5
 
155
 *      0110 = SYSCLK multiplied by 7
 
156
 *      0111 = SYSCLK multiplied by 8
 
157
 *              of 33.3MHz
 
158
 **/
 
159
static int gx_freq_mult[16] = {
 
160
                4, 10, 4, 6, 9, 5, 7, 8,
 
161
                0, 0, 0, 0, 0, 0, 0, 0
 
162
};
 
163
 
 
164
 
 
165
/****************************************************************
 
166
 *      Low Level chipset interface                             *
 
167
 ****************************************************************/
 
168
static struct pci_device_id gx_chipset_tbl[] __initdata = {
 
169
        { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY), },
 
170
        { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5520), },
 
171
        { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), },
 
172
        { 0, },
 
173
};
 
174
 
 
175
static void gx_write_byte(int reg, int value)
 
176
{
 
177
        pci_write_config_byte(gx_params->cs55x0, reg, value);
 
178
}
 
179
 
 
180
/**
 
181
 * gx_detect_chipset:
 
182
 *
 
183
 **/
 
184
static __init struct pci_dev *gx_detect_chipset(void)
 
185
{
 
186
        struct pci_dev *gx_pci = NULL;
 
187
 
 
188
        /* check if CPU is a MediaGX or a Geode. */
 
189
        if ((boot_cpu_data.x86_vendor != X86_VENDOR_NSC) &&
 
190
            (boot_cpu_data.x86_vendor != X86_VENDOR_CYRIX)) {
 
191
                pr_debug("error: no MediaGX/Geode processor found!\n");
 
192
                return NULL;
 
193
        }
 
194
 
 
195
        /* detect which companion chip is used */
 
196
        for_each_pci_dev(gx_pci) {
 
197
                if ((pci_match_id(gx_chipset_tbl, gx_pci)) != NULL)
 
198
                        return gx_pci;
 
199
        }
 
200
 
 
201
        pr_debug("error: no supported chipset found!\n");
 
202
        return NULL;
 
203
}
 
204
 
 
205
/**
 
206
 * gx_get_cpuspeed:
 
207
 *
 
208
 * Finds out at which efficient frequency the Cyrix MediaGX/NatSemi
 
209
 * Geode CPU runs.
 
210
 */
 
211
static unsigned int gx_get_cpuspeed(unsigned int cpu)
 
212
{
 
213
        if ((gx_params->pci_suscfg & SUSMOD) == 0)
 
214
                return stock_freq;
 
215
 
 
216
        return (stock_freq * gx_params->off_duration)
 
217
                / (gx_params->on_duration + gx_params->off_duration);
 
218
}
 
219
 
 
220
/**
 
221
 *      gx_validate_speed:
 
222
 *      determine current cpu speed
 
223
 *
 
224
 **/
 
225
 
 
226
static unsigned int gx_validate_speed(unsigned int khz, u8 *on_duration,
 
227
                u8 *off_duration)
 
228
{
 
229
        unsigned int i;
 
230
        u8 tmp_on, tmp_off;
 
231
        int old_tmp_freq = stock_freq;
 
232
        int tmp_freq;
 
233
 
 
234
        *off_duration = 1;
 
235
        *on_duration = 0;
 
236
 
 
237
        for (i = max_duration; i > 0; i--) {
 
238
                tmp_off = ((khz * i) / stock_freq) & 0xff;
 
239
                tmp_on = i - tmp_off;
 
240
                tmp_freq = (stock_freq * tmp_off) / i;
 
241
                /* if this relation is closer to khz, use this. If it's equal,
 
242
                 * prefer it, too - lower latency */
 
243
                if (abs(tmp_freq - khz) <= abs(old_tmp_freq - khz)) {
 
244
                        *on_duration = tmp_on;
 
245
                        *off_duration = tmp_off;
 
246
                        old_tmp_freq = tmp_freq;
 
247
                }
 
248
        }
 
249
 
 
250
        return old_tmp_freq;
 
251
}
 
252
 
 
253
 
 
254
/**
 
255
 * gx_set_cpuspeed:
 
256
 * set cpu speed in khz.
 
257
 **/
 
258
 
 
259
static void gx_set_cpuspeed(unsigned int khz)
 
260
{
 
261
        u8 suscfg, pmer1;
 
262
        unsigned int new_khz;
 
263
        unsigned long flags;
 
264
        struct cpufreq_freqs freqs;
 
265
 
 
266
        freqs.cpu = 0;
 
267
        freqs.old = gx_get_cpuspeed(0);
 
268
 
 
269
        new_khz = gx_validate_speed(khz, &gx_params->on_duration,
 
270
                        &gx_params->off_duration);
 
271
 
 
272
        freqs.new = new_khz;
 
273
 
 
274
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
275
        local_irq_save(flags);
 
276
 
 
277
 
 
278
 
 
279
        if (new_khz != stock_freq) {
 
280
                /* if new khz == 100% of CPU speed, it is special case */
 
281
                switch (gx_params->cs55x0->device) {
 
282
                case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
 
283
                        pmer1 = gx_params->pci_pmer1 | IRQ_SPDUP | VID_SPDUP;
 
284
                        /* FIXME: need to test other values -- Zwane,Miura */
 
285
                        /* typical 2 to 4ms */
 
286
                        gx_write_byte(PCI_IRQTC, 4);
 
287
                        /* typical 50 to 100ms */
 
288
                        gx_write_byte(PCI_VIDTC, 100);
 
289
                        gx_write_byte(PCI_PMER1, pmer1);
 
290
 
 
291
                        if (gx_params->cs55x0->revision < 0x10) {
 
292
                                /* CS5530(rev 1.2, 1.3) */
 
293
                                suscfg = gx_params->pci_suscfg|SUSMOD;
 
294
                        } else {
 
295
                                /* CS5530A,B.. */
 
296
                                suscfg = gx_params->pci_suscfg|SUSMOD|PWRSVE;
 
297
                        }
 
298
                        break;
 
299
                case PCI_DEVICE_ID_CYRIX_5520:
 
300
                case PCI_DEVICE_ID_CYRIX_5510:
 
301
                        suscfg = gx_params->pci_suscfg | SUSMOD;
 
302
                        break;
 
303
                default:
 
304
                        local_irq_restore(flags);
 
305
                        pr_debug("fatal: try to set unknown chipset.\n");
 
306
                        return;
 
307
                }
 
308
        } else {
 
309
                suscfg = gx_params->pci_suscfg & ~(SUSMOD);
 
310
                gx_params->off_duration = 0;
 
311
                gx_params->on_duration = 0;
 
312
                pr_debug("suspend modulation disabled: cpu runs 100%% speed.\n");
 
313
        }
 
314
 
 
315
        gx_write_byte(PCI_MODOFF, gx_params->off_duration);
 
316
        gx_write_byte(PCI_MODON, gx_params->on_duration);
 
317
 
 
318
        gx_write_byte(PCI_SUSCFG, suscfg);
 
319
        pci_read_config_byte(gx_params->cs55x0, PCI_SUSCFG, &suscfg);
 
320
 
 
321
        local_irq_restore(flags);
 
322
 
 
323
        gx_params->pci_suscfg = suscfg;
 
324
 
 
325
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
326
 
 
327
        pr_debug("suspend modulation w/ duration of ON:%d us, OFF:%d us\n",
 
328
                gx_params->on_duration * 32, gx_params->off_duration * 32);
 
329
        pr_debug("suspend modulation w/ clock speed: %d kHz.\n", freqs.new);
 
330
}
 
331
 
 
332
/****************************************************************
 
333
 *             High level functions                             *
 
334
 ****************************************************************/
 
335
 
 
336
/*
 
337
 *      cpufreq_gx_verify: test if frequency range is valid
 
338
 *
 
339
 *      This function checks if a given frequency range in kHz is valid
 
340
 *      for the hardware supported by the driver.
 
341
 */
 
342
 
 
343
static int cpufreq_gx_verify(struct cpufreq_policy *policy)
 
344
{
 
345
        unsigned int tmp_freq = 0;
 
346
        u8 tmp1, tmp2;
 
347
 
 
348
        if (!stock_freq || !policy)
 
349
                return -EINVAL;
 
350
 
 
351
        policy->cpu = 0;
 
352
        cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
 
353
                        stock_freq);
 
354
 
 
355
        /* it needs to be assured that at least one supported frequency is
 
356
         * within policy->min and policy->max. If it is not, policy->max
 
357
         * needs to be increased until one freuqency is supported.
 
358
         * policy->min may not be decreased, though. This way we guarantee a
 
359
         * specific processing capacity.
 
360
         */
 
361
        tmp_freq = gx_validate_speed(policy->min, &tmp1, &tmp2);
 
362
        if (tmp_freq < policy->min)
 
363
                tmp_freq += stock_freq / max_duration;
 
364
        policy->min = tmp_freq;
 
365
        if (policy->min > policy->max)
 
366
                policy->max = tmp_freq;
 
367
        tmp_freq = gx_validate_speed(policy->max, &tmp1, &tmp2);
 
368
        if (tmp_freq > policy->max)
 
369
                tmp_freq -= stock_freq / max_duration;
 
370
        policy->max = tmp_freq;
 
371
        if (policy->max < policy->min)
 
372
                policy->max = policy->min;
 
373
        cpufreq_verify_within_limits(policy, (stock_freq / max_duration),
 
374
                        stock_freq);
 
375
 
 
376
        return 0;
 
377
}
 
378
 
 
379
/*
 
380
 *      cpufreq_gx_target:
 
381
 *
 
382
 */
 
383
static int cpufreq_gx_target(struct cpufreq_policy *policy,
 
384
                             unsigned int target_freq,
 
385
                             unsigned int relation)
 
386
{
 
387
        u8 tmp1, tmp2;
 
388
        unsigned int tmp_freq;
 
389
 
 
390
        if (!stock_freq || !policy)
 
391
                return -EINVAL;
 
392
 
 
393
        policy->cpu = 0;
 
394
 
 
395
        tmp_freq = gx_validate_speed(target_freq, &tmp1, &tmp2);
 
396
        while (tmp_freq < policy->min) {
 
397
                tmp_freq += stock_freq / max_duration;
 
398
                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
 
399
        }
 
400
        while (tmp_freq > policy->max) {
 
401
                tmp_freq -= stock_freq / max_duration;
 
402
                tmp_freq = gx_validate_speed(tmp_freq, &tmp1, &tmp2);
 
403
        }
 
404
 
 
405
        gx_set_cpuspeed(tmp_freq);
 
406
 
 
407
        return 0;
 
408
}
 
409
 
 
410
static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy)
 
411
{
 
412
        unsigned int maxfreq, curfreq;
 
413
 
 
414
        if (!policy || policy->cpu != 0)
 
415
                return -ENODEV;
 
416
 
 
417
        /* determine maximum frequency */
 
418
        if (pci_busclk)
 
419
                maxfreq = pci_busclk * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
 
420
        else if (cpu_khz)
 
421
                maxfreq = cpu_khz;
 
422
        else
 
423
                maxfreq = 30000 * gx_freq_mult[getCx86(CX86_DIR1) & 0x0f];
 
424
 
 
425
        stock_freq = maxfreq;
 
426
        curfreq = gx_get_cpuspeed(0);
 
427
 
 
428
        pr_debug("cpu max frequency is %d.\n", maxfreq);
 
429
        pr_debug("cpu current frequency is %dkHz.\n", curfreq);
 
430
 
 
431
        /* setup basic struct for cpufreq API */
 
432
        policy->cpu = 0;
 
433
 
 
434
        if (max_duration < POLICY_MIN_DIV)
 
435
                policy->min = maxfreq / max_duration;
 
436
        else
 
437
                policy->min = maxfreq / POLICY_MIN_DIV;
 
438
        policy->max = maxfreq;
 
439
        policy->cur = curfreq;
 
440
        policy->cpuinfo.min_freq = maxfreq / max_duration;
 
441
        policy->cpuinfo.max_freq = maxfreq;
 
442
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 
443
 
 
444
        return 0;
 
445
}
 
446
 
 
447
/*
 
448
 * cpufreq_gx_init:
 
449
 *   MediaGX/Geode GX initialize cpufreq driver
 
450
 */
 
451
static struct cpufreq_driver gx_suspmod_driver = {
 
452
        .get            = gx_get_cpuspeed,
 
453
        .verify         = cpufreq_gx_verify,
 
454
        .target         = cpufreq_gx_target,
 
455
        .init           = cpufreq_gx_cpu_init,
 
456
        .name           = "gx-suspmod",
 
457
        .owner          = THIS_MODULE,
 
458
};
 
459
 
 
460
static int __init cpufreq_gx_init(void)
 
461
{
 
462
        int ret;
 
463
        struct gxfreq_params *params;
 
464
        struct pci_dev *gx_pci;
 
465
 
 
466
        /* Test if we have the right hardware */
 
467
        gx_pci = gx_detect_chipset();
 
468
        if (gx_pci == NULL)
 
469
                return -ENODEV;
 
470
 
 
471
        /* check whether module parameters are sane */
 
472
        if (max_duration > 0xff)
 
473
                max_duration = 0xff;
 
474
 
 
475
        pr_debug("geode suspend modulation available.\n");
 
476
 
 
477
        params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
 
478
        if (params == NULL)
 
479
                return -ENOMEM;
 
480
 
 
481
        params->cs55x0 = gx_pci;
 
482
        gx_params = params;
 
483
 
 
484
        /* keep cs55x0 configurations */
 
485
        pci_read_config_byte(params->cs55x0, PCI_SUSCFG, &(params->pci_suscfg));
 
486
        pci_read_config_byte(params->cs55x0, PCI_PMER1, &(params->pci_pmer1));
 
487
        pci_read_config_byte(params->cs55x0, PCI_PMER2, &(params->pci_pmer2));
 
488
        pci_read_config_byte(params->cs55x0, PCI_MODON, &(params->on_duration));
 
489
        pci_read_config_byte(params->cs55x0, PCI_MODOFF,
 
490
                        &(params->off_duration));
 
491
 
 
492
        ret = cpufreq_register_driver(&gx_suspmod_driver);
 
493
        if (ret) {
 
494
                kfree(params);
 
495
                return ret;                   /* register error! */
 
496
        }
 
497
 
 
498
        return 0;
 
499
}
 
500
 
 
501
static void __exit cpufreq_gx_exit(void)
 
502
{
 
503
        cpufreq_unregister_driver(&gx_suspmod_driver);
 
504
        pci_dev_put(gx_params->cs55x0);
 
505
        kfree(gx_params);
 
506
}
 
507
 
 
508
MODULE_AUTHOR("Hiroshi Miura <miura@da-cha.org>");
 
509
MODULE_DESCRIPTION("Cpufreq driver for Cyrix MediaGX and NatSemi Geode");
 
510
MODULE_LICENSE("GPL");
 
511
 
 
512
module_init(cpufreq_gx_init);
 
513
module_exit(cpufreq_gx_exit);
 
514