Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1/*
  2 * arch/ia64/kernel/cpufreq/acpi-cpufreq.c
  3 * This file provides the ACPI based P-state support. This
  4 * module works with generic cpufreq infrastructure. Most of
  5 * the code is based on i386 version
  6 * (arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c)
  7 *
  8 * Copyright (C) 2005 Intel Corp
  9 *      Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
 10 */
 11
 12#include <linux/kernel.h>
 13#include <linux/slab.h>
 14#include <linux/module.h>
 15#include <linux/init.h>
 16#include <linux/cpufreq.h>
 17#include <linux/proc_fs.h>
 18#include <linux/seq_file.h>
 19#include <asm/io.h>
 20#include <asm/uaccess.h>
 21#include <asm/pal.h>
 22
 23#include <linux/acpi.h>
 24#include <acpi/processor.h>
 25
 26MODULE_AUTHOR("Venkatesh Pallipadi");
 27MODULE_DESCRIPTION("ACPI Processor P-States Driver");
 28MODULE_LICENSE("GPL");
 29
 30
 31struct cpufreq_acpi_io {
 32	struct acpi_processor_performance	acpi_data;
 33	struct cpufreq_frequency_table		*freq_table;
 34	unsigned int				resume;
 35};
 36
 37static struct cpufreq_acpi_io	*acpi_io_data[NR_CPUS];
 38
 39static struct cpufreq_driver acpi_cpufreq_driver;
 40
 41
 42static int
 43processor_set_pstate (
 44	u32	value)
 45{
 46	s64 retval;
 47
 48	pr_debug("processor_set_pstate\n");
 49
 50	retval = ia64_pal_set_pstate((u64)value);
 51
 52	if (retval) {
 53		pr_debug("Failed to set freq to 0x%x, with error 0x%lx\n",
 54		        value, retval);
 55		return -ENODEV;
 56	}
 57	return (int)retval;
 58}
 59
 60
 61static int
 62processor_get_pstate (
 63	u32	*value)
 64{
 65	u64	pstate_index = 0;
 66	s64 	retval;
 67
 68	pr_debug("processor_get_pstate\n");
 69
 70	retval = ia64_pal_get_pstate(&pstate_index,
 71	                             PAL_GET_PSTATE_TYPE_INSTANT);
 72	*value = (u32) pstate_index;
 73
 74	if (retval)
 75		pr_debug("Failed to get current freq with "
 76			"error 0x%lx, idx 0x%x\n", retval, *value);
 77
 78	return (int)retval;
 79}
 80
 81
 82/* To be used only after data->acpi_data is initialized */
 83static unsigned
 84extract_clock (
 85	struct cpufreq_acpi_io *data,
 86	unsigned value,
 87	unsigned int cpu)
 88{
 89	unsigned long i;
 90
 91	pr_debug("extract_clock\n");
 92
 93	for (i = 0; i < data->acpi_data.state_count; i++) {
 94		if (value == data->acpi_data.states[i].status)
 95			return data->acpi_data.states[i].core_frequency;
 96	}
 97	return data->acpi_data.states[i-1].core_frequency;
 98}
 99
