Subversion Repositories shark

Rev

Rev 600 | 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
 
770 mauro 168
        /* Added by Nino */
169
        cpufreq_frequency_table_get_attr(&clock_ratio[0], policy->cpu);
170
 
582 mauro 171
        return cpufreq_frequency_table_cpuinfo(policy, &clock_ratio[0]);
172
}
173
 
174
 
175
static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
176
{
177
        unsigned int i;
178
        for (i=0; i<8; i++) {
179
                if (i==max_multiplier)
180
                        powernow_k6_set_state(i);
181
        }
770 mauro 182
 
183
        /* Added by Nino */
184
        cpufreq_frequency_table_put_attr(policy->cpu);
185
 
582 mauro 186
        return 0;
187
}
188
 
189
 
190
static struct cpufreq_driver powernow_k6_driver = {
191
        .verify         = powernow_k6_verify,
192
        .target         = powernow_k6_target,
193
        .init           = powernow_k6_cpu_init,
194
        .exit           = powernow_k6_cpu_exit,
195
        .name           = "powernow-k6",
196
        .owner          = THIS_MODULE,
197
};
198
 
199
 
200
/**
201
 * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
202
 *
203
 *   Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
204
 * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
205
 * on success.
206
 */
207
/*static*/ int __init powernow_k6_init(void)
208
{      
209
        struct cpuinfo_x86 *c = &new_cpu_data;
210
 
211
        if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
212
                ((c->x86_model != 12) && (c->x86_model != 13)))
213
                return -ENODEV;
214
 
215
        if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
216
                printk("cpufreq: PowerNow IOPORT region already used.\n");
217
                return -EIO;
218
        }
219
 
220
        if (cpufreq_register_driver(&powernow_k6_driver)) {
221
                release_region (POWERNOW_IOPORT, 16);
222
                return -EINVAL;
223
        }
224
 
225
        return 0;
226
}
227
 
228
 
229
/**
230
 * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
231
 *
232
 *   Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
233
 */
234
/*static*/ void __exit powernow_k6_exit(void)
235
{
236
        cpufreq_unregister_driver(&powernow_k6_driver);
237
        release_region (POWERNOW_IOPORT, 16);
238
}
239
 
240
 
241
MODULE_AUTHOR ("Arjan van de Ven <arjanv@redhat.com>, Dave Jones <davej@codemonkey.org.uk>, Dominik Brodowski <linux@brodo.de>");
242
MODULE_DESCRIPTION ("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
243
MODULE_LICENSE ("GPL");
244
 
245
module_init(powernow_k6_init);
246
module_exit(powernow_k6_exit);