diff --git a/drivers/cpufreq/exynos4x12-dvfs-hotplug.c b/drivers/cpufreq/exynos4x12-dvfs-hotplug.c index a3fdece7fc1108..02607e1bc0168f 100644 --- a/drivers/cpufreq/exynos4x12-dvfs-hotplug.c +++ b/drivers/cpufreq/exynos4x12-dvfs-hotplug.c @@ -29,22 +29,28 @@ static unsigned int hotplug_freq_load_tolerance; static unsigned int hotplug_tick_interval; static unsigned int hotplug_tick_anticipation; -// cpufreq state +// cpufreq state variables static char governor_name[CPUFREQ_NAME_LEN]; static unsigned int freq_current; static unsigned int freq_min; static unsigned int freq_max; -// hotplug state +// hotplug state variables static int freq_out_target; static int freq_out_limit; static int freq_in_target; static int freq_in_limit; +static unsigned int available_cpu_count; static unsigned int can_hotplug; static struct delayed_work hotplug_dynamic_tick_work; static struct delayed_work hotplug_fixed_tick_work; void (*dynamic_tick_step)(void); static unsigned int fixed_tick_cpu_count; +static unsigned int in_dynamic_mode; +static unsigned int dynamic_tick_suspended; +static unsigned int reached_max_cpu; +static unsigned int reached_min_cpu; +static unsigned int hotplug_effective_interval; // function declerations static void dynamic_hotplug_work(); @@ -52,10 +58,158 @@ static void fixed_hotplug_work(); static void start_hotplug_dynamic_tick(); static void start_hotplug_fixed_tick(unsigned int); static void stop_hotplug_ticks(); +static void set_anticipation(); +static void unset_anticipation(); static void boot_fixed_cores(); +static int cpu_adjust(unsigned int); static void cpu_increase(); static void cpu_decrease(); static void hotplug_deploy(struct cpufreq_policy*); +static int hotplug_cpufreq_transition(struct notifier_block*,unsigned long, void*); + + +// sysfs objects and functions +struct kobject * dvfs_hotplug_kobject; + +#define show_one(file_name, object) \ +static ssize_t show_##file_name \ +(struct kobject *kobj, struct attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "%u\n", object); \ +} + +show_one(min_cpu_count, hotplug_min_cpu_count); +show_one(max_cpu_count, hotplug_max_cpu_count); +show_one(freq_load_tolerance, hotplug_freq_load_tolerance); +show_one(tick_interval, hotplug_tick_interval); +show_one(tick_anticipation, hotplug_tick_anticipation); + +static ssize_t store_min_cpu_count(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; + + if(input > hotplug_max_cpu_count || input <= 0) + { + return -EINVAL; + } + + // Adjusting cpu count for dynamic governors is deferred + if(!in_dynamic_mode && fixed_tick_cpu_count == hotplug_min_cpu_count) + { + hotplug_min_cpu_count = input; + start_hotplug_fixed_tick(input); + } + + hotplug_min_cpu_count = input; + + return count; +} + +static ssize_t store_max_cpu_count(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; + + if(input < hotplug_min_cpu_count || input > available_cpu_count) + { + return -EINVAL; + } + + // Adjusting cpu count for dynamic governors is deferred + if(!in_dynamic_mode && fixed_tick_cpu_count == hotplug_max_cpu_count) + { + hotplug_max_cpu_count = input; + start_hotplug_fixed_tick(input); + } + + hotplug_max_cpu_count = input; + + return count; +} + +static ssize_t store_freq_load_tolerance(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; + + if(input <= 20 || input > 100) + { + return -EINVAL; + } + + hotplug_freq_load_tolerance = input; + + return count; +} + +static ssize_t store_tick_interval(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; + + if(input <= 0) + { + return -EINVAL; + } + + hotplug_tick_interval = input; + + return count; +} + +static ssize_t store_tick_anticipation(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; + + if(input != 0 && input != 1) + { + return -EINVAL; + } + + unset_anticipation(); + hotplug_tick_anticipation = input; + + return count; +} + +define_one_global_rw(min_cpu_count); +define_one_global_rw(max_cpu_count); +define_one_global_rw(freq_load_tolerance); +define_one_global_rw(tick_interval); +define_one_global_rw(tick_anticipation); + +static struct attribute *hotplug_attributes[] = { + &min_cpu_count.attr, + &max_cpu_count.attr, + &freq_load_tolerance.attr, + &tick_interval.attr, + &tick_anticipation.attr, + NULL +}; + +// End of "sysfs objects and functions" static void __hotplug_tick_step_freq_track() { @@ -68,8 +222,7 @@ static void __hotplug_tick_step_freq_track() if (freq_current >= tolerated_freq_in) { - if (freq_out_target > 0) - freq_out_target = 0; + freq_out_target = 0; if (++freq_in_target == freq_in_limit) { @@ -77,7 +230,7 @@ static void __hotplug_tick_step_freq_track() freq_in_target = 0; if (hotplug_tick_anticipation) - freq_out_target = -1 * freq_out_limit; + set_anticipation(); } } else if (freq_current <= tolerated_freq_out) @@ -87,6 +240,9 @@ static void __hotplug_tick_step_freq_track() { cpu_decrease(); freq_out_target = 0; + + if (hotplug_tick_anticipation) + unset_anticipation(); } } } @@ -95,7 +251,8 @@ static void dynamic_hotplug_work() { (*dynamic_tick_step)(); - start_hotplug_dynamic_tick(); + if(!dynamic_tick_suspended) + start_hotplug_dynamic_tick(); } static void fixed_hotplug_work() @@ -105,8 +262,10 @@ static void fixed_hotplug_work() static void start_hotplug_dynamic_tick() { + dynamic_tick_suspended = 0; + schedule_delayed_work_on(0, &hotplug_dynamic_tick_work, - msecs_to_jiffies(hotplug_tick_interval)); + msecs_to_jiffies(hotplug_effective_interval)); } static void start_hotplug_fixed_tick(unsigned int cpu_count) @@ -147,11 +306,56 @@ static void boot_fixed_cores() (*fix_operation)(); } +static void set_anticipation() +{ + if(freq_in_limit > 1) + { + hotplug_effective_interval = hotplug_tick_interval * 3 / --freq_in_limit; + } +} + +static void unset_anticipation() +{ + hotplug_effective_interval = hotplug_tick_interval; + freq_in_limit = 3; +} + +static int cpu_adjust(unsigned int increase) +{ + unsigned int nr_cpus; + + nr_cpus = num_online_cpus(); + + if(nr_cpus <= hotplug_min_cpu_count) + { + reached_min_cpu = 1; + reached_max_cpu = 0; + + return dynamic_tick_suspended = (1 && !increase); + } + else if(nr_cpus > hotplug_min_cpu_count && + nr_cpus < hotplug_max_cpu_count) + { + reached_min_cpu = 0; + reached_max_cpu = 0; + dynamic_tick_suspended = 0; + + return 0; + } + else + { + reached_min_cpu = 0; + reached_max_cpu = 1; + + return dynamic_tick_suspended = (1 && increase); + } +} + static void cpu_increase() { unsigned int i; - if(num_online_cpus() >= hotplug_max_cpu_count) + if(cpu_adjust(1)) return; for(i = 0; i < 4; i++) @@ -168,8 +372,8 @@ static void cpu_decrease() { unsigned int i; - if(num_online_cpus() <= hotplug_min_cpu_count) - return; + if(cpu_adjust(0)) + return; for(i = 3; i >= 0; i--) { @@ -185,12 +389,12 @@ static void hotplug_deploy(struct cpufreq_policy * policy) { unsigned int cpu; - /* * no governor, no hot-plug, all cores up */ if (!policy->governor) { + in_dynamic_mode = 0; stop_hotplug_ticks(); for_each_cpu_mask(cpu, policy->cpus[0]) @@ -209,19 +413,25 @@ static void hotplug_deploy(struct cpufreq_policy * policy) { stop_hotplug_ticks(); + in_dynamic_mode = 0; + strncpy(governor_name, policy->governor->name, CPUFREQ_NAME_LEN); if (0 == strnicmp(governor_name, "performance", CPUFREQ_NAME_LEN)) { + freq_current = freq_max; start_hotplug_fixed_tick(hotplug_max_cpu_count); } else if (0 == strnicmp(governor_name, "powersave", CPUFREQ_NAME_LEN)) { + freq_current = freq_min; start_hotplug_fixed_tick(hotplug_min_cpu_count); } else { dynamic_tick_step = __hotplug_tick_step_freq_track; + in_dynamic_mode = 1; + start_hotplug_dynamic_tick(); } } @@ -231,10 +441,28 @@ static int hotplug_cpufreq_transition(struct notifier_block *nb, unsigned long val, void *data) { struct cpufreq_freqs *freqs = (struct cpufreq_freqs *) data; + unsigned int tolerated_freq_in, tolerated_freq_out, rising, falling; if ((val == CPUFREQ_POSTCHANGE)) + { + rising = (freqs->new > freq_current); + falling = (freqs->new < freq_current); + freq_current = freqs->new; + if(in_dynamic_mode && dynamic_tick_suspended) + { + tolerated_freq_in = freq_max / 100 * hotplug_freq_load_tolerance; + tolerated_freq_out = freq_max / 100 * (hotplug_freq_load_tolerance - 20); + + if((freq_current <= tolerated_freq_out && falling && !reached_min_cpu) || + (freq_current >= tolerated_freq_in && rising && !reached_max_cpu)) + { + start_hotplug_dynamic_tick(); + } + } + } + return 0; } @@ -293,9 +521,10 @@ static int __init exynos4_dvfs_hotplug_init(void) hotplug_min_cpu_count = 2; if(soc_is_exynos4412()) - hotplug_max_cpu_count = 4; + available_cpu_count = 4; else - hotplug_max_cpu_count = 2; + available_cpu_count = 2; + hotplug_max_cpu_count = available_cpu_count; hotplug_freq_load_tolerance = 60; hotplug_tick_interval = 200; hotplug_tick_anticipation = 1; @@ -305,6 +534,11 @@ static int __init exynos4_dvfs_hotplug_init(void) freq_in_target = 0; freq_in_limit = 3; can_hotplug = 1; + in_dynamic_mode = 0; + dynamic_tick_suspended = 1; + reached_max_cpu = 0; + reached_min_cpu = 0; + hotplug_effective_interval = hotplug_tick_interval; table = cpufreq_frequency_get_table(0); if (IS_ERR(table)) @@ -323,7 +557,7 @@ static int __init exynos4_dvfs_hotplug_init(void) freq_min = freq; } - freq_current = freq_min; + freq_current = freq_max; INIT_DEFERRABLE_WORK(&hotplug_dynamic_tick_work, dynamic_hotplug_work); INIT_DEFERRABLE_WORK(&hotplug_fixed_tick_work, fixed_hotplug_work); @@ -338,6 +572,9 @@ static int __init exynos4_dvfs_hotplug_init(void) register_result |= cpufreq_register_notifier(&dvfs_hotplug, CPUFREQ_TRANSITION_NOTIFIER); + dvfs_hotplug_kobject = kobject_create_and_add("dvfs-hotplug", &cpu_subsys.dev_root->kobj); + sysfs_create_files(dvfs_hotplug_kobject,&hotplug_attributes); + cpufreq_get_policy(&policy, 0); hotplug_deploy(&policy);