summaryrefslogtreecommitdiff
path: root/drivers/cpufreq/cpufreq_interactive.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq/cpufreq_interactive.c')
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c334
1 files changed, 106 insertions, 228 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index baf4326e84c4..cf6ba6c1958e 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -30,8 +30,6 @@
#include <asm/cputime.h>
-static atomic_t active_count = ATOMIC_INIT(0);
-
struct cpufreq_interactive_cpuinfo {
struct timer_list cpu_timer;
int timer_idlecancel;
@@ -43,6 +41,7 @@ struct cpufreq_interactive_cpuinfo {
u64 freq_change_time;
u64 freq_change_time_in_idle;
u64 freq_change_time_in_iowait;
+ u64 last_high_freq_time;
struct cpufreq_policy *policy;
struct cpufreq_frequency_table *freq_table;
unsigned int target_freq;
@@ -60,6 +59,9 @@ static spinlock_t up_cpumask_lock;
static cpumask_t down_cpumask;
static spinlock_t down_cpumask_lock;
static struct mutex set_speed_lock;
+static struct mutex gov_state_lock;
+static struct kobject *interactive_kobj;
+static unsigned int active_count;
/* Go to max speed when CPU load at or above this value. */
#define DEFAULT_GO_MAXSPEED_LOAD 85
@@ -92,6 +94,24 @@ static unsigned long min_sample_time;
#define DEFAULT_TIMER_RATE 20000;
static unsigned long timer_rate;
+/*
+ * The minimum delay before frequency is allowed to raise over normal rate.
+ * Since it must remain at high frequency for a minimum of MIN_SAMPLE_TIME
+ * once it rises, setting this delay to a multiple of MIN_SAMPLE_TIME
+ * becomes the best way to enforce a square wave.
+ * e.g. 5*MIN_SAMPLE_TIME = 20% high freq duty cycle
+ */
+#define DEFAULT_HIGH_FREQ_MIN_DELAY 5*DEFAULT_MIN_SAMPLE_TIME
+static unsigned long high_freq_min_delay;
+
+/*
+ * The maximum frequency CPUs are allowed to run normally
+ * 0 if disabled
+ */
+#define DEFAULT_MAX_NORMAL_FREQ 0
+static unsigned long max_normal_freq;
+
+
/* Defines to control mid-range frequencies */
#define DEFAULT_MID_RANGE_GO_MAXSPEED_LOAD 95
@@ -274,6 +294,20 @@ static void cpufreq_interactive_timer(unsigned long data)
goto rearm;
}
+ /*
+ * Can only overclock if the delay is satisfy. Otherwise, cap it to
+ * maximum allowed normal frequency
+ */
+ if (max_normal_freq && (new_freq > max_normal_freq)) {
+ if (cputime64_sub(pcpu->timer_run_time, pcpu->last_high_freq_time)
+ < high_freq_min_delay) {
+ new_freq = max_normal_freq;
+ }
+ else {
+ pcpu->last_high_freq_time = pcpu->timer_run_time;
+ }
+ }
+
if (new_freq < pcpu->target_freq) {
pcpu->target_freq = new_freq;
spin_lock_irqsave(&down_cpumask_lock, flags);
@@ -520,220 +554,43 @@ static void cpufreq_interactive_freq_down(struct work_struct *work)
}
}
-static ssize_t show_go_maxspeed_load(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", go_maxspeed_load);
-}
-
-static ssize_t store_go_maxspeed_load(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- go_maxspeed_load = val;
- return count;
-}
-
-static struct global_attr go_maxspeed_load_attr = __ATTR(go_maxspeed_load, 0644,
- show_go_maxspeed_load, store_go_maxspeed_load);
-
-static ssize_t show_midrange_freq(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", midrange_freq);
-}
-
-static ssize_t store_midrange_freq(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- midrange_freq = val;
- return count;
-}
-
-static struct global_attr midrange_freq_attr = __ATTR(midrange_freq, 0644,
- show_midrange_freq, store_midrange_freq);
-
-static ssize_t show_midrange_go_maxspeed_load(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", midrange_go_maxspeed_load);
-}
-
-static ssize_t store_midrange_go_maxspeed_load(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- midrange_go_maxspeed_load = val;
- return count;
-}
-
-static struct global_attr midrange_go_maxspeed_load_attr = __ATTR(midrange_go_maxspeed_load, 0644,
- show_midrange_go_maxspeed_load, store_midrange_go_maxspeed_load);
-
-static ssize_t show_boost_factor(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", boost_factor);
-}
-
-static ssize_t store_boost_factor(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- boost_factor = val;
- return count;
-}
-
-static struct global_attr boost_factor_attr = __ATTR(boost_factor, 0644,
- show_boost_factor, store_boost_factor);
-
-static ssize_t show_io_is_busy(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", io_is_busy);
-}
-
-static ssize_t store_io_is_busy(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- if (!strict_strtoul(buf, 0, &io_is_busy))
- return count;
- return -EINVAL;
-}
-
-static struct global_attr io_is_busy_attr = __ATTR(io_is_busy, 0644,
- show_io_is_busy, store_io_is_busy);
-
-static ssize_t show_max_boost(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", max_boost);
-}
-
-static ssize_t store_max_boost(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- max_boost = val;
- return count;
-}
-
-static struct global_attr max_boost_attr = __ATTR(max_boost, 0644,
- show_max_boost, store_max_boost);
-
-static ssize_t show_midrange_max_boost(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", midrange_max_boost);
-}
-
-static ssize_t store_midrange_max_boost(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- midrange_max_boost = val;
- return count;
-}
-
-static struct global_attr midrange_max_boost_attr = __ATTR(midrange_max_boost, 0644,
- show_midrange_max_boost, store_midrange_max_boost);
-
-static ssize_t show_sustain_load(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", sustain_load);
-}
-
-static ssize_t store_sustain_load(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- sustain_load = val;
- return count;
-}
-
-static struct global_attr sustain_load_attr = __ATTR(sustain_load, 0644,
- show_sustain_load, store_sustain_load);
-
-static ssize_t show_min_sample_time(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", min_sample_time);
-}
-
-static ssize_t store_min_sample_time(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- min_sample_time = val;
- return count;
-}
-
-static struct global_attr min_sample_time_attr = __ATTR(min_sample_time, 0644,
- show_min_sample_time, store_min_sample_time);
-
-static ssize_t show_timer_rate(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- return sprintf(buf, "%lu\n", timer_rate);
-}
-
-static ssize_t store_timer_rate(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t count)
-{
- int ret;
- unsigned long val;
-
- ret = strict_strtoul(buf, 0, &val);
- if (ret < 0)
- return ret;
- timer_rate = val;
- return count;
-}
-
-static struct global_attr timer_rate_attr = __ATTR(timer_rate, 0644,
- show_timer_rate, store_timer_rate);
+#define DECL_CPUFREQ_INTERACTIVE_ATTR(name) \
+static ssize_t show_##name(struct kobject *kobj, \
+ struct attribute *attr, char *buf) \
+{ \
+ return sprintf(buf, "%lu\n", name); \
+} \
+\
+static ssize_t store_##name(struct kobject *kobj,\
+ struct attribute *attr, const char *buf, size_t count) \
+{ \
+ int ret; \
+ unsigned long val; \
+\
+ ret = strict_strtoul(buf, 0, &val); \
+ if (ret < 0) \
+ return ret; \
+ name = val; \
+ return count; \
+} \
+\
+static struct global_attr name##_attr = __ATTR(name, 0644, \
+ show_##name, store_##name);
+
+DECL_CPUFREQ_INTERACTIVE_ATTR(go_maxspeed_load)
+DECL_CPUFREQ_INTERACTIVE_ATTR(midrange_freq)
+DECL_CPUFREQ_INTERACTIVE_ATTR(midrange_go_maxspeed_load)
+DECL_CPUFREQ_INTERACTIVE_ATTR(boost_factor)
+DECL_CPUFREQ_INTERACTIVE_ATTR(io_is_busy)
+DECL_CPUFREQ_INTERACTIVE_ATTR(max_boost)
+DECL_CPUFREQ_INTERACTIVE_ATTR(midrange_max_boost)
+DECL_CPUFREQ_INTERACTIVE_ATTR(sustain_load)
+DECL_CPUFREQ_INTERACTIVE_ATTR(min_sample_time)
+DECL_CPUFREQ_INTERACTIVE_ATTR(timer_rate)
+DECL_CPUFREQ_INTERACTIVE_ATTR(high_freq_min_delay)
+DECL_CPUFREQ_INTERACTIVE_ATTR(max_normal_freq)
+
+#undef DECL_CPUFREQ_INTERACTIVE_ATTR
static struct attribute *interactive_attributes[] = {
&go_maxspeed_load_attr.attr,
@@ -746,6 +603,8 @@ static struct attribute *interactive_attributes[] = {
&sustain_load_attr.attr,
&min_sample_time_attr.attr,
&timer_rate_attr.attr,
+ &high_freq_min_delay_attr.attr,
+ &max_normal_freq_attr.attr,
NULL,
};
@@ -783,7 +642,8 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
pcpu->freq_change_time_in_iowait =
get_cpu_iowait_time(j, NULL);
pcpu->time_in_iowait = pcpu->freq_change_time_in_iowait;
-
+ if (!pcpu->last_high_freq_time)
+ pcpu->last_high_freq_time = pcpu->freq_change_time;
pcpu->timer_idlecancel = 1;
pcpu->governor_enabled = 1;
smp_wmb();
@@ -792,17 +652,25 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
mod_timer(&pcpu->cpu_timer, jiffies + 2);
}
+ mutex_lock(&gov_state_lock);
+ active_count++;
/*
* Do not register the idle hook and create sysfs
* entries if we have already done so.
*/
- if (atomic_inc_return(&active_count) > 1)
- return 0;
-
- rc = sysfs_create_group(cpufreq_global_kobject,
- &interactive_attr_group);
- if (rc)
- return rc;
+ if (active_count == 1) {
+ rc = sysfs_create_group(cpufreq_global_kobject,
+ &interactive_attr_group);
+ interactive_kobj = kobject_create_and_add(
+ "gov_interactive",
+ cpufreq_global_kobject);
+ kobject_uevent(interactive_kobj, KOBJ_ADD);
+ if (rc) {
+ mutex_unlock(&gov_state_lock);
+ return rc;
+ }
+ }
+ mutex_unlock(&gov_state_lock);
break;
@@ -823,11 +691,18 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
}
flush_work(&freq_scale_down_work);
- if (atomic_dec_return(&active_count) > 0)
- return 0;
+ mutex_lock(&gov_state_lock);
+
+ active_count--;
+
+ if (active_count == 0) {
+ sysfs_remove_group(cpufreq_global_kobject,
+ &interactive_attr_group);
+ kobject_uevent(interactive_kobj, KOBJ_REMOVE);
+ kobject_put(interactive_kobj);
+ }
- sysfs_remove_group(cpufreq_global_kobject,
- &interactive_attr_group);
+ mutex_unlock(&gov_state_lock);
break;
@@ -873,6 +748,8 @@ static int __init cpufreq_interactive_init(void)
midrange_go_maxspeed_load = DEFAULT_MID_RANGE_GO_MAXSPEED_LOAD;
min_sample_time = DEFAULT_MIN_SAMPLE_TIME;
timer_rate = DEFAULT_TIMER_RATE;
+ high_freq_min_delay = DEFAULT_HIGH_FREQ_MIN_DELAY;
+ max_normal_freq = DEFAULT_MAX_NORMAL_FREQ;
/* Initalize per-cpu timers */
for_each_possible_cpu(i) {
@@ -903,6 +780,7 @@ static int __init cpufreq_interactive_init(void)
spin_lock_init(&up_cpumask_lock);
spin_lock_init(&down_cpumask_lock);
mutex_init(&set_speed_lock);
+ mutex_init(&gov_state_lock);
idle_notifier_register(&cpufreq_interactive_idle_nb);