diff --git a/drivers/devfreq/governor_cpufreq.c b/drivers/devfreq/governor_cpufreq.c index 2ca000920349..554d673c566d 100644 --- a/drivers/devfreq/governor_cpufreq.c +++ b/drivers/devfreq/governor_cpufreq.c @@ -46,25 +46,98 @@ struct devfreq_node { struct list_head list; struct freq_map **map; struct freq_map *common_map; + unsigned int timeout; + struct delayed_work dwork; + bool drop; + unsigned long prev_tgt; }; static LIST_HEAD(devfreq_list); static DEFINE_MUTEX(state_lock); +#define show_attr(name) \ +static ssize_t show_##name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct devfreq *df = to_devfreq(dev); \ + struct devfreq_node *n = df->data; \ + return snprintf(buf, PAGE_SIZE, "%u\n", n->name); \ +} + +#define store_attr(name, _min, _max) \ +static ssize_t store_##name(struct device *dev, \ + struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct devfreq *df = to_devfreq(dev); \ + struct devfreq_node *n = df->data; \ + int ret; \ + unsigned int val; \ + ret = sscanf(buf, "%u", &val); \ + if (ret != 1) \ + return -EINVAL; \ + val = max(val, _min); \ + val = min(val, _max); \ + n->name = val; \ + return count; \ +} + +#define gov_attr(__attr, min, max) \ +show_attr(__attr) \ +store_attr(__attr, min, max) \ +static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) + +static int update_node(struct devfreq_node *node) +{ + int ret; + struct devfreq *df = node->df; + + if (!df) + return 0; + + cancel_delayed_work_sync(&node->dwork); + + mutex_lock(&df->lock); + node->drop = false; + ret = update_devfreq(df); + if (ret) { + dev_err(df->dev.parent, "Unable to update frequency\n"); + goto out; + } + + if (!node->timeout) + goto out; + + if (df->previous_freq <= df->min_freq) + goto out; + + schedule_delayed_work(&node->dwork, + msecs_to_jiffies(node->timeout)); +out: + mutex_unlock(&df->lock); + return ret; +} + static void update_all_devfreqs(void) { struct devfreq_node *node; list_for_each_entry(node, &devfreq_list, list) { - struct devfreq *df = node->df; - if (!node->df) - continue; - mutex_lock(&df->lock); - update_devfreq(df); - mutex_unlock(&df->lock); - + update_node(node); } } +static void do_timeout(struct work_struct *work) +{ + struct devfreq_node *node = container_of(to_delayed_work(work), + struct devfreq_node, dwork); + struct devfreq *df = node->df; + + mutex_lock(&df->lock); + node->drop = true; + update_devfreq(df); + mutex_unlock(&df->lock); +} + static struct devfreq_node *find_devfreq_node(struct device *dev) { struct devfreq_node *node; @@ -300,10 +373,21 @@ static int devfreq_cpufreq_get_freq(struct devfreq *df, return -ENODEV; } + if (node->drop) { + *freq = 0; + return 0; + } + for_each_possible_cpu(cpu) tgt_freq = max(tgt_freq, cpu_to_dev_freq(df, cpu)); - *freq = tgt_freq; + if (node->timeout && tgt_freq < node->prev_tgt) + *freq = 0; + else + *freq = tgt_freq; + + node->prev_tgt = tgt_freq; + return 0; } @@ -362,8 +446,11 @@ static ssize_t show_map(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(freq_map, 0444, show_map, NULL); +gov_attr(timeout, 0U, 100U); + static struct attribute *dev_attr[] = { &dev_attr_freq_map.attr, + &dev_attr_timeout.attr, NULL, }; @@ -402,17 +489,16 @@ static int devfreq_cpufreq_gov_start(struct devfreq *devfreq) node->dev = devfreq->dev.parent; list_add_tail(&node->list, &devfreq_list); } + + INIT_DELAYED_WORK(&node->dwork, do_timeout); + node->df = devfreq; node->orig_data = devfreq->data; devfreq->data = node; - mutex_lock(&devfreq->lock); - ret = update_devfreq(devfreq); - mutex_unlock(&devfreq->lock); - if (ret) { - pr_err("Freq update failed!\n"); + ret = update_node(node); + if (ret) goto update_fail; - } mutex_unlock(&state_lock); return 0; @@ -434,6 +520,8 @@ static void devfreq_cpufreq_gov_stop(struct devfreq *devfreq) { struct devfreq_node *node = devfreq->data; + cancel_delayed_work_sync(&node->dwork); + mutex_lock(&state_lock); devfreq->data = node->orig_data; if (node->map || node->common_map) {