aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Shields <keepcalm444@gmail.com>2016-02-19 22:37:57 +1100
committerWolfgang Wiedmeyer <wolfgit@wiedmeyer.de>2016-03-18 01:51:05 +0100
commite1cdf797f3e137ff4481d8cc42790de80a36d300 (patch)
treef8db15382597b0ee95d88b3ad8c950431392a6a7
parent767acb8c1b427e5cc361a47e5580c0bdcb8defa9 (diff)
downloadkernel_samsung_smdk4412-e1cdf797f3e137ff4481d8cc42790de80a36d300.zip
kernel_samsung_smdk4412-e1cdf797f3e137ff4481d8cc42790de80a36d300.tar.gz
kernel_samsung_smdk4412-e1cdf797f3e137ff4481d8cc42790de80a36d300.tar.bz2
cpufreq: pegasusq: add support for setting a boost freq/cpulock
Currently, the pegasusq cpufreq driver has no way of allowing a userspace component (such as a powerhal) to indicate that the cpu should be brought to a higher clockspeed in anticipation of user input (such as on POWER_HINT_INTERACTION). This commit adds three interfaces to pegasusq, boost_freq and boost_mincpus, and boost_lock_time. boost_freq sets the minimum frequency for the cpus held online, and boost_mincpus specifies the minimum number of cpus to hold online. boost_lock_time is measured in nanoseconds, and is the amount of time to wait before restoring normal control. boost_mincpus and boost_freq will not have an effect until boost_lock_time != 0. Reading from boost_lock_time will return 1 if there is currently a boost, and 0 if there is not a boost. Writing 0 will cancel any current boost, and writing a non-zero number will cancel any current boost and begin boosting for that amount of time. Writing -1 will indefinitely boost until another value is written. This feature is hidden behind the CPU_FREQ_GOV_PEGASUSQ_BOOST Kconfig flag. Change-Id: I6e81dd3ec3af5d1408b99e136e5a63789b79dfbe
-rw-r--r--drivers/cpufreq/Kconfig5
-rw-r--r--drivers/cpufreq/cpufreq_pegasusq.c168
2 files changed, 172 insertions, 1 deletions
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 8112af3..578f807 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -266,6 +266,11 @@ config CPU_FREQ_GOV_ADAPTIVE
config CPU_FREQ_GOV_PEGASUSQ
tristate "'pegasusq' cpufreq policy governor"
+config CPU_FREQ_GOV_PEGASUSQ_BOOST
+ bool "pegasusq - enable suport for userspace-controlled cpu boosts"
+ depends on CPU_FREQ_GOV_PEGASUSQ
+ default n
+
config CPU_FREQ_GOV_SLP
tristate "'slp' cpufreq policy governor"
diff --git a/drivers/cpufreq/cpufreq_pegasusq.c b/drivers/cpufreq/cpufreq_pegasusq.c
index c44af54..107b4e5 100644
--- a/drivers/cpufreq/cpufreq_pegasusq.c
+++ b/drivers/cpufreq/cpufreq_pegasusq.c
@@ -194,6 +194,27 @@ static int hotplug_freq[4][2] = {
};
#endif
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+static unsigned int is_boosting = 0;
+static struct hrtimer boost_timer;
+static DEFINE_MUTEX(boost_mutex);
+
+static void finish_boost_do_work(struct work_struct *work) {
+ mutex_lock(&boost_mutex);
+ is_boosting = 0;
+ boost_timer.function = NULL;
+ mutex_unlock(&boost_mutex);
+ printk(KERN_DEBUG "[boost] ended boost\n");
+}
+
+static DECLARE_WORK(finish_boost_work, finish_boost_do_work);
+
+static enum hrtimer_restart end_boost(struct hrtimer *timer) {
+ schedule_work(&finish_boost_work);
+ return HRTIMER_NORESTART;
+}
+#endif
+
static unsigned int min_sampling_rate;
static void do_dbs_timer(struct work_struct *work);
@@ -262,6 +283,10 @@ static struct dbs_tuners {
unsigned int dvfs_debug;
unsigned int max_freq;
unsigned int min_freq;
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ unsigned int boost_freq;
+ unsigned int boost_mincpus;
+#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
int early_suspend;
#endif
@@ -485,6 +510,17 @@ show_one(up_nr_cpus, up_nr_cpus);
show_one(max_cpu_lock, max_cpu_lock);
show_one(min_cpu_lock, min_cpu_lock);
show_one(dvfs_debug, dvfs_debug);
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+show_one(boost_freq, boost_freq);
+show_one(boost_mincpus, boost_mincpus);
+
+static ssize_t show_boost_lock_time(struct kobject *kobj,
+ struct attribute *attr, char *buf) {
+ return sprintf(buf, "%d\n", is_boosting);
+}
+
+#endif
+
static ssize_t show_hotplug_lock(struct kobject *kobj,
struct attribute *attr, char *buf)
{
@@ -496,7 +532,6 @@ static ssize_t show_cpucore_table(struct kobject *kobj,
{
ssize_t count = 0;
int i;
-
for (i = CONFIG_NR_CPUS; i > 0; i--) {
count += sprintf(&buf[count], "%d ", i);
}
@@ -819,6 +854,72 @@ static ssize_t store_dvfs_debug(struct kobject *a, struct attribute *b,
return count;
}
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+static ssize_t store_boost_freq(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ ret = sscanf(buf, "%u", &input);
+ if (ret != 1)
+ return -EINVAL;
+
+ dbs_tuners_ins.boost_freq = input;
+ return count;
+}
+
+static ssize_t store_boost_mincpus(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ unsigned int input;
+ int ret;
+
+ ret = sscanf(buf, "%u", &input);
+ if (ret != 1)
+ return -EINVAL;
+
+ dbs_tuners_ins.boost_mincpus = min(input, 4u);
+ return count;
+}
+static ssize_t store_boost_lock_time(struct kobject *a, struct attribute *b,
+ const char *buf, size_t count)
+{
+ long input;
+ int ret;
+ ktime_t time;
+ ret = sscanf(buf, "%ld", &input);
+ if (ret != 1 || input < -1)
+ return -EINVAL;
+ mutex_lock(&boost_mutex);
+ if (input != 0) {
+ if (boost_timer.function != NULL) {
+ // ensure last boost is cancelled
+ hrtimer_cancel(&boost_timer);
+ }
+ is_boosting = 1;
+ if (input != -1) {
+ time = ktime_set(0, (unsigned long)input);
+ hrtimer_init(&boost_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ boost_timer.function = &end_boost;
+ printk(KERN_DEBUG "[boost] starting boost lock: %lu\n", input);
+ hrtimer_start(&boost_timer, time, HRTIMER_MODE_REL);
+ }
+ } else {
+ printk(KERN_DEBUG "[boost] cancelling boost lock");
+ if (boost_timer.function != NULL) {
+ hrtimer_cancel(&boost_timer);
+ }
+ is_boosting = 0;
+ }
+ mutex_unlock(&boost_mutex);
+
+
+ return count;
+}
+#endif
+
+
define_one_global_rw(sampling_rate);
define_one_global_rw(io_is_busy);
define_one_global_rw(up_threshold);
@@ -836,6 +937,11 @@ define_one_global_rw(min_cpu_lock);
define_one_global_rw(hotplug_lock);
define_one_global_rw(dvfs_debug);
define_one_global_ro(cpucore_table);
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+define_one_global_rw(boost_freq);
+define_one_global_rw(boost_mincpus);
+define_one_global_rw(boost_lock_time);
+#endif
static struct attribute *dbs_attributes[] = {
&sampling_rate_min.attr,
@@ -870,6 +976,11 @@ static struct attribute *dbs_attributes[] = {
&hotplug_rq_3_1.attr,
&hotplug_rq_4_0.attr,
&cpucore_table.attr,
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ &boost_freq.attr,
+ &boost_mincpus.attr,
+ &boost_lock_time.attr,
+#endif
NULL
};
@@ -886,6 +997,9 @@ static void cpu_up_work(struct work_struct *work)
int online = num_online_cpus();
int nr_up = dbs_tuners_ins.up_nr_cpus;
int min_cpu_lock = dbs_tuners_ins.min_cpu_lock;
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ int boost_mincpus = dbs_tuners_ins.boost_mincpus;
+#endif
int hotplug_lock = atomic_read(&g_hotplug_lock);
if (hotplug_lock && min_cpu_lock)
@@ -894,6 +1008,12 @@ static void cpu_up_work(struct work_struct *work)
nr_up = hotplug_lock - online;
else if (min_cpu_lock)
nr_up = max(nr_up, min_cpu_lock - online);
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ if (is_boosting && boost_mincpus) {
+ printk(KERN_DEBUG "[PEGASUSQ_BOOST] boost mincpus to %d", boost_mincpus);
+ nr_up = max(nr_up, boost_mincpus - online);
+ }
+#endif
if (online == 1) {
printk(KERN_ERR "CPU_UP 3\n");
@@ -921,6 +1041,11 @@ static void cpu_down_work(struct work_struct *work)
if (hotplug_lock)
nr_down = online - hotplug_lock;
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ if (is_boosting && dbs_tuners_ins.boost_mincpus)
+ nr_down = min(nr_down, online - (int)dbs_tuners_ins.boost_mincpus);
+#endif
+
for_each_online_cpu(cpu) {
if (cpu == 0)
continue;
@@ -988,6 +1113,12 @@ static int check_up(void)
&& online < dbs_tuners_ins.min_cpu_lock)
return 1;
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ if (is_boosting && dbs_tuners_ins.boost_mincpus != 0
+ && online < dbs_tuners_ins.boost_mincpus)
+ return 1;
+#endif
+
if (num_hist == 0 || num_hist % up_rate)
return 0;
@@ -1033,6 +1164,14 @@ static int check_down(void)
down_freq = hotplug_freq[online - 1][HOTPLUG_DOWN_INDEX];
down_rq = hotplug_rq[online - 1][HOTPLUG_DOWN_INDEX];
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ /* don't bother trying to turn off cpu if we're not done boosting yet,
+ * but allow turning off cpus above minimum */
+ if (is_boosting && dbs_tuners_ins.boost_mincpus != 0
+ && online <= dbs_tuners_ins.boost_mincpus)
+ return 0;
+#endif
+
if (online == 1)
return 0;
@@ -1169,6 +1308,21 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
up_threshold = UP_THRESHOLD_AT_MIN_FREQ;
}
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ if (is_boosting && policy->cur < dbs_tuners_ins.boost_freq) {
+ printk(KERN_DEBUG "[PEGASUSQ_BOOST] boosting to %d\n", dbs_tuners_ins.boost_freq);
+ /* disallow boosting beyond max freq */
+ int target = min(policy->max, dbs_tuners_ins.boost_freq);
+ dbs_tuners_ins.boost_freq = target;
+ /* If switching to max speed, apply sampling_down_factor */
+ if (policy->cur < policy->max && target == policy->max)
+ this_dbs_info->rate_mult =
+ dbs_tuners_ins.sampling_down_factor;
+ dbs_freq_increase(policy, target);
+ return;
+ }
+#endif
+
if (max_load_freq > up_threshold * policy->cur) {
int inc = (policy->max * dbs_tuners_ins.freq_step) / 100;
int target = min(policy->max, policy->cur + inc);
@@ -1187,6 +1341,12 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
return;
#endif
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ /* don't bother trying to downclock below boost_freq */
+ if (is_boosting && policy->cur <= dbs_tuners_ins.boost_freq)
+ return;
+#endif
+
/*
* The optimal frequency is the frequency that is the lowest that
* can support the current CPU usage without triggering the up
@@ -1217,6 +1377,12 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
&& (max_load_freq / freq_next) > down_thres)
freq_next = FREQ_FOR_RESPONSIVENESS;
+#ifdef CONFIG_CPU_FREQ_GOV_PEGASUSQ_BOOST
+ if (is_boosting)
+ freq_next = max(freq_next, dbs_tuners_ins.boost_freq);
+
+#endif
+
if (policy->cur == freq_next)
return;