diff options
| -rw-r--r-- | include/linux/cpufreq.h | 72 | ||||
| -rw-r--r-- | kernel/cpufreq.c | 357 |
2 files changed, 378 insertions, 51 deletions
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index a75d268c4f0e..68571eca8e70 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -2,10 +2,10 @@ * linux/include/linux/cpufreq.h * * Copyright (C) 2001 Russell King - * (C) 2002 Dominik Brodowski <linux@brodo.de> + * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * - * $Id: cpufreq.h,v 1.29 2002/11/11 15:35:47 db Exp $ + * $Id: cpufreq.h,v 1.36 2003/01/20 17:31:48 db Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,6 +20,9 @@ #include <linux/device.h> +#define CPUFREQ_NAME_LEN 16 + + /********************************************************************* * CPUFREQ NOTIFIER INTERFACE * *********************************************************************/ @@ -37,14 +40,17 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list); #define CPUFREQ_POLICY_POWERSAVE (1) #define CPUFREQ_POLICY_PERFORMANCE (2) +#define CPUFREQ_POLICY_GOVERNOR (3) /* Frequency values here are CPU kHz so that hardware which doesn't run * with some frequencies can complain without having to guess what per * cent / per mille means. - * Maximum transition latency is in nanoseconds - if it's unknown, + * Maximum transition latency is in microseconds - if it's unknown, * CPUFREQ_ETERNAL shall be used. */ +struct cpufreq_governor; + #define CPUFREQ_ETERNAL (-1) struct cpufreq_cpuinfo { unsigned int max_freq; @@ -57,6 +63,7 @@ struct cpufreq_policy { unsigned int min; /* in kHz */ unsigned int max; /* in kHz */ unsigned int policy; /* see above */ + struct cpufreq_governor *governor; /* see below */ struct cpufreq_cpuinfo cpuinfo; /* see above */ struct intf_data intf; /* interface data */ }; @@ -104,25 +111,62 @@ static inline unsigned long cpufreq_scale(unsigned long old, u_int div, u_int mu return carry + val; }; +/********************************************************************* + * CPUFREQ GOVERNORS * + *********************************************************************/ + +#define CPUFREQ_GOV_START 1 +#define CPUFREQ_GOV_STOP 2 +#define CPUFREQ_GOV_LIMITS 3 + +struct cpufreq_governor { + char name[CPUFREQ_NAME_LEN]; + int (*governor) (struct cpufreq_policy *policy, + unsigned int event); + struct list_head governor_list; + struct module *owner; +}; + +/* pass a target to the cpufreq driver + * _l : (cpufreq_driver_sem is not held) + */ +inline int cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); + +inline int cpufreq_driver_target_l(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); + +/* pass an event to the cpufreq governor */ +int cpufreq_governor_l(unsigned int cpu, unsigned int event); + +int cpufreq_register_governor(struct cpufreq_governor *governor); +void cpufreq_unregister_governor(struct cpufreq_governor *governor); /********************************************************************* * CPUFREQ DRIVER INTERFACE * *********************************************************************/ -#define CPUFREQ_NAME_LEN 16 +#define CPUFREQ_RELATION_L 0 /* lowest frequency at or above target */ +#define CPUFREQ_RELATION_H 1 /* highest frequency below or at target */ struct cpufreq_driver { /* needed by all drivers */ - int (*verify) (struct cpufreq_policy *policy); - int (*setpolicy) (struct cpufreq_policy *policy); - struct cpufreq_policy *policy; - char name[CPUFREQ_NAME_LEN]; + int (*verify) (struct cpufreq_policy *policy); + struct cpufreq_policy *policy; + char name[CPUFREQ_NAME_LEN]; + /* define one out of two */ + int (*setpolicy) (struct cpufreq_policy *policy); + int (*target) (struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation); /* optional, for the moment */ - int (*init) (struct cpufreq_policy *policy); - int (*exit) (struct cpufreq_policy *policy); + int (*init) (struct cpufreq_policy *policy); + int (*exit) (struct cpufreq_policy *policy); /* 2.4. compatible API */ #ifdef CONFIG_CPU_FREQ_24_API - unsigned int cpu_cur_freq[NR_CPUS]; + unsigned int cpu_cur_freq[NR_CPUS]; #endif }; @@ -276,4 +320,10 @@ int cpufreq_frequency_table_setpolicy(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table, unsigned int *index); +int cpufreq_frequency_table_target(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq, + unsigned int relation, + unsigned int *index); + #endif /* _LINUX_CPUFREQ_H */ diff --git a/kernel/cpufreq.c b/kernel/cpufreq.c index 792894f8050c..b28ebbd7ef3b 100644 --- a/kernel/cpufreq.c +++ b/kernel/cpufreq.c @@ -2,9 +2,9 @@ * linux/kernel/cpufreq.c * * Copyright (C) 2001 Russell King - * (C) 2002 Dominik Brodowski <linux@brodo.de> + * (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * - * $Id: cpufreq.c,v 1.50 2002/11/11 15:35:48 db Exp $ + * $Id: cpufreq.c,v 1.59 2003/01/20 17:31:48 db Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -34,7 +34,6 @@ #include <linux/sysctl.h> #endif - /** * The "cpufreq driver" - the arch- or hardware-dependend low * level driver of CPUFreq support, and its locking mutex. @@ -67,6 +66,9 @@ static unsigned int cpu_min_freq[NR_CPUS]; static unsigned int cpu_cur_freq[NR_CPUS]; #endif +LIST_HEAD(cpufreq_governor_list); + +static int cpufreq_governor(unsigned int cpu, unsigned int event); /********************************************************************* * SYSFS INTERFACE * @@ -75,16 +77,31 @@ static unsigned int cpu_cur_freq[NR_CPUS]; /** * cpufreq_parse_governor - parse a governor string */ -static int cpufreq_parse_governor (char *str_governor, unsigned int *governor) +static int cpufreq_parse_governor (char *str_governor, unsigned int *policy, struct cpufreq_governor **governor) { - if (!strnicmp(str_governor, "performance", 11)) { - *governor = CPUFREQ_POLICY_PERFORMANCE; + if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) { + *policy = CPUFREQ_POLICY_PERFORMANCE; return 0; - } else if (!strnicmp(str_governor, "powersave", 9)) { - *governor = CPUFREQ_POLICY_POWERSAVE; + } else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) { + *policy = CPUFREQ_POLICY_POWERSAVE; return 0; - } else - return -EINVAL; + } else { + struct cpufreq_governor *t; + down(&cpufreq_driver_sem); + if (!cpufreq_driver || !cpufreq_driver->target) + goto out; + list_for_each_entry(t, &cpufreq_governor_list, governor_list) { + if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) { + *governor = t; + *policy = CPUFREQ_POLICY_GOVERNOR; + up(&cpufreq_driver_sem); + return 0; + } + } + out: + up(&cpufreq_driver_sem); + } + return -EINVAL; } @@ -171,6 +188,8 @@ static ssize_t store_##file_name \ static ssize_t show_scaling_governor (struct device *dev, char *buf) { unsigned int value = 0; + char value2[CPUFREQ_NAME_LEN]; + if (!dev) return 0; @@ -178,6 +197,8 @@ static ssize_t show_scaling_governor (struct device *dev, char *buf) down(&cpufreq_driver_sem); if (cpufreq_driver) value = cpufreq_driver->policy[to_cpu_nr(dev)].policy; + if (value == CPUFREQ_POLICY_GOVERNOR) + strncpy(value2, cpufreq_driver->policy[to_cpu_nr(dev)].governor->name, CPUFREQ_NAME_LEN); up(&cpufreq_driver_sem); switch (value) { @@ -185,6 +206,8 @@ static ssize_t show_scaling_governor (struct device *dev, char *buf) return sprintf(buf, "powersave\n"); case CPUFREQ_POLICY_PERFORMANCE: return sprintf(buf, "performance\n"); + case CPUFREQ_POLICY_GOVERNOR: + return sprintf(buf, "%s\n", value2); } return -EINVAL; @@ -212,7 +235,7 @@ store_scaling_governor (struct device *dev, const char *buf, size_t count) if (ret != 1) return -EINVAL; - if (cpufreq_parse_governor(str_governor, &policy.policy)) + if (cpufreq_parse_governor(str_governor, &policy.policy, &policy.governor)) return -EINVAL; ret = cpufreq_set_policy(&policy); @@ -241,6 +264,34 @@ static ssize_t show_scaling_driver (struct device *dev, char *buf) return sprintf(buf, "%s\n", value); } +/** + * show_available_govs - show the available CPUfreq governors + */ +static ssize_t show_available_govs(struct device *dev, char *buf) +{ + ssize_t i = 0; + struct cpufreq_governor *t; + + if (!dev) + return 0; + + i += sprintf(buf, "performance powersave"); + + down(&cpufreq_driver_sem); + if (!cpufreq_driver || !cpufreq_driver->target) + goto out; + + list_for_each_entry(t, &cpufreq_governor_list, governor_list) { + if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2))) + goto out; + i += snprintf(&buf[i], CPUFREQ_NAME_LEN, " %s", t->name); + } + out: + up(&cpufreq_driver_sem); + i += sprintf(&buf[i], "\n"); + return i; +} + /** * cpufreq_per_cpu_attr_ro - read-only cpufreq per-CPU file @@ -267,6 +318,7 @@ cpufreq_per_cpu_attr_rw(scaling_max_freq, max); static DEVICE_ATTR(scaling_governor, (S_IRUGO | S_IWUSR), show_scaling_governor, store_scaling_governor); static DEVICE_ATTR(scaling_driver, S_IRUGO, show_scaling_driver, NULL); +static DEVICE_ATTR(available_scaling_governors, S_IRUGO, show_available_govs, NULL); /** @@ -299,10 +351,12 @@ static int cpufreq_add_dev (struct device * dev) } /* set default policy on this CPU */ - policy.policy = cpufreq_driver->policy[cpu].policy; - policy.min = cpufreq_driver->policy[cpu].min; - policy.max = cpufreq_driver->policy[cpu].max; - policy.cpu = cpu; + memcpy(&policy, + &cpufreq_driver->policy[cpu], + sizeof(struct cpufreq_policy)); + + if (cpufreq_driver->target) + cpufreq_governor(cpu, CPUFREQ_GOV_START); up(&cpufreq_driver_sem); ret = cpufreq_set_policy(&policy); @@ -339,6 +393,7 @@ static int cpufreq_add_dev (struct device * dev) device_create_file (dev, &dev_attr_scaling_max_freq); device_create_file (dev, &dev_attr_scaling_governor); device_create_file (dev, &dev_attr_scaling_driver); + device_create_file (dev, &dev_attr_available_scaling_governors); up(&cpufreq_driver_sem); return ret; @@ -356,6 +411,9 @@ static int cpufreq_remove_dev (struct intf_data *intf) struct device * dev = intf->dev; unsigned int cpu = to_cpu_nr(dev); + if (cpufreq_driver->target) + cpufreq_governor(cpu, CPUFREQ_GOV_STOP); + if (cpufreq_driver->exit) cpufreq_driver->exit(&cpufreq_driver->policy[cpu]); @@ -364,7 +422,8 @@ static int cpufreq_remove_dev (struct intf_data *intf) device_remove_file (dev, &dev_attr_scaling_min_freq); device_remove_file (dev, &dev_attr_scaling_max_freq); device_remove_file (dev, &dev_attr_scaling_governor); - device_remove_file (dev, &dev_attr_scaling_governor); + device_remove_file (dev, &dev_attr_scaling_driver); + device_remove_file (dev, &dev_attr_available_scaling_governors); return 0; } @@ -443,12 +502,11 @@ static int cpufreq_parse_policy(char input_string[42], struct cpufreq_policy *po return -EINVAL; scan_policy: - result = cpufreq_parse_governor(str_governor, &policy->policy); + result = cpufreq_parse_governor(str_governor, &policy->policy, &policy->governor); return result; } - /** * cpufreq_proc_read - read /proc/cpufreq * @@ -477,7 +535,8 @@ static int cpufreq_proc_read ( if (!cpu_online(i)) continue; - cpufreq_get_policy(&policy, i); + if (cpufreq_get_policy(&policy, i)) + continue; if (!policy.cpuinfo.max_freq) continue; @@ -494,6 +553,9 @@ static int cpufreq_proc_read ( 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; @@ -1065,6 +1127,136 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list) EXPORT_SYMBOL(cpufreq_unregister_notifier); +/********************************************************************* + * GOVERNORS * + *********************************************************************/ + +inline int cpufreq_driver_target_l(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int ret; + down(&cpufreq_driver_sem); + if (!cpufreq_driver) + ret = -EINVAL; + else + ret = cpufreq_driver->target(policy, target_freq, relation); + up(&cpufreq_driver_sem); + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_driver_target_l); + + +inline int cpufreq_driver_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + return cpufreq_driver->target(policy, target_freq, relation); +} +EXPORT_SYMBOL_GPL(cpufreq_driver_target); + + +static int cpufreq_governor(unsigned int cpu, unsigned int event) +{ + int ret = 0; + struct cpufreq_policy *policy = &cpufreq_driver->policy[cpu]; + + switch (policy->policy) { + case CPUFREQ_POLICY_POWERSAVE: + if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) + ret = cpufreq_driver->target(policy, policy->min, CPUFREQ_RELATION_L); + break; + case CPUFREQ_POLICY_PERFORMANCE: + if ((event == CPUFREQ_GOV_LIMITS) || (event == CPUFREQ_GOV_START)) + ret = cpufreq_driver->target(policy, policy->max, CPUFREQ_RELATION_H); + break; + case CPUFREQ_POLICY_GOVERNOR: + ret = -EINVAL; + if (event == CPUFREQ_GOV_START) + if (!try_module_get(cpufreq_driver->policy[cpu].governor->owner)) + break; + ret = cpufreq_driver->policy[cpu].governor->governor(policy, event); + if ((event == CPUFREQ_GOV_STOP) || + (ret && (event == CPUFREQ_GOV_START))) + module_put(cpufreq_driver->policy[cpu].governor->owner); + break; + default: + ret = -EINVAL; + } + return ret; +} + + +int cpufreq_governor_l(unsigned int cpu, unsigned int event) +{ + int ret = 0; + down(&cpufreq_driver_sem); + ret = cpufreq_governor(cpu, event); + up(&cpufreq_driver_sem); + return ret; +} +EXPORT_SYMBOL_GPL(cpufreq_governor_l); + + +int cpufreq_register_governor(struct cpufreq_governor *governor) +{ + struct cpufreq_governor *t; + + if (!governor) + return -EINVAL; + + if (!strnicmp(governor->name,"powersave",CPUFREQ_NAME_LEN)) + return -EBUSY; + if (!strnicmp(governor->name,"performance",CPUFREQ_NAME_LEN)) + return -EBUSY; + + down(&cpufreq_driver_sem); + + list_for_each_entry(t, &cpufreq_governor_list, governor_list) { + if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) { + up(&cpufreq_driver_sem); + return -EBUSY; + } + } + list_add(&governor->governor_list, &cpufreq_governor_list); + up(&cpufreq_driver_sem); + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_register_governor); + + +void cpufreq_unregister_governor(struct cpufreq_governor *governor) +{ + unsigned int i; + + if (!governor) + return; + + down(&cpufreq_driver_sem); + /* + * Unless the user uses rmmod -f, we can be safe. But we never + * know, so check whether if it's currently used. If so, + * stop it and replace it with the default governor. + */ + for (i=0; i<NR_CPUS; i++) + { + if (cpufreq_driver && + (cpufreq_driver->policy[i].policy == CPUFREQ_POLICY_GOVERNOR) && + (cpufreq_driver->policy[i].governor == governor)) { + cpufreq_governor(i, CPUFREQ_GOV_STOP); + cpufreq_driver->policy[i].policy = CPUFREQ_POLICY_PERFORMANCE; + cpufreq_governor(i, CPUFREQ_GOV_START); + } + } + /* now we can safely remove it from the list */ + list_del(&governor->governor_list); + up(&cpufreq_driver_sem); + return; +} +EXPORT_SYMBOL_GPL(cpufreq_unregister_governor); + + /********************************************************************* * POLICY INTERFACE * @@ -1084,15 +1276,11 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu) up(&cpufreq_driver_sem); return -EINVAL; } - - policy->min = cpufreq_driver->policy[cpu].min; - policy->max = cpufreq_driver->policy[cpu].max; - policy->policy = cpufreq_driver->policy[cpu].policy; - policy->cpuinfo.max_freq = cpufreq_driver->policy[cpu].cpuinfo.max_freq; - policy->cpuinfo.min_freq = cpufreq_driver->policy[cpu].cpuinfo.min_freq; - policy->cpuinfo.transition_latency = cpufreq_driver->policy[cpu].cpuinfo.transition_latency; - policy->cpu = cpu; + memcpy(policy, + &cpufreq_driver->policy[cpu], + sizeof(struct cpufreq_policy)); + up(&cpufreq_driver_sem); return 0; @@ -1111,16 +1299,15 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) int ret; down(&cpufreq_driver_sem); - if (!cpufreq_driver || !cpufreq_driver->verify || - !cpufreq_driver->setpolicy || !policy || + if (!cpufreq_driver || !policy || (policy->cpu >= NR_CPUS) || (!cpu_online(policy->cpu))) { up(&cpufreq_driver_sem); return -EINVAL; } - policy->cpuinfo.max_freq = cpufreq_driver->policy[policy->cpu].cpuinfo.max_freq; - policy->cpuinfo.min_freq = cpufreq_driver->policy[policy->cpu].cpuinfo.min_freq; - policy->cpuinfo.transition_latency = cpufreq_driver->policy[policy->cpu].cpuinfo.transition_latency; + memcpy(&policy->cpuinfo, + &cpufreq_driver->policy[policy->cpu].cpuinfo, + sizeof(struct cpufreq_cpuinfo)); /* verify the cpu speed can be set within this limit */ ret = cpufreq_driver->verify(policy); @@ -1156,13 +1343,35 @@ int cpufreq_set_policy(struct cpufreq_policy *policy) cpufreq_driver->policy[policy->cpu].min = policy->min; cpufreq_driver->policy[policy->cpu].max = policy->max; - cpufreq_driver->policy[policy->cpu].policy = policy->policy; #ifdef CONFIG_CPU_FREQ_24_API cpu_cur_freq[policy->cpu] = policy->max; #endif - ret = cpufreq_driver->setpolicy(policy); + if (cpufreq_driver->setpolicy) { + cpufreq_driver->policy[policy->cpu].policy = policy->policy; + ret = cpufreq_driver->setpolicy(policy); + } else { + if ((policy->policy != cpufreq_driver->policy[policy->cpu].policy) || + ((policy->policy == CPUFREQ_POLICY_GOVERNOR) && (policy->governor != cpufreq_driver->policy[policy->cpu].governor))) { + unsigned int old_pol = cpufreq_driver->policy[policy->cpu].policy; + struct cpufreq_governor *old_gov = cpufreq_driver->policy[policy->cpu].governor; + /* end old governor */ + cpufreq_governor(policy->cpu, CPUFREQ_GOV_STOP); + cpufreq_driver->policy[policy->cpu].policy = policy->policy; + cpufreq_driver->policy[policy->cpu].governor = policy->governor; + /* start new governor */ + if (cpufreq_governor(policy->cpu, CPUFREQ_GOV_START)) { + cpufreq_driver->policy[policy->cpu].policy = old_pol; + cpufreq_driver->policy[policy->cpu].governor = old_gov; + cpufreq_governor(policy->cpu, CPUFREQ_GOV_START); + } + /* might be a policy change, too */ + cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); + } else { + cpufreq_governor(policy->cpu, CPUFREQ_GOV_LIMITS); + } + } up(&cpufreq_driver_sem); @@ -1253,7 +1462,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) return -EBUSY; if (!driver_data || !driver_data->verify || - !driver_data->setpolicy) + ((!driver_data->setpolicy) && (!driver_data->target))) return -EINVAL; down(&cpufreq_driver_sem); @@ -1271,6 +1480,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) up(&cpufreq_driver_sem); return -ENOMEM; } + memset(cpufreq_driver->policy, 0, NR_CPUS * sizeof(struct cpufreq_policy)); } up(&cpufreq_driver_sem); @@ -1359,11 +1569,8 @@ int cpufreq_restore(void) up(&cpufreq_driver_sem); return 0; } - - policy.min = cpufreq_driver->policy[i].min; - policy.max = cpufreq_driver->policy[i].max; - policy.policy = cpufreq_driver->policy[i].policy; - policy.cpu = i; + + memcpy(&policy, &cpufreq_driver->policy[i], sizeof(struct cpufreq_policy)); up(&cpufreq_driver_sem); ret += cpufreq_set_policy(&policy); @@ -1493,3 +1700,73 @@ int cpufreq_frequency_table_setpolicy(struct cpufreq_policy *policy, return 0; } EXPORT_SYMBOL_GPL(cpufreq_frequency_table_setpolicy); + +int cpufreq_frequency_table_target(struct cpufreq_policy *policy, + struct cpufreq_frequency_table *table, + unsigned int target_freq, + unsigned int relation, + unsigned int *index) +{ + struct cpufreq_frequency_table optimal = { .index = ~0, }; + struct cpufreq_frequency_table suboptimal = { .index = ~0, }; + unsigned int i; + + switch (relation) { + case CPUFREQ_RELATION_H: + optimal.frequency = 0; + suboptimal.frequency = ~0; + break; + case CPUFREQ_RELATION_L: + optimal.frequency = ~0; + suboptimal.frequency = 0; + break; + } + + if (!cpu_online(policy->cpu)) + return -EINVAL; + + for (i=0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { + unsigned int freq = table[i].frequency; + if (freq == CPUFREQ_ENTRY_INVALID) + continue; + if ((freq < policy->min) || (freq > policy->max)) + continue; + switch(relation) { + case CPUFREQ_RELATION_H: + if (freq <= target_freq) { + if (freq >= optimal.frequency) { + optimal.frequency = freq; + optimal.index = i; + } + } else { + if (freq <= suboptimal.frequency) { + suboptimal.frequency = freq; + suboptimal.index = i; + } + } + break; + case CPUFREQ_RELATION_L: + if (freq >= target_freq) { + if (freq <= optimal.frequency) { + optimal.frequency = freq; + optimal.index = i; + } + } else { + if (freq >= suboptimal.frequency) { + suboptimal.frequency = freq; + suboptimal.index = i; + } + } + break; + } + } + if (optimal.index > i) { + if (suboptimal.index > i) + return -EINVAL; + *index = suboptimal.index; + } else + *index = optimal.index; + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); |
