~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/cpufreq/elanfreq.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      elanfreq:       cpufreq driver for the AMD ELAN family
 
3
 *
 
4
 *      (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
 
5
 *
 
6
 *      Parts of this code are (c) Sven Geggus <sven@geggus.net>
 
7
 *
 
8
 *      All Rights Reserved.
 
9
 *
 
10
 *      This program is free software; you can redistribute it and/or
 
11
 *      modify it under the terms of the GNU General Public License
 
12
 *      as published by the Free Software Foundation; either version
 
13
 *      2 of the License, or (at your option) any later version.
 
14
 *
 
15
 *      2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
 
16
 *
 
17
 */
 
18
 
 
19
#include <linux/kernel.h>
 
20
#include <linux/module.h>
 
21
#include <linux/init.h>
 
22
 
 
23
#include <linux/delay.h>
 
24
#include <linux/cpufreq.h>
 
25
 
 
26
#include <asm/msr.h>
 
27
#include <linux/timex.h>
 
28
#include <linux/io.h>
 
29
 
 
30
#define REG_CSCIR 0x22          /* Chip Setup and Control Index Register    */
 
31
#define REG_CSCDR 0x23          /* Chip Setup and Control Data  Register    */
 
32
 
 
33
/* Module parameter */
 
34
static int max_freq;
 
35
 
 
36
struct s_elan_multiplier {
 
37
        int clock;              /* frequency in kHz                         */
 
38
        int val40h;             /* PMU Force Mode register                  */
 
39
        int val80h;             /* CPU Clock Speed Register                 */
 
40
};
 
41
 
 
42
/*
 
43
 * It is important that the frequencies
 
44
 * are listed in ascending order here!
 
45
 */
 
46
static struct s_elan_multiplier elan_multiplier[] = {
 
47
        {1000,  0x02,   0x18},
 
48
        {2000,  0x02,   0x10},
 
49
        {4000,  0x02,   0x08},
 
50
        {8000,  0x00,   0x00},
 
51
        {16000, 0x00,   0x02},
 
52
        {33000, 0x00,   0x04},
 
53
        {66000, 0x01,   0x04},
 
54
        {99000, 0x01,   0x05}
 
55
};
 
56
 
 
57
static struct cpufreq_frequency_table elanfreq_table[] = {
 
58
        {0,     1000},
 
59
        {1,     2000},
 
60
        {2,     4000},
 
61
        {3,     8000},
 
62
        {4,     16000},
 
63
        {5,     33000},
 
64
        {6,     66000},
 
65
        {7,     99000},
 
66
        {0,     CPUFREQ_TABLE_END},
 
67
};
 
68
 
 
69
 
 
70
/**
 
71
 *      elanfreq_get_cpu_frequency: determine current cpu speed
 
72
 *
 
73
 *      Finds out at which frequency the CPU of the Elan SOC runs
 
74
 *      at the moment. Frequencies from 1 to 33 MHz are generated
 
75
 *      the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
 
76
 *      and have the rest of the chip running with 33 MHz.
 
77
 */
 
78
 
 
79
static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
 
80
{
 
81
        u8 clockspeed_reg;    /* Clock Speed Register */
 
82
 
 
83
        local_irq_disable();
 
84
        outb_p(0x80, REG_CSCIR);
 
85
        clockspeed_reg = inb_p(REG_CSCDR);
 
86
        local_irq_enable();
 
87
 
 
88
        if ((clockspeed_reg & 0xE0) == 0xE0)
 
89
                return 0;
 
90
 
 
91
        /* Are we in CPU clock multiplied mode (66/99 MHz)? */
 
92
        if ((clockspeed_reg & 0xE0) == 0xC0) {
 
93
                if ((clockspeed_reg & 0x01) == 0)
 
94
                        return 66000;
 
95
                else
 
96
                        return 99000;
 
97
        }
 
98
 
 
99
        /* 33 MHz is not 32 MHz... */
 
100
        if ((clockspeed_reg & 0xE0) == 0xA0)
 
101
                return 33000;
 
102
 
 
103
        return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;
 
104
}
 
105
 
 
106
 
 
107
/**
 
108
 *      elanfreq_set_cpu_frequency: Change the CPU core frequency
 
109
 *      @cpu: cpu number
 
110
 *      @freq: frequency in kHz
 
111
 *
 
112
 *      This function takes a frequency value and changes the CPU frequency
 
113
 *      according to this. Note that the frequency has to be checked by
 
114
 *      elanfreq_validatespeed() for correctness!
 
115
 *
 
116
 *      There is no return value.
 
117
 */
 
118
 
 
119
static void elanfreq_set_cpu_state(unsigned int state)
 
120
{
 
121
        struct cpufreq_freqs    freqs;
 
122
 
 
123
        freqs.old = elanfreq_get_cpu_frequency(0);
 
124
        freqs.new = elan_multiplier[state].clock;
 
125
        freqs.cpu = 0; /* elanfreq.c is UP only driver */
 
126
 
 
127
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
 
128
 
 
129
        printk(KERN_INFO "elanfreq: attempting to set frequency to %i kHz\n",
 
130
                        elan_multiplier[state].clock);
 
131
 
 
132
 
 
133
        /*
 
134
         * Access to the Elan's internal registers is indexed via
 
135
         * 0x22: Chip Setup & Control Register Index Register (CSCI)
 
136
         * 0x23: Chip Setup & Control Register Data  Register (CSCD)
 
137
         *
 
138
         */
 
139
 
 
140
        /*
 
141
         * 0x40 is the Power Management Unit's Force Mode Register.
 
142
         * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
 
143
         */
 
144
 
 
145
        local_irq_disable();
 
146
        outb_p(0x40, REG_CSCIR);                /* Disable hyperspeed mode */
 
147
        outb_p(0x00, REG_CSCDR);
 
148
        local_irq_enable();             /* wait till internal pipelines and */
 
149
        udelay(1000);                   /* buffers have cleaned up          */
 
150
 
 
151
        local_irq_disable();
 
152
 
 
153
        /* now, set the CPU clock speed register (0x80) */
 
154
        outb_p(0x80, REG_CSCIR);
 
155
        outb_p(elan_multiplier[state].val80h, REG_CSCDR);
 
156
 
 
157
        /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
 
158
        outb_p(0x40, REG_CSCIR);
 
159
        outb_p(elan_multiplier[state].val40h, REG_CSCDR);
 
160
        udelay(10000);
 
161
        local_irq_enable();
 
162
 
 
163
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
164
};
 
165
 
 
166
 
 
167
/**
 
168
 *      elanfreq_validatespeed: test if frequency range is valid
 
169
 *      @policy: the policy to validate
 
170
 *
 
171
 *      This function checks if a given frequency range in kHz is valid
 
172
 *      for the hardware supported by the driver.
 
173
 */
 
174
 
 
175
static int elanfreq_verify(struct cpufreq_policy *policy)
 
176
{
 
177
        return cpufreq_frequency_table_verify(policy, &elanfreq_table[0]);
 
178
}
 
179
 
 
180
static int elanfreq_target(struct cpufreq_policy *policy,
 
181
                            unsigned int target_freq,
 
182
                            unsigned int relation)
 
183
{
 
184
        unsigned int newstate = 0;
 
185
 
 
186
        if (cpufreq_frequency_table_target(policy, &elanfreq_table[0],
 
187
                                target_freq, relation, &newstate))
 
188
                return -EINVAL;
 
189
 
 
190
        elanfreq_set_cpu_state(newstate);
 
191
 
 
192
        return 0;
 
193
}
 
194
 
 
195
 
 
196
/*
 
197
 *      Module init and exit code
 
198
 */
 
199
 
 
200
static int elanfreq_cpu_init(struct cpufreq_policy *policy)
 
201
{
 
202
        struct cpuinfo_x86 *c = &cpu_data(0);
 
203
        unsigned int i;
 
204
        int result;
 
205
 
 
206
        /* capability check */
 
207
        if ((c->x86_vendor != X86_VENDOR_AMD) ||
 
208
            (c->x86 != 4) || (c->x86_model != 10))
 
209
                return -ENODEV;
 
210
 
 
211
        /* max freq */
 
212
        if (!max_freq)
 
213
                max_freq = elanfreq_get_cpu_frequency(0);
 
214
 
 
215
        /* table init */
 
216
        for (i = 0; (elanfreq_table[i].frequency != CPUFREQ_TABLE_END); i++) {
 
217
                if (elanfreq_table[i].frequency > max_freq)
 
218
                        elanfreq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
 
219
        }
 
220
 
 
221
        /* cpuinfo and default policy values */
 
222
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 
223
        policy->cur = elanfreq_get_cpu_frequency(0);
 
224
 
 
225
        result = cpufreq_frequency_table_cpuinfo(policy, elanfreq_table);
 
226
        if (result)
 
227
                return result;
 
228
 
 
229
        cpufreq_frequency_table_get_attr(elanfreq_table, policy->cpu);
 
230
        return 0;
 
231
}
 
232
 
 
233
 
 
234
static int elanfreq_cpu_exit(struct cpufreq_policy *policy)
 
235
{
 
236
        cpufreq_frequency_table_put_attr(policy->cpu);
 
237
        return 0;
 
238
}
 
239
 
 
240
 
 
241
#ifndef MODULE
 
242
/**
 
243
 * elanfreq_setup - elanfreq command line parameter parsing
 
244
 *
 
245
 * elanfreq command line parameter.  Use:
 
246
 *  elanfreq=66000
 
247
 * to set the maximum CPU frequency to 66 MHz. Note that in
 
248
 * case you do not give this boot parameter, the maximum
 
249
 * frequency will fall back to _current_ CPU frequency which
 
250
 * might be lower. If you build this as a module, use the
 
251
 * max_freq module parameter instead.
 
252
 */
 
253
static int __init elanfreq_setup(char *str)
 
254
{
 
255
        max_freq = simple_strtoul(str, &str, 0);
 
256
        printk(KERN_WARNING "You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
 
257
        return 1;
 
258
}
 
259
__setup("elanfreq=", elanfreq_setup);
 
260
#endif
 
261
 
 
262
 
 
263
static struct freq_attr *elanfreq_attr[] = {
 
264
        &cpufreq_freq_attr_scaling_available_freqs,
 
265
        NULL,
 
266
};
 
267
 
 
268
 
 
269
static struct cpufreq_driver elanfreq_driver = {
 
270
        .get            = elanfreq_get_cpu_frequency,
 
271
        .verify         = elanfreq_verify,
 
272
        .target         = elanfreq_target,
 
273
        .init           = elanfreq_cpu_init,
 
274
        .exit           = elanfreq_cpu_exit,
 
275
        .name           = "elanfreq",
 
276
        .owner          = THIS_MODULE,
 
277
        .attr           = elanfreq_attr,
 
278
};
 
279
 
 
280
 
 
281
static int __init elanfreq_init(void)
 
282
{
 
283
        struct cpuinfo_x86 *c = &cpu_data(0);
 
284
 
 
285
        /* Test if we have the right hardware */
 
286
        if ((c->x86_vendor != X86_VENDOR_AMD) ||
 
287
                (c->x86 != 4) || (c->x86_model != 10)) {
 
288
                printk(KERN_INFO "elanfreq: error: no Elan processor found!\n");
 
289
                return -ENODEV;
 
290
        }
 
291
        return cpufreq_register_driver(&elanfreq_driver);
 
292
}
 
293
 
 
294
 
 
295
static void __exit elanfreq_exit(void)
 
296
{
 
297
        cpufreq_unregister_driver(&elanfreq_driver);
 
298
}
 
299
 
 
300
 
 
301
module_param(max_freq, int, 0444);
 
302
 
 
303
MODULE_LICENSE("GPL");
 
304
MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
 
305
                "Sven Geggus <sven@geggus.net>");
 
306
MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
 
307
 
 
308
module_init(elanfreq_init);
 
309
module_exit(elanfreq_exit);