aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2013-12-11 19:38:53 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2014-01-06 09:52:49 -0500
commitd82b922a4acc1781d368aceac2f9da43b038cab2 (patch)
tree2ce5ad378eb8d1fd2d4a6d12e6e9184d44418df3
parente20e1d0ac02308e2211306fc67abcd0b2668fb8b (diff)
powernow-k6: correctly initialize default parameters
The powernow-k6 driver used to read the initial multiplier from the powernow register. However, there is a problem with this: * If there was a frequency transition before, the multiplier read from the register corresponds to the current multiplier. * If there was no frequency transition since reset, the field in the register always reads as zero, regardless of the current multiplier that is set using switches on the mainboard and that the CPU is running at. The zero value corresponds to multiplier 4.5, so as a consequence, the powernow-k6 driver always assumes multiplier 4.5. For example, if we have 550MHz CPU with bus frequency 100MHz and multiplier 5.5, the powernow-k6 driver thinks that the multiplier is 4.5 and bus frequency is 122MHz. The powernow-k6 driver then sets the multiplier to 4.5, underclocking the CPU to 450MHz, but reports the current frequency as 550MHz. There is no reliable way how to read the initial multiplier. I modified the driver so that it contains a table of known frequencies (based on parameters of existing CPUs and some common overclocking schemes) and sets the multiplier according to the frequency. If the frequency is unknown (because of unusual overclocking or underclocking), the user must supply the bus speed and maximum multiplier as module parameters. This patch should be backported to all stable kernels. If it doesn't apply cleanly, change it, or ask me to change it. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/cpufreq/powernow-k6.c76
1 files changed, 72 insertions, 4 deletions
diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c
index 1332b72f373a..16359ab2c459 100644
--- a/drivers/cpufreq/powernow-k6.c
+++ b/drivers/cpufreq/powernow-k6.c
@@ -26,6 +26,14 @@
26static unsigned int busfreq; /* FSB, in 10 kHz */ 26static unsigned int busfreq; /* FSB, in 10 kHz */
27static unsigned int max_multiplier; 27static unsigned int max_multiplier;
28 28
29static unsigned int param_busfreq = 0;
30static unsigned int param_max_multiplier = 0;
31
32module_param_named(max_multiplier, param_max_multiplier, uint, S_IRUGO);
33MODULE_PARM_DESC(max_multiplier, "Maximum multiplier (allowed values: 20 30 35 40 45 50 55 60)");
34
35module_param_named(bus_frequency, param_busfreq, uint, S_IRUGO);
36MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz");
29 37
30/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */ 38/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
31static struct cpufreq_frequency_table clock_ratio[] = { 39static struct cpufreq_frequency_table clock_ratio[] = {
@@ -40,6 +48,27 @@ static struct cpufreq_frequency_table clock_ratio[] = {
40 {0, CPUFREQ_TABLE_END} 48 {0, CPUFREQ_TABLE_END}
41}; 49};
42 50
51static const struct {
52 unsigned freq;
53 unsigned mult;
54} usual_frequency_table[] = {
55 { 400000, 40 }, // 100 * 4
56 { 450000, 45 }, // 100 * 4.5
57 { 475000, 50 }, // 95 * 5
58 { 500000, 50 }, // 100 * 5
59 { 506250, 45 }, // 112.5 * 4.5
60 { 533500, 55 }, // 97 * 5.5
61 { 550000, 55 }, // 100 * 5.5
62 { 562500, 50 }, // 112.5 * 5
63 { 570000, 60 }, // 95 * 6
64 { 600000, 60 }, // 100 * 6
65 { 618750, 55 }, // 112.5 * 5.5
66 { 660000, 55 }, // 120 * 5.5
67 { 675000, 60 }, // 112.5 * 6
68 { 720000, 60 }, // 120 * 6
69};
70
71#define FREQ_RANGE 3000
43 72
44/** 73/**
45 * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier 74 * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
@@ -125,17 +154,56 @@ static int powernow_k6_target(struct cpufreq_policy *policy,
125 return 0; 154 return 0;
126} 155}
127 156
128
129static int powernow_k6_cpu_init(struct cpufreq_policy *policy) 157static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
130{ 158{
131 unsigned int i, f; 159 unsigned int i, f;
160 unsigned khz;
132 161
133 if (policy->cpu != 0) 162 if (policy->cpu != 0)
134 return -ENODEV; 163 return -ENODEV;
135 164
136 /* get frequencies */ 165 max_multiplier = 0;
137 max_multiplier = powernow_k6_get_cpu_multiplier(); 166 khz = cpu_khz;
138 busfreq = cpu_khz / max_multiplier; 167 for (i = 0; i < ARRAY_SIZE(usual_frequency_table); i++) {
168 if (khz >= usual_frequency_table[i].freq - FREQ_RANGE &&
169 khz <= usual_frequency_table[i].freq + FREQ_RANGE) {
170 khz = usual_frequency_table[i].freq;
171 max_multiplier = usual_frequency_table[i].mult;
172 break;
173 }
174 }
175 if (param_max_multiplier) {
176 for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
177 if (clock_ratio[i].driver_data == param_max_multiplier) {
178 max_multiplier = param_max_multiplier;
179 goto have_max_multiplier;
180 }
181 }
182 printk(KERN_ERR "powernow-k6: invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n");
183 return -EINVAL;
184 }
185
186 if (!max_multiplier) {
187 printk(KERN_WARNING "powernow-k6: unknown frequency %u, cannot determine current multiplier\n", khz);
188 printk(KERN_WARNING "powernow-k6: use module parameters max_multiplier and bus_frequency\n");
189 return -EOPNOTSUPP;
190 }
191
192have_max_multiplier:
193 param_max_multiplier = max_multiplier;
194
195 if (param_busfreq) {
196 if (param_busfreq >= 50000 && param_busfreq <= 150000) {
197 busfreq = param_busfreq / 10;
198 goto have_busfreq;
199 }
200 printk(KERN_ERR "powernow-k6: invalid bus_frequency parameter, allowed range 50000 - 150000 kHz\n");
201 return -EINVAL;
202 }
203
204 busfreq = khz / max_multiplier;
205have_busfreq:
206 param_busfreq = busfreq * 10;
139 207
140 /* table init */ 208 /* table init */
141 for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) { 209 for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {