Merge tag 'pull_req_20121120' of git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq into pm-devfreq
Devfreq pull request for Rafael from MyungJoo Created on 2012.11.20 based on Linux 3.7-rc5 * tag 'pull_req_20121120' of git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq: PM / devfreq: exynos4_bus.c: Fixed an alignment of the func call args. PM / devfreq: Add sysfs node to expose available governors PM / devfreq: allow sysfs governor node to switch governor PM / devfreq: governors: add GPL module license and allow module build PM / devfreq: map devfreq drivers to governor using name PM / devfreq: register governors with devfreq framework PM / devfreq: provide hooks for governors to be registered PM / devfreq: export update_devfreq PM / devfreq: Add sysfs node for representing frequency transition information. PM / devfreq: Add sysfs node to expose available frequencies PM / devfreq: documentation cleanups for devfreq header PM / devfreq: Use devm_* functions in exynos4_bus.c
This commit is contained in:
@@ -11,7 +11,7 @@ What: /sys/class/devfreq/.../governor
|
||||
Date: September 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../governor shows the name of the
|
||||
The /sys/class/devfreq/.../governor show or set the name of the
|
||||
governor used by the corresponding devfreq object.
|
||||
|
||||
What: /sys/class/devfreq/.../cur_freq
|
||||
@@ -44,6 +44,17 @@ Description:
|
||||
(/sys/class/devfreq/.../central_polling is 0), this value
|
||||
may be useless.
|
||||
|
||||
What: /sys/class/devfreq/.../trans_stat
|
||||
Date: October 2012
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
Descrtiption:
|
||||
This ABI shows the statistics of devfreq behavior on a
|
||||
specific device. It shows the time spent in each state and
|
||||
the number of transitions between states.
|
||||
In order to activate this ABI, the devfreq target device
|
||||
driver should provide the list of available frequencies
|
||||
with its profile.
|
||||
|
||||
What: /sys/class/devfreq/.../userspace/set_freq
|
||||
Date: September 2011
|
||||
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
|
||||
@@ -51,3 +62,19 @@ Description:
|
||||
The /sys/class/devfreq/.../userspace/set_freq shows and
|
||||
sets the requested frequency for the devfreq object if
|
||||
userspace governor is in effect.
|
||||
|
||||
What: /sys/class/devfreq/.../available_frequencies
|
||||
Date: October 2012
|
||||
Contact: Nishanth Menon <nm@ti.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../available_frequencies shows
|
||||
the available frequencies of the corresponding devfreq object.
|
||||
This is a snapshot of available frequencies and not limited
|
||||
by the min/max frequency restrictions.
|
||||
|
||||
What: /sys/class/devfreq/.../available_governors
|
||||
Date: October 2012
|
||||
Contact: Nishanth Menon <nm@ti.com>
|
||||
Description:
|
||||
The /sys/class/devfreq/.../available_governors shows
|
||||
currently available governors in the system.
|
||||
|
||||
@@ -30,7 +30,7 @@ if PM_DEVFREQ
|
||||
comment "DEVFREQ Governors"
|
||||
|
||||
config DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
bool "Simple Ondemand"
|
||||
tristate "Simple Ondemand"
|
||||
help
|
||||
Chooses frequency based on the recent load on the device. Works
|
||||
similar as ONDEMAND governor of CPUFREQ does. A device with
|
||||
@@ -39,7 +39,7 @@ config DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
values to the governor with data field at devfreq_add_device().
|
||||
|
||||
config DEVFREQ_GOV_PERFORMANCE
|
||||
bool "Performance"
|
||||
tristate "Performance"
|
||||
help
|
||||
Sets the frequency at the maximum available frequency.
|
||||
This governor always returns UINT_MAX as frequency so that
|
||||
@@ -47,7 +47,7 @@ config DEVFREQ_GOV_PERFORMANCE
|
||||
at any time.
|
||||
|
||||
config DEVFREQ_GOV_POWERSAVE
|
||||
bool "Powersave"
|
||||
tristate "Powersave"
|
||||
help
|
||||
Sets the frequency at the minimum available frequency.
|
||||
This governor always returns 0 as frequency so that
|
||||
@@ -55,7 +55,7 @@ config DEVFREQ_GOV_POWERSAVE
|
||||
at any time.
|
||||
|
||||
config DEVFREQ_GOV_USERSPACE
|
||||
bool "Userspace"
|
||||
tristate "Userspace"
|
||||
help
|
||||
Sets the frequency at the user specified one.
|
||||
This governor returns the user configured frequency if there
|
||||
|
||||
+378
-9
@@ -36,6 +36,8 @@ static struct class *devfreq_class;
|
||||
*/
|
||||
static struct workqueue_struct *devfreq_wq;
|
||||
|
||||
/* The list of all device-devfreq governors */
|
||||
static LIST_HEAD(devfreq_governor_list);
|
||||
/* The list of all device-devfreq */
|
||||
static LIST_HEAD(devfreq_list);
|
||||
static DEFINE_MUTEX(devfreq_list_lock);
|
||||
@@ -66,6 +68,77 @@ static struct devfreq *find_device_devfreq(struct device *dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
/**
|
||||
* devfreq_get_freq_level() - Lookup freq_table for the frequency
|
||||
* @devfreq: the devfreq instance
|
||||
* @freq: the target frequency
|
||||
*/
|
||||
static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
|
||||
{
|
||||
int lev;
|
||||
|
||||
for (lev = 0; lev < devfreq->profile->max_state; lev++)
|
||||
if (freq == devfreq->profile->freq_table[lev])
|
||||
return lev;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* devfreq_update_status() - Update statistics of devfreq behavior
|
||||
* @devfreq: the devfreq instance
|
||||
* @freq: the update target frequency
|
||||
*/
|
||||
static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
|
||||
{
|
||||
int lev, prev_lev;
|
||||
unsigned long cur_time;
|
||||
|
||||
lev = devfreq_get_freq_level(devfreq, freq);
|
||||
if (lev < 0)
|
||||
return lev;
|
||||
|
||||
cur_time = jiffies;
|
||||
devfreq->time_in_state[lev] +=
|
||||
cur_time - devfreq->last_stat_updated;
|
||||
if (freq != devfreq->previous_freq) {
|
||||
prev_lev = devfreq_get_freq_level(devfreq,
|
||||
devfreq->previous_freq);
|
||||
devfreq->trans_table[(prev_lev *
|
||||
devfreq->profile->max_state) + lev]++;
|
||||
devfreq->total_trans++;
|
||||
}
|
||||
devfreq->last_stat_updated = cur_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_devfreq_governor() - find devfreq governor from name
|
||||
* @name: name of the governor
|
||||
*
|
||||
* Search the list of devfreq governors and return the matched
|
||||
* governor's pointer. devfreq_list_lock should be held by the caller.
|
||||
*/
|
||||
static struct devfreq_governor *find_devfreq_governor(const char *name)
|
||||
{
|
||||
struct devfreq_governor *tmp_governor;
|
||||
|
||||
if (unlikely(IS_ERR_OR_NULL(name))) {
|
||||
pr_err("DEVFREQ: %s: Invalid parameters\n", __func__);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
WARN(!mutex_is_locked(&devfreq_list_lock),
|
||||
"devfreq_list_lock must be locked.");
|
||||
|
||||
list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
|
||||
if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
|
||||
return tmp_governor;
|
||||
}
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
/* Load monitoring helper functions for governors use */
|
||||
|
||||
/**
|
||||
@@ -86,6 +159,9 @@ int update_devfreq(struct devfreq *devfreq)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!devfreq->governor)
|
||||
return -EINVAL;
|
||||
|
||||
/* Reevaluate the proper frequency */
|
||||
err = devfreq->governor->get_target_freq(devfreq, &freq);
|
||||
if (err)
|
||||
@@ -112,9 +188,15 @@ int update_devfreq(struct devfreq *devfreq)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (devfreq->profile->freq_table)
|
||||
if (devfreq_update_status(devfreq, freq))
|
||||
dev_err(&devfreq->dev,
|
||||
"Couldn't update frequency transition information.\n");
|
||||
|
||||
devfreq->previous_freq = freq;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(update_devfreq);
|
||||
|
||||
/**
|
||||
* devfreq_monitor() - Periodically poll devfreq objects.
|
||||
@@ -300,7 +382,9 @@ static void _remove_devfreq(struct devfreq *devfreq, bool skip)
|
||||
list_del(&devfreq->node);
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
devfreq->governor->event_handler(devfreq, DEVFREQ_GOV_STOP, NULL);
|
||||
if (devfreq->governor)
|
||||
devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_STOP, NULL);
|
||||
|
||||
if (devfreq->profile->exit)
|
||||
devfreq->profile->exit(devfreq->dev.parent);
|
||||
@@ -333,19 +417,20 @@ static void devfreq_dev_release(struct device *dev)
|
||||
* devfreq_add_device() - Add devfreq feature to the device
|
||||
* @dev: the device to add devfreq feature.
|
||||
* @profile: device-specific profile to run devfreq.
|
||||
* @governor: the policy to choose frequency.
|
||||
* @governor_name: name of the policy to choose frequency.
|
||||
* @data: private data for the governor. The devfreq framework does not
|
||||
* touch this value.
|
||||
*/
|
||||
struct devfreq *devfreq_add_device(struct device *dev,
|
||||
struct devfreq_dev_profile *profile,
|
||||
const struct devfreq_governor *governor,
|
||||
const char *governor_name,
|
||||
void *data)
|
||||
{
|
||||
struct devfreq *devfreq;
|
||||
struct devfreq_governor *governor;
|
||||
int err = 0;
|
||||
|
||||
if (!dev || !profile || !governor) {
|
||||
if (!dev || !profile || !governor_name) {
|
||||
dev_err(dev, "%s: Invalid parameters.\n", __func__);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
@@ -373,11 +458,20 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
||||
devfreq->dev.class = devfreq_class;
|
||||
devfreq->dev.release = devfreq_dev_release;
|
||||
devfreq->profile = profile;
|
||||
devfreq->governor = governor;
|
||||
strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
|
||||
devfreq->previous_freq = profile->initial_freq;
|
||||
devfreq->data = data;
|
||||
devfreq->nb.notifier_call = devfreq_notifier_call;
|
||||
|
||||
devfreq->trans_table = devm_kzalloc(dev, sizeof(unsigned int) *
|
||||
devfreq->profile->max_state *
|
||||
devfreq->profile->max_state,
|
||||
GFP_KERNEL);
|
||||
devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) *
|
||||
devfreq->profile->max_state,
|
||||
GFP_KERNEL);
|
||||
devfreq->last_stat_updated = jiffies;
|
||||
|
||||
dev_set_name(&devfreq->dev, dev_name(dev));
|
||||
err = device_register(&devfreq->dev);
|
||||
if (err) {
|
||||
@@ -390,10 +484,14 @@ struct devfreq *devfreq_add_device(struct device *dev,
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
list_add(&devfreq->node, &devfreq_list);
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
err = devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_START, NULL);
|
||||
governor = find_devfreq_governor(devfreq->governor_name);
|
||||
if (!IS_ERR(governor))
|
||||
devfreq->governor = governor;
|
||||
if (devfreq->governor)
|
||||
err = devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_START, NULL);
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
if (err) {
|
||||
dev_err(dev, "%s: Unable to start governor for the device\n",
|
||||
__func__);
|
||||
@@ -436,6 +534,9 @@ int devfreq_suspend_device(struct devfreq *devfreq)
|
||||
if (!devfreq)
|
||||
return -EINVAL;
|
||||
|
||||
if (!devfreq->governor)
|
||||
return 0;
|
||||
|
||||
return devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_SUSPEND, NULL);
|
||||
}
|
||||
@@ -450,17 +551,207 @@ int devfreq_resume_device(struct devfreq *devfreq)
|
||||
if (!devfreq)
|
||||
return -EINVAL;
|
||||
|
||||
if (!devfreq->governor)
|
||||
return 0;
|
||||
|
||||
return devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_RESUME, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_resume_device);
|
||||
|
||||
/**
|
||||
* devfreq_add_governor() - Add devfreq governor
|
||||
* @governor: the devfreq governor to be added
|
||||
*/
|
||||
int devfreq_add_governor(struct devfreq_governor *governor)
|
||||
{
|
||||
struct devfreq_governor *g;
|
||||
struct devfreq *devfreq;
|
||||
int err = 0;
|
||||
|
||||
if (!governor) {
|
||||
pr_err("%s: Invalid parameters.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
g = find_devfreq_governor(governor->name);
|
||||
if (!IS_ERR(g)) {
|
||||
pr_err("%s: governor %s already registered\n", __func__,
|
||||
g->name);
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
list_add(&governor->node, &devfreq_governor_list);
|
||||
|
||||
list_for_each_entry(devfreq, &devfreq_list, node) {
|
||||
int ret = 0;
|
||||
struct device *dev = devfreq->dev.parent;
|
||||
|
||||
if (!strncmp(devfreq->governor_name, governor->name,
|
||||
DEVFREQ_NAME_LEN)) {
|
||||
/* The following should never occur */
|
||||
if (devfreq->governor) {
|
||||
dev_warn(dev,
|
||||
"%s: Governor %s already present\n",
|
||||
__func__, devfreq->governor->name);
|
||||
ret = devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev,
|
||||
"%s: Governor %s stop = %d\n",
|
||||
__func__,
|
||||
devfreq->governor->name, ret);
|
||||
}
|
||||
/* Fall through */
|
||||
}
|
||||
devfreq->governor = governor;
|
||||
ret = devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_START, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s start=%d\n",
|
||||
__func__, devfreq->governor->name,
|
||||
ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err_out:
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_add_governor);
|
||||
|
||||
/**
|
||||
* devfreq_remove_device() - Remove devfreq feature from a device.
|
||||
* @governor: the devfreq governor to be removed
|
||||
*/
|
||||
int devfreq_remove_governor(struct devfreq_governor *governor)
|
||||
{
|
||||
struct devfreq_governor *g;
|
||||
struct devfreq *devfreq;
|
||||
int err = 0;
|
||||
|
||||
if (!governor) {
|
||||
pr_err("%s: Invalid parameters.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
g = find_devfreq_governor(governor->name);
|
||||
if (IS_ERR(g)) {
|
||||
pr_err("%s: governor %s not registered\n", __func__,
|
||||
g->name);
|
||||
err = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
list_for_each_entry(devfreq, &devfreq_list, node) {
|
||||
int ret;
|
||||
struct device *dev = devfreq->dev.parent;
|
||||
|
||||
if (!strncmp(devfreq->governor_name, governor->name,
|
||||
DEVFREQ_NAME_LEN)) {
|
||||
/* we should have a devfreq governor! */
|
||||
if (!devfreq->governor) {
|
||||
dev_warn(dev, "%s: Governor %s NOT present\n",
|
||||
__func__, governor->name);
|
||||
continue;
|
||||
/* Fall through */
|
||||
}
|
||||
ret = devfreq->governor->event_handler(devfreq,
|
||||
DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s stop=%d\n",
|
||||
__func__, devfreq->governor->name,
|
||||
ret);
|
||||
}
|
||||
devfreq->governor = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&governor->node);
|
||||
err_out:
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(devfreq_remove_governor);
|
||||
|
||||
static ssize_t show_governor(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (!to_devfreq(dev)->governor)
|
||||
return -EINVAL;
|
||||
|
||||
return sprintf(buf, "%s\n", to_devfreq(dev)->governor->name);
|
||||
}
|
||||
|
||||
static ssize_t store_governor(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(dev);
|
||||
int ret;
|
||||
char str_governor[DEVFREQ_NAME_LEN + 1];
|
||||
struct devfreq_governor *governor;
|
||||
|
||||
ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
governor = find_devfreq_governor(str_governor);
|
||||
if (IS_ERR(governor)) {
|
||||
ret = PTR_ERR(governor);
|
||||
goto out;
|
||||
}
|
||||
if (df->governor == governor)
|
||||
goto out;
|
||||
|
||||
if (df->governor) {
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
|
||||
if (ret) {
|
||||
dev_warn(dev, "%s: Governor %s not stopped(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
df->governor = governor;
|
||||
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
|
||||
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
|
||||
if (ret)
|
||||
dev_warn(dev, "%s: Governor %s not started(%d)\n",
|
||||
__func__, df->governor->name, ret);
|
||||
out:
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
if (!ret)
|
||||
ret = count;
|
||||
return ret;
|
||||
}
|
||||
static ssize_t show_available_governors(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct devfreq_governor *tmp_governor;
|
||||
ssize_t count = 0;
|
||||
|
||||
mutex_lock(&devfreq_list_lock);
|
||||
list_for_each_entry(tmp_governor, &devfreq_governor_list, node)
|
||||
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
|
||||
"%s ", tmp_governor->name);
|
||||
mutex_unlock(&devfreq_list_lock);
|
||||
|
||||
/* Truncate the trailing space */
|
||||
if (count)
|
||||
count--;
|
||||
|
||||
count += sprintf(&buf[count], "\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_freq(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
@@ -494,6 +785,9 @@ static ssize_t store_polling_interval(struct device *dev,
|
||||
unsigned int value;
|
||||
int ret;
|
||||
|
||||
if (!df->governor)
|
||||
return -EINVAL;
|
||||
|
||||
ret = sscanf(buf, "%u", &value);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
@@ -570,14 +864,89 @@ static ssize_t show_max_freq(struct device *dev, struct device_attribute *attr,
|
||||
return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq);
|
||||
}
|
||||
|
||||
static ssize_t show_available_freqs(struct device *d,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct devfreq *df = to_devfreq(d);
|
||||
struct device *dev = df->dev.parent;
|
||||
struct opp *opp;
|
||||
ssize_t count = 0;
|
||||
unsigned long freq = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
opp = opp_find_freq_ceil(dev, &freq);
|
||||
if (IS_ERR(opp))
|
||||
break;
|
||||
|
||||
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
|
||||
"%lu ", freq);
|
||||
freq++;
|
||||
} while (1);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* Truncate the trailing space */
|
||||
if (count)
|
||||
count--;
|
||||
|
||||
count += sprintf(&buf[count], "\n");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct devfreq *devfreq = to_devfreq(dev);
|
||||
ssize_t len;
|
||||
int i, j, err;
|
||||
unsigned int max_state = devfreq->profile->max_state;
|
||||
|
||||
err = devfreq_update_status(devfreq, devfreq->previous_freq);
|
||||
if (err)
|
||||
return 0;
|
||||
|
||||
len = sprintf(buf, " From : To\n");
|
||||
len += sprintf(buf + len, " :");
|
||||
for (i = 0; i < max_state; i++)
|
||||
len += sprintf(buf + len, "%8u",
|
||||
devfreq->profile->freq_table[i]);
|
||||
|
||||
len += sprintf(buf + len, " time(ms)\n");
|
||||
|
||||
for (i = 0; i < max_state; i++) {
|
||||
if (devfreq->profile->freq_table[i]
|
||||
== devfreq->previous_freq) {
|
||||
len += sprintf(buf + len, "*");
|
||||
} else {
|
||||
len += sprintf(buf + len, " ");
|
||||
}
|
||||
len += sprintf(buf + len, "%8u:",
|
||||
devfreq->profile->freq_table[i]);
|
||||
for (j = 0; j < max_state; j++)
|
||||
len += sprintf(buf + len, "%8u",
|
||||
devfreq->trans_table[(i * max_state) + j]);
|
||||
len += sprintf(buf + len, "%10u\n",
|
||||
jiffies_to_msecs(devfreq->time_in_state[i]));
|
||||
}
|
||||
|
||||
len += sprintf(buf + len, "Total transition : %u\n",
|
||||
devfreq->total_trans);
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct device_attribute devfreq_attrs[] = {
|
||||
__ATTR(governor, S_IRUGO, show_governor, NULL),
|
||||
__ATTR(governor, S_IRUGO | S_IWUSR, show_governor, store_governor),
|
||||
__ATTR(available_governors, S_IRUGO, show_available_governors, NULL),
|
||||
__ATTR(cur_freq, S_IRUGO, show_freq, NULL),
|
||||
__ATTR(available_frequencies, S_IRUGO, show_available_freqs, NULL),
|
||||
__ATTR(target_freq, S_IRUGO, show_target_freq, NULL),
|
||||
__ATTR(polling_interval, S_IRUGO | S_IWUSR, show_polling_interval,
|
||||
store_polling_interval),
|
||||
__ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq),
|
||||
__ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq),
|
||||
__ATTR(trans_stat, S_IRUGO, show_trans_table, NULL),
|
||||
{ },
|
||||
};
|
||||
|
||||
|
||||
@@ -987,7 +987,7 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
int err = 0;
|
||||
|
||||
data = kzalloc(sizeof(struct busfreq_data), GFP_KERNEL);
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct busfreq_data), GFP_KERNEL);
|
||||
if (data == NULL) {
|
||||
dev_err(dev, "Cannot allocate memory.\n");
|
||||
return -ENOMEM;
|
||||
@@ -1012,31 +1012,26 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev)
|
||||
err = -EINVAL;
|
||||
}
|
||||
if (err)
|
||||
goto err_regulator;
|
||||
return err;
|
||||
|
||||
data->vdd_int = regulator_get(dev, "vdd_int");
|
||||
data->vdd_int = devm_regulator_get(dev, "vdd_int");
|
||||
if (IS_ERR(data->vdd_int)) {
|
||||
dev_err(dev, "Cannot get the regulator \"vdd_int\"\n");
|
||||
err = PTR_ERR(data->vdd_int);
|
||||
goto err_regulator;
|
||||
return PTR_ERR(data->vdd_int);
|
||||
}
|
||||
if (data->type == TYPE_BUSF_EXYNOS4x12) {
|
||||
data->vdd_mif = regulator_get(dev, "vdd_mif");
|
||||
data->vdd_mif = devm_regulator_get(dev, "vdd_mif");
|
||||
if (IS_ERR(data->vdd_mif)) {
|
||||
dev_err(dev, "Cannot get the regulator \"vdd_mif\"\n");
|
||||
err = PTR_ERR(data->vdd_mif);
|
||||
regulator_put(data->vdd_int);
|
||||
goto err_regulator;
|
||||
|
||||
return PTR_ERR(data->vdd_mif);
|
||||
}
|
||||
}
|
||||
|
||||
opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq);
|
||||
if (IS_ERR(opp)) {
|
||||
dev_err(dev, "Invalid initial frequency %lu kHz.\n",
|
||||
exynos4_devfreq_profile.initial_freq);
|
||||
err = PTR_ERR(opp);
|
||||
goto err_opp_add;
|
||||
exynos4_devfreq_profile.initial_freq);
|
||||
return PTR_ERR(opp);
|
||||
}
|
||||
data->curr_opp = opp;
|
||||
|
||||
@@ -1045,30 +1040,20 @@ static __devinit int exynos4_busfreq_probe(struct platform_device *pdev)
|
||||
busfreq_mon_reset(data);
|
||||
|
||||
data->devfreq = devfreq_add_device(dev, &exynos4_devfreq_profile,
|
||||
&devfreq_simple_ondemand, NULL);
|
||||
if (IS_ERR(data->devfreq)) {
|
||||
err = PTR_ERR(data->devfreq);
|
||||
goto err_opp_add;
|
||||
}
|
||||
"simple_ondemand", NULL);
|
||||
if (IS_ERR(data->devfreq))
|
||||
return PTR_ERR(data->devfreq);
|
||||
|
||||
devfreq_register_opp_notifier(dev, data->devfreq);
|
||||
|
||||
err = register_pm_notifier(&data->pm_notifier);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to setup pm notifier\n");
|
||||
goto err_devfreq_add;
|
||||
devfreq_remove_device(data->devfreq);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_devfreq_add:
|
||||
devfreq_remove_device(data->devfreq);
|
||||
err_opp_add:
|
||||
if (data->vdd_mif)
|
||||
regulator_put(data->vdd_mif);
|
||||
regulator_put(data->vdd_int);
|
||||
err_regulator:
|
||||
kfree(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static __devexit int exynos4_busfreq_remove(struct platform_device *pdev)
|
||||
@@ -1077,10 +1062,6 @@ static __devexit int exynos4_busfreq_remove(struct platform_device *pdev)
|
||||
|
||||
unregister_pm_notifier(&data->pm_notifier);
|
||||
devfreq_remove_device(data->devfreq);
|
||||
regulator_put(data->vdd_int);
|
||||
if (data->vdd_mif)
|
||||
regulator_put(data->vdd_mif);
|
||||
kfree(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -34,4 +34,8 @@ extern void devfreq_monitor_suspend(struct devfreq *devfreq);
|
||||
extern void devfreq_monitor_resume(struct devfreq *devfreq);
|
||||
extern void devfreq_interval_update(struct devfreq *devfreq,
|
||||
unsigned int *delay);
|
||||
|
||||
extern int devfreq_add_governor(struct devfreq_governor *governor);
|
||||
extern int devfreq_remove_governor(struct devfreq_governor *governor);
|
||||
|
||||
#endif /* _GOVERNOR_H */
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/module.h>
|
||||
#include "governor.h"
|
||||
|
||||
static int devfreq_performance_func(struct devfreq *df,
|
||||
@@ -40,8 +41,27 @@ static int devfreq_performance_handler(struct devfreq *devfreq,
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct devfreq_governor devfreq_performance = {
|
||||
static struct devfreq_governor devfreq_performance = {
|
||||
.name = "performance",
|
||||
.get_target_freq = devfreq_performance_func,
|
||||
.event_handler = devfreq_performance_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_performance_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_performance);
|
||||
}
|
||||
subsys_initcall(devfreq_performance_init);
|
||||
|
||||
static void __exit devfreq_performance_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_performance);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_performance_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/module.h>
|
||||
#include "governor.h"
|
||||
|
||||
static int devfreq_powersave_func(struct devfreq *df,
|
||||
@@ -37,8 +38,27 @@ static int devfreq_powersave_handler(struct devfreq *devfreq,
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct devfreq_governor devfreq_powersave = {
|
||||
static struct devfreq_governor devfreq_powersave = {
|
||||
.name = "powersave",
|
||||
.get_target_freq = devfreq_powersave_func,
|
||||
.event_handler = devfreq_powersave_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_powersave_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_powersave);
|
||||
}
|
||||
subsys_initcall(devfreq_powersave_init);
|
||||
|
||||
static void __exit devfreq_powersave_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_powersave);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_powersave_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/math64.h>
|
||||
#include "governor.h"
|
||||
@@ -120,8 +121,27 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct devfreq_governor devfreq_simple_ondemand = {
|
||||
static struct devfreq_governor devfreq_simple_ondemand = {
|
||||
.name = "simple_ondemand",
|
||||
.get_target_freq = devfreq_simple_ondemand_func,
|
||||
.event_handler = devfreq_simple_ondemand_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_simple_ondemand_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_simple_ondemand);
|
||||
}
|
||||
subsys_initcall(devfreq_simple_ondemand_init);
|
||||
|
||||
static void __exit devfreq_simple_ondemand_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_simple_ondemand);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_simple_ondemand_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/devfreq.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include "governor.h"
|
||||
|
||||
struct userspace_data {
|
||||
@@ -135,8 +136,27 @@ static int devfreq_userspace_handler(struct devfreq *devfreq,
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct devfreq_governor devfreq_userspace = {
|
||||
static struct devfreq_governor devfreq_userspace = {
|
||||
.name = "userspace",
|
||||
.get_target_freq = devfreq_userspace_func,
|
||||
.event_handler = devfreq_userspace_handler,
|
||||
};
|
||||
|
||||
static int __init devfreq_userspace_init(void)
|
||||
{
|
||||
return devfreq_add_governor(&devfreq_userspace);
|
||||
}
|
||||
subsys_initcall(devfreq_userspace_init);
|
||||
|
||||
static void __exit devfreq_userspace_exit(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = devfreq_remove_governor(&devfreq_userspace);
|
||||
if (ret)
|
||||
pr_err("%s: failed remove governor %d\n", __func__, ret);
|
||||
|
||||
return;
|
||||
}
|
||||
module_exit(devfreq_userspace_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
+49
-44
@@ -25,12 +25,12 @@ struct devfreq;
|
||||
* struct devfreq_dev_status - Data given from devfreq user device to
|
||||
* governors. Represents the performance
|
||||
* statistics.
|
||||
* @total_time The total time represented by this instance of
|
||||
* @total_time: The total time represented by this instance of
|
||||
* devfreq_dev_status
|
||||
* @busy_time The time that the device was working among the
|
||||
* @busy_time: The time that the device was working among the
|
||||
* total_time.
|
||||
* @current_frequency The operating frequency.
|
||||
* @private_data An entry not specified by the devfreq framework.
|
||||
* @current_frequency: The operating frequency.
|
||||
* @private_data: An entry not specified by the devfreq framework.
|
||||
* A device and a specific governor may have their
|
||||
* own protocol with private_data. However, because
|
||||
* this is governor-specific, a governor using this
|
||||
@@ -54,25 +54,27 @@ struct devfreq_dev_status {
|
||||
|
||||
/**
|
||||
* struct devfreq_dev_profile - Devfreq's user device profile
|
||||
* @initial_freq The operating frequency when devfreq_add_device() is
|
||||
* @initial_freq: The operating frequency when devfreq_add_device() is
|
||||
* called.
|
||||
* @polling_ms The polling interval in ms. 0 disables polling.
|
||||
* @target The device should set its operating frequency at
|
||||
* @polling_ms: The polling interval in ms. 0 disables polling.
|
||||
* @target: The device should set its operating frequency at
|
||||
* freq or lowest-upper-than-freq value. If freq is
|
||||
* higher than any operable frequency, set maximum.
|
||||
* Before returning, target function should set
|
||||
* freq at the current frequency.
|
||||
* The "flags" parameter's possible values are
|
||||
* explained above with "DEVFREQ_FLAG_*" macros.
|
||||
* @get_dev_status The device should provide the current performance
|
||||
* @get_dev_status: The device should provide the current performance
|
||||
* status to devfreq, which is used by governors.
|
||||
* @get_cur_freq The device should provide the current frequency
|
||||
* @get_cur_freq: The device should provide the current frequency
|
||||
* at which it is operating.
|
||||
* @exit An optional callback that is called when devfreq
|
||||
* @exit: An optional callback that is called when devfreq
|
||||
* is removing the devfreq object due to error or
|
||||
* from devfreq_remove_device() call. If the user
|
||||
* has registered devfreq->nb at a notifier-head,
|
||||
* this is the time to unregister it.
|
||||
* @freq_table: Optional list of frequencies to support statistics.
|
||||
* @max_state: The size of freq_table.
|
||||
*/
|
||||
struct devfreq_dev_profile {
|
||||
unsigned long initial_freq;
|
||||
@@ -83,18 +85,22 @@ struct devfreq_dev_profile {
|
||||
struct devfreq_dev_status *stat);
|
||||
int (*get_cur_freq)(struct device *dev, unsigned long *freq);
|
||||
void (*exit)(struct device *dev);
|
||||
|
||||
unsigned int *freq_table;
|
||||
unsigned int max_state;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct devfreq_governor - Devfreq policy governor
|
||||
* @name Governor's name
|
||||
* @get_target_freq Returns desired operating frequency for the device.
|
||||
* @node: list node - contains registered devfreq governors
|
||||
* @name: Governor's name
|
||||
* @get_target_freq: Returns desired operating frequency for the device.
|
||||
* Basically, get_target_freq will run
|
||||
* devfreq_dev_profile.get_dev_status() to get the
|
||||
* status of the device (load = busy_time / total_time).
|
||||
* If no_central_polling is set, this callback is called
|
||||
* only with update_devfreq() notified by OPP.
|
||||
* @event_handler Callback for devfreq core framework to notify events
|
||||
* @event_handler: Callback for devfreq core framework to notify events
|
||||
* to governors. Events include per device governor
|
||||
* init and exit, opp changes out of devfreq, suspend
|
||||
* and resume of per device devfreq during device idle.
|
||||
@@ -102,6 +108,8 @@ struct devfreq_dev_profile {
|
||||
* Note that the callbacks are called with devfreq->lock locked by devfreq.
|
||||
*/
|
||||
struct devfreq_governor {
|
||||
struct list_head node;
|
||||
|
||||
const char name[DEVFREQ_NAME_LEN];
|
||||
int (*get_target_freq)(struct devfreq *this, unsigned long *freq);
|
||||
int (*event_handler)(struct devfreq *devfreq,
|
||||
@@ -110,23 +118,28 @@ struct devfreq_governor {
|
||||
|
||||
/**
|
||||
* struct devfreq - Device devfreq structure
|
||||
* @node list node - contains the devices with devfreq that have been
|
||||
* @node: list node - contains the devices with devfreq that have been
|
||||
* registered.
|
||||
* @lock a mutex to protect accessing devfreq.
|
||||
* @dev device registered by devfreq class. dev.parent is the device
|
||||
* @lock: a mutex to protect accessing devfreq.
|
||||
* @dev: device registered by devfreq class. dev.parent is the device
|
||||
* using devfreq.
|
||||
* @profile device-specific devfreq profile
|
||||
* @governor method how to choose frequency based on the usage.
|
||||
* @nb notifier block used to notify devfreq object that it should
|
||||
* @profile: device-specific devfreq profile
|
||||
* @governor: method how to choose frequency based on the usage.
|
||||
* @governor_name: devfreq governor name for use with this devfreq
|
||||
* @nb: notifier block used to notify devfreq object that it should
|
||||
* reevaluate operable frequencies. Devfreq users may use
|
||||
* devfreq.nb to the corresponding register notifier call chain.
|
||||
* @work delayed work for load monitoring.
|
||||
* @previous_freq previously configured frequency value.
|
||||
* @data Private data of the governor. The devfreq framework does not
|
||||
* @work: delayed work for load monitoring.
|
||||
* @previous_freq: previously configured frequency value.
|
||||
* @data: Private data of the governor. The devfreq framework does not
|
||||
* touch this.
|
||||
* @min_freq Limit minimum frequency requested by user (0: none)
|
||||
* @max_freq Limit maximum frequency requested by user (0: none)
|
||||
* @stop_polling devfreq polling status of a device.
|
||||
* @min_freq: Limit minimum frequency requested by user (0: none)
|
||||
* @max_freq: Limit maximum frequency requested by user (0: none)
|
||||
* @stop_polling: devfreq polling status of a device.
|
||||
* @total_trans: Number of devfreq transitions
|
||||
* @trans_table: Statistics of devfreq transitions
|
||||
* @time_in_state: Statistics of devfreq states
|
||||
* @last_stat_updated: The last time stat updated
|
||||
*
|
||||
* This structure stores the devfreq information for a give device.
|
||||
*
|
||||
@@ -143,6 +156,7 @@ struct devfreq {
|
||||
struct device dev;
|
||||
struct devfreq_dev_profile *profile;
|
||||
const struct devfreq_governor *governor;
|
||||
char governor_name[DEVFREQ_NAME_LEN];
|
||||
struct notifier_block nb;
|
||||
struct delayed_work work;
|
||||
|
||||
@@ -153,12 +167,18 @@ struct devfreq {
|
||||
unsigned long min_freq;
|
||||
unsigned long max_freq;
|
||||
bool stop_polling;
|
||||
|
||||
/* information for device freqeuncy transition */
|
||||
unsigned int total_trans;
|
||||
unsigned int *trans_table;
|
||||
unsigned long *time_in_state;
|
||||
unsigned long last_stat_updated;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_PM_DEVFREQ)
|
||||
extern struct devfreq *devfreq_add_device(struct device *dev,
|
||||
struct devfreq_dev_profile *profile,
|
||||
const struct devfreq_governor *governor,
|
||||
const char *governor_name,
|
||||
void *data);
|
||||
extern int devfreq_remove_device(struct devfreq *devfreq);
|
||||
extern int devfreq_suspend_device(struct devfreq *devfreq);
|
||||
@@ -172,23 +192,13 @@ extern int devfreq_register_opp_notifier(struct device *dev,
|
||||
extern int devfreq_unregister_opp_notifier(struct device *dev,
|
||||
struct devfreq *devfreq);
|
||||
|
||||
#ifdef CONFIG_DEVFREQ_GOV_POWERSAVE
|
||||
extern const struct devfreq_governor devfreq_powersave;
|
||||
#endif
|
||||
#ifdef CONFIG_DEVFREQ_GOV_PERFORMANCE
|
||||
extern const struct devfreq_governor devfreq_performance;
|
||||
#endif
|
||||
#ifdef CONFIG_DEVFREQ_GOV_USERSPACE
|
||||
extern const struct devfreq_governor devfreq_userspace;
|
||||
#endif
|
||||
#ifdef CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND
|
||||
extern const struct devfreq_governor devfreq_simple_ondemand;
|
||||
/**
|
||||
* struct devfreq_simple_ondemand_data - void *data fed to struct devfreq
|
||||
* and devfreq_add_device
|
||||
* @ upthreshold If the load is over this value, the frequency jumps.
|
||||
* @upthreshold: If the load is over this value, the frequency jumps.
|
||||
* Specify 0 to use the default. Valid value = 0 to 100.
|
||||
* @ downdifferential If the load is under upthreshold - downdifferential,
|
||||
* @downdifferential: If the load is under upthreshold - downdifferential,
|
||||
* the governor may consider slowing the frequency down.
|
||||
* Specify 0 to use the default. Valid value = 0 to 100.
|
||||
* downdifferential < upthreshold must hold.
|
||||
@@ -205,7 +215,7 @@ struct devfreq_simple_ondemand_data {
|
||||
#else /* !CONFIG_PM_DEVFREQ */
|
||||
static struct devfreq *devfreq_add_device(struct device *dev,
|
||||
struct devfreq_dev_profile *profile,
|
||||
struct devfreq_governor *governor,
|
||||
const char *governor_name,
|
||||
void *data)
|
||||
{
|
||||
return NULL;
|
||||
@@ -244,11 +254,6 @@ static int devfreq_unregister_opp_notifier(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define devfreq_powersave NULL
|
||||
#define devfreq_performance NULL
|
||||
#define devfreq_userspace NULL
|
||||
#define devfreq_simple_ondemand NULL
|
||||
|
||||
#endif /* CONFIG_PM_DEVFREQ */
|
||||
|
||||
#endif /* __LINUX_DEVFREQ_H__ */
|
||||
|
||||
Reference in New Issue
Block a user