mirror of https://github.com/OpenIPC/firmware.git
1422 lines
35 KiB
Diff
1422 lines
35 KiB
Diff
diff -drupN a/drivers/cpufreq/autohotplug.c b/drivers/cpufreq/autohotplug.c
|
|
--- a/drivers/cpufreq/autohotplug.c 1970-01-01 03:00:00.000000000 +0300
|
|
+++ b/drivers/cpufreq/autohotplug.c 2022-06-12 05:28:14.000000000 +0300
|
|
@@ -0,0 +1,1417 @@
|
|
+/*
|
|
+ * drivers/cpufreq/autohotplug.c
|
|
+ *
|
|
+ * Copyright (C) 2016-2020 Allwinnertech.
|
|
+ * East Yang <yangdong@allwinnertech.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; either version 2 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ */
|
|
+
|
|
+#include <linux/cpu.h>
|
|
+#include <linux/cpumask.h>
|
|
+#include <linux/cpufreq.h>
|
|
+#include <linux/freezer.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/sched/rt.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/kthread.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/reboot.h>
|
|
+#include <asm/uaccess.h>
|
|
+#include "autohotplug.h"
|
|
+
|
|
+#define CREATE_TRACE_POINTS
|
|
+#include <trace/events/autohotplug.h>
|
|
+
|
|
+#define AUTOHOTPLUG_ERR(format, args...) \
|
|
+ pr_err("[autohotplug] ERR:"format, ##args)
|
|
+
|
|
+static struct autohotplug_governor *cur_governor;
|
|
+static struct autohotplug_data_struct autohotplug_data;
|
|
+static DEFINE_PER_CPU(struct autohotplug_cpuinfo, cpuinfo);
|
|
+static struct autohotplug_governor_loadinfo governor_load;
|
|
+
|
|
+static struct cpumask autohotplug_fast_cpumask;
|
|
+static struct cpumask autohotplug_slow_cpumask;
|
|
+
|
|
+static struct task_struct *autohotplug_task;
|
|
+static struct timer_list hotplug_task_timer;
|
|
+static struct timer_list hotplug_sample_timer;
|
|
+
|
|
+static struct mutex hotplug_enable_mutex;
|
|
+static spinlock_t hotplug_load_lock;
|
|
+static spinlock_t cpumask_lock;
|
|
+
|
|
+static atomic_t hotplug_lock = ATOMIC_INIT(0);
|
|
+static atomic_t g_hotplug_lock = ATOMIC_INIT(0);
|
|
+
|
|
+static unsigned int hotplug_enable;
|
|
+static unsigned int boost_all_online;
|
|
+static unsigned int hotplug_period_us = 200000;
|
|
+static unsigned int hotplug_sample_us = 20000;
|
|
+static unsigned int cpu_up_lastcpu = INVALID_CPU;
|
|
+static unsigned int total_nr_cpus = CONFIG_NR_CPUS;
|
|
+
|
|
+static unsigned int cpu_up_last_hold_us = 1500000;
|
|
+
|
|
+unsigned int load_try_down = 30;
|
|
+unsigned int load_try_up = 80;
|
|
+
|
|
+unsigned int load_up_stable_us = 200000;
|
|
+unsigned int load_down_stable_us = 1000000;
|
|
+
|
|
+unsigned int load_last_big_min_freq = 300000;
|
|
+unsigned long cpu_up_lasttime;
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_STATS
|
|
+static unsigned long cpu_on_lasttime[CONFIG_NR_CPUS];
|
|
+static unsigned long cpu_on_time_total[CONFIG_NR_CPUS];
|
|
+static unsigned long cpu_up_count_total[CONFIG_NR_CPUS];
|
|
+static unsigned long cpu_down_count_total[CONFIG_NR_CPUS];
|
|
+
|
|
+static char *cpu_on_time_total_sysfs[] = {
|
|
+ "cpu0_on_time",
|
|
+ "cpu1_on_time",
|
|
+ "cpu2_on_time",
|
|
+ "cpu3_on_time",
|
|
+ "cpu4_on_time",
|
|
+ "cpu5_on_time",
|
|
+ "cpu6_on_time",
|
|
+ "cpu7_on_time"
|
|
+};
|
|
+
|
|
+static char *cpu_count_up_sysfs[] = {
|
|
+ "cpu0_up_count",
|
|
+ "cpu1_up_count",
|
|
+ "cpu2_up_count",
|
|
+ "cpu3_up_count",
|
|
+ "cpu4_up_count",
|
|
+ "cpu5_up_count",
|
|
+ "cpu6_up_count",
|
|
+ "cpu7_up_count"
|
|
+};
|
|
+
|
|
+static char *cpu_count_down_sysfs[] = {
|
|
+ "cpu0_down_count",
|
|
+ "cpu1_down_count",
|
|
+ "cpu2_down_count",
|
|
+ "cpu3_down_count",
|
|
+ "cpu4_down_count",
|
|
+ "cpu5_down_count",
|
|
+ "cpu6_down_count",
|
|
+ "cpu7_down_count"
|
|
+};
|
|
+#endif /* CONFIG_CPU_AUTOHOTPLUG_STATS */
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_ROOMAGE
|
|
+static int cluster0_min_online;
|
|
+static int cluster0_max_online;
|
|
+static int cluster1_min_online;
|
|
+static int cluster1_max_online;
|
|
+#endif
|
|
+
|
|
+/* Check if cpu is in fastest hmp_domain */
|
|
+unsigned int is_cpu_big(int cpu)
|
|
+{
|
|
+ return cpumask_test_cpu(cpu, &autohotplug_fast_cpumask);
|
|
+}
|
|
+
|
|
+/* Check if cpu is in slowest hmp_domain */
|
|
+unsigned int is_cpu_little(int cpu)
|
|
+{
|
|
+ return cpumask_test_cpu(cpu, &autohotplug_slow_cpumask);
|
|
+}
|
|
+
|
|
+int do_cpu_down(unsigned int cpu)
|
|
+{
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_ROOMAGE
|
|
+ int i, cur_c0_online = 0, cur_c1_online = 0;
|
|
+ struct cpumask *c0_mask;
|
|
+ struct cpumask *c1_mask;
|
|
+
|
|
+ if (cpu == 0 || cpu >= total_nr_cpus)
|
|
+ return 0;
|
|
+
|
|
+ if (cpumask_test_cpu(0, &autohotplug_slow_cpumask)) {
|
|
+ c0_mask = &autohotplug_slow_cpumask;
|
|
+ c1_mask = &autohotplug_fast_cpumask;
|
|
+ } else {
|
|
+ c0_mask = &autohotplug_fast_cpumask;
|
|
+ c1_mask = &autohotplug_slow_cpumask;
|
|
+ }
|
|
+
|
|
+ for_each_online_cpu(i) {
|
|
+ if (cpumask_test_cpu(i, c0_mask))
|
|
+ cur_c0_online++;
|
|
+ else if (cpumask_test_cpu(i, c1_mask))
|
|
+ cur_c1_online++;
|
|
+ }
|
|
+
|
|
+ if (cpumask_test_cpu(cpu, c0_mask) &&
|
|
+ cur_c0_online <= cluster0_min_online) {
|
|
+ trace_autohotplug_roomage(0, "min", cur_c0_online,
|
|
+ cluster0_min_online);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (cpumask_test_cpu(cpu, c1_mask) &&
|
|
+ cur_c1_online <= cluster1_min_online) {
|
|
+ trace_autohotplug_roomage(1, "min", cur_c1_online,
|
|
+ cluster1_min_online);
|
|
+ return 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (cpu == cpu_up_lastcpu && time_before(jiffies,
|
|
+ cpu_up_lasttime + usecs_to_jiffies(cpu_up_last_hold_us))) {
|
|
+ trace_autohotplug_notyet("down", cpu);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (cpu_down(cpu))
|
|
+ return 0;
|
|
+
|
|
+ trace_autohotplug_operate(cpu, "down");
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int __ref do_cpu_up(unsigned int cpu)
|
|
+{
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_ROOMAGE
|
|
+ int i, cur_c0_online = 0, cur_c1_online = 0;
|
|
+ struct cpumask *c0_mask;
|
|
+ struct cpumask *c1_mask;
|
|
+
|
|
+ if (cpu == 0 || cpu >= total_nr_cpus)
|
|
+ return 0;
|
|
+
|
|
+ if (cpumask_test_cpu(0, &autohotplug_slow_cpumask)) {
|
|
+ c0_mask = &autohotplug_slow_cpumask;
|
|
+ c1_mask = &autohotplug_fast_cpumask;
|
|
+ } else {
|
|
+ c0_mask = &autohotplug_fast_cpumask;
|
|
+ c1_mask = &autohotplug_slow_cpumask;
|
|
+ }
|
|
+
|
|
+ for_each_online_cpu(i) {
|
|
+ if (cpumask_test_cpu(i, c0_mask))
|
|
+ cur_c0_online++;
|
|
+ else if (cpumask_test_cpu(i, c1_mask))
|
|
+ cur_c1_online++;
|
|
+ }
|
|
+
|
|
+ if (cpumask_test_cpu(cpu, c0_mask) &&
|
|
+ cur_c0_online >= cluster0_max_online) {
|
|
+ trace_autohotplug_roomage(0, "max", cur_c0_online,
|
|
+ cluster0_max_online);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (cpumask_test_cpu(cpu, c1_mask) &&
|
|
+ cur_c1_online >= cluster1_max_online) {
|
|
+ trace_autohotplug_roomage(1, "max", cur_c1_online,
|
|
+ cluster1_max_online);
|
|
+ return 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (cpu_up(cpu))
|
|
+ return 0;
|
|
+
|
|
+ cpu_up_lastcpu = cpu;
|
|
+ cpu_up_lasttime = jiffies;
|
|
+
|
|
+ trace_autohotplug_operate(cpu, "up");
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int get_bigs_under(struct autohotplug_loadinfo *load,
|
|
+ unsigned char level, unsigned int *first)
|
|
+{
|
|
+ int i, found = 0, count = 0;
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ if ((load->cpu_load[i] != INVALID_LOAD)
|
|
+ && (load->cpu_load[i] < level)
|
|
+ && is_cpu_big(i)) {
|
|
+ if (first && (!found)) {
|
|
+ *first = i;
|
|
+ found = 1;
|
|
+ }
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+int get_bigs_above(struct autohotplug_loadinfo *load,
|
|
+ unsigned char level, unsigned int *first)
|
|
+{
|
|
+ int i, found = 0, count = 0;
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ if ((load->cpu_load[i] != INVALID_LOAD)
|
|
+ && (load->cpu_load[i] >= level)
|
|
+ && is_cpu_big(i)) {
|
|
+ if (first && (!found)) {
|
|
+ *first = i;
|
|
+ found = 1;
|
|
+ }
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+int get_cpus_under(struct autohotplug_loadinfo *load,
|
|
+ unsigned char level, unsigned int *first)
|
|
+{
|
|
+ int i, found = 0, count = 0;
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ if ((load->cpu_load[i] != INVALID_LOAD) &&
|
|
+ load->cpu_load[i] < level) {
|
|
+ if (first && (!found)) {
|
|
+ *first = i;
|
|
+ found = 1;
|
|
+ }
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ trace_autohotplug_under(level, count);
|
|
+ return count;
|
|
+}
|
|
+
|
|
+int get_littles_under(struct autohotplug_loadinfo *load,
|
|
+ unsigned char level, unsigned int *first)
|
|
+{
|
|
+ int i, found = 0, count = 0;
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ if ((load->cpu_load[i] != INVALID_LOAD)
|
|
+ && (load->cpu_load[i] < level)
|
|
+ && is_cpu_little(i)) {
|
|
+ if (first && (!found)) {
|
|
+ *first = i;
|
|
+ found = 1;
|
|
+ }
|
|
+ count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+int get_cpus_online(struct autohotplug_loadinfo *load,
|
|
+ int *little, int *big)
|
|
+{
|
|
+ int i, big_count = 0, little_count = 0;
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ if (load->cpu_load[i] != INVALID_LOAD) {
|
|
+ if (is_cpu_little(i))
|
|
+ little_count++;
|
|
+ else
|
|
+ big_count++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *little = little_count;
|
|
+ *big = big_count;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int try_up_big(void)
|
|
+{
|
|
+ unsigned int cpu = 0;
|
|
+ unsigned int found = total_nr_cpus;
|
|
+
|
|
+ while (cpu < total_nr_cpus && cpu_possible(cpu)) {
|
|
+ cpu = cpumask_next_zero(cpu, cpu_online_mask);
|
|
+ if (is_cpu_big(cpu)) {
|
|
+ found = cpu;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found < total_nr_cpus && cpu_possible(found))
|
|
+ return do_cpu_up(found);
|
|
+ else
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int try_up_little(void)
|
|
+{
|
|
+ unsigned int cpu = 0;
|
|
+ unsigned int found = total_nr_cpus;
|
|
+
|
|
+ while (cpu < total_nr_cpus && cpu_possible(cpu)) {
|
|
+ cpu = cpumask_next_zero(cpu, cpu_online_mask);
|
|
+ if (is_cpu_little(cpu)) {
|
|
+ found = cpu;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (found < total_nr_cpus && cpu_possible(found))
|
|
+ return do_cpu_up(found);
|
|
+
|
|
+ trace_autohotplug_notyet("up", found);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int autohotplug_try_any_up(void)
|
|
+{
|
|
+ unsigned int cpu = 0;
|
|
+
|
|
+ while (cpu < total_nr_cpus && cpu_possible(cpu)) {
|
|
+ cpu = cpumask_next_zero(cpu, cpu_online_mask);
|
|
+ if (cpu < total_nr_cpus && cpu_possible(cpu))
|
|
+ return do_cpu_up(cpu);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int autohotplug_try_lock_up(int hotplug_lock)
|
|
+{
|
|
+ struct cpumask tmp_core_up_mask, tmp_core_down_mask;
|
|
+ int cpu, lock_flag = hotplug_lock - num_online_cpus();
|
|
+ unsigned long flags;
|
|
+
|
|
+ spin_lock_irqsave(&cpumask_lock, flags);
|
|
+ cpumask_clear(&tmp_core_up_mask);
|
|
+ cpumask_clear(&tmp_core_down_mask);
|
|
+ spin_unlock_irqrestore(&cpumask_lock, flags);
|
|
+
|
|
+ if (lock_flag > 0) {
|
|
+ for_each_cpu_not(cpu, cpu_online_mask) {
|
|
+ if (lock_flag-- == 0)
|
|
+ break;
|
|
+ spin_lock_irqsave(&cpumask_lock, flags);
|
|
+ cpumask_or(&tmp_core_up_mask, cpumask_of(cpu),
|
|
+ &tmp_core_up_mask);
|
|
+ spin_unlock_irqrestore(&cpumask_lock, flags);
|
|
+ }
|
|
+ } else if (lock_flag < 0) {
|
|
+ lock_flag = -lock_flag;
|
|
+ for (cpu = nr_cpu_ids - 1; cpu > 0; cpu--) {
|
|
+ if (cpumask_test_cpu(cpu, cpu_online_mask)) {
|
|
+ if (lock_flag-- == 0)
|
|
+ break;
|
|
+ spin_lock_irqsave(&cpumask_lock, flags);
|
|
+ cpumask_or(&tmp_core_down_mask, cpumask_of(cpu),
|
|
+ &tmp_core_down_mask);
|
|
+ spin_unlock_irqrestore(&cpumask_lock, flags);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!cpumask_empty(&tmp_core_up_mask)) {
|
|
+ for_each_cpu(cpu, &tmp_core_up_mask) {
|
|
+ if (!cpu_online(cpu))
|
|
+ return do_cpu_up(cpu);
|
|
+ }
|
|
+ } else if (!cpumask_empty(&tmp_core_down_mask)) {
|
|
+ for_each_cpu(cpu, &tmp_core_down_mask)
|
|
+ if (cpu_online(cpu))
|
|
+ cpu_down(cpu);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_ROOMAGE
|
|
+static int autohotplug_roomage_get_offline(const cpumask_t *mask)
|
|
+{
|
|
+ int cpu, lastcpu = 0xffff;
|
|
+
|
|
+ for_each_cpu(cpu, mask) {
|
|
+ if ((cpu != 0) && !cpu_online(cpu))
|
|
+ return cpu;
|
|
+ }
|
|
+
|
|
+ return lastcpu;
|
|
+}
|
|
+static int autohotplug_roomage_get_online(const cpumask_t *mask)
|
|
+{
|
|
+ int cpu, lastcpu = 0xffff;
|
|
+
|
|
+ for_each_cpu(cpu, mask) {
|
|
+ if ((cpu != 0) && cpu_online(cpu)) {
|
|
+ if (lastcpu == 0xffff)
|
|
+ lastcpu = cpu;
|
|
+ else if (cpu > lastcpu)
|
|
+ lastcpu = cpu;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return lastcpu;
|
|
+}
|
|
+
|
|
+static void autohotplug_roomage_update(void)
|
|
+{
|
|
+ unsigned int to_down, to_up;
|
|
+ int i, cur_c0_online = 0, cur_c1_online = 0;
|
|
+ struct cpumask *c0_mask;
|
|
+ struct cpumask *c1_mask;
|
|
+
|
|
+ if (cpumask_test_cpu(0, &autohotplug_slow_cpumask)) {
|
|
+ c0_mask = &autohotplug_slow_cpumask;
|
|
+ c1_mask = &autohotplug_fast_cpumask;
|
|
+ } else {
|
|
+ c0_mask = &autohotplug_fast_cpumask;
|
|
+ c1_mask = &autohotplug_slow_cpumask;
|
|
+ }
|
|
+
|
|
+ for_each_online_cpu(i) {
|
|
+ if (cpumask_test_cpu(i, c0_mask))
|
|
+ cur_c0_online++;
|
|
+ else if (cpumask_test_cpu(i, c1_mask))
|
|
+ cur_c1_online++;
|
|
+ }
|
|
+
|
|
+ while (cur_c1_online > cluster1_max_online) {
|
|
+ to_down = autohotplug_roomage_get_online(c1_mask);
|
|
+ do_cpu_down(to_down);
|
|
+ cur_c1_online--;
|
|
+ }
|
|
+
|
|
+ while (cur_c0_online > cluster0_max_online) {
|
|
+ to_down = autohotplug_roomage_get_online(c0_mask);
|
|
+ do_cpu_down(to_down);
|
|
+ cur_c0_online--;
|
|
+ }
|
|
+
|
|
+ while (cur_c1_online < cluster1_min_online) {
|
|
+ to_up = autohotplug_roomage_get_offline(c1_mask);
|
|
+ do_cpu_up(to_up);
|
|
+ cur_c1_online++;
|
|
+ }
|
|
+
|
|
+ while (cur_c0_online < cluster0_min_online) {
|
|
+ to_up = autohotplug_roomage_get_offline(c0_mask);
|
|
+ do_cpu_up(to_up);
|
|
+ cur_c0_online++;
|
|
+ }
|
|
+}
|
|
+
|
|
+int autohotplug_roomage_limit(unsigned int cluster_id, unsigned int min,
|
|
+ unsigned int max)
|
|
+{
|
|
+ mutex_lock(&hotplug_enable_mutex);
|
|
+
|
|
+ if (cluster_id == 0) {
|
|
+ cluster0_min_online = min;
|
|
+ cluster0_max_online = max;
|
|
+ } else if (cluster_id == 1) {
|
|
+ cluster1_min_online = min;
|
|
+ cluster1_max_online = max;
|
|
+ }
|
|
+
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(autohotplug_roomage_limit);
|
|
+#endif /* CONFIG_CPU_AUTOHOTPLUG_ROOMAGE */
|
|
+
|
|
+static void autohotplug_load_parse(struct autohotplug_loadinfo *load)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ load->max_load = 0;
|
|
+ load->min_load = 255;
|
|
+ load->max_cpu = INVALID_CPU;
|
|
+ load->min_cpu = INVALID_CPU;
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ if (!cpu_online(i))
|
|
+ load->cpu_load[i] = INVALID_LOAD;
|
|
+
|
|
+ if ((load->cpu_load[i] != INVALID_LOAD)
|
|
+ && (load->cpu_load[i] >= load->max_load)) {
|
|
+ load->max_load = load->cpu_load[i];
|
|
+ load->max_cpu = i;
|
|
+ }
|
|
+
|
|
+ if ((load->cpu_load[i] != INVALID_LOAD)
|
|
+ && (load->cpu_load[i] < load->min_load)) {
|
|
+ load->min_load = load->cpu_load[i];
|
|
+ load->min_cpu = i;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void autohotplug_governor_judge(void)
|
|
+{
|
|
+ int hotplug_lock, try_attemp = 0;
|
|
+ unsigned long flags;
|
|
+ struct autohotplug_loadinfo load;
|
|
+ int ret;
|
|
+
|
|
+ try_attemp = 0;
|
|
+ spin_lock_irqsave(&hotplug_load_lock, flags);
|
|
+ memcpy(&load, &governor_load.load, sizeof(load));
|
|
+ spin_unlock_irqrestore(&hotplug_load_lock, flags);
|
|
+
|
|
+ mutex_lock(&hotplug_enable_mutex);
|
|
+
|
|
+ if (boost_all_online) {
|
|
+ autohotplug_try_any_up();
|
|
+ } else {
|
|
+ hotplug_lock = atomic_read(&g_hotplug_lock);
|
|
+ /* check if current is in hotplug lock */
|
|
+ if (hotplug_lock) {
|
|
+ autohotplug_try_lock_up(hotplug_lock);
|
|
+ } else {
|
|
+ autohotplug_load_parse(&load);
|
|
+ trace_autohotplug_try("up");
|
|
+ ret = cur_governor->try_up(&load);
|
|
+ if (ret) {
|
|
+ try_attemp = 1;
|
|
+ if (cur_governor->try_freq_limit)
|
|
+ cur_governor->try_freq_limit();
|
|
+ } else {
|
|
+ trace_autohotplug_try("down");
|
|
+ ret = cur_governor->try_down(&load);
|
|
+ if (ret) {
|
|
+ try_attemp = 2;
|
|
+ if (cur_governor->try_freq_limit)
|
|
+ cur_governor->try_freq_limit();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_ROOMAGE
|
|
+ if (!try_attemp)
|
|
+ autohotplug_roomage_update();
|
|
+#endif
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+}
|
|
+
|
|
+
|
|
+static int autohotplug_thread_task(void *data)
|
|
+{
|
|
+ set_freezable();
|
|
+ while (1) {
|
|
+ if (freezing(current)) {
|
|
+ if (try_to_freeze())
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (hotplug_enable)
|
|
+ autohotplug_governor_judge();
|
|
+
|
|
+ set_current_state(TASK_INTERRUPTIBLE);
|
|
+ schedule();
|
|
+ if (kthread_should_stop())
|
|
+ break;
|
|
+ set_current_state(TASK_RUNNING);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static unsigned int autohotplug_updateload(int cpu)
|
|
+{
|
|
+ struct autohotplug_cpuinfo *pcpu = &per_cpu(cpuinfo, cpu);
|
|
+ u64 now, now_idle, delta_idle, delta_time, active_time, load;
|
|
+
|
|
+ now_idle = get_cpu_idle_time(cpu, &now, 1);
|
|
+ delta_idle = now_idle - pcpu->time_in_idle;
|
|
+ delta_time = now - pcpu->time_in_idle_timestamp;
|
|
+
|
|
+ if (delta_time <= delta_idle)
|
|
+ active_time = 0;
|
|
+ else
|
|
+ active_time = delta_time - delta_idle;
|
|
+
|
|
+ pcpu->time_in_idle = now_idle;
|
|
+ pcpu->time_in_idle_timestamp = now;
|
|
+
|
|
+ load = active_time * 100;
|
|
+ do_div(load, delta_time);
|
|
+
|
|
+ return (unsigned int)load;
|
|
+}
|
|
+
|
|
+static void autohotplug_set_load(unsigned int cpu, unsigned int load,
|
|
+ unsigned int load_relative)
|
|
+{
|
|
+ if (cpu >= total_nr_cpus)
|
|
+ return;
|
|
+
|
|
+ governor_load.load.cpu_load[cpu] = load;
|
|
+ governor_load.load.cpu_load_relative[cpu] = load_relative;
|
|
+}
|
|
+
|
|
+static void autohotplug_governor_updateload(int cpu, unsigned int load,
|
|
+ unsigned int target,
|
|
+ struct cpufreq_policy *policy)
|
|
+{
|
|
+
|
|
+ unsigned long flags;
|
|
+ unsigned int cur = target;
|
|
+
|
|
+ if (cur) {
|
|
+ spin_lock_irqsave(&hotplug_load_lock, flags);
|
|
+ autohotplug_set_load(cpu, (load * cur) / policy->max, load);
|
|
+ if (is_cpu_big(cpu)) {
|
|
+ governor_load.load.big_min_load =
|
|
+ load_last_big_min_freq * 100 / policy->max;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&hotplug_load_lock, flags);
|
|
+ }
|
|
+
|
|
+}
|
|
+static void autohotplug_sample_timer(unsigned long data)
|
|
+{
|
|
+ unsigned int i, load;
|
|
+ struct cpufreq_policy policy;
|
|
+ struct autohotplug_cpuinfo *pcpu;
|
|
+ unsigned long flags, expires;
|
|
+ static const char performance_governor[] = "performance";
|
|
+ static const char powersave_governor[] = "powersave";
|
|
+
|
|
+ for_each_possible_cpu(i) {
|
|
+ if ((cpufreq_get_policy(&policy, i) == 0)
|
|
+ && policy.governor->name
|
|
+ && !(!strncmp(policy.governor->name,
|
|
+ performance_governor,
|
|
+ strlen(performance_governor)) ||
|
|
+ !strncmp(policy.governor->name,
|
|
+ powersave_governor,
|
|
+ strlen(powersave_governor))
|
|
+ )) {
|
|
+ pcpu = &per_cpu(cpuinfo, i);
|
|
+ spin_lock_irqsave(&pcpu->load_lock, flags);
|
|
+ load = autohotplug_updateload(i);
|
|
+ autohotplug_governor_updateload(i, load,
|
|
+ (policy.cur ? policy.cur : policy.min),
|
|
+ &policy);
|
|
+ spin_unlock_irqrestore(&pcpu->load_lock, flags);
|
|
+ } else {
|
|
+ autohotplug_set_load(i, INVALID_LOAD, INVALID_LOAD);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!timer_pending(&hotplug_sample_timer)) {
|
|
+ expires = jiffies + usecs_to_jiffies(hotplug_sample_us);
|
|
+ mod_timer(&hotplug_sample_timer, expires);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void autohotplug_task_timer(unsigned long data)
|
|
+{
|
|
+ unsigned long expires;
|
|
+
|
|
+ if (hotplug_enable)
|
|
+ wake_up_process(autohotplug_task);
|
|
+
|
|
+ if (!timer_pending(&hotplug_task_timer)) {
|
|
+ expires = jiffies + usecs_to_jiffies(hotplug_period_us);
|
|
+ mod_timer(&hotplug_task_timer, expires);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int autohotplug_timer_start(void)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ mutex_lock(&hotplug_enable_mutex);
|
|
+
|
|
+ /* init sample timer */
|
|
+ init_timer_deferrable(&hotplug_sample_timer);
|
|
+ hotplug_sample_timer.function = autohotplug_sample_timer;
|
|
+ hotplug_sample_timer.data = (unsigned long)&governor_load.load;
|
|
+ hotplug_sample_timer.expires = jiffies +
|
|
+ usecs_to_jiffies(hotplug_sample_us);
|
|
+ add_timer_on(&hotplug_sample_timer, 0);
|
|
+
|
|
+ for (i = total_nr_cpus - 1; i >= 0; i--) {
|
|
+ autohotplug_set_load(i, INVALID_LOAD, INVALID_LOAD);
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_STATS
|
|
+ cpu_on_time_total[i] = 0;
|
|
+ cpu_up_count_total[i] = 1;
|
|
+ cpu_down_count_total[i] = 0;
|
|
+ cpu_on_lasttime[i] = get_jiffies_64();
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ cpu_up_lasttime = jiffies;
|
|
+
|
|
+ /* init hotplug timer */
|
|
+ init_timer(&hotplug_task_timer);
|
|
+ hotplug_task_timer.function = autohotplug_task_timer;
|
|
+ hotplug_task_timer.data = (unsigned long)&governor_load.load;
|
|
+ hotplug_task_timer.expires = jiffies +
|
|
+ usecs_to_jiffies(hotplug_period_us);
|
|
+ add_timer_on(&hotplug_task_timer, 0);
|
|
+
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int autohotplug_timer_stop(void)
|
|
+{
|
|
+ mutex_lock(&hotplug_enable_mutex);
|
|
+ del_timer_sync(&hotplug_task_timer);
|
|
+ del_timer_sync(&hotplug_sample_timer);
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int autohotplug_cpu_lock(int num_core)
|
|
+{
|
|
+ int prev_lock;
|
|
+
|
|
+ mutex_lock(&hotplug_enable_mutex);
|
|
+ if (num_core < 1 || num_core > num_possible_cpus()) {
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ prev_lock = atomic_read(&g_hotplug_lock);
|
|
+ if (prev_lock != 0 && prev_lock < num_core) {
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ atomic_set(&g_hotplug_lock, num_core);
|
|
+ wake_up_process(autohotplug_task);
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int autohotplug_cpu_unlock(int num_core)
|
|
+{
|
|
+ int prev_lock = atomic_read(&g_hotplug_lock);
|
|
+
|
|
+ mutex_lock(&hotplug_enable_mutex);
|
|
+
|
|
+ if (prev_lock != num_core) {
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ atomic_set(&g_hotplug_lock, 0);
|
|
+ mutex_unlock(&hotplug_enable_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+unsigned int autohotplug_enable_from_sysfs(unsigned int temp,
|
|
+ unsigned int *value)
|
|
+{
|
|
+ int prev_lock;
|
|
+
|
|
+ if (temp && (!hotplug_enable)) {
|
|
+ autohotplug_timer_start();
|
|
+ } else if (!temp && hotplug_enable) {
|
|
+ prev_lock = atomic_read(&hotplug_lock);
|
|
+ if (prev_lock)
|
|
+ autohotplug_cpu_unlock(prev_lock);
|
|
+
|
|
+ atomic_set(&hotplug_lock, 0);
|
|
+ autohotplug_timer_stop();
|
|
+ }
|
|
+
|
|
+ return temp;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_STATS
|
|
+static unsigned int autohotplug_cpu_time_up_to_sysfs(unsigned int temp,
|
|
+ unsigned int *value)
|
|
+{
|
|
+ u64 cur_jiffies = get_jiffies_64();
|
|
+ unsigned int index = ((unsigned long)value -
|
|
+ (unsigned long)cpu_on_time_total)
|
|
+ / sizeof(unsigned long);
|
|
+
|
|
+ if (cpu_online(index))
|
|
+ return cpu_on_time_total[index] +
|
|
+ (cur_jiffies - cpu_on_lasttime[index]);
|
|
+ else
|
|
+ return cpu_on_time_total[index];
|
|
+}
|
|
+
|
|
+static unsigned int autohotplug_cpu_count_up_to_sysfs(unsigned int temp,
|
|
+ unsigned int *value)
|
|
+{
|
|
+ unsigned int index = ((unsigned long)value -
|
|
+ (unsigned long)cpu_up_count_total)
|
|
+ / sizeof(unsigned long);
|
|
+
|
|
+ return cpu_up_count_total[index];
|
|
+}
|
|
+
|
|
+static unsigned int autohotplug_cpu_count_down_to_sysfs(unsigned int temp,
|
|
+ unsigned int *value)
|
|
+{
|
|
+ unsigned int index = ((unsigned long)value -
|
|
+ (unsigned long)cpu_down_count_total)
|
|
+ / sizeof(unsigned long);
|
|
+
|
|
+ return cpu_down_count_total[index];
|
|
+}
|
|
+
|
|
+static void autohotplug_attr_stats_init(void)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < CONFIG_NR_CPUS; i++) {
|
|
+ autohotplug_attr_add(cpu_on_time_total_sysfs[i],
|
|
+ (unsigned int *)&cpu_on_time_total[i],
|
|
+ 0444, autohotplug_cpu_time_up_to_sysfs,
|
|
+ NULL);
|
|
+ autohotplug_attr_add(cpu_count_up_sysfs[i],
|
|
+ (unsigned int *)&cpu_up_count_total[i],
|
|
+ 0444, autohotplug_cpu_count_up_to_sysfs,
|
|
+ NULL);
|
|
+ autohotplug_attr_add(cpu_count_down_sysfs[i],
|
|
+ (unsigned int *)&cpu_down_count_total[i],
|
|
+ 0444, autohotplug_cpu_count_down_to_sysfs,
|
|
+ NULL);
|
|
+ }
|
|
+}
|
|
+#endif /* CONFIG_CPU_AUTOHOTPLUG_STATS */
|
|
+
|
|
+unsigned int autohotplug_lock_from_sysfs(unsigned int temp, unsigned int *value)
|
|
+{
|
|
+ int ret, prev_lock;
|
|
+
|
|
+ if ((!hotplug_enable) || temp > num_possible_cpus())
|
|
+ return atomic_read(&g_hotplug_lock);
|
|
+
|
|
+ prev_lock = atomic_read(&hotplug_lock);
|
|
+ if (prev_lock)
|
|
+ autohotplug_cpu_unlock(prev_lock);
|
|
+
|
|
+ if (temp == 0) {
|
|
+ atomic_set(&hotplug_lock, 0);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = autohotplug_cpu_lock(temp);
|
|
+ if (ret) {
|
|
+ pr_err("[HOTPLUG] already locked with smaller value %d < %d\n",
|
|
+ atomic_read(&g_hotplug_lock), temp);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ atomic_set(&hotplug_lock, temp);
|
|
+
|
|
+ return temp;
|
|
+}
|
|
+
|
|
+unsigned int autohotplug_lock_to_sysfs(unsigned int temp, unsigned int *value)
|
|
+{
|
|
+ return atomic_read(&hotplug_lock);
|
|
+}
|
|
+
|
|
+static ssize_t autohotplug_show(struct kobject *kobj,
|
|
+ struct attribute *attr, char *buf)
|
|
+{
|
|
+ ssize_t ret = 0;
|
|
+ struct autohotplug_global_attr *auto_attr =
|
|
+ container_of(attr, struct autohotplug_global_attr, attr);
|
|
+ unsigned int temp = *(auto_attr->value);
|
|
+
|
|
+ if (auto_attr->to_sysfs != NULL)
|
|
+ temp = auto_attr->to_sysfs(temp, auto_attr->value);
|
|
+
|
|
+ ret = sprintf(buf, "%u\n", temp);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t autohotplug_store(struct kobject *a, struct attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ unsigned int temp;
|
|
+ ssize_t ret = count;
|
|
+ struct autohotplug_global_attr *auto_attr =
|
|
+ container_of(attr, struct autohotplug_global_attr, attr);
|
|
+ char *str = vmalloc(count + 1);
|
|
+
|
|
+ if (str == NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ memcpy(str, buf, count);
|
|
+ str[count] = 0;
|
|
+ if (kstrtouint(str, 10, &temp) != 0) {
|
|
+ ret = -EINVAL;
|
|
+ } else {
|
|
+ if (auto_attr->from_sysfs != NULL)
|
|
+ temp = auto_attr->from_sysfs(temp, auto_attr->value);
|
|
+ if (temp < 0)
|
|
+ ret = -EINVAL;
|
|
+ else
|
|
+ *(auto_attr->value) = temp;
|
|
+ }
|
|
+
|
|
+ vfree(str);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void autohotplug_attr_add(const char *name, unsigned int *value, umode_t mode,
|
|
+ unsigned int (*to_sysfs)(unsigned int, unsigned int *),
|
|
+ unsigned int (*from_sysfs)(unsigned int, unsigned int *))
|
|
+{
|
|
+ int i = 0;
|
|
+
|
|
+ while (autohotplug_data.attributes[i] != NULL) {
|
|
+ i++;
|
|
+ if (i >= HOTPLUG_DATA_SYSFS_MAX)
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ autohotplug_data.attr[i].attr.mode = mode;
|
|
+ autohotplug_data.attr[i].show = autohotplug_show;
|
|
+ autohotplug_data.attr[i].store = autohotplug_store;
|
|
+ autohotplug_data.attr[i].attr.name = name;
|
|
+ autohotplug_data.attr[i].value = value;
|
|
+ autohotplug_data.attr[i].to_sysfs = to_sysfs;
|
|
+ autohotplug_data.attr[i].from_sysfs = from_sysfs;
|
|
+ autohotplug_data.attributes[i] = &autohotplug_data.attr[i].attr;
|
|
+ autohotplug_data.attributes[i + 1] = NULL;
|
|
+}
|
|
+
|
|
+static int autohotplug_attr_init(void)
|
|
+{
|
|
+ memset(&autohotplug_data, 0, sizeof(autohotplug_data));
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_STATS
|
|
+ autohotplug_attr_stats_init();
|
|
+#endif
|
|
+
|
|
+ autohotplug_attr_add("enable", &hotplug_enable, 0644,
|
|
+ NULL, autohotplug_enable_from_sysfs);
|
|
+ autohotplug_attr_add("boost_all", &boost_all_online, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("polling_us", &hotplug_period_us, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("try_up_load", &load_try_up, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("try_down_load", &load_try_down, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("hold_last_up_us", &cpu_up_last_hold_us, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("stable_up_us", &load_up_stable_us, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("stable_down_us", &load_down_stable_us, 0644,
|
|
+ NULL, NULL);
|
|
+ autohotplug_attr_add("lock", (unsigned int *)&hotplug_lock, 0644,
|
|
+ autohotplug_lock_to_sysfs,
|
|
+ autohotplug_lock_from_sysfs);
|
|
+
|
|
+ /* init governor attr */
|
|
+ if (cur_governor->init_attr)
|
|
+ cur_governor->init_attr();
|
|
+
|
|
+ autohotplug_data.attr_group.name = "autohotplug";
|
|
+ autohotplug_data.attr_group.attrs = autohotplug_data.attributes;
|
|
+
|
|
+ return sysfs_create_group(kernel_kobj, &autohotplug_data.attr_group);
|
|
+}
|
|
+
|
|
+static int reboot_notifier_call(struct notifier_block *this,
|
|
+ unsigned long code, void *_cmd)
|
|
+{
|
|
+ /* disable auto hotplug */
|
|
+ autohotplug_enable_from_sysfs(0, NULL);
|
|
+
|
|
+ pr_err("%s:%s: stop autohotplug done\n", __FILE__, __func__);
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static struct notifier_block reboot_notifier = {
|
|
+ .notifier_call = reboot_notifier_call,
|
|
+ /* autohotplug notifier must be invoked before cpufreq notifier */
|
|
+ .priority = 1,
|
|
+};
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_STATS
|
|
+static int autohotplug_cpu_callback(struct notifier_block *nfb,
|
|
+ unsigned long action, void *hcpu)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ unsigned int cpu = (unsigned long)hcpu;
|
|
+ struct device *dev;
|
|
+
|
|
+ dev = get_cpu_device(cpu);
|
|
+ if (dev) {
|
|
+ switch (action) {
|
|
+ case CPU_ONLINE:
|
|
+ spin_lock_irqsave(&hotplug_load_lock, flags);
|
|
+ autohotplug_set_load(cpu, INVALID_LOAD, INVALID_LOAD);
|
|
+ cpu_on_lasttime[cpu] = get_jiffies_64();
|
|
+ cpu_up_count_total[cpu]++;
|
|
+ spin_unlock_irqrestore(&hotplug_load_lock, flags);
|
|
+ break;
|
|
+ case CPU_DOWN_PREPARE:
|
|
+ case CPU_UP_CANCELED_FROZEN:
|
|
+ case CPU_DOWN_FAILED:
|
|
+ spin_lock_irqsave(&hotplug_load_lock, flags);
|
|
+ autohotplug_set_load(cpu, INVALID_LOAD, INVALID_LOAD);
|
|
+ spin_unlock_irqrestore(&hotplug_load_lock, flags);
|
|
+ break;
|
|
+ case CPU_DEAD:
|
|
+ spin_lock_irqsave(&hotplug_load_lock, flags);
|
|
+ if (cpu_on_lasttime[cpu]) {
|
|
+ cpu_on_time_total[cpu] += get_jiffies_64() -
|
|
+ cpu_on_lasttime[cpu];
|
|
+ cpu_on_lasttime[cpu] = 0;
|
|
+ cpu_down_count_total[cpu]++;
|
|
+ }
|
|
+ spin_unlock_irqrestore(&hotplug_load_lock, flags);
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return NOTIFY_OK;
|
|
+}
|
|
+
|
|
+static struct notifier_block __refdata autohotplug_cpu_notifier = {
|
|
+ .notifier_call = autohotplug_cpu_callback,
|
|
+};
|
|
+#endif /* CONFIG_CPU_AUTOHOTPLUG_STATS */
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_INPUT_EVNT_NOTIFY
|
|
+static struct timer_list autohotplug_input_timer;
|
|
+static unsigned int period_us_bak;
|
|
+static unsigned int up_stable_us_bak;
|
|
+static unsigned int down_stable_us_bak;
|
|
+
|
|
+static void autohotplug_input_mode(bool input_mode)
|
|
+{
|
|
+ if (input_mode) {
|
|
+ hotplug_period_us = 50000;
|
|
+ load_up_stable_us = 50000;
|
|
+ load_down_stable_us = UINT_MAX;
|
|
+ } else {
|
|
+ hotplug_period_us = period_us_bak;
|
|
+ load_up_stable_us = up_stable_us_bak;
|
|
+ load_down_stable_us = down_stable_us_bak;
|
|
+ }
|
|
+
|
|
+ wake_up_process(autohotplug_task);
|
|
+}
|
|
+
|
|
+static void autohotplug_do_input_timer(unsigned long data)
|
|
+{
|
|
+ autohotplug_input_mode(false);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * trigger cpu frequency to a high speed when input event coming.
|
|
+ * such as key, ir, touchpannel for ex. , but skip gsensor.
|
|
+ */
|
|
+static void autohotplug_input_event(struct input_handle *handle,
|
|
+ unsigned int type, unsigned int code, int value)
|
|
+{
|
|
+ if (type == EV_SYN && code == SYN_REPORT) {
|
|
+ autohotplug_input_mode(true);
|
|
+ mod_timer(&autohotplug_input_timer,
|
|
+ jiffies + msecs_to_jiffies(2000));
|
|
+ }
|
|
+}
|
|
+
|
|
+static int autohotplug_input_connect(struct input_handler *handler,
|
|
+ struct input_dev *dev,
|
|
+ const struct input_device_id *id)
|
|
+{
|
|
+ struct input_handle *handle;
|
|
+ int error;
|
|
+
|
|
+ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
|
|
+ if (!handle)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ handle->dev = dev;
|
|
+ handle->handler = handler;
|
|
+ handle->name = "autohotplug";
|
|
+
|
|
+ error = input_register_handle(handle);
|
|
+ if (error)
|
|
+ goto err;
|
|
+
|
|
+ error = input_open_device(handle);
|
|
+ if (error)
|
|
+ goto err_open;
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_open:
|
|
+ input_unregister_handle(handle);
|
|
+err:
|
|
+ kfree(handle);
|
|
+ return error;
|
|
+}
|
|
+
|
|
+static void autohotplug_input_disconnect(struct input_handle *handle)
|
|
+{
|
|
+ input_close_device(handle);
|
|
+ input_unregister_handle(handle);
|
|
+ kfree(handle);
|
|
+}
|
|
+
|
|
+static const struct input_device_id autohotplug_ids[] = {
|
|
+ {
|
|
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
|
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
|
|
+ .evbit = { BIT_MASK(EV_ABS) },
|
|
+ .absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
|
|
+ BIT_MASK(ABS_MT_POSITION_X) |
|
|
+ BIT_MASK(ABS_MT_POSITION_Y) },
|
|
+ }, /* multi-touch touchscreen */
|
|
+ {
|
|
+ .flags = INPUT_DEVICE_ID_MATCH_KEYBIT |
|
|
+ INPUT_DEVICE_ID_MATCH_ABSBIT,
|
|
+ .keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
|
|
+ .absbit = { [BIT_WORD(ABS_X)] =
|
|
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) },
|
|
+ }, /* touchpad */
|
|
+ {
|
|
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT |
|
|
+ INPUT_DEVICE_ID_MATCH_BUS |
|
|
+ INPUT_DEVICE_ID_MATCH_VENDOR |
|
|
+ INPUT_DEVICE_ID_MATCH_PRODUCT |
|
|
+ INPUT_DEVICE_ID_MATCH_VERSION,
|
|
+ .bustype = BUS_HOST,
|
|
+ .vendor = 0x0001,
|
|
+ .product = 0x0001,
|
|
+ .version = 0x0100,
|
|
+ .evbit = { BIT_MASK(EV_KEY) },
|
|
+ }, /* keyboard/ir */
|
|
+ { },
|
|
+};
|
|
+
|
|
+static struct input_handler autohotplug_input_handler = {
|
|
+ .event = autohotplug_input_event,
|
|
+ .connect = autohotplug_input_connect,
|
|
+ .disconnect = autohotplug_input_disconnect,
|
|
+ .name = "autohotplug",
|
|
+ .id_table = autohotplug_ids,
|
|
+};
|
|
+#endif /* #ifdef CONFIG_CPU_AUTOHOTPLUG_INPUT_EVNT_NOTIFY */
|
|
+
|
|
+static int autohotplug_init(void)
|
|
+{
|
|
+ int cpu;
|
|
+ struct autohotplug_cpuinfo *pcpu;
|
|
+
|
|
+ cur_governor = &autohotplug_smart;
|
|
+ if (cur_governor == NULL) {
|
|
+ AUTOHOTPLUG_ERR("autohotplug governor is NULL, failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (cur_governor->get_fast_and_slow_cpus == NULL) {
|
|
+ AUTOHOTPLUG_ERR("get_fast_and_slow_cpus is NULL, failed\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ cur_governor->get_fast_and_slow_cpus(&autohotplug_fast_cpumask,
|
|
+ &autohotplug_slow_cpumask);
|
|
+
|
|
+ /* init per_cpu load_lock */
|
|
+ for_each_possible_cpu(cpu) {
|
|
+ pcpu = &per_cpu(cpuinfo, cpu);
|
|
+ spin_lock_init(&pcpu->load_lock);
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_ROOMAGE
|
|
+ if (cpumask_test_cpu(cpu, &autohotplug_fast_cpumask))
|
|
+ cluster1_max_online++;
|
|
+ else
|
|
+ cluster0_max_online++;
|
|
+#endif
|
|
+ }
|
|
+
|
|
+ mutex_init(&hotplug_enable_mutex);
|
|
+ spin_lock_init(&hotplug_load_lock);
|
|
+ spin_lock_init(&cpumask_lock);
|
|
+
|
|
+ if (hotplug_enable)
|
|
+ autohotplug_timer_start();
|
|
+
|
|
+ /* start task */
|
|
+ autohotplug_task = kthread_create(autohotplug_thread_task, NULL,
|
|
+ "autohotplug");
|
|
+ if (IS_ERR(autohotplug_task))
|
|
+ return PTR_ERR(autohotplug_task);
|
|
+ get_task_struct(autohotplug_task);
|
|
+
|
|
+ /* attr init */
|
|
+ autohotplug_attr_init();
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_STATS
|
|
+ register_hotcpu_notifier(&autohotplug_cpu_notifier);
|
|
+#endif
|
|
+
|
|
+ /* register reboot notifier for process cpus when reboot */
|
|
+ register_reboot_notifier(&reboot_notifier);
|
|
+
|
|
+#ifdef CONFIG_CPU_AUTOHOTPLUG_INPUT_EVNT_NOTIFY
|
|
+ period_us_bak = hotplug_period_us;
|
|
+ up_stable_us_bak = load_up_stable_us;
|
|
+ down_stable_us_bak = load_down_stable_us;
|
|
+
|
|
+ if (input_register_handler(&autohotplug_input_handler))
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* init input event timer */
|
|
+ init_timer_deferrable(&autohotplug_input_timer);
|
|
+ autohotplug_input_timer.function = autohotplug_do_input_timer;
|
|
+ autohotplug_input_timer.expires = jiffies + msecs_to_jiffies(2000);
|
|
+ add_timer_on(&autohotplug_input_timer, 0);
|
|
+#endif
|
|
+
|
|
+ /* turn hotplug task on*/
|
|
+ wake_up_process(autohotplug_task);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+device_initcall(autohotplug_init);
|
|
+
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
+static struct dentry *debugfs_autohotplug_root;
|
|
+static char autohotplug_load_info[256];
|
|
+
|
|
+static ssize_t autohotplug_load_read(struct file *file,
|
|
+ char __user *buf, size_t count,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ int i, len;
|
|
+
|
|
+ for_each_possible_cpu(i)
|
|
+ sprintf(autohotplug_load_info + i * 5, "%4d ",
|
|
+ governor_load.load.cpu_load[i]);
|
|
+
|
|
+ len = strlen(autohotplug_load_info);
|
|
+ autohotplug_load_info[len] = 0x0A;
|
|
+ autohotplug_load_info[len + 1] = 0x0;
|
|
+
|
|
+ len = strlen(autohotplug_load_info);
|
|
+ if (len) {
|
|
+ if (*ppos >= len)
|
|
+ return 0;
|
|
+ if (count >= len)
|
|
+ count = len;
|
|
+ if (count > (len - *ppos))
|
|
+ count = (len - *ppos);
|
|
+ if (copy_to_user((void __user *)buf,
|
|
+ (const void *)autohotplug_load_info,
|
|
+ (unsigned long)len))
|
|
+ return 0;
|
|
+ *ppos += count;
|
|
+ } else {
|
|
+ count = 0;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const struct file_operations load_ops = {
|
|
+ .read = autohotplug_load_read,
|
|
+};
|
|
+
|
|
+static ssize_t autohotplug_load_relative_read(struct file *file,
|
|
+ char __user *buf, size_t count,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ int i, len;
|
|
+
|
|
+ for_each_possible_cpu(i)
|
|
+ sprintf(autohotplug_load_info + i * 5, "%4d ",
|
|
+ governor_load.load.cpu_load_relative[i]);
|
|
+
|
|
+ len = strlen(autohotplug_load_info);
|
|
+ autohotplug_load_info[len] = 0x0A;
|
|
+ autohotplug_load_info[len + 1] = 0x0;
|
|
+
|
|
+ len = strlen(autohotplug_load_info);
|
|
+ if (len) {
|
|
+ if (*ppos >= len)
|
|
+ return 0;
|
|
+ if (count >= len)
|
|
+ count = len;
|
|
+ if (count > (len - *ppos))
|
|
+ count = (len - *ppos);
|
|
+ if (copy_to_user((void __user *)buf,
|
|
+ (const void *)autohotplug_load_info,
|
|
+ (unsigned long)len))
|
|
+ return 0;
|
|
+ *ppos += count;
|
|
+ } else {
|
|
+ count = 0;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static const struct file_operations load_relative_ops = {
|
|
+ .read = autohotplug_load_relative_read,
|
|
+};
|
|
+
|
|
+static int __init autohotplug_debugfs_init(void)
|
|
+{
|
|
+ int err = 0;
|
|
+
|
|
+ debugfs_autohotplug_root = debugfs_create_dir("autohotplug", NULL);
|
|
+ if (!debugfs_autohotplug_root)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ if (!debugfs_create_file("load", 0444, debugfs_autohotplug_root,
|
|
+ NULL, &load_ops)) {
|
|
+ err = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (!debugfs_create_file("load_relative", 0444,
|
|
+ debugfs_autohotplug_root, NULL,
|
|
+ &load_relative_ops)) {
|
|
+ err = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+out:
|
|
+ debugfs_remove_recursive(debugfs_autohotplug_root);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void __exit autohotplug_debugfs_exit(void)
|
|
+{
|
|
+ debugfs_remove_recursive(debugfs_autohotplug_root);
|
|
+}
|
|
+
|
|
+late_initcall(autohotplug_debugfs_init);
|
|
+module_exit(autohotplug_debugfs_exit);
|
|
+#endif /* CONFIG_DEBUG_FS */
|
|
+
|
|
+MODULE_DESCRIPTION("CPU Autohotplug");
|
|
+MODULE_LICENSE("GPL");
|