ia64/xen-unstable

view xen/arch/x86/acpi/cpufreq/powernow.c @ 18532:415c3da25b26

x86: fix powernow

... by allocating the necessary cpufreq_policy structures.

Signed-off-by: Jan Beulich <jbeulich@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Sep 22 15:56:12 2008 +0100 (2008-09-22)
parents d8a2d117225c
children 101e50cffc78
line source
1 /*
2 * powernow - AMD Architectural P-state Driver ($Revision: 1.4 $)
3 *
4 * Copyright (C) 2008 Mark Langsdorf <mark.langsdorf@amd.com>
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21 *
22 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23 */
25 #include <xen/types.h>
26 #include <xen/errno.h>
27 #include <xen/delay.h>
28 #include <xen/cpumask.h>
29 #include <xen/timer.h>
30 #include <xen/xmalloc.h>
31 #include <asm/bug.h>
32 #include <asm/msr.h>
33 #include <asm/io.h>
34 #include <asm/config.h>
35 #include <asm/processor.h>
36 #include <asm/percpu.h>
37 #include <asm/cpufeature.h>
38 #include <acpi/acpi.h>
39 #include <acpi/cpufreq/cpufreq.h>
41 #define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
42 #define USE_HW_PSTATE 0x00000080
43 #define HW_PSTATE_MASK 0x00000007
44 #define HW_PSTATE_VALID_MASK 0x80000000
45 #define HW_PSTATE_MAX_MASK 0x000000f0
46 #define HW_PSTATE_MAX_SHIFT 4
47 #define MSR_PSTATE_DEF_BASE 0xc0010064 /* base of Pstate MSRs */
48 #define MSR_PSTATE_STATUS 0xc0010063 /* Pstate Status MSR */
49 #define MSR_PSTATE_CTRL 0xc0010062 /* Pstate control MSR */
50 #define MSR_PSTATE_CUR_LIMIT 0xc0010061 /* pstate current limit MSR */
52 struct powernow_cpufreq_data {
53 struct processor_performance *acpi_data;
54 struct cpufreq_frequency_table *freq_table;
55 unsigned int max_freq;
56 unsigned int resume;
57 unsigned int cpu_feature;
58 };
60 static struct powernow_cpufreq_data *drv_data[NR_CPUS];
62 struct drv_cmd {
63 unsigned int type;
64 cpumask_t mask;
65 u64 addr;
66 u32 val;
67 };
69 static void transition_pstate(void *drvcmd)
70 {
71 struct drv_cmd *cmd;
72 cmd = (struct drv_cmd *) drvcmd;
74 wrmsr(MSR_PSTATE_CTRL, cmd->val, 0);
75 }
77 static int powernow_cpufreq_target(struct cpufreq_policy *policy,
78 unsigned int target_freq, unsigned int relation)
79 {
80 struct powernow_cpufreq_data *data = drv_data[policy->cpu];
81 struct processor_performance *perf;
82 struct cpufreq_freqs freqs;
83 cpumask_t online_policy_cpus;
84 struct drv_cmd cmd;
85 unsigned int next_state = 0; /* Index into freq_table */
86 unsigned int next_perf_state = 0; /* Index into perf table */
87 int result = 0;
89 if (unlikely(data == NULL ||
90 data->acpi_data == NULL || data->freq_table == NULL)) {
91 return -ENODEV;
92 }
94 perf = data->acpi_data;
95 result = cpufreq_frequency_table_target(policy,
96 data->freq_table,
97 target_freq,
98 relation, &next_state);
99 if (unlikely(result))
100 return -ENODEV;
102 online_policy_cpus = policy->cpus;
104 next_perf_state = data->freq_table[next_state].index;
105 if (perf->state == next_perf_state) {
106 if (unlikely(data->resume))
107 data->resume = 0;
108 else
109 return 0;
110 }
112 cpus_clear(cmd.mask);
114 if (policy->shared_type != CPUFREQ_SHARED_TYPE_ANY)
115 cmd.mask = online_policy_cpus;
116 else
117 cpu_set(policy->cpu, cmd.mask);
119 freqs.old = perf->states[perf->state].core_frequency * 1000;
120 freqs.new = data->freq_table[next_state].frequency;
122 cmd.val = next_perf_state;
124 on_selected_cpus( cmd.mask, transition_pstate, (void *) &cmd, 0, 0);
126 perf->state = next_perf_state;
127 policy->cur = freqs.new;
129 return result;
130 }
132 static int powernow_cpufreq_cpu_init(struct cpufreq_policy *policy)
133 {
134 unsigned int i;
135 unsigned int valid_states = 0;
136 unsigned int cpu = policy->cpu;
137 struct powernow_cpufreq_data *data;
138 unsigned int result = 0;
139 struct processor_performance *perf;
140 u32 max_hw_pstate, hi = 0, lo = 0;
142 data = xmalloc(struct powernow_cpufreq_data);
143 if (!data)
144 return -ENOMEM;
145 memset(data, 0, sizeof(struct powernow_cpufreq_data));
147 drv_data[cpu] = data;
149 data->acpi_data = &processor_pminfo[cpu]->perf;
151 perf = data->acpi_data;
152 policy->shared_type = perf->shared_type;
154 /*
155 * Will let policy->cpus know about dependency only when software
156 * coordination is required.
157 */
158 if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL ||
159 policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
160 policy->cpus = perf->shared_cpu_map;
161 } else {
162 policy->cpus = cpumask_of_cpu(cpu);
163 }
165 /* capability check */
166 if (perf->state_count <= 1) {
167 printk("No P-States\n");
168 result = -ENODEV;
169 goto err_unreg;
170 }
171 rdmsr(MSR_PSTATE_CUR_LIMIT, hi, lo);
172 max_hw_pstate = (hi & HW_PSTATE_MAX_MASK) >> HW_PSTATE_MAX_SHIFT;
174 if (perf->control_register.space_id != perf->status_register.space_id) {
175 result = -ENODEV;
176 goto err_unreg;
177 }
179 data->freq_table = xmalloc_array(struct cpufreq_frequency_table,
180 (perf->state_count+1));
181 if (!data->freq_table) {
182 result = -ENOMEM;
183 goto err_unreg;
184 }
186 /* detect transition latency */
187 policy->cpuinfo.transition_latency = 0;
188 for (i=0; i<perf->state_count; i++) {
189 if ((perf->states[i].transition_latency * 1000) >
190 policy->cpuinfo.transition_latency)
191 policy->cpuinfo.transition_latency =
192 perf->states[i].transition_latency * 1000;
193 }
195 data->max_freq = perf->states[0].core_frequency * 1000;
196 /* table init */
197 for (i = 0; i < perf->state_count && i <= max_hw_pstate; i++) {
198 if (i > 0 && perf->states[i].core_frequency >=
199 data->freq_table[valid_states-1].frequency / 1000)
200 continue;
202 data->freq_table[valid_states].index = perf->states[i].control & HW_PSTATE_MASK;
203 data->freq_table[valid_states].frequency =
204 perf->states[i].core_frequency * 1000;
205 valid_states++;
206 }
207 data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
208 perf->state = 0;
210 result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
211 if (result)
212 goto err_freqfree;
214 /*
215 * the first call to ->target() should result in us actually
216 * writing something to the appropriate registers.
217 */
218 data->resume = 1;
220 policy->cur = data->freq_table[i].frequency;
221 return result;
223 err_freqfree:
224 xfree(data->freq_table);
225 err_unreg:
226 xfree(data);
227 drv_data[cpu] = NULL;
229 return result;
230 }
232 static struct cpufreq_driver powernow_cpufreq_driver = {
233 .target = powernow_cpufreq_target,
234 .init = powernow_cpufreq_cpu_init,
235 };
237 int powernow_cpufreq_init(void)
238 {
239 unsigned int i, ret = 0;
240 unsigned int dom, max_dom = 0;
241 cpumask_t *pt;
242 unsigned long *dom_mask;
244 for_each_online_cpu(i) {
245 struct cpuinfo_x86 *c = &cpu_data[i];
246 if (c->x86_vendor != X86_VENDOR_AMD)
247 ret = -ENODEV;
248 else
249 {
250 u32 eax, ebx, ecx, edx;
251 cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
252 if ((edx & USE_HW_PSTATE) != USE_HW_PSTATE)
253 ret = -ENODEV;
254 }
255 if (ret)
256 return ret;
257 if (max_dom < processor_pminfo[i]->perf.domain_info.domain)
258 max_dom = processor_pminfo[i]->perf.domain_info.domain;
259 }
260 max_dom++;
262 dom_mask = xmalloc_array(unsigned long, BITS_TO_LONGS(max_dom));
263 if (!dom_mask)
264 return -ENOMEM;
265 bitmap_zero(dom_mask, max_dom);
267 pt = xmalloc_array(cpumask_t, max_dom);
268 if (!pt)
269 return -ENOMEM;
270 memset(pt, 0, max_dom * sizeof(cpumask_t));
272 /* get cpumask of each psd domain */
273 for_each_online_cpu(i) {
274 __set_bit(processor_pminfo[i]->perf.domain_info.domain, dom_mask);
275 cpu_set(i, pt[processor_pminfo[i]->perf.domain_info.domain]);
276 }
278 for_each_online_cpu(i)
279 processor_pminfo[i]->perf.shared_cpu_map =
280 pt[processor_pminfo[i]->perf.domain_info.domain];
282 cpufreq_driver = &powernow_cpufreq_driver;
284 /* setup cpufreq infrastructure */
285 for_each_online_cpu(i) {
286 struct cpufreq_policy *policy = cpufreq_cpu_policy[i];
288 if (!policy) {
289 unsigned int firstcpu;
291 firstcpu = first_cpu(processor_pminfo[i]->perf.shared_cpu_map);
292 if (i == firstcpu) {
293 policy = xmalloc(struct cpufreq_policy);
294 if (!policy) {
295 ret = -ENOMEM;
296 goto cpufreq_init_out;
297 }
298 memset(policy, 0, sizeof(struct cpufreq_policy));
299 policy->cpu = i;
300 } else
301 policy = cpufreq_cpu_policy[firstcpu];
302 cpu_set(i, policy->cpus);
303 cpufreq_cpu_policy[i] = policy;
304 }
306 ret = powernow_cpufreq_cpu_init(policy);
307 if (ret)
308 goto cpufreq_init_out;
309 }
311 /* setup ondemand cpufreq */
312 for (dom = 0; dom < max_dom; dom++) {
313 if (!test_bit(dom, dom_mask))
314 continue;
315 i = first_cpu(pt[dom]);
316 ret = cpufreq_governor_dbs(cpufreq_cpu_policy[i], CPUFREQ_GOV_START);
317 if (ret)
318 goto cpufreq_init_out;
319 }
321 cpufreq_init_out:
322 xfree(pt);
323 xfree(dom_mask);
325 return ret;
326 }