Subversion Repositories shark

Rev

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

Rev Author Line No. Line
582 mauro 1
/*
2
 *  This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
3
 *  (C) 2000-2003  Dave Jones, Arjan van de Ven, Janne P�k�� Dominik Brodowski.
4
 *
5
 *  Licensed under the terms of the GNU GPL License version 2.
6
 *
7
 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
8
 */
9
 
10
#include <linuxcomp.h>
11
 
12
#include <linux/kernel.h>
13
#include <linux/module.h> 
14
#include <linux/init.h>
15
#include <linux/cpufreq.h>
16
#include <linux/ioport.h>
17
#include <linux/slab.h>
18
 
19
#include <asm/msr.h>
20
#include <asm/timex.h>
21
#include <asm/io.h>
22
 
23
extern struct cpuinfo_x86 new_cpu_data;
24
extern unsigned long cpu_khz;
25
 
26
#define POWERNOW_IOPORT 0xfff0         /* it doesn't matter where, as long
27
                                          as it is unused */
28
 
29
static unsigned int                     busfreq;   /* FSB, in 10 kHz */
30
static unsigned int                     max_multiplier;
31
 
32
 
33
/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
34
static struct cpufreq_frequency_table clock_ratio[] = {
35
        {45,  /* 000 -> 4.5x */ 0},
36
        {50,  /* 001 -> 5.0x */ 0},
37
        {40,  /* 010 -> 4.0x */ 0},
38
        {55,  /* 011 -> 5.5x */ 0},
39
        {20,  /* 100 -> 2.0x */ 0},
40
        {30,  /* 101 -> 3.0x */ 0},
41
        {60,  /* 110 -> 6.0x */ 0},
42
        {35,  /* 111 -> 3.5x */ 0},
43
        {0, CPUFREQ_TABLE_END}
44
};
45
 
46
 
47
/**
48
 * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
49
 *
50
 *   Returns the current setting of the frequency multiplier. Core clock
51
 * speed is frequency of the Front-Side Bus multiplied with this value.
52
 */
53
static int powernow_k6_get_cpu_multiplier(void)
54
{
55
        u64             invalue = 0;
56
        u32             msrval;
57
 
58
        msrval = POWERNOW_IOPORT + 0x1;
59
        wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
60
        invalue=inl(POWERNOW_IOPORT + 0x8);
61
        msrval = POWERNOW_IOPORT + 0x0;
62
        wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
63
 
64
        return clock_ratio[(invalue >> 5)&7].index;
65
}
66
 
67
 
68
/**
69
 * powernow_k6_set_state - set the PowerNow! multiplier
70
 * @best_i: clock_ratio[best_i] is the target multiplier
71
 *
72
 *   Tries to change the PowerNow! multiplier
73
 */
74
static void powernow_k6_set_state (unsigned int best_i)
75
{
76
        unsigned long           outvalue=0, invalue=0;
77
        unsigned long           msrval;
78
        struct cpufreq_freqs    freqs;
79
 
80
        if (clock_ratio[best_i].index > max_multiplier) {
81
                printk(KERN_ERR "cpufreq: invalid target frequency\n");
82
                return;
83
        }
84
 
85
        freqs.old = busfreq * powernow_k6_get_cpu_multiplier();
86
        freqs.new = busfreq * clock_ratio[best_i].index;
87
        freqs.cpu = 0; // powernow-k6.c is UP only driver
88
 
600 mauro 89
        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
582 mauro 90
 
91
        /* we now need to transform best_i to the BVC format, see AMD#23446 */
92
 
93
        outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5);
94
 
95
        msrval = POWERNOW_IOPORT + 0x1;
96
        wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
97
        invalue=inl(POWERNOW_IOPORT + 0x8);
98
        invalue = invalue & 0xf;
99
        outvalue = outvalue | invalue;
100
        outl(outvalue ,(POWERNOW_IOPORT + 0x8));
101
        msrval = POWERNOW_IOPORT + 0x0;
102
        wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
103
 
600 mauro 104
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
582 mauro 105
 
106
        return;
107
}
108
 
109
 
110
/**
111
 * powernow_k6_verify - verifies a new CPUfreq policy
112
 * @policy: new policy
113
 *
114
 * Policy must be within lowest and highest possible CPU Frequency,
115
 * and at least one possible state must be within min and max.
116
 */
117
static int powernow_k6_verify(struct cpufreq_policy *policy)
118
{
119
        return cpufreq_frequency_table_verify(policy, &clock_ratio[0]);
120
}
121
 
122
 
123
/**
124
 * powernow_k6_setpolicy - sets a new CPUFreq policy
125
 * @policy - new policy
126
 *
127
 * sets a new CPUFreq policy
128
 */
129
static int powernow_k6_target (struct cpufreq_policy *policy,
130
                               unsigned int target_freq,
131
                               unsigned int relation)
132
{
133
        unsigned int    newstate = 0;
134
 
135
        if (cpufreq_frequency_table_target(policy, &clock_ratio[0], target_freq, relation, &newstate))
136
                return -EINVAL;
137
 
138
        powernow_k6_set_state(newstate);
139
 
140
        return 0;
141
}
142
 
143
 
144
static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
145
{
146
        unsigned int i;
147
 
148
        if (policy->cpu != 0)
149
                return -ENODEV;
150
 
151
        /* get frequencies */
152
        max_multiplier = powernow_k6_get_cpu_multiplier();
153
        busfreq = cpu_khz / max_multiplier;
154
 
155
        /* table init */
156
        for (i=0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
157
                if (clock_ratio[i].index > max_multiplier)
158
                        clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
159
                else
160
                        clock_ratio[i].frequency = busfreq * clock_ratio[i].index;
161
        }
162
 
163
        /* cpuinfo and default policy values */
164
        policy->governor = 0; //!!!CPUFREQ_DEFAULT_GOVERNOR;
165
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
166
        policy->cur = busfreq * max_multiplier;
167
 
168
        return cpufreq_frequency_table_cpuinfo(policy, &clock_ratio[0]);
169
}
170
 
171
 
172
static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
173
{
174
        unsigned int i;
175
        for (i=0; i<8; i++) {
176
                if (i==max_multiplier)
177
                        powernow_k6_set_state(i);
178
        }
179
        return 0;
180
}
181
 
182
 
183
static struct cpufreq_driver powernow_k6_driver = {
184
        .verify         = powernow_k6_verify,
185
        .target         = powernow_k6_target,
186
        .init           = powernow_k6_cpu_init,
187
        .exit           = powernow_k6_cpu_exit,
188
        .name           = "powernow-k6",
189
        .owner          = THIS_MODULE,
190
};
191
 
192
 
193
/**
194
 * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
195
 *
196
 *   Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
197
 * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
198
 * on success.
199
 */
200
/*static*/ int __init powernow_k6_init(void)
201
{      
202
        struct cpuinfo_x86 *c = &new_cpu_data;
203
 
204
        if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
205
                ((c->x86_model != 12) && (c->x86_model != 13)))
206
                return -ENODEV;
207
 
208
        if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
209
                printk("cpufreq: PowerNow IOPORT region already used.\n");
210
                return -EIO;
211
        }
212
 
213
        if (cpufreq_register_driver(&powernow_k6_driver)) {
214
                release_region (POWERNOW_IOPORT, 16);
215
                return -EINVAL;
216
        }
217
 
218
        return 0;
219
}
220
 
221
 
222
/**
223
 * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
224
 *
225
 *   Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
226
 */
227
/*static*/ void __exit powernow_k6_exit(void)
228
{
229
        cpufreq_unregister_driver(&powernow_k6_driver);
230
        release_region (POWERNOW_IOPORT, 16);
231
}
232
 
233
 
234
MODULE_AUTHOR ("Arjan van de Ven <arjanv@redhat.com>, Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
235
MODULE_DESCRIPTION ("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
236
MODULE_LICENSE ("GPL");
237
 
238
module_init(powernow_k6_init);
239
module_exit(powernow_k6_exit);