/root/src/xen/xen/drivers/cpufreq/cpufreq.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
3 | | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
4 | | * Copyright (C) 2002 - 2004 Dominik Brodowski <linux@brodo.de> |
5 | | * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com> |
6 | | * |
7 | | * Feb 2008 - Liu Jinsong <jinsong.liu@intel.com> |
8 | | * Add cpufreq limit change handle and per-cpu cpufreq add/del |
9 | | * to cope with cpu hotplug |
10 | | * |
11 | | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
12 | | * |
13 | | * This program is free software; you can redistribute it and/or modify |
14 | | * it under the terms of the GNU General Public License as published by |
15 | | * the Free Software Foundation; either version 2 of the License, or (at |
16 | | * your option) any later version. |
17 | | * |
18 | | * This program is distributed in the hope that it will be useful, but |
19 | | * WITHOUT ANY WARRANTY; without even the implied warranty of |
20 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
21 | | * General Public License for more details. |
22 | | * |
23 | | * You should have received a copy of the GNU General Public License along |
24 | | * with this program; If not, see <http://www.gnu.org/licenses/>. |
25 | | * |
26 | | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
27 | | */ |
28 | | |
29 | | #include <xen/types.h> |
30 | | #include <xen/errno.h> |
31 | | #include <xen/delay.h> |
32 | | #include <xen/cpumask.h> |
33 | | #include <xen/list.h> |
34 | | #include <xen/sched.h> |
35 | | #include <xen/string.h> |
36 | | #include <xen/timer.h> |
37 | | #include <xen/xmalloc.h> |
38 | | #include <xen/guest_access.h> |
39 | | #include <xen/domain.h> |
40 | | #include <xen/cpu.h> |
41 | | #include <asm/bug.h> |
42 | | #include <asm/io.h> |
43 | | #include <asm/processor.h> |
44 | | #include <asm/percpu.h> |
45 | | #include <acpi/acpi.h> |
46 | | #include <acpi/cpufreq/cpufreq.h> |
47 | | |
48 | | static unsigned int __read_mostly usr_min_freq; |
49 | | static unsigned int __read_mostly usr_max_freq; |
50 | | static void cpufreq_cmdline_common_para(struct cpufreq_policy *new_policy); |
51 | | |
52 | | struct cpufreq_dom { |
53 | | unsigned int dom; |
54 | | cpumask_var_t map; |
55 | | struct list_head node; |
56 | | }; |
57 | | static LIST_HEAD_READ_MOSTLY(cpufreq_dom_list_head); |
58 | | |
59 | | struct cpufreq_governor *__read_mostly cpufreq_opt_governor; |
60 | | LIST_HEAD_READ_MOSTLY(cpufreq_governor_list); |
61 | | |
62 | | /* set xen as default cpufreq */ |
63 | | enum cpufreq_controller cpufreq_controller = FREQCTL_xen; |
64 | | |
65 | | static int __init cpufreq_cmdline_parse(const char *s); |
66 | | |
67 | | static int __init setup_cpufreq_option(const char *str) |
68 | 0 | { |
69 | 0 | const char *arg = strpbrk(str, ",:"); |
70 | 0 | int choice; |
71 | 0 |
|
72 | 0 | if ( !arg ) |
73 | 0 | arg = strchr(str, '\0'); |
74 | 0 | choice = parse_bool(str, arg); |
75 | 0 |
|
76 | 0 | if ( choice < 0 && !strncmp(str, "dom0-kernel", arg - str) ) |
77 | 0 | { |
78 | 0 | xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; |
79 | 0 | cpufreq_controller = FREQCTL_dom0_kernel; |
80 | 0 | opt_dom0_vcpus_pin = 1; |
81 | 0 | return 0; |
82 | 0 | } |
83 | 0 |
|
84 | 0 | if ( choice == 0 || !strncmp(str, "none", arg - str) ) |
85 | 0 | { |
86 | 0 | xen_processor_pmbits &= ~XEN_PROCESSOR_PM_PX; |
87 | 0 | cpufreq_controller = FREQCTL_none; |
88 | 0 | return 0; |
89 | 0 | } |
90 | 0 |
|
91 | 0 | if ( choice > 0 || !strncmp(str, "xen", arg - str) ) |
92 | 0 | { |
93 | 0 | xen_processor_pmbits |= XEN_PROCESSOR_PM_PX; |
94 | 0 | cpufreq_controller = FREQCTL_xen; |
95 | 0 | if ( *arg && *(arg + 1) ) |
96 | 0 | return cpufreq_cmdline_parse(arg + 1); |
97 | 0 | } |
98 | 0 |
|
99 | 0 | return (choice < 0) ? -EINVAL : 0; |
100 | 0 | } |
101 | | custom_param("cpufreq", setup_cpufreq_option); |
102 | | |
103 | | bool_t __read_mostly cpufreq_verbose; |
104 | | |
105 | | struct cpufreq_governor *__find_governor(const char *governor) |
106 | 4 | { |
107 | 4 | struct cpufreq_governor *t; |
108 | 4 | |
109 | 4 | if (!governor) |
110 | 0 | return NULL; |
111 | 4 | |
112 | 4 | list_for_each_entry(t, &cpufreq_governor_list, governor_list) |
113 | 6 | if (!strnicmp(governor, t->name, CPUFREQ_NAME_LEN)) |
114 | 0 | return t; |
115 | 4 | |
116 | 4 | return NULL; |
117 | 4 | } |
118 | | |
119 | | int __init cpufreq_register_governor(struct cpufreq_governor *governor) |
120 | 4 | { |
121 | 4 | if (!governor) |
122 | 0 | return -EINVAL; |
123 | 4 | |
124 | 4 | if (__find_governor(governor->name) != NULL) |
125 | 0 | return -EEXIST; |
126 | 4 | |
127 | 4 | list_add(&governor->governor_list, &cpufreq_governor_list); |
128 | 4 | return 0; |
129 | 4 | } |
130 | | |
131 | | int cpufreq_limit_change(unsigned int cpu) |
132 | 0 | { |
133 | 0 | struct processor_performance *perf; |
134 | 0 | struct cpufreq_policy *data; |
135 | 0 | struct cpufreq_policy policy; |
136 | 0 |
|
137 | 0 | if (!cpu_online(cpu) || !(data = per_cpu(cpufreq_cpu_policy, cpu)) || |
138 | 0 | !processor_pminfo[cpu]) |
139 | 0 | return -ENODEV; |
140 | 0 |
|
141 | 0 | perf = &processor_pminfo[cpu]->perf; |
142 | 0 |
|
143 | 0 | if (perf->platform_limit >= perf->state_count) |
144 | 0 | return -EINVAL; |
145 | 0 |
|
146 | 0 | memcpy(&policy, data, sizeof(struct cpufreq_policy)); |
147 | 0 |
|
148 | 0 | policy.max = |
149 | 0 | perf->states[perf->platform_limit].core_frequency * 1000; |
150 | 0 |
|
151 | 0 | return __cpufreq_set_policy(data, &policy); |
152 | 0 | } |
153 | | |
154 | | int cpufreq_add_cpu(unsigned int cpu) |
155 | 11 | { |
156 | 11 | int ret = 0; |
157 | 11 | unsigned int firstcpu; |
158 | 11 | unsigned int dom, domexist = 0; |
159 | 11 | unsigned int hw_all = 0; |
160 | 11 | struct list_head *pos; |
161 | 11 | struct cpufreq_dom *cpufreq_dom = NULL; |
162 | 11 | struct cpufreq_policy new_policy; |
163 | 11 | struct cpufreq_policy *policy; |
164 | 11 | struct processor_performance *perf; |
165 | 11 | |
166 | 11 | /* to protect the case when Px was not controlled by xen */ |
167 | 11 | if ( !processor_pminfo[cpu] || !cpu_online(cpu) ) |
168 | 11 | return -EINVAL; |
169 | 11 | |
170 | 0 | perf = &processor_pminfo[cpu]->perf; |
171 | 0 |
|
172 | 0 | if ( !(perf->init & XEN_PX_INIT) ) |
173 | 0 | return -EINVAL; |
174 | 0 |
|
175 | 0 | if (!cpufreq_driver) |
176 | 0 | return 0; |
177 | 0 |
|
178 | 0 | if (per_cpu(cpufreq_cpu_policy, cpu)) |
179 | 0 | return 0; |
180 | 0 |
|
181 | 0 | if (perf->shared_type == CPUFREQ_SHARED_TYPE_HW) |
182 | 0 | hw_all = 1; |
183 | 0 |
|
184 | 0 | dom = perf->domain_info.domain; |
185 | 0 |
|
186 | 0 | list_for_each(pos, &cpufreq_dom_list_head) { |
187 | 0 | cpufreq_dom = list_entry(pos, struct cpufreq_dom, node); |
188 | 0 | if (dom == cpufreq_dom->dom) { |
189 | 0 | domexist = 1; |
190 | 0 | break; |
191 | 0 | } |
192 | 0 | } |
193 | 0 |
|
194 | 0 | if (!domexist) { |
195 | 0 | cpufreq_dom = xzalloc(struct cpufreq_dom); |
196 | 0 | if (!cpufreq_dom) |
197 | 0 | return -ENOMEM; |
198 | 0 |
|
199 | 0 | if (!zalloc_cpumask_var(&cpufreq_dom->map)) { |
200 | 0 | xfree(cpufreq_dom); |
201 | 0 | return -ENOMEM; |
202 | 0 | } |
203 | 0 |
|
204 | 0 | cpufreq_dom->dom = dom; |
205 | 0 | list_add(&cpufreq_dom->node, &cpufreq_dom_list_head); |
206 | 0 | } else { |
207 | 0 | /* domain sanity check under whatever coordination type */ |
208 | 0 | firstcpu = cpumask_first(cpufreq_dom->map); |
209 | 0 | if ((perf->domain_info.coord_type != |
210 | 0 | processor_pminfo[firstcpu]->perf.domain_info.coord_type) || |
211 | 0 | (perf->domain_info.num_processors != |
212 | 0 | processor_pminfo[firstcpu]->perf.domain_info.num_processors)) { |
213 | 0 |
|
214 | 0 | printk(KERN_WARNING "cpufreq fail to add CPU%d:" |
215 | 0 | "incorrect _PSD(%"PRIu64":%"PRIu64"), " |
216 | 0 | "expect(%"PRIu64"/%"PRIu64")\n", |
217 | 0 | cpu, perf->domain_info.coord_type, |
218 | 0 | perf->domain_info.num_processors, |
219 | 0 | processor_pminfo[firstcpu]->perf.domain_info.coord_type, |
220 | 0 | processor_pminfo[firstcpu]->perf.domain_info.num_processors |
221 | 0 | ); |
222 | 0 | return -EINVAL; |
223 | 0 | } |
224 | 0 | } |
225 | 0 |
|
226 | 0 | if (!domexist || hw_all) { |
227 | 0 | policy = xzalloc(struct cpufreq_policy); |
228 | 0 | if (!policy) { |
229 | 0 | ret = -ENOMEM; |
230 | 0 | goto err0; |
231 | 0 | } |
232 | 0 |
|
233 | 0 | if (!zalloc_cpumask_var(&policy->cpus)) { |
234 | 0 | xfree(policy); |
235 | 0 | ret = -ENOMEM; |
236 | 0 | goto err0; |
237 | 0 | } |
238 | 0 |
|
239 | 0 | policy->cpu = cpu; |
240 | 0 | per_cpu(cpufreq_cpu_policy, cpu) = policy; |
241 | 0 |
|
242 | 0 | ret = cpufreq_driver->init(policy); |
243 | 0 | if (ret) { |
244 | 0 | free_cpumask_var(policy->cpus); |
245 | 0 | xfree(policy); |
246 | 0 | per_cpu(cpufreq_cpu_policy, cpu) = NULL; |
247 | 0 | goto err0; |
248 | 0 | } |
249 | 0 | if (cpufreq_verbose) |
250 | 0 | printk("CPU %u initialization completed\n", cpu); |
251 | 0 | } else { |
252 | 0 | firstcpu = cpumask_first(cpufreq_dom->map); |
253 | 0 | policy = per_cpu(cpufreq_cpu_policy, firstcpu); |
254 | 0 |
|
255 | 0 | per_cpu(cpufreq_cpu_policy, cpu) = policy; |
256 | 0 | if (cpufreq_verbose) |
257 | 0 | printk("adding CPU %u\n", cpu); |
258 | 0 | } |
259 | 0 |
|
260 | 0 | cpumask_set_cpu(cpu, policy->cpus); |
261 | 0 | cpumask_set_cpu(cpu, cpufreq_dom->map); |
262 | 0 |
|
263 | 0 | ret = cpufreq_statistic_init(cpu); |
264 | 0 | if (ret) |
265 | 0 | goto err1; |
266 | 0 |
|
267 | 0 | if (hw_all || (cpumask_weight(cpufreq_dom->map) == |
268 | 0 | perf->domain_info.num_processors)) { |
269 | 0 | memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); |
270 | 0 | policy->governor = NULL; |
271 | 0 |
|
272 | 0 | cpufreq_cmdline_common_para(&new_policy); |
273 | 0 |
|
274 | 0 | ret = __cpufreq_set_policy(policy, &new_policy); |
275 | 0 | if (ret) { |
276 | 0 | if (new_policy.governor == CPUFREQ_DEFAULT_GOVERNOR) |
277 | 0 | /* if default governor fail, cpufreq really meet troubles */ |
278 | 0 | goto err2; |
279 | 0 | else { |
280 | 0 | /* grub option governor fail */ |
281 | 0 | /* give one more chance to default gov */ |
282 | 0 | memcpy(&new_policy, policy, sizeof(struct cpufreq_policy)); |
283 | 0 | new_policy.governor = CPUFREQ_DEFAULT_GOVERNOR; |
284 | 0 | ret = __cpufreq_set_policy(policy, &new_policy); |
285 | 0 | if (ret) |
286 | 0 | goto err2; |
287 | 0 | } |
288 | 0 | } |
289 | 0 | } |
290 | 0 |
|
291 | 0 | return 0; |
292 | 0 |
|
293 | 0 | err2: |
294 | 0 | cpufreq_statistic_exit(cpu); |
295 | 0 | err1: |
296 | 0 | per_cpu(cpufreq_cpu_policy, cpu) = NULL; |
297 | 0 | cpumask_clear_cpu(cpu, policy->cpus); |
298 | 0 | cpumask_clear_cpu(cpu, cpufreq_dom->map); |
299 | 0 |
|
300 | 0 | if (cpumask_empty(policy->cpus)) { |
301 | 0 | cpufreq_driver->exit(policy); |
302 | 0 | free_cpumask_var(policy->cpus); |
303 | 0 | xfree(policy); |
304 | 0 | } |
305 | 0 | err0: |
306 | 0 | if (cpumask_empty(cpufreq_dom->map)) { |
307 | 0 | list_del(&cpufreq_dom->node); |
308 | 0 | free_cpumask_var(cpufreq_dom->map); |
309 | 0 | xfree(cpufreq_dom); |
310 | 0 | } |
311 | 0 |
|
312 | 0 | return ret; |
313 | 0 | } |
314 | | |
315 | | int cpufreq_del_cpu(unsigned int cpu) |
316 | 0 | { |
317 | 0 | unsigned int dom, domexist = 0; |
318 | 0 | unsigned int hw_all = 0; |
319 | 0 | struct list_head *pos; |
320 | 0 | struct cpufreq_dom *cpufreq_dom = NULL; |
321 | 0 | struct cpufreq_policy *policy; |
322 | 0 | struct processor_performance *perf; |
323 | 0 |
|
324 | 0 | /* to protect the case when Px was not controlled by xen */ |
325 | 0 | if ( !processor_pminfo[cpu] || !cpu_online(cpu) ) |
326 | 0 | return -EINVAL; |
327 | 0 |
|
328 | 0 | perf = &processor_pminfo[cpu]->perf; |
329 | 0 |
|
330 | 0 | if ( !(perf->init & XEN_PX_INIT) ) |
331 | 0 | return -EINVAL; |
332 | 0 |
|
333 | 0 | if (!per_cpu(cpufreq_cpu_policy, cpu)) |
334 | 0 | return 0; |
335 | 0 |
|
336 | 0 | if (perf->shared_type == CPUFREQ_SHARED_TYPE_HW) |
337 | 0 | hw_all = 1; |
338 | 0 |
|
339 | 0 | dom = perf->domain_info.domain; |
340 | 0 | policy = per_cpu(cpufreq_cpu_policy, cpu); |
341 | 0 |
|
342 | 0 | list_for_each(pos, &cpufreq_dom_list_head) { |
343 | 0 | cpufreq_dom = list_entry(pos, struct cpufreq_dom, node); |
344 | 0 | if (dom == cpufreq_dom->dom) { |
345 | 0 | domexist = 1; |
346 | 0 | break; |
347 | 0 | } |
348 | 0 | } |
349 | 0 |
|
350 | 0 | if (!domexist) |
351 | 0 | return -EINVAL; |
352 | 0 |
|
353 | 0 | /* for HW_ALL, stop gov for each core of the _PSD domain */ |
354 | 0 | /* for SW_ALL & SW_ANY, stop gov for the 1st core of the _PSD domain */ |
355 | 0 | if (hw_all || (cpumask_weight(cpufreq_dom->map) == |
356 | 0 | perf->domain_info.num_processors)) |
357 | 0 | __cpufreq_governor(policy, CPUFREQ_GOV_STOP); |
358 | 0 |
|
359 | 0 | cpufreq_statistic_exit(cpu); |
360 | 0 | per_cpu(cpufreq_cpu_policy, cpu) = NULL; |
361 | 0 | cpumask_clear_cpu(cpu, policy->cpus); |
362 | 0 | cpumask_clear_cpu(cpu, cpufreq_dom->map); |
363 | 0 |
|
364 | 0 | if (cpumask_empty(policy->cpus)) { |
365 | 0 | cpufreq_driver->exit(policy); |
366 | 0 | free_cpumask_var(policy->cpus); |
367 | 0 | xfree(policy); |
368 | 0 | } |
369 | 0 |
|
370 | 0 | /* for the last cpu of the domain, clean room */ |
371 | 0 | /* It's safe here to free freq_table, drv_data and policy */ |
372 | 0 | if (cpumask_empty(cpufreq_dom->map)) { |
373 | 0 | list_del(&cpufreq_dom->node); |
374 | 0 | free_cpumask_var(cpufreq_dom->map); |
375 | 0 | xfree(cpufreq_dom); |
376 | 0 | } |
377 | 0 |
|
378 | 0 | if (cpufreq_verbose) |
379 | 0 | printk("deleting CPU %u\n", cpu); |
380 | 0 | return 0; |
381 | 0 | } |
382 | | |
383 | | static void print_PCT(struct xen_pct_register *ptr) |
384 | 0 | { |
385 | 0 | printk("\t_PCT: descriptor=%d, length=%d, space_id=%d, " |
386 | 0 | "bit_width=%d, bit_offset=%d, reserved=%d, address=%"PRId64"\n", |
387 | 0 | ptr->descriptor, ptr->length, ptr->space_id, ptr->bit_width, |
388 | 0 | ptr->bit_offset, ptr->reserved, ptr->address); |
389 | 0 | } |
390 | | |
391 | | static void print_PSS(struct xen_processor_px *ptr, int count) |
392 | 0 | { |
393 | 0 | int i; |
394 | 0 | printk("\t_PSS: state_count=%d\n", count); |
395 | 0 | for (i=0; i<count; i++){ |
396 | 0 | printk("\tState%d: %"PRId64"MHz %"PRId64"mW %"PRId64"us " |
397 | 0 | "%"PRId64"us %#"PRIx64" %#"PRIx64"\n", |
398 | 0 | i, |
399 | 0 | ptr[i].core_frequency, |
400 | 0 | ptr[i].power, |
401 | 0 | ptr[i].transition_latency, |
402 | 0 | ptr[i].bus_master_latency, |
403 | 0 | ptr[i].control, |
404 | 0 | ptr[i].status); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | static void print_PSD( struct xen_psd_package *ptr) |
409 | 0 | { |
410 | 0 | printk("\t_PSD: num_entries=%"PRId64" rev=%"PRId64 |
411 | 0 | " domain=%"PRId64" coord_type=%"PRId64" num_processors=%"PRId64"\n", |
412 | 0 | ptr->num_entries, ptr->revision, ptr->domain, ptr->coord_type, |
413 | 0 | ptr->num_processors); |
414 | 0 | } |
415 | | |
416 | | static void print_PPC(unsigned int platform_limit) |
417 | 0 | { |
418 | 0 | printk("\t_PPC: %d\n", platform_limit); |
419 | 0 | } |
420 | | |
421 | | int set_px_pminfo(uint32_t acpi_id, struct xen_processor_performance *dom0_px_info) |
422 | 0 | { |
423 | 0 | int ret=0, cpuid; |
424 | 0 | struct processor_pminfo *pmpt; |
425 | 0 | struct processor_performance *pxpt; |
426 | 0 |
|
427 | 0 | cpuid = get_cpu_id(acpi_id); |
428 | 0 | if ( cpuid < 0 || !dom0_px_info) |
429 | 0 | { |
430 | 0 | ret = -EINVAL; |
431 | 0 | goto out; |
432 | 0 | } |
433 | 0 | if ( cpufreq_verbose ) |
434 | 0 | printk("Set CPU acpi_id(%d) cpuid(%d) Px State info:\n", |
435 | 0 | acpi_id, cpuid); |
436 | 0 |
|
437 | 0 | pmpt = processor_pminfo[cpuid]; |
438 | 0 | if ( !pmpt ) |
439 | 0 | { |
440 | 0 | pmpt = xzalloc(struct processor_pminfo); |
441 | 0 | if ( !pmpt ) |
442 | 0 | { |
443 | 0 | ret = -ENOMEM; |
444 | 0 | goto out; |
445 | 0 | } |
446 | 0 | processor_pminfo[cpuid] = pmpt; |
447 | 0 | } |
448 | 0 | pxpt = &pmpt->perf; |
449 | 0 | pmpt->acpi_id = acpi_id; |
450 | 0 | pmpt->id = cpuid; |
451 | 0 |
|
452 | 0 | if ( dom0_px_info->flags & XEN_PX_PCT ) |
453 | 0 | { |
454 | 0 | /* space_id check */ |
455 | 0 | if (dom0_px_info->control_register.space_id != |
456 | 0 | dom0_px_info->status_register.space_id) |
457 | 0 | { |
458 | 0 | ret = -EINVAL; |
459 | 0 | goto out; |
460 | 0 | } |
461 | 0 |
|
462 | 0 | memcpy ((void *)&pxpt->control_register, |
463 | 0 | (void *)&dom0_px_info->control_register, |
464 | 0 | sizeof(struct xen_pct_register)); |
465 | 0 | memcpy ((void *)&pxpt->status_register, |
466 | 0 | (void *)&dom0_px_info->status_register, |
467 | 0 | sizeof(struct xen_pct_register)); |
468 | 0 |
|
469 | 0 | if ( cpufreq_verbose ) |
470 | 0 | { |
471 | 0 | print_PCT(&pxpt->control_register); |
472 | 0 | print_PCT(&pxpt->status_register); |
473 | 0 | } |
474 | 0 | } |
475 | 0 |
|
476 | 0 | if ( dom0_px_info->flags & XEN_PX_PSS ) |
477 | 0 | { |
478 | 0 | /* capability check */ |
479 | 0 | if (dom0_px_info->state_count <= 1) |
480 | 0 | { |
481 | 0 | ret = -EINVAL; |
482 | 0 | goto out; |
483 | 0 | } |
484 | 0 |
|
485 | 0 | if ( !(pxpt->states = xmalloc_array(struct xen_processor_px, |
486 | 0 | dom0_px_info->state_count)) ) |
487 | 0 | { |
488 | 0 | ret = -ENOMEM; |
489 | 0 | goto out; |
490 | 0 | } |
491 | 0 | if ( copy_from_guest(pxpt->states, dom0_px_info->states, |
492 | 0 | dom0_px_info->state_count) ) |
493 | 0 | { |
494 | 0 | ret = -EFAULT; |
495 | 0 | goto out; |
496 | 0 | } |
497 | 0 | pxpt->state_count = dom0_px_info->state_count; |
498 | 0 |
|
499 | 0 | if ( cpufreq_verbose ) |
500 | 0 | print_PSS(pxpt->states,pxpt->state_count); |
501 | 0 | } |
502 | 0 |
|
503 | 0 | if ( dom0_px_info->flags & XEN_PX_PSD ) |
504 | 0 | { |
505 | 0 | /* check domain coordination */ |
506 | 0 | if (dom0_px_info->shared_type != CPUFREQ_SHARED_TYPE_ALL && |
507 | 0 | dom0_px_info->shared_type != CPUFREQ_SHARED_TYPE_ANY && |
508 | 0 | dom0_px_info->shared_type != CPUFREQ_SHARED_TYPE_HW) |
509 | 0 | { |
510 | 0 | ret = -EINVAL; |
511 | 0 | goto out; |
512 | 0 | } |
513 | 0 |
|
514 | 0 | pxpt->shared_type = dom0_px_info->shared_type; |
515 | 0 | memcpy ((void *)&pxpt->domain_info, |
516 | 0 | (void *)&dom0_px_info->domain_info, |
517 | 0 | sizeof(struct xen_psd_package)); |
518 | 0 |
|
519 | 0 | if ( cpufreq_verbose ) |
520 | 0 | print_PSD(&pxpt->domain_info); |
521 | 0 | } |
522 | 0 |
|
523 | 0 | if ( dom0_px_info->flags & XEN_PX_PPC ) |
524 | 0 | { |
525 | 0 | pxpt->platform_limit = dom0_px_info->platform_limit; |
526 | 0 |
|
527 | 0 | if ( cpufreq_verbose ) |
528 | 0 | print_PPC(pxpt->platform_limit); |
529 | 0 |
|
530 | 0 | if ( pxpt->init == XEN_PX_INIT ) |
531 | 0 | { |
532 | 0 | ret = cpufreq_limit_change(cpuid); |
533 | 0 | goto out; |
534 | 0 | } |
535 | 0 | } |
536 | 0 |
|
537 | 0 | if ( dom0_px_info->flags == ( XEN_PX_PCT | XEN_PX_PSS | |
538 | 0 | XEN_PX_PSD | XEN_PX_PPC ) ) |
539 | 0 | { |
540 | 0 | pxpt->init = XEN_PX_INIT; |
541 | 0 |
|
542 | 0 | ret = cpufreq_cpu_init(cpuid); |
543 | 0 | goto out; |
544 | 0 | } |
545 | 0 |
|
546 | 0 | out: |
547 | 0 | return ret; |
548 | 0 | } |
549 | | |
550 | | static void cpufreq_cmdline_common_para(struct cpufreq_policy *new_policy) |
551 | 0 | { |
552 | 0 | if (usr_max_freq) |
553 | 0 | new_policy->max = usr_max_freq; |
554 | 0 | if (usr_min_freq) |
555 | 0 | new_policy->min = usr_min_freq; |
556 | 0 | } |
557 | | |
558 | | static int __init cpufreq_handle_common_option(const char *name, const char *val) |
559 | 0 | { |
560 | 0 | if (!strcmp(name, "maxfreq") && val) { |
561 | 0 | usr_max_freq = simple_strtoul(val, NULL, 0); |
562 | 0 | return 1; |
563 | 0 | } |
564 | 0 |
|
565 | 0 | if (!strcmp(name, "minfreq") && val) { |
566 | 0 | usr_min_freq = simple_strtoul(val, NULL, 0); |
567 | 0 | return 1; |
568 | 0 | } |
569 | 0 |
|
570 | 0 | if (!strcmp(name, "verbose")) { |
571 | 0 | cpufreq_verbose = !val || !!simple_strtoul(val, NULL, 0); |
572 | 0 | return 1; |
573 | 0 | } |
574 | 0 |
|
575 | 0 | return 0; |
576 | 0 | } |
577 | | |
578 | | static int __init cpufreq_cmdline_parse(const char *s) |
579 | 0 | { |
580 | 0 | static struct cpufreq_governor *__initdata cpufreq_governors[] = |
581 | 0 | { |
582 | 0 | CPUFREQ_DEFAULT_GOVERNOR, |
583 | 0 | &cpufreq_gov_userspace, |
584 | 0 | &cpufreq_gov_dbs, |
585 | 0 | &cpufreq_gov_performance, |
586 | 0 | &cpufreq_gov_powersave |
587 | 0 | }; |
588 | 0 | static char __initdata buf[128]; |
589 | 0 | char *str = buf; |
590 | 0 | unsigned int gov_index = 0; |
591 | 0 | int rc = 0; |
592 | 0 |
|
593 | 0 | strlcpy(buf, s, sizeof(buf)); |
594 | 0 | do { |
595 | 0 | char *val, *end = strchr(str, ','); |
596 | 0 | unsigned int i; |
597 | 0 |
|
598 | 0 | if (end) |
599 | 0 | *end++ = '\0'; |
600 | 0 | val = strchr(str, '='); |
601 | 0 | if (val) |
602 | 0 | *val++ = '\0'; |
603 | 0 |
|
604 | 0 | if (!cpufreq_opt_governor) { |
605 | 0 | if (!val) { |
606 | 0 | for (i = 0; i < ARRAY_SIZE(cpufreq_governors); ++i) { |
607 | 0 | if (!strcmp(str, cpufreq_governors[i]->name)) { |
608 | 0 | cpufreq_opt_governor = cpufreq_governors[i]; |
609 | 0 | gov_index = i; |
610 | 0 | str = NULL; |
611 | 0 | break; |
612 | 0 | } |
613 | 0 | } |
614 | 0 | } else { |
615 | 0 | cpufreq_opt_governor = CPUFREQ_DEFAULT_GOVERNOR; |
616 | 0 | } |
617 | 0 | } |
618 | 0 |
|
619 | 0 | if (str && !cpufreq_handle_common_option(str, val) && |
620 | 0 | (!cpufreq_governors[gov_index]->handle_option || |
621 | 0 | !cpufreq_governors[gov_index]->handle_option(str, val))) |
622 | 0 | { |
623 | 0 | printk(XENLOG_WARNING "cpufreq/%s: option '%s' not recognized\n", |
624 | 0 | cpufreq_governors[gov_index]->name, str); |
625 | 0 | rc = -EINVAL; |
626 | 0 | } |
627 | 0 |
|
628 | 0 | str = end; |
629 | 0 | } while (str); |
630 | 0 |
|
631 | 0 | return rc; |
632 | 0 | } |
633 | | |
634 | | static int cpu_callback( |
635 | | struct notifier_block *nfb, unsigned long action, void *hcpu) |
636 | 33 | { |
637 | 33 | unsigned int cpu = (unsigned long)hcpu; |
638 | 33 | |
639 | 33 | switch ( action ) |
640 | 33 | { |
641 | 11 | case CPU_DOWN_FAILED: |
642 | 11 | case CPU_ONLINE: |
643 | 11 | (void)cpufreq_add_cpu(cpu); |
644 | 11 | break; |
645 | 0 | case CPU_DOWN_PREPARE: |
646 | 0 | (void)cpufreq_del_cpu(cpu); |
647 | 0 | break; |
648 | 22 | default: |
649 | 22 | break; |
650 | 33 | } |
651 | 33 | |
652 | 33 | return NOTIFY_DONE; |
653 | 33 | } |
654 | | |
655 | | static struct notifier_block cpu_nfb = { |
656 | | .notifier_call = cpu_callback |
657 | | }; |
658 | | |
659 | | static int __init cpufreq_presmp_init(void) |
660 | 1 | { |
661 | 1 | register_cpu_notifier(&cpu_nfb); |
662 | 1 | return 0; |
663 | 1 | } |
664 | | presmp_initcall(cpufreq_presmp_init); |
665 | | |
666 | | int __init cpufreq_register_driver(struct cpufreq_driver *driver_data) |
667 | 1 | { |
668 | 1 | if ( !driver_data || !driver_data->init || |
669 | 1 | !driver_data->verify || !driver_data->exit || |
670 | 1 | (!driver_data->target == !driver_data->setpolicy) ) |
671 | 0 | return -EINVAL; |
672 | 1 | |
673 | 1 | if ( cpufreq_driver ) |
674 | 0 | return -EBUSY; |
675 | 1 | |
676 | 1 | cpufreq_driver = driver_data; |
677 | 1 | |
678 | 1 | return 0; |
679 | 1 | } |