From adb7181f83a20fce3317a7b4ee7ea8958b5363b8 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Tue, 30 Sep 2014 18:48:21 -0700 Subject: [PATCH] PM / devfreq: Add timeout feature for cpufreq governor Some devfreq devices might need their frequency to be set only for a short time after the CPU frequency changes. For such devices, add a "timeout" tunable that determines for how many milliseconds the governor sets the device frequency based on the CPU frequency. After "timeout" milliseconds from the CPU frequency change, the governor will set the device frequency back to its minimum value. Change-Id: I17fc972c0b03fab781864ce735013710c2df4647 Signed-off-by: Saravana Kannan --- drivers/devfreq/governor_cpufreq.c | 116 +++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 14 deletions(-) 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) {