100
101static unsigned int
102processor_get_freq (
103	struct cpufreq_acpi_io	*data,
104	unsigned int		cpu)
105{
106	int			ret = 0;
107	u32			value = 0;
108	cpumask_t		saved_mask;
109	unsigned long 		clock_freq;
110
111	pr_debug("processor_get_freq\n");
112
113	saved_mask = current->cpus_allowed;
114	set_cpus_allowed_ptr(current, cpumask_of(cpu));
115	if (smp_processor_id() != cpu)
116		goto migrate_end;
117
118	/* processor_get_pstate gets the instantaneous frequency */
119	ret = processor_get_pstate(&value);
120
121	if (ret) {
122		set_cpus_allowed_ptr(current, &saved_mask);
123		printk(KERN_WARNING "get performance failed with error %d\n",
124		       ret);
125		ret = 0;
126		goto migrate_end;
127	}
128	clock_freq = extract_clock(data, value, cpu);
129	ret = (clock_freq*1000);
130
131migrate_end:
132	set_cpus_allowed_ptr(current, &saved_mask);
133	return ret;
134}
135
136
137static int
138processor_set_freq (
139	struct cpufreq_acpi_io	*data,
140	unsigned int		cpu,
141	int			state)
142{
143	int			ret = 0;
144	u32			value = 0;
145	struct cpufreq_freqs    cpufreq_freqs;
146	cpumask_t		saved_mask;
147	int			retval;
148
149	pr_debug("processor_set_freq\n");
150
151	saved_mask = current->cpus_allowed;
152	set_cpus_allowed_ptr(current, cpumask_of(cpu));
153	if (smp_processor_id() != cpu) {
154		retval = -EAGAIN;
155		goto migrate_end;
156	}
157
158	if (state == data->acpi_data.state) {
159		if (unlikely(data->resume)) {
160			pr_debug("Called after resume, resetting to P%d\n", state);
161			data->resume = 0;
162		} else {
163			pr_debug("Already at target state (P%d)\n", state);
164			retval = 0;
165			goto migrate_end;
166		}
167	}
168
169	pr_debug("Transitioning from P%d to P%d\n",
170		data->acpi_data.state, state);
171
172	/* cpufreq frequency struct */
173	cpufreq_freqs.cpu = cpu;
174	cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency;
175	cpufreq_freqs.new = data->freq_table[state].frequency;
176
177	/* notify cpufreq */
178	cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
179
180	/*
181	 * First we write the target state's 'control' value to the
182	 * control_register.
183	 */
184
185	value = (u32) data->acpi_data.states[state].control;
186
187	pr_debug("Transitioning to state: 0x%08x\n", value);
188
189	ret = processor_set_pstate(value);
190	if (ret) {
191		unsigned int tmp = cpufreq_freqs.new;
192		cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
193		cpufreq_freqs.new = cpufreq_freqs.old;
194		cpufreq_freqs.old = tmp;
195		cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_PRECHANGE);
196		cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
197		printk(KERN_WARNING "Transition failed with error %d\n", ret);
198		retval = -ENODEV;
199		goto migrate_end;
200	}
201
202	cpufreq_notify_transition(&cpufreq_freqs, CPUFREQ_POSTCHANGE);
203
204	data->acpi_data.state = state;
205
206	retval = 0;
207
208migrate_end:
209	set_cpus_allowed_ptr(current, &saved_mask);
210	return (retval);
211}
212
213
214static unsigned int
215acpi_cpufreq_get (
216	unsigned int		cpu)
217{
218	struct cpufreq_acpi_io *data = acpi_io_data[cpu];
219
220	pr_debug("acpi_cpufreq_get\n");
221
222	return processor_get_freq(data, cpu);
223}
224
225
226static int
227acpi_cpufreq_target (
228	struct cpufreq_policy   *policy,
229	unsigned int target_freq,
230	unsigned int relation)
231{
232	struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
233	unsigned int next_state = 0;
234	unsigned int result = 0;
235
236	pr_debug("acpi_cpufreq_setpolicy\n");
237
238	result = cpufreq_frequency_table_target(policy,
239			data->freq_table, target_freq, relation, &next_state);
240	if (result)
241		return (result);
242
243	result = processor_set_freq(data, policy->cpu, next_state);
244
245	return (result);
246}
247
248
249static int
250acpi_cpufreq_verify (
251	struct cpufreq_policy   *policy)
252{
253	unsigned int result = 0;
254	struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
255
256	pr_debug("acpi_cpufreq_verify\n");
257
258	result = cpufreq_frequency_table_verify(policy,
259			data->freq_table);
260
261	return (result);
262}
263
264
265static int
266acpi_cpufreq_cpu_init (
267	struct cpufreq_policy   *policy)
268{
269	unsigned int		i;
270	unsigned int		cpu = policy->cpu;
271	struct cpufreq_acpi_io	*data;
272	unsigned int		result = 0;
273
274	pr_debug("acpi_cpufreq_cpu_init\n");
275
276	data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
277	if (!data)
278		return (-ENOMEM);
279
280	acpi_io_data[cpu] = data;
281
282	result = acpi_processor_register_performance(&data->acpi_data, cpu);
283
284	if (result)
285		goto err_free;
286
287	/* capability check */
288	if (data->acpi_data.state_count <= 1) {
289		pr_debug("No P-States\n");
290		result = -ENODEV;
291		goto err_unreg;
292	}
293
294	if ((data->acpi_data.control_register.space_id !=
295					ACPI_ADR_SPACE_FIXED_HARDWARE) ||
296	    (data->acpi_data.status_register.space_id !=
297					ACPI_ADR_SPACE_FIXED_HARDWARE)) {
298		pr_debug("Unsupported address space [%d, %d]\n",
299			(u32) (data->acpi_data.control_register.space_id),
300			(u32) (data->acpi_data.status_register.space_id));
301		result = -ENODEV;
302		goto err_unreg;
303	}
304
305	/* alloc freq_table */
306	data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
307	                           (data->acpi_data.state_count + 1),
308	                           GFP_KERNEL);
309	if (!data->freq_table) {
310		result = -ENOMEM;
311		goto err_unreg;
312	}
313
314	/* detect transition latency */
315	policy->cpuinfo.transition_latency = 0;
316	for (i=0; i<data->acpi_data.state_count; i++) {
317		if ((data->acpi_data.states[i].transition_latency * 1000) >
318		    policy->cpuinfo.transition_latency) {
319			policy->cpuinfo.transition_latency =
320			    data->acpi_data.states[i].transition_latency * 1000;
321		}
322	}
323	policy->cur = processor_get_freq(data, policy->cpu);
324
325	/* table init */
326	for (i = 0; i <= data->acpi_data.state_count; i++)
327	{
328		data->freq_table[i].index = i;
329		if (i < data->acpi_data.state_count) {
330			data->freq_table[i].frequency =
331			      data->acpi_data.states[i].core_frequency * 1000;
332		} else {
333			data->freq_table[i].frequency = CPUFREQ_TABLE_END;
334		}
335	}
336
337	result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
338	if (result) {
339		goto err_freqfree;
340	}
341
342	/* notify BIOS that we exist */
343	acpi_processor_notify_smm(THIS_MODULE);
344
345	printk(KERN_INFO "acpi-cpufreq: CPU%u - ACPI performance management "
346	       "activated.\n", cpu);
347
348	for (i = 0; i < data->acpi_data.state_count; i++)
349		pr_debug("     %cP%d: %d MHz, %d mW, %d uS, %d uS, 0x%x 0x%x\n",
350			(i == data->acpi_data.state?'*':' '), i,
351			(u32) data->acpi_data.states[i].core_frequency,
352			(u32) data->acpi_data.states[i].power,
353			(u32) data->acpi_data.states[i].transition_latency,
354			(u32) data->acpi_data.states[i].bus_master_latency,
355			(u32) data->acpi_data.states[i].status,
356			(u32) data->acpi_data.states[i].control);
357
358	cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu);
359
360	/* the first call to ->target() should result in us actually
361	 * writing something to the appropriate registers. */
362	data->resume = 1;
363
364	return (result);
365
366 err_freqfree:
367	kfree(data->freq_table);
368 err_unreg:
369	acpi_processor_unregister_performance(&data->acpi_data, cpu);
370 err_free:
371	kfree(data);
372	acpi_io_data[cpu] = NULL;
373
374	return (result);
375}
376
377
378static int
379acpi_cpufreq_cpu_exit (
380	struct cpufreq_policy   *policy)
381{
382	struct cpufreq_acpi_io *data = acpi_io_data[policy->cpu];
383
384	pr_debug("acpi_cpufreq_cpu_exit\n");
385
386	if (data) {
387		cpufreq_frequency_table_put_attr(policy->cpu);
388		acpi_io_data[policy->cpu] = NULL;
389		acpi_processor_unregister_performance(&data->acpi_data,
390		                                      policy->cpu);
391		kfree(data);
392	}
393
394	return (0);
395}
396
397
398static struct freq_attr* acpi_cpufreq_attr[] = {
399	&cpufreq_freq_attr_scaling_available_freqs,
400	NULL,
401};
402
403
404static struct cpufreq_driver acpi_cpufreq_driver = {
405	.verify 	= acpi_cpufreq_verify,
406	.target 	= acpi_cpufreq_target,
407	.get 		= acpi_cpufreq_get,
408	.init		= acpi_cpufreq_cpu_init,
409	.exit		= acpi_cpufreq_cpu_exit,
410	.name		= "acpi-cpufreq",
411	.owner		= THIS_MODULE,
412	.attr           = acpi_cpufreq_attr,
413};
414
415
416static int __init
417acpi_cpufreq_init (void)
418{
419	pr_debug("acpi_cpufreq_init\n");
420
421 	return cpufreq_register_driver(&acpi_cpufreq_driver);
422}
423
424
425static void __exit
426acpi_cpufreq_exit (void)
427{
428	pr_debug("acpi_cpufreq_exit\n");
429
430	cpufreq_unregister_driver(&acpi_cpufreq_driver);
431	return;
432}
433
434
435late_initcall(acpi_cpufreq_init);
436module_exit(acpi_cpufreq_exit);
437