Skip to content

Commit dbaf8cc

Browse files
committed
Merge pull request #19 from ggezer/odroid-3.8.y
Added DVFS CPU hot-plug driver for EXYNOS4x12
2 parents c931bf6 + e216099 commit dbaf8cc

File tree

3 files changed

+358
-0
lines changed

3 files changed

+358
-0
lines changed

drivers/cpufreq/Kconfig.arm

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ config ARM_EXYNOS4X12_CPUFREQ
7171
This adds the CPUFreq driver for Samsung EXYNOS4X12
7272
SoC (EXYNOS4212 or EXYNOS4412).
7373

74+
config ARM_EXYNOS4x12_DVFS_HOTPLUG
75+
bool "DVFS hotplug driver"
76+
depends on ARM_EXYNOS4X12_CPUFREQ && HOTPLUG_CPU
77+
default y
78+
help
79+
This adds the CPUFreq DVFS hotplug capability for Samsung EXYNOS4x12
80+
SoC (EXYNOS4212 or EXYNOS4412).
81+
82+
7483
config ARM_EXYNOS5250_CPUFREQ
7584
def_bool SOC_EXYNOS5250
7685
help

drivers/cpufreq/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
5252
obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
5353
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
5454
obj-$(CONFIG_ARM_SPEAR_CPUFREQ) += spear-cpufreq.o
55+
obj-$(CONFIG_ARM_EXYNOS4x12_DVFS_HOTPLUG) += exynos4x12-dvfs-hotplug.o
5556

5657
##################################################################################
5758
# PowerPC platform drivers
Lines changed: 348 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,348 @@
1+
/*
2+
* drivers/cpufreq/exynos4x12-dvfs-hotplug.c
3+
*
4+
* DVFS cpu-hotplug driver for Samsung Exynos 4x12 SoCs
5+
*
6+
* Author: Gokturk Gezer <[email protected]>
7+
*
8+
* This program is free software; you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License version 2 as
10+
* published by the Free Software Foundation.
11+
*/
12+
13+
#include <linux/sched.h>
14+
#include <linux/cpufreq.h>
15+
#include <linux/cpu.h>
16+
#include <linux/err.h>
17+
#include <linux/notifier.h>
18+
#include <linux/reboot.h>
19+
#include <linux/suspend.h>
20+
#include <linux/io.h>
21+
#include <linux/workqueue.h>
22+
23+
#include <plat/cpu.h>
24+
25+
// tunables
26+
static unsigned int hotplug_min_cpu_count;
27+
static unsigned int hotplug_max_cpu_count;
28+
static unsigned int hotplug_freq_load_tolerance;
29+
static unsigned int hotplug_tick_interval;
30+
static unsigned int hotplug_tick_anticipation;
31+
32+
// cpufreq state
33+
static char governor_name[CPUFREQ_NAME_LEN];
34+
static unsigned int freq_current;
35+
static unsigned int freq_min;
36+
static unsigned int freq_max;
37+
38+
// hotplug state
39+
static int freq_out_target;
40+
static int freq_out_limit;
41+
static int freq_in_target;
42+
static int freq_in_limit;
43+
static unsigned int can_hotplug;
44+
static struct delayed_work hotplug_dynamic_tick_work;
45+
static struct delayed_work hotplug_fixed_tick_work;
46+
void (*dynamic_tick_step)(void);
47+
static unsigned int fixed_tick_cpu_count;
48+
49+
// function declerations
50+
static void dynamic_hotplug_work();
51+
static void fixed_hotplug_work();
52+
static void start_hotplug_dynamic_tick();
53+
static void start_hotplug_fixed_tick(unsigned int);
54+
static void stop_hotplug_ticks();
55+
static void boot_fixed_cores();
56+
static void cpu_increase();
57+
static void cpu_decrease();
58+
static void hotplug_deploy(struct cpufreq_policy*);
59+
60+
static void __hotplug_tick_step_freq_track()
61+
{
62+
unsigned int tolerated_freq_in, tolerated_freq_out;
63+
64+
tolerated_freq_in = freq_max / 100 * hotplug_freq_load_tolerance;
65+
tolerated_freq_out = freq_max / 100 * (hotplug_freq_load_tolerance - 20);
66+
if (tolerated_freq_out < freq_min)
67+
tolerated_freq_out = freq_min;
68+
69+
if (freq_current >= tolerated_freq_in)
70+
{
71+
if (freq_out_target > 0)
72+
freq_out_target = 0;
73+
74+
if (++freq_in_target == freq_in_limit)
75+
{
76+
cpu_increase();
77+
freq_in_target = 0;
78+
79+
if (hotplug_tick_anticipation)
80+
freq_out_target = -1 * freq_out_limit;
81+
}
82+
}
83+
else if (freq_current <= tolerated_freq_out)
84+
{
85+
freq_in_target = 0;
86+
if (++freq_out_target == freq_out_limit)
87+
{
88+
cpu_decrease();
89+
freq_out_target = 0;
90+
}
91+
}
92+
}
93+
94+
static void dynamic_hotplug_work()
95+
{
96+
(*dynamic_tick_step)();
97+
98+
start_hotplug_dynamic_tick();
99+
}
100+
101+
static void fixed_hotplug_work()
102+
{
103+
boot_fixed_cores();
104+
}
105+
106+
static void start_hotplug_dynamic_tick()
107+
{
108+
schedule_delayed_work_on(0, &hotplug_dynamic_tick_work,
109+
msecs_to_jiffies(hotplug_tick_interval));
110+
}
111+
112+
static void start_hotplug_fixed_tick(unsigned int cpu_count)
113+
{
114+
fixed_tick_cpu_count = cpu_count;
115+
116+
schedule_delayed_work_on(0, &hotplug_fixed_tick_work,
117+
msecs_to_jiffies(500));
118+
}
119+
120+
static void stop_hotplug_ticks()
121+
{
122+
cancel_delayed_work_sync(&hotplug_dynamic_tick_work);
123+
cancel_delayed_work_sync(&hotplug_fixed_tick_work);
124+
}
125+
126+
static void boot_fixed_cores()
127+
{
128+
int operation_count;
129+
unsigned int i,online_count;
130+
131+
void (*fix_operation)(void) = cpu_increase;
132+
133+
for(i = 0, online_count = 0; i < 4; i++)
134+
{
135+
if(cpu_online(i))
136+
online_count++;
137+
}
138+
139+
operation_count = fixed_tick_cpu_count - online_count;
140+
if(operation_count < 0)
141+
{
142+
operation_count *= -1;
143+
fix_operation = cpu_decrease;
144+
}
145+
146+
for(i = 0; i < operation_count; i++)
147+
(*fix_operation)();
148+
}
149+
150+
static void cpu_increase()
151+
{
152+
unsigned int i;
153+
154+
if(num_online_cpus() >= hotplug_max_cpu_count)
155+
return;
156+
157+
for(i = 0; i < 4; i++)
158+
{
159+
if(!cpu_online(i))
160+
{
161+
cpu_up(i);
162+
break;
163+
}
164+
}
165+
}
166+
167+
static void cpu_decrease()
168+
{
169+
unsigned int i;
170+
171+
if(num_online_cpus() <= hotplug_min_cpu_count)
172+
return;
173+
174+
for(i = 3; i >= 0; i--)
175+
{
176+
if(cpu_online(i))
177+
{
178+
cpu_down(i);
179+
break;
180+
}
181+
}
182+
}
183+
184+
static void hotplug_deploy(struct cpufreq_policy * policy)
185+
{
186+
unsigned int cpu;
187+
188+
189+
/*
190+
* no governor, no hot-plug, all cores up
191+
*/
192+
if (!policy->governor)
193+
{
194+
stop_hotplug_ticks();
195+
196+
for_each_cpu_mask(cpu, policy->cpus[0])
197+
{
198+
if (!cpu_online(cpu))
199+
cpu_up(cpu);
200+
}
201+
202+
return;
203+
}
204+
205+
freq_max = policy->max;
206+
freq_min = policy->min;
207+
208+
if( 0 != strnicmp(policy->governor->name, governor_name, CPUFREQ_NAME_LEN))
209+
{
210+
stop_hotplug_ticks();
211+
212+
strncpy(governor_name, policy->governor->name, CPUFREQ_NAME_LEN);
213+
214+
if (0 == strnicmp(governor_name, "performance", CPUFREQ_NAME_LEN))
215+
{
216+
start_hotplug_fixed_tick(hotplug_max_cpu_count);
217+
}
218+
else if (0 == strnicmp(governor_name, "powersave", CPUFREQ_NAME_LEN))
219+
{
220+
start_hotplug_fixed_tick(hotplug_min_cpu_count);
221+
}
222+
else
223+
{
224+
dynamic_tick_step = __hotplug_tick_step_freq_track;
225+
start_hotplug_dynamic_tick();
226+
}
227+
}
228+
}
229+
230+
static int hotplug_cpufreq_transition(struct notifier_block *nb,
231+
unsigned long val, void *data)
232+
{
233+
struct cpufreq_freqs *freqs = (struct cpufreq_freqs *) data;
234+
235+
if ((val == CPUFREQ_POSTCHANGE))
236+
freq_current = freqs->new;
237+
238+
return 0;
239+
}
240+
241+
static int hotplug_cpufreq_policy(struct notifier_block *nb, unsigned long val, void * data)
242+
{
243+
struct cpufreq_policy * policy = (struct cpufreq_policy*) data;
244+
245+
if (val != CPUFREQ_ADJUST)
246+
return 0;
247+
248+
249+
hotplug_deploy(policy);
250+
251+
return 0;
252+
}
253+
254+
static int hotplug_pm_transition(struct notifier_block *nb, unsigned long val, void *data)
255+
{
256+
switch (val) {
257+
case PM_SUSPEND_PREPARE:
258+
stop_hotplug_ticks();
259+
can_hotplug = 0;
260+
freq_out_target = 0;
261+
freq_in_target = 0;
262+
break;
263+
case PM_POST_RESTORE:
264+
case PM_POST_SUSPEND:
265+
can_hotplug = 1;
266+
start_hotplug_dynamic_tick();
267+
break;
268+
}
269+
270+
return 0;
271+
}
272+
273+
static struct notifier_block dvfs_hotplug = { .notifier_call =
274+
hotplug_cpufreq_transition, };
275+
276+
static struct notifier_block dvfs_policy_change =
277+
{ .notifier_call = hotplug_cpufreq_policy, };
278+
279+
static struct notifier_block pm_hotplug =
280+
{ .notifier_call = hotplug_pm_transition, };
281+
282+
/*
283+
* Note : This function should be called after intialization of CPUFreq
284+
* driver for exynos4. The cpufreq_frequency_table for exynos4 should be
285+
* established before calling this function.
286+
*/
287+
static int __init exynos4_dvfs_hotplug_init(void)
288+
{
289+
int i, register_result = 0;
290+
struct cpufreq_frequency_table *table;
291+
unsigned int freq;
292+
struct cpufreq_policy policy;
293+
294+
hotplug_min_cpu_count = 2;
295+
if(soc_is_exynos4412())
296+
hotplug_max_cpu_count = 4;
297+
else
298+
hotplug_max_cpu_count = 2;
299+
hotplug_freq_load_tolerance = 60;
300+
hotplug_tick_interval = 200;
301+
hotplug_tick_anticipation = 1;
302+
303+
freq_out_target = 0;
304+
freq_out_limit = 3;
305+
freq_in_target = 0;
306+
freq_in_limit = 3;
307+
can_hotplug = 1;
308+
309+
table = cpufreq_frequency_get_table(0);
310+
if (IS_ERR(table))
311+
{
312+
printk(KERN_ERR "%s: Check loading cpufreq before\n", __func__);
313+
return PTR_ERR(table);
314+
}
315+
316+
for (i=0; table[i].frequency != CPUFREQ_TABLE_END; i++)
317+
{
318+
freq = table[i].frequency;
319+
320+
if (freq != CPUFREQ_ENTRY_INVALID && freq > freq_max)
321+
freq_max = freq;
322+
else if (freq != CPUFREQ_ENTRY_INVALID && freq_min > freq)
323+
freq_min = freq;
324+
}
325+
326+
freq_current = freq_min;
327+
328+
INIT_DEFERRABLE_WORK(&hotplug_dynamic_tick_work, dynamic_hotplug_work);
329+
INIT_DEFERRABLE_WORK(&hotplug_fixed_tick_work, fixed_hotplug_work);
330+
331+
printk(KERN_INFO "%s, max(%d),min(%d)\n", __func__, freq_max, freq_min);
332+
333+
register_result |= register_pm_notifier(&pm_hotplug);
334+
335+
register_result |= cpufreq_register_notifier(&dvfs_policy_change,
336+
CPUFREQ_POLICY_NOTIFIER);
337+
338+
register_result |= cpufreq_register_notifier(&dvfs_hotplug,
339+
CPUFREQ_TRANSITION_NOTIFIER);
340+
341+
cpufreq_get_policy(&policy, 0);
342+
hotplug_deploy(&policy);
343+
344+
return register_result;
345+
346+
}
347+
348+
late_initcall(exynos4_dvfs_hotplug_init);

0 commit comments

Comments
 (0)