| /* |
| * linux/drivers/cpufreq/proc_intf.c |
| * |
| * Copyright (C) 2002 - 2003 Dominik Brodowski |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/init.h> |
| #include <linux/cpufreq.h> |
| #include <linux/ctype.h> |
| #include <linux/proc_fs.h> |
| #include <asm/uaccess.h> |
| |
| |
| /** |
| * cpufreq_parse_policy - parse a policy string |
| * @input_string: the string to parse. |
| * @policy: the policy written inside input_string |
| * |
| * This function parses a "policy string" - something the user echo'es into |
| * /proc/cpufreq or gives as boot parameter - into a struct cpufreq_policy. |
| * If there are invalid/missing entries, they are replaced with current |
| * cpufreq policy. |
| */ |
| static int cpufreq_parse_policy(char input_string[42], struct cpufreq_policy *policy) |
| { |
| unsigned int min = 0; |
| unsigned int max = 0; |
| unsigned int cpu = 0; |
| char str_governor[16]; |
| struct cpufreq_policy current_policy; |
| unsigned int result = -EFAULT; |
| |
| if (!policy) |
| return -EINVAL; |
| |
| policy->min = 0; |
| policy->max = 0; |
| policy->policy = 0; |
| policy->cpu = CPUFREQ_ALL_CPUS; |
| |
| if (sscanf(input_string, "%d:%d:%d:%15s", &cpu, &min, &max, str_governor) == 4) |
| { |
| policy->min = min; |
| policy->max = max; |
| policy->cpu = cpu; |
| result = 0; |
| goto scan_policy; |
| } |
| if (sscanf(input_string, "%d%%%d%%%d%%%15s", &cpu, &min, &max, str_governor) == 4) |
| { |
| if (!cpufreq_get_policy(¤t_policy, cpu)) { |
| policy->min = (min * current_policy.cpuinfo.max_freq) / 100; |
| policy->max = (max * current_policy.cpuinfo.max_freq) / 100; |
| policy->cpu = cpu; |
| result = 0; |
| goto scan_policy; |
| } |
| } |
| |
| if (sscanf(input_string, "%d:%d:%15s", &min, &max, str_governor) == 3) |
| { |
| policy->min = min; |
| policy->max = max; |
| result = 0; |
| goto scan_policy; |
| } |
| |
| if (sscanf(input_string, "%d%%%d%%%15s", &min, &max, str_governor) == 3) |
| { |
| if (!cpufreq_get_policy(¤t_policy, cpu)) { |
| policy->min = (min * current_policy.cpuinfo.max_freq) / 100; |
| policy->max = (max * current_policy.cpuinfo.max_freq) / 100; |
| result = 0; |
| goto scan_policy; |
| } |
| } |
| |
| return -EINVAL; |
| |
| scan_policy: |
| result = cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor); |
| |
| return result; |
| } |
| |
| /** |
| * cpufreq_proc_read - read /proc/cpufreq |
| * |
| * This function prints out the current cpufreq policy. |
| */ |
| static int cpufreq_proc_read ( |
| char *page, |
| char **start, |
| off_t off, |
| int count, |
| int *eof, |
| void *data) |
| { |
| char *p = page; |
| int len = 0; |
| struct cpufreq_policy policy; |
| unsigned int min_pctg = 0; |
| unsigned int max_pctg = 0; |
| unsigned int i = 0; |
| |
| if (off != 0) |
| goto end; |
| |
| p += sprintf(p, " minimum CPU frequency - maximum CPU frequency - policy\n"); |
| for (i=0;i<NR_CPUS;i++) { |
| if (!cpu_online(i)) |
| continue; |
| |
| if (cpufreq_get_policy(&policy, i)) |
| continue; |
| |
| if (!policy.cpuinfo.max_freq) |
| continue; |
| |
| min_pctg = (policy.min * 100) / policy.cpuinfo.max_freq; |
| max_pctg = (policy.max * 100) / policy.cpuinfo.max_freq; |
| |
| p += sprintf(p, "CPU%3d %9d kHz (%3d %%) - %9d kHz (%3d %%) - ", |
| i , policy.min, min_pctg, policy.max, max_pctg); |
| switch (policy.policy) { |
| case CPUFREQ_POLICY_POWERSAVE: |
| p += sprintf(p, "powersave\n"); |
| break; |
| case CPUFREQ_POLICY_PERFORMANCE: |
| p += sprintf(p, "performance\n"); |
| break; |
| case CPUFREQ_POLICY_GOVERNOR: |
| p += snprintf(p, CPUFREQ_NAME_LEN, "%s\n", policy.governor->name); |
| break; |
| default: |
| p += sprintf(p, "INVALID\n"); |
| break; |
| } |
| } |
| end: |
| len = (p - page); |
| if (len <= off+count) |
| *eof = 1; |
| *start = page + off; |
| len -= off; |
| if (len>count) |
| len = count; |
| if (len<0) |
| len = 0; |
| |
| return len; |
| } |
| |
| |
| /** |
| * cpufreq_proc_write - handles writing into /proc/cpufreq |
| * |
| * This function calls the parsing script and then sets the policy |
| * accordingly. |
| */ |
| static int cpufreq_proc_write ( |
| struct file *file, |
| const char *buffer, |
| unsigned long count, |
| void *data) |
| { |
| int result = 0; |
| char proc_string[42] = {'\0'}; |
| struct cpufreq_policy policy; |
| unsigned int i = 0; |
| |
| |
| if ((count > sizeof(proc_string) - 1)) |
| return -EINVAL; |
| |
| if (copy_from_user(proc_string, buffer, count)) |
| return -EFAULT; |
| |
| proc_string[count] = '\0'; |
| |
| result = cpufreq_parse_policy(proc_string, &policy); |
| if (result) |
| return -EFAULT; |
| |
| if (policy.cpu == CPUFREQ_ALL_CPUS) |
| { |
| for (i=0; i<NR_CPUS; i++) |
| { |
| policy.cpu = i; |
| if (cpu_online(i)) |
| cpufreq_set_policy(&policy); |
| } |
| } |
| else |
| cpufreq_set_policy(&policy); |
| |
| return count; |
| } |
| |
| |
| /** |
| * cpufreq_proc_init - add "cpufreq" to the /proc root directory |
| * |
| * This function adds "cpufreq" to the /proc root directory. |
| */ |
| static int __init cpufreq_proc_init (void) |
| { |
| struct proc_dir_entry *entry = NULL; |
| |
| /* are these acceptable values? */ |
| entry = create_proc_entry("cpufreq", S_IFREG|S_IRUGO|S_IWUSR, |
| &proc_root); |
| |
| if (!entry) { |
| printk(KERN_ERR "unable to create /proc/cpufreq entry\n"); |
| return -EIO; |
| } else { |
| entry->read_proc = cpufreq_proc_read; |
| entry->write_proc = cpufreq_proc_write; |
| } |
| |
| return 0; |
| } |
| |
| |
| /** |
| * cpufreq_proc_exit - removes "cpufreq" from the /proc root directory. |
| * |
| * This function removes "cpufreq" from the /proc root directory. |
| */ |
| static void __exit cpufreq_proc_exit (void) |
| { |
| remove_proc_entry("cpufreq", &proc_root); |
| return; |
| } |
| |
| MODULE_AUTHOR ("Dominik Brodowski <linux@brodo.de>"); |
| MODULE_DESCRIPTION ("CPUfreq /proc/cpufreq interface"); |
| MODULE_LICENSE ("GPL"); |
| |
| module_init(cpufreq_proc_init); |
| module_exit(cpufreq_proc_exit); |