summaryrefslogtreecommitdiff
path: root/drivers/devfreq/devfreq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/devfreq/devfreq.c')
-rw-r--r--drivers/devfreq/devfreq.c60
1 files changed, 50 insertions, 10 deletions
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index ca848cc6a8fd..844a8ad666a9 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -69,11 +69,34 @@ static struct devfreq *find_device_devfreq(struct device *dev)
}
/**
+ * devfreq_set_freq_limits() - Set min and max frequency from freq_table
+ * @devfreq: the devfreq instance
+ */
+static void devfreq_set_freq_limits(struct devfreq *devfreq)
+{
+ int idx;
+ unsigned long min = ~0, max = 0;
+
+ if (!devfreq->profile->freq_table)
+ return;
+
+ for (idx = 0; idx < devfreq->profile->max_state; idx++) {
+ if (min > devfreq->profile->freq_table[idx])
+ min = devfreq->profile->freq_table[idx];
+ if (max < devfreq->profile->freq_table[idx])
+ max = devfreq->profile->freq_table[idx];
+ }
+
+ devfreq->min_freq = min;
+ devfreq->max_freq = max;
+}
+
+/**
* 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 devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
{
int lev;
@@ -83,6 +106,7 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
return -EINVAL;
}
+EXPORT_SYMBOL(devfreq_get_freq_level);
/**
* devfreq_update_status() - Update statistics of devfreq behavior
@@ -172,7 +196,7 @@ int update_devfreq(struct devfreq *devfreq)
return -EINVAL;
/* Reevaluate the proper frequency */
- err = devfreq->governor->get_target_freq(devfreq, &freq);
+ err = devfreq->governor->get_target_freq(devfreq, &freq, &flags);
if (err)
return err;
@@ -486,6 +510,7 @@ struct devfreq *devfreq_add_device(struct device *dev,
devfreq->profile->max_state,
GFP_KERNEL);
devfreq->last_stat_updated = jiffies;
+ devfreq_set_freq_limits(devfreq);
dev_set_name(&devfreq->dev, "%s", dev_name(dev));
err = device_register(&devfreq->dev);
@@ -536,7 +561,6 @@ int devfreq_remove_device(struct devfreq *devfreq)
return -EINVAL;
device_unregister(&devfreq->dev);
- put_device(&devfreq->dev);
return 0;
}
@@ -782,7 +806,7 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
struct devfreq *df = to_devfreq(dev);
int ret;
char str_governor[DEVFREQ_NAME_LEN + 1];
- struct devfreq_governor *governor;
+ const struct devfreq_governor *governor, *prev_gov;
ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
if (ret != 1)
@@ -807,12 +831,21 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
goto out;
}
}
+ prev_gov = df->governor;
df->governor = governor;
strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
- if (ret)
+ if (ret) {
dev_warn(dev, "%s: Governor %s not started(%d)\n",
__func__, df->governor->name, ret);
+ if (prev_gov) {
+ df->governor = prev_gov;
+ strncpy(df->governor_name, prev_gov->name,
+ DEVFREQ_NAME_LEN);
+ df->governor->event_handler(df, DEVFREQ_GOV_START,
+ NULL);
+ }
+ }
out:
mutex_unlock(&devfreq_list_lock);
@@ -969,19 +1002,26 @@ static ssize_t available_frequencies_show(struct device *d,
struct devfreq *df = to_devfreq(d);
struct device *dev = df->dev.parent;
struct dev_pm_opp *opp;
+ unsigned int i = 0, max_state = df->profile->max_state;
+ bool use_opp;
ssize_t count = 0;
unsigned long freq = 0;
rcu_read_lock();
- do {
- opp = dev_pm_opp_find_freq_ceil(dev, &freq);
- if (IS_ERR(opp))
- break;
+ use_opp = dev_pm_opp_get_opp_count(dev) > 0;
+ while (use_opp || (!use_opp && i < max_state)) {
+ if (use_opp) {
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ if (IS_ERR(opp))
+ break;
+ } else {
+ freq = df->profile->freq_table[i++];
+ }
count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
"%lu ", freq);
freq++;
- } while (1);
+ }
rcu_read_unlock();
/* Truncate the trailing space */