Commit 7cc3ef9a authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://linux-dj.bkbits.net/cpufreq

into penguin.transmeta.com:/home/penguin/torvalds/repositories/kernel/linux
parents 26a97bb1 ce2d3f00
......@@ -242,6 +242,8 @@ static int __init elanfreq_init(void)
NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!driver)
return -ENOMEM;
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
driver->policy = (struct cpufreq_policy *) (driver + 1);
......@@ -260,8 +262,6 @@ static int __init elanfreq_init(void)
driver->verify = &elanfreq_verify;
driver->setpolicy = &elanfreq_setpolicy;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "elanfreq", CPUFREQ_NAME_LEN);
driver->policy[0].cpu = 0;
......
......@@ -431,11 +431,15 @@ static int __init cpufreq_gx_init(void)
driver = kmalloc(sizeof(struct cpufreq_driver) + NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (driver == NULL)
return -ENOMEM;
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
params = kmalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
if (params == NULL) {
kfree(driver);
return -ENOMEM;
}
memset(params, 0, sizeof(struct gxfreq_params));
driver->policy = (struct cpufreq_policy *)(driver + 1);
params->cs55x0 = gx_pci;
......
......@@ -762,6 +762,8 @@ static int __init longhaul_init (void)
NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!driver)
return -ENOMEM;
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
driver->policy = (struct cpufreq_policy *) (driver + 1);
......@@ -771,8 +773,7 @@ static int __init longhaul_init (void)
driver->verify = &longhaul_verify;
driver->setpolicy = &longhaul_setpolicy;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "longhaul", CPUFREQ_NAME_LEN);
driver->policy[0].cpu = 0;
......
......@@ -54,7 +54,7 @@ static void longrun_get_policy(struct cpufreq_policy *policy)
policy->min = longrun_low_freq + msr_lo *
((longrun_high_freq - longrun_low_freq) / 100);
policy->min = longrun_low_freq + msr_hi *
policy->max = longrun_low_freq + msr_hi *
((longrun_high_freq - longrun_low_freq) / 100);
policy->cpu = 0;
}
......@@ -241,6 +241,8 @@ static int __init longrun_init(void)
NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!driver)
return -ENOMEM;
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
driver->policy = (struct cpufreq_policy *) (driver + 1);
......@@ -251,8 +253,7 @@ static int __init longrun_init(void)
driver->policy[0].cpuinfo.min_freq = longrun_low_freq;
driver->policy[0].cpuinfo.max_freq = longrun_high_freq;
driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "longrun", CPUFREQ_NAME_LEN);
longrun_get_policy(&driver->policy[0]);
......
......@@ -220,6 +220,8 @@ static int __init cpufreq_p4_init(void)
NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!driver)
return -ENOMEM;
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
driver->policy = (struct cpufreq_policy *) (driver + 1);
......@@ -240,8 +242,6 @@ static int __init cpufreq_p4_init(void)
driver->verify = &cpufreq_p4_verify;
driver->setpolicy = &cpufreq_p4_setpolicy;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "p4-clockmod", CPUFREQ_NAME_LEN);
for (i=0;i<NR_CPUS;i++) {
......
......@@ -172,6 +172,8 @@ static int __init powernow_k6_init(void)
release_region (POWERNOW_IOPORT, 16);
return -ENOMEM;
}
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
driver->policy = (struct cpufreq_policy *) (driver + 1);
/* table init */
......@@ -184,8 +186,6 @@ static int __init powernow_k6_init(void)
driver->verify = &powernow_k6_verify;
driver->setpolicy = &powernow_k6_setpolicy;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "powernow-k6", CPUFREQ_NAME_LEN);
/* cpuinfo and default policy values */
......
......@@ -674,6 +674,8 @@ static int __init speedstep_init(void)
NR_CPUS * sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!driver)
return -ENOMEM;
memset(driver, 0, sizeof(struct cpufreq_driver) +
NR_CPUS * sizeof(struct cpufreq_policy));
driver->policy = (struct cpufreq_policy *) (driver + 1);
......@@ -690,8 +692,6 @@ static int __init speedstep_init(void)
driver->verify = &speedstep_verify;
driver->setpolicy = &speedstep_setpolicy;
driver->init = NULL;
driver->exit = NULL;
strncpy(driver->name, "speedstep", CPUFREQ_NAME_LEN);
driver->policy[0].cpuinfo.transition_latency = CPUFREQ_ETERNAL;
......
......@@ -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 */
......@@ -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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment