ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / cpufreq / cpufreq_userspace.c
1 /*
2  *  linux/drivers/cpufreq/cpufreq_userspace.c
3  *
4  *  Copyright (C)  2001 Russell King
5  *            (C)  2002 - 2004 Dominik Brodowski <linux@brodo.de>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  */
12
13 #include <linux/config.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
16 #include <linux/smp.h>
17 #include <linux/init.h>
18 #include <linux/spinlock.h>
19 #include <linux/interrupt.h>
20 #include <linux/ctype.h>
21 #include <linux/cpufreq.h>
22 #include <linux/sysctl.h>
23 #include <linux/types.h>
24 #include <linux/fs.h>
25 #include <linux/sysfs.h>
26
27 #include <asm/uaccess.h>
28
29 #define CTL_CPU_VARS_SPEED_MAX(cpunr) { \
30                 .ctl_name       = CPU_NR_FREQ_MAX, \
31                 .data           = &cpu_max_freq[cpunr], \
32                 .procname       = "speed-max", \
33                 .maxlen         = sizeof(cpu_max_freq[cpunr]),\
34                 .mode           = 0444, \
35                 .proc_handler   = proc_dointvec, }
36
37 #define CTL_CPU_VARS_SPEED_MIN(cpunr) { \
38                 .ctl_name       = CPU_NR_FREQ_MIN, \
39                 .data           = &cpu_min_freq[cpunr], \
40                 .procname       = "speed-min", \
41                 .maxlen         = sizeof(cpu_min_freq[cpunr]),\
42                 .mode           = 0444, \
43                 .proc_handler   = proc_dointvec, }
44
45 #define CTL_CPU_VARS_SPEED(cpunr) { \
46                 .ctl_name       = CPU_NR_FREQ, \
47                 .procname       = "speed", \
48                 .mode           = 0644, \
49                 .proc_handler   = cpufreq_procctl, \
50                 .strategy       = cpufreq_sysctl, \
51                 .extra1         = (void*) (cpunr), }
52
53 #define CTL_TABLE_CPU_VARS(cpunr) static ctl_table ctl_cpu_vars_##cpunr[] = {\
54                 CTL_CPU_VARS_SPEED_MAX(cpunr), \
55                 CTL_CPU_VARS_SPEED_MIN(cpunr), \
56                 CTL_CPU_VARS_SPEED(cpunr),  \
57                 { .ctl_name = 0, }, }
58
59 /* the ctl_table entry for each CPU */
60 #define CPU_ENUM(s) { \
61                 .ctl_name       = (CPU_NR + s), \
62                 .procname       = #s, \
63                 .mode           = 0555, \
64                 .child          = ctl_cpu_vars_##s }
65
66 /**
67  * A few values needed by the userspace governor
68  */
69 static unsigned int     cpu_max_freq[NR_CPUS];
70 static unsigned int     cpu_min_freq[NR_CPUS];
71 static unsigned int     cpu_cur_freq[NR_CPUS];
72 static unsigned int     cpu_is_managed[NR_CPUS];
73 static struct cpufreq_policy current_policy[NR_CPUS];
74
75 static DECLARE_MUTEX    (userspace_sem); 
76
77
78 /* keep track of frequency transitions */
79 static int 
80 userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
81                        void *data)
82 {
83         struct cpufreq_freqs *freq = data;
84
85         cpu_cur_freq[freq->cpu] = freq->new;
86
87         return 0;
88 }
89
90 static struct notifier_block userspace_cpufreq_notifier_block = {
91         .notifier_call  = userspace_cpufreq_notifier
92 };
93
94
95 /** 
96  * cpufreq_set - set the CPU frequency
97  * @freq: target frequency in kHz
98  * @cpu: CPU for which the frequency is to be set
99  *
100  * Sets the CPU frequency to freq.
101  */
102 int cpufreq_set(unsigned int freq, unsigned int cpu)
103 {
104         int ret = -EINVAL;
105
106         down(&userspace_sem);
107         if (!cpu_is_managed[cpu])
108                 goto err;
109
110         if (freq < cpu_min_freq[cpu])
111                 freq = cpu_min_freq[cpu];
112         if (freq > cpu_max_freq[cpu])
113                 freq = cpu_max_freq[cpu];
114
115         /*
116          * We're safe from concurrent calls to ->target() here
117          * as we hold the userspace_sem lock. If we were calling
118          * cpufreq_driver_target, a deadlock situation might occur:
119          * A: cpufreq_set (lock userspace_sem) -> cpufreq_driver_target(lock policy->lock)
120          * B: cpufreq_set_policy(lock policy->lock) -> __cpufreq_governor -> cpufreq_governor_userspace (lock userspace_sem)
121          */
122         ret = __cpufreq_driver_target(&current_policy[cpu], freq, 
123               CPUFREQ_RELATION_L);
124
125  err:
126         up(&userspace_sem);
127         return ret;
128 }
129 EXPORT_SYMBOL_GPL(cpufreq_set);
130
131
132 /** 
133  * cpufreq_setmax - set the CPU to the maximum frequency
134  * @cpu - affected cpu;
135  *
136  * Sets the CPU frequency to the maximum frequency supported by
137  * this CPU.
138  */
139 int cpufreq_setmax(unsigned int cpu)
140 {
141         if (!cpu_is_managed[cpu] || !cpu_online(cpu))
142                 return -EINVAL;
143         return cpufreq_set(cpu_max_freq[cpu], cpu);
144 }
145 EXPORT_SYMBOL_GPL(cpufreq_setmax);
146
147
148 /** 
149  * cpufreq_get - get the current CPU frequency (in kHz)
150  * @cpu: CPU number
151  *
152  * Get the CPU current (static) CPU frequency
153  */
154 unsigned int cpufreq_get(unsigned int cpu)
155 {
156         return cpu_cur_freq[cpu];
157 }
158 EXPORT_SYMBOL(cpufreq_get);
159
160
161 #ifdef CONFIG_CPU_FREQ_24_API
162
163
164 /*********************** cpufreq_sysctl interface ********************/
165 static int
166 cpufreq_procctl(ctl_table *ctl, int write, struct file *filp,
167                 void __user *buffer, size_t *lenp)
168 {
169         char buf[16], *p;
170         int cpu = (long) ctl->extra1;
171         unsigned int len, left = *lenp;
172
173         if (!left || (filp->f_pos && !write) || !cpu_online(cpu)) {
174                 *lenp = 0;
175                 return 0;
176         }
177
178         if (write) {
179                 unsigned int freq;
180
181                 len = left;
182                 if (left > sizeof(buf))
183                         left = sizeof(buf);
184                 if (copy_from_user(buf, buffer, left))
185                         return -EFAULT;
186                 buf[sizeof(buf) - 1] = '\0';
187
188                 freq = simple_strtoul(buf, &p, 0);
189                 cpufreq_set(freq, cpu);
190         } else {
191                 len = sprintf(buf, "%d\n", cpufreq_get(cpu));
192                 if (len > left)
193                         len = left;
194                 if (copy_to_user(buffer, buf, len))
195                         return -EFAULT;
196         }
197
198         *lenp = len;
199         filp->f_pos += len;
200         return 0;
201 }
202
203 static int
204 cpufreq_sysctl(ctl_table *table, int __user *name, int nlen,
205                void __user *oldval, size_t __user *oldlenp,
206                void __user *newval, size_t newlen, void **context)
207 {
208         int cpu = (long) table->extra1;
209
210         if (!cpu_online(cpu))
211                 return -EINVAL;
212
213         if (oldval && oldlenp) {
214                 size_t oldlen;
215
216                 if (get_user(oldlen, oldlenp))
217                         return -EFAULT;
218
219                 if (oldlen != sizeof(unsigned int))
220                         return -EINVAL;
221
222                 if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) ||
223                     put_user(sizeof(unsigned int), oldlenp))
224                         return -EFAULT;
225         }
226         if (newval && newlen) {
227                 unsigned int freq;
228
229                 if (newlen != sizeof(unsigned int))
230                         return -EINVAL;
231
232                 if (get_user(freq, (unsigned int *)newval))
233                         return -EFAULT;
234
235                 cpufreq_set(freq, cpu);
236         }
237         return 1;
238 }
239
240 /* ctl_table ctl_cpu_vars_{0,1,...,(NR_CPUS-1)} */
241 /* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
242         CTL_TABLE_CPU_VARS(0);
243 #if NR_CPUS > 1
244         CTL_TABLE_CPU_VARS(1);
245 #endif
246 #if NR_CPUS > 2
247         CTL_TABLE_CPU_VARS(2);
248 #endif
249 #if NR_CPUS > 3
250         CTL_TABLE_CPU_VARS(3);
251 #endif
252 #if NR_CPUS > 4
253         CTL_TABLE_CPU_VARS(4);
254 #endif
255 #if NR_CPUS > 5
256         CTL_TABLE_CPU_VARS(5);
257 #endif
258 #if NR_CPUS > 6
259         CTL_TABLE_CPU_VARS(6);
260 #endif
261 #if NR_CPUS > 7
262         CTL_TABLE_CPU_VARS(7);
263 #endif
264 #if NR_CPUS > 8
265         CTL_TABLE_CPU_VARS(8);
266 #endif
267 #if NR_CPUS > 9
268         CTL_TABLE_CPU_VARS(9);
269 #endif
270 #if NR_CPUS > 10
271         CTL_TABLE_CPU_VARS(10);
272 #endif
273 #if NR_CPUS > 11
274         CTL_TABLE_CPU_VARS(11);
275 #endif
276 #if NR_CPUS > 12
277         CTL_TABLE_CPU_VARS(12);
278 #endif
279 #if NR_CPUS > 13
280         CTL_TABLE_CPU_VARS(13);
281 #endif
282 #if NR_CPUS > 14
283         CTL_TABLE_CPU_VARS(14);
284 #endif
285 #if NR_CPUS > 15
286         CTL_TABLE_CPU_VARS(15);
287 #endif
288 #if NR_CPUS > 16
289         CTL_TABLE_CPU_VARS(16);
290 #endif
291 #if NR_CPUS > 17
292         CTL_TABLE_CPU_VARS(17);
293 #endif
294 #if NR_CPUS > 18
295         CTL_TABLE_CPU_VARS(18);
296 #endif
297 #if NR_CPUS > 19
298         CTL_TABLE_CPU_VARS(19);
299 #endif
300 #if NR_CPUS > 20
301         CTL_TABLE_CPU_VARS(20);
302 #endif
303 #if NR_CPUS > 21
304         CTL_TABLE_CPU_VARS(21);
305 #endif
306 #if NR_CPUS > 22
307         CTL_TABLE_CPU_VARS(22);
308 #endif
309 #if NR_CPUS > 23
310         CTL_TABLE_CPU_VARS(23);
311 #endif
312 #if NR_CPUS > 24
313         CTL_TABLE_CPU_VARS(24);
314 #endif
315 #if NR_CPUS > 25
316         CTL_TABLE_CPU_VARS(25);
317 #endif
318 #if NR_CPUS > 26
319         CTL_TABLE_CPU_VARS(26);
320 #endif
321 #if NR_CPUS > 27
322         CTL_TABLE_CPU_VARS(27);
323 #endif
324 #if NR_CPUS > 28
325         CTL_TABLE_CPU_VARS(28);
326 #endif
327 #if NR_CPUS > 29
328         CTL_TABLE_CPU_VARS(29);
329 #endif
330 #if NR_CPUS > 30
331         CTL_TABLE_CPU_VARS(30);
332 #endif
333 #if NR_CPUS > 31
334         CTL_TABLE_CPU_VARS(31);
335 #endif
336 #if NR_CPUS > 32
337 #error please extend CPU enumeration
338 #endif
339
340 /* due to NR_CPUS tweaking, a lot of if/endifs are required, sorry */
341 static ctl_table ctl_cpu_table[NR_CPUS + 1] = {
342         CPU_ENUM(0),
343 #if NR_CPUS > 1
344         CPU_ENUM(1),
345 #endif
346 #if NR_CPUS > 2
347         CPU_ENUM(2),
348 #endif
349 #if NR_CPUS > 3
350         CPU_ENUM(3),
351 #endif
352 #if NR_CPUS > 4
353         CPU_ENUM(4),
354 #endif
355 #if NR_CPUS > 5
356         CPU_ENUM(5),
357 #endif
358 #if NR_CPUS > 6
359         CPU_ENUM(6),
360 #endif
361 #if NR_CPUS > 7
362         CPU_ENUM(7),
363 #endif
364 #if NR_CPUS > 8
365         CPU_ENUM(8),
366 #endif
367 #if NR_CPUS > 9
368         CPU_ENUM(9),
369 #endif
370 #if NR_CPUS > 10
371         CPU_ENUM(10),
372 #endif
373 #if NR_CPUS > 11
374         CPU_ENUM(11),
375 #endif
376 #if NR_CPUS > 12
377         CPU_ENUM(12),
378 #endif
379 #if NR_CPUS > 13
380         CPU_ENUM(13),
381 #endif
382 #if NR_CPUS > 14
383         CPU_ENUM(14),
384 #endif
385 #if NR_CPUS > 15
386         CPU_ENUM(15),
387 #endif
388 #if NR_CPUS > 16
389         CPU_ENUM(16),
390 #endif
391 #if NR_CPUS > 17
392         CPU_ENUM(17),
393 #endif
394 #if NR_CPUS > 18
395         CPU_ENUM(18),
396 #endif
397 #if NR_CPUS > 19
398         CPU_ENUM(19),
399 #endif
400 #if NR_CPUS > 20
401         CPU_ENUM(20),
402 #endif
403 #if NR_CPUS > 21
404         CPU_ENUM(21),
405 #endif
406 #if NR_CPUS > 22
407         CPU_ENUM(22),
408 #endif
409 #if NR_CPUS > 23
410         CPU_ENUM(23),
411 #endif
412 #if NR_CPUS > 24
413         CPU_ENUM(24),
414 #endif
415 #if NR_CPUS > 25
416         CPU_ENUM(25),
417 #endif
418 #if NR_CPUS > 26
419         CPU_ENUM(26),
420 #endif
421 #if NR_CPUS > 27
422         CPU_ENUM(27),
423 #endif
424 #if NR_CPUS > 28
425         CPU_ENUM(28),
426 #endif
427 #if NR_CPUS > 29
428         CPU_ENUM(29),
429 #endif
430 #if NR_CPUS > 30
431         CPU_ENUM(30),
432 #endif
433 #if NR_CPUS > 31
434         CPU_ENUM(31),
435 #endif
436 #if NR_CPUS > 32
437 #error please extend CPU enumeration
438 #endif
439         {
440                 .ctl_name       = 0,
441         }
442 };
443
444 static ctl_table ctl_cpu[2] = {
445         {
446                 .ctl_name       = CTL_CPU,
447                 .procname       = "cpu",
448                 .mode           = 0555,
449                 .child          = ctl_cpu_table,
450         },
451         {
452                 .ctl_name       = 0,
453         }
454 };
455
456 struct ctl_table_header *cpufreq_sysctl_table;
457
458 static inline void cpufreq_sysctl_init(void)
459 {
460         cpufreq_sysctl_table = register_sysctl_table(ctl_cpu, 0);
461 }
462
463 static inline void cpufreq_sysctl_exit(void)
464 {
465         unregister_sysctl_table(cpufreq_sysctl_table);
466 }
467
468 #else
469 #define cpufreq_sysctl_init() do {} while(0)
470 #define cpufreq_sysctl_exit() do {} while(0)
471 #endif /* CONFIG_CPU_FREQ_24API */
472
473
474 /************************** sysfs interface ************************/
475 static ssize_t show_speed (struct cpufreq_policy *policy, char *buf)
476 {
477         return sprintf (buf, "%u\n", cpu_cur_freq[policy->cpu]);
478 }
479
480 static ssize_t 
481 store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) 
482 {
483         unsigned int freq = 0;
484         unsigned int ret;
485
486         ret = sscanf (buf, "%u", &freq);
487         if (ret != 1)
488                 return -EINVAL;
489
490         cpufreq_set(freq, policy->cpu);
491
492         return count;
493 }
494
495 static struct freq_attr freq_attr_scaling_setspeed = {
496         .attr = { .name = "scaling_setspeed", .mode = 0644 },
497         .show = show_speed,
498         .store = store_speed,
499 };
500
501 static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
502                                    unsigned int event)
503 {
504         unsigned int cpu = policy->cpu;
505         switch (event) {
506         case CPUFREQ_GOV_START:
507                 if ((!cpu_online(cpu)) || (!try_module_get(THIS_MODULE)))
508                         return -EINVAL;
509                 BUG_ON(!policy->cur);
510                 down(&userspace_sem);
511                 cpu_is_managed[cpu] = 1;                
512                 cpu_min_freq[cpu] = policy->min;
513                 cpu_max_freq[cpu] = policy->max;
514                 cpu_cur_freq[cpu] = policy->cur;
515                 sysfs_create_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
516                 memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
517                 up(&userspace_sem);
518                 break;
519         case CPUFREQ_GOV_STOP:
520                 down(&userspace_sem);
521                 cpu_is_managed[cpu] = 0;
522                 cpu_min_freq[cpu] = 0;
523                 cpu_max_freq[cpu] = 0;
524                 sysfs_remove_file (&policy->kobj, &freq_attr_scaling_setspeed.attr);
525                 up(&userspace_sem);
526                 module_put(THIS_MODULE);
527                 break;
528         case CPUFREQ_GOV_LIMITS:
529                 down(&userspace_sem);
530                 cpu_min_freq[cpu] = policy->min;
531                 cpu_max_freq[cpu] = policy->max;
532                 if (policy->max < cpu_cur_freq[cpu])
533                         __cpufreq_driver_target(&current_policy[cpu], policy->max, 
534                               CPUFREQ_RELATION_H);
535                 else if (policy->min > cpu_cur_freq[cpu])
536                         __cpufreq_driver_target(&current_policy[cpu], policy->min, 
537                               CPUFREQ_RELATION_L);
538                 memcpy (&current_policy[cpu], policy, sizeof(struct cpufreq_policy));
539                 up(&userspace_sem);
540                 break;
541         }
542         return 0;
543 }
544
545 /* on ARM SA1100 we need to rely on the values of cpufreq_get() - because 
546  * of this, cpu_cur_freq[] needs to be set early.
547  */
548 #if defined(CONFIG_ARM) && defined(CONFIG_ARCH_SA1100)
549 extern unsigned int sa11x0_getspeed(void);
550
551 static void cpufreq_sa11x0_compat(void)
552 {
553         cpu_cur_freq[0] = sa11x0_getspeed();
554 }
555 #else
556 #define cpufreq_sa11x0_compat() do {} while(0)
557 #endif
558
559
560 struct cpufreq_governor cpufreq_gov_userspace = {
561         .name           = "userspace",
562         .governor       = cpufreq_governor_userspace,
563         .owner          = THIS_MODULE,
564 };
565 EXPORT_SYMBOL(cpufreq_gov_userspace);
566
567 static int already_init = 0;
568
569 int cpufreq_gov_userspace_init(void)
570 {
571         if (!already_init) {
572                 down(&userspace_sem);
573                 cpufreq_sa11x0_compat();
574                 cpufreq_sysctl_init();
575                 cpufreq_register_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
576                 already_init = 1;
577                 up(&userspace_sem);
578         }
579         return cpufreq_register_governor(&cpufreq_gov_userspace);
580 }
581 EXPORT_SYMBOL(cpufreq_gov_userspace_init);
582
583
584 static void __exit cpufreq_gov_userspace_exit(void)
585 {
586         cpufreq_unregister_governor(&cpufreq_gov_userspace);
587         cpufreq_unregister_notifier(&userspace_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
588         cpufreq_sysctl_exit();
589 }
590
591
592 MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>, Russell King <rmk@arm.linux.org.uk>");
593 MODULE_DESCRIPTION ("CPUfreq policy governor 'userspace'");
594 MODULE_LICENSE ("GPL");
595
596 fs_initcall(cpufreq_gov_userspace_init);
597 module_exit(cpufreq_gov_userspace_exit);