diff options
Diffstat (limited to 'drivers/devfreq/devfreq.c')
-rw-r--r-- | drivers/devfreq/devfreq.c | 60 |
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 */ |