Blame view

drivers/cpufreq/powernow-k6.c 6.55 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   *  This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
3
4
   *  (C) 2000-2003  Dave Jones, Arjan van de Ven, Janne Pänkälä,
   *                 Dominik Brodowski.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
10
11
   *
   *  Licensed under the terms of the GNU GPL License version 2.
   *
   *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
   */
  
  #include <linux/kernel.h>
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
12
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
  #include <linux/init.h>
  #include <linux/cpufreq.h>
  #include <linux/ioport.h>
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
16
17
  #include <linux/timex.h>
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18

14a6650f1   Dave Jones   [CPUFREQ] checkpa...
19
  #include <asm/msr.h>
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
20
21
  #define POWERNOW_IOPORT 0xfff0          /* it doesn't matter where, as long
  					   as it is unused */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22

14a6650f1   Dave Jones   [CPUFREQ] checkpa...
23
  #define PFX "powernow-k6: "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  static unsigned int                     busfreq;   /* FSB, in 10 kHz */
  static unsigned int                     max_multiplier;
  
  
  /* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
  static struct cpufreq_frequency_table clock_ratio[] = {
  	{45,  /* 000 -> 4.5x */ 0},
  	{50,  /* 001 -> 5.0x */ 0},
  	{40,  /* 010 -> 4.0x */ 0},
  	{55,  /* 011 -> 5.5x */ 0},
  	{20,  /* 100 -> 2.0x */ 0},
  	{30,  /* 101 -> 3.0x */ 0},
  	{60,  /* 110 -> 6.0x */ 0},
  	{35,  /* 111 -> 3.5x */ 0},
  	{0, CPUFREQ_TABLE_END}
  };
  
  
  /**
   * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
   *
   *   Returns the current setting of the frequency multiplier. Core clock
   * speed is frequency of the Front-Side Bus multiplied with this value.
   */
  static int powernow_k6_get_cpu_multiplier(void)
  {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
50
51
  	u64 invalue = 0;
  	u32 msrval;
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
52

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
54
  	msrval = POWERNOW_IOPORT + 0x1;
  	wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
55
  	invalue = inl(POWERNOW_IOPORT + 0x8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
58
59
60
61
62
63
64
65
66
67
68
  	msrval = POWERNOW_IOPORT + 0x0;
  	wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
  
  	return clock_ratio[(invalue >> 5)&7].index;
  }
  
  
  /**
   * powernow_k6_set_state - set the PowerNow! multiplier
   * @best_i: clock_ratio[best_i] is the target multiplier
   *
   *   Tries to change the PowerNow! multiplier
   */
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
69
  static void powernow_k6_set_state(unsigned int best_i)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
  {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
71
72
73
  	unsigned long outvalue = 0, invalue = 0;
  	unsigned long msrval;
  	struct cpufreq_freqs freqs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
  
  	if (clock_ratio[best_i].index > max_multiplier) {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
76
77
  		printk(KERN_ERR PFX "invalid target frequency
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
  		return;
  	}
  
  	freqs.old = busfreq * powernow_k6_get_cpu_multiplier();
  	freqs.new = busfreq * clock_ratio[best_i].index;
  	freqs.cpu = 0; /* powernow-k6.c is UP only driver */
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
84

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
85
86
87
88
89
90
91
92
  	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
  
  	/* we now need to transform best_i to the BVC format, see AMD#23446 */
  
  	outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5);
  
  	msrval = POWERNOW_IOPORT + 0x1;
  	wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
93
  	invalue = inl(POWERNOW_IOPORT + 0x8);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
  	invalue = invalue & 0xf;
  	outvalue = outvalue | invalue;
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
96
  	outl(outvalue , (POWERNOW_IOPORT + 0x8));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  	msrval = POWERNOW_IOPORT + 0x0;
  	wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
  
  	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
  
  	return;
  }
  
  
  /**
   * powernow_k6_verify - verifies a new CPUfreq policy
   * @policy: new policy
   *
   * Policy must be within lowest and highest possible CPU Frequency,
   * and at least one possible state must be within min and max.
   */
  static int powernow_k6_verify(struct cpufreq_policy *policy)
  {
  	return cpufreq_frequency_table_verify(policy, &clock_ratio[0]);
  }
  
  
  /**
   * powernow_k6_setpolicy - sets a new CPUFreq policy
   * @policy: new policy
   * @target_freq: the target frequency
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
123
124
   * @relation: how that frequency relates to achieved frequency
   *  (CPUFREQ_RELATION_L or CPUFREQ_RELATION_H)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
   *
   * sets a new CPUFreq policy
   */
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
128
  static int powernow_k6_target(struct cpufreq_policy *policy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
129
130
131
  			       unsigned int target_freq,
  			       unsigned int relation)
  {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
132
  	unsigned int newstate = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133

14a6650f1   Dave Jones   [CPUFREQ] checkpa...
134
135
  	if (cpufreq_frequency_table_target(policy, &clock_ratio[0],
  				target_freq, relation, &newstate))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
137
138
139
140
141
142
143
144
145
  		return -EINVAL;
  
  	powernow_k6_set_state(newstate);
  
  	return 0;
  }
  
  
  static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
  {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
146
  	unsigned int i, f;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
148
149
150
151
152
153
154
155
156
  	int result;
  
  	if (policy->cpu != 0)
  		return -ENODEV;
  
  	/* get frequencies */
  	max_multiplier = powernow_k6_get_cpu_multiplier();
  	busfreq = cpu_khz / max_multiplier;
  
  	/* table init */
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
157
  	for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
158
159
  		f = clock_ratio[i].index;
  		if (f > max_multiplier)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
  			clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
  		else
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
162
  			clock_ratio[i].frequency = busfreq * f;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
164
165
  	}
  
  	/* cpuinfo and default policy values */
db2820dd5   Krzysztof Helt   [CPUFREQ] powerno...
166
  	policy->cpuinfo.transition_latency = 200000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
167
168
169
170
  	policy->cur = busfreq * max_multiplier;
  
  	result = cpufreq_frequency_table_cpuinfo(policy, clock_ratio);
  	if (result)
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
171
  		return result;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172
173
174
175
176
177
178
179
180
181
  
  	cpufreq_frequency_table_get_attr(clock_ratio, policy->cpu);
  
  	return 0;
  }
  
  
  static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
  {
  	unsigned int i;
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
182
183
  	for (i = 0; i < 8; i++) {
  		if (i == max_multiplier)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
  			powernow_k6_set_state(i);
  	}
  	cpufreq_frequency_table_put_attr(policy->cpu);
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
187
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
  }
  
  static unsigned int powernow_k6_get(unsigned int cpu)
  {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
192
193
194
  	unsigned int ret;
  	ret = (busfreq * powernow_k6_get_cpu_multiplier());
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  }
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
196
  static struct freq_attr *powernow_k6_attr[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
  	&cpufreq_freq_attr_scaling_available_freqs,
  	NULL,
  };
