From e1cdf797f3e137ff4481d8cc42790de80a36d300 Mon Sep 17 00:00:00 2001 From: Simon Shields Date: Fri, 19 Feb 2016 22:37:57 +1100 Subject: 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 --- drivers/cpufreq/Kconfig | 5 ++ drivers/cpufreq/cpufreq_pegasusq.c | 168 ++++++++++++++++++++++++++++++++++++- 2 files changed, 172 insertions(+), 1 deletion(-) (limited to 'drivers/cpufreq') 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; -- cgit v1.1