221dee285   Linus Torvalds   Revert "[CPUFREQ]...
200
  static struct cpufreq_driver powernow_k6_driver = {
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
201
202
  	.verify		= powernow_k6_verify,
  	.target		= powernow_k6_target,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
  	.init		= powernow_k6_cpu_init,
  	.exit		= powernow_k6_cpu_exit,
  	.get		= powernow_k6_get,
  	.name		= "powernow-k6",
  	.owner		= THIS_MODULE,
  	.attr		= powernow_k6_attr,
  };
  
  
  /**
   * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
   *
   *   Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
   * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
   * on success.
   */
  static int __init powernow_k6_init(void)
32ee8c3e4   Dave Jones   [CPUFREQ] Lots of...
220
  {
92cb7612a   Mike Travis   x86: convert cpui...
221
  	struct cpuinfo_x86 *c = &cpu_data(0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
  
  	if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 != 5) ||
  		((c->x86_model != 12) && (c->x86_model != 13)))
  		return -ENODEV;
  
  	if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
228
229
  		printk(KERN_INFO PFX "PowerNow IOPORT region already used.
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
231
232
233
  		return -EIO;
  	}
  
  	if (cpufreq_register_driver(&powernow_k6_driver)) {
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
234
  		release_region(POWERNOW_IOPORT, 16);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  
  /**
   * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
   *
   *   Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
   */
  static void __exit powernow_k6_exit(void)
  {
  	cpufreq_unregister_driver(&powernow_k6_driver);
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
250
  	release_region(POWERNOW_IOPORT, 16);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  }
14a6650f1   Dave Jones   [CPUFREQ] checkpa...
252
253
  MODULE_AUTHOR("Arjan van de Ven, Dave Jones <davej@redhat.com>, "
  		"Dominik Brodowski <linux@brodo.de>");
8d2d2051e   Paolo Ciarrocchi   [CPUFREQ] Coding ...
254
255
  MODULE_DESCRIPTION("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
  MODULE_LICENSE("GPL");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
256
257
258
  
  module_init(powernow_k6_init);
  module_exit(powernow_k6_exit);