aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-exynos/include/mach/cpufreq.h34
-rw-r--r--drivers/cpufreq/Kconfig.arm15
-rw-r--r--drivers/cpufreq/Makefile1
-rw-r--r--drivers/cpufreq/exynos-cpufreq.c296
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c385
5 files changed, 414 insertions, 317 deletions
diff --git a/arch/arm/mach-exynos/include/mach/cpufreq.h b/arch/arm/mach-exynos/include/mach/cpufreq.h
new file mode 100644
index 000000000000..3df27f2d5034
--- /dev/null
+++ b/arch/arm/mach-exynos/include/mach/cpufreq.h
@@ -0,0 +1,34 @@
1/* linux/arch/arm/mach-exynos/include/mach/cpufreq.h
2 *
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
5 *
6 * EXYNOS - CPUFreq support
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
13enum cpufreq_level_index {
14 L0, L1, L2, L3, L4,
15 L5, L6, L7, L8, L9,
16 L10, L11, L12, L13, L14,
17 L15, L16, L17, L18, L19,
18 L20,
19};
20
21struct exynos_dvfs_info {
22 unsigned long mpll_freq_khz;
23 unsigned int pll_safe_idx;
24 unsigned int pm_lock_idx;
25 unsigned int max_support_idx;
26 unsigned int min_support_idx;
27 struct clk *cpu_clk;
28 unsigned int *volt_table;
29 struct cpufreq_frequency_table *freq_table;
30 void (*set_freq)(unsigned int, unsigned int);
31 bool (*need_apll_change)(unsigned int, unsigned int);
32};
33
34extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 72a0044c1baa..e0664fed018a 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -21,12 +21,19 @@ config ARM_S5PV210_CPUFREQ
21 21
22 If in doubt, say N. 22 If in doubt, say N.
23 23
24config ARM_EXYNOS_CPUFREQ
25 bool "SAMSUNG EXYNOS SoCs"
26 depends on ARCH_EXYNOS
27 select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210
28 default y
29 help
30 This adds the CPUFreq driver common part for Samsung
31 EXYNOS SoCs.
32
33 If in doubt, say N.
34
24config ARM_EXYNOS4210_CPUFREQ 35config ARM_EXYNOS4210_CPUFREQ
25 bool "Samsung EXYNOS4210" 36 bool "Samsung EXYNOS4210"
26 depends on CPU_EXYNOS4210
27 default y
28 help 37 help
29 This adds the CPUFreq driver for Samsung EXYNOS4210 38 This adds the CPUFreq driver for Samsung EXYNOS4210
30 SoC (S5PV310 or S5PC210). 39 SoC (S5PV310 or S5PC210).
31
32 If in doubt, say N.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ce75fcbcca4f..ac000fa76bbb 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
42obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o 42obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
43obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o 43obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
44obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o 44obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
45obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o
45obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o 46obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
46obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o 47obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o
47 48
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
new file mode 100644
index 000000000000..24e4dd453fab
--- /dev/null
+++ b/drivers/cpufreq/exynos-cpufreq.c
@@ -0,0 +1,296 @@
1/*
2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * EXYNOS - CPU frequency scaling support for EXYNOS series
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12#include <linux/types.h>
13#include <linux/kernel.h>
14#include <linux/err.h>
15#include <linux/clk.h>
16#include <linux/io.h>
17#include <linux/slab.h>
18#include <linux/regulator/consumer.h>
19#include <linux/cpufreq.h>
20#include <linux/suspend.h>
21#include <linux/reboot.h>
22
23#include <mach/map.h>
24#include <mach/regs-clock.h>
25#include <mach/regs-mem.h>
26#include <mach/cpufreq.h>
27
28#include <plat/clock.h>
29#include <plat/pm.h>
30
31static struct exynos_dvfs_info *exynos_info;
32
33static struct regulator *arm_regulator;
34static struct cpufreq_freqs freqs;
35
36static unsigned int locking_frequency;
37static bool frequency_locked;
38static DEFINE_MUTEX(cpufreq_lock);
39
40int exynos_verify_speed(struct cpufreq_policy *policy)
41{
42 return cpufreq_frequency_table_verify(policy,
43 exynos_info->freq_table);
44}
45
46unsigned int exynos_getspeed(unsigned int cpu)
47{
48 return clk_get_rate(exynos_info->cpu_clk) / 1000;
49}
50
51static int exynos_target(struct cpufreq_policy *policy,
52 unsigned int target_freq,
53 unsigned int relation)
54{
55 unsigned int index, old_index;
56 unsigned int arm_volt, safe_arm_volt = 0;
57 int ret = 0;
58 struct cpufreq_frequency_table *freq_table = exynos_info->freq_table;
59 unsigned int *volt_table = exynos_info->volt_table;
60 unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz;
61
62 mutex_lock(&cpufreq_lock);
63
64 freqs.old = policy->cur;
65
66 if (frequency_locked && target_freq != locking_frequency) {
67 ret = -EAGAIN;
68 goto out;
69 }
70
71 if (cpufreq_frequency_table_target(policy, freq_table,
72 freqs.old, relation, &old_index)) {
73 ret = -EINVAL;
74 goto out;
75 }
76
77 if (cpufreq_frequency_table_target(policy, freq_table,
78 target_freq, relation, &index)) {
79 ret = -EINVAL;
80 goto out;
81 }
82
83 freqs.new = freq_table[index].frequency;
84 freqs.cpu = policy->cpu;
85
86 /*
87 * ARM clock source will be changed APLL to MPLL temporary
88 * To support this level, need to control regulator for
89 * required voltage level
90 */
91 if (exynos_info->need_apll_change != NULL) {
92 if (exynos_info->need_apll_change(old_index, index) &&
93 (freq_table[index].frequency < mpll_freq_khz) &&
94 (freq_table[old_index].frequency < mpll_freq_khz))
95 safe_arm_volt = volt_table[exynos_info->pll_safe_idx];
96 }
97 arm_volt = volt_table[index];
98
99 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
100
101 /* When the new frequency is higher than current frequency */
102 if ((freqs.new > freqs.old) && !safe_arm_volt) {
103 /* Firstly, voltage up to increase frequency */
104 regulator_set_voltage(arm_regulator, arm_volt,
105 arm_volt);
106 }
107
108 if (safe_arm_volt)
109 regulator_set_voltage(arm_regulator, safe_arm_volt,
110 safe_arm_volt);
111 if (freqs.new != freqs.old)
112 exynos_info->set_freq(old_index, index);
113
114 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
115
116 /* When the new frequency is lower than current frequency */
117 if ((freqs.new < freqs.old) ||
118 ((freqs.new > freqs.old) && safe_arm_volt)) {
119 /* down the voltage after frequency change */
120 regulator_set_voltage(arm_regulator, arm_volt,
121 arm_volt);
122 }
123
124out:
125 mutex_unlock(&cpufreq_lock);
126
127 return ret;
128}
129
130#ifdef CONFIG_PM
131static int exynos_cpufreq_suspend(struct cpufreq_policy *policy)
132{
133 return 0;
134}
135
136static int exynos_cpufreq_resume(struct cpufreq_policy *policy)
137{
138 return 0;
139}
140#endif
141
142/**
143 * exynos_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
144 * context
145 * @notifier
146 * @pm_event
147 * @v
148 *
149 * While frequency_locked == true, target() ignores every frequency but
150 * locking_frequency. The locking_frequency value is the initial frequency,
151 * which is set by the bootloader. In order to eliminate possible
152 * inconsistency in clock values, we save and restore frequencies during
153 * suspend and resume and block CPUFREQ activities. Note that the standard
154 * suspend/resume cannot be used as they are too deep (syscore_ops) for
155 * regulator actions.
156 */
157static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier,
158 unsigned long pm_event, void *v)
159{
160 struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
161 static unsigned int saved_frequency;
162 unsigned int temp;
163
164 mutex_lock(&cpufreq_lock);
165 switch (pm_event) {
166 case PM_SUSPEND_PREPARE:
167 if (frequency_locked)
168 goto out;
169
170 frequency_locked = true;
171
172 if (locking_frequency) {
173 saved_frequency = exynos_getspeed(0);
174
175 mutex_unlock(&cpufreq_lock);
176 exynos_target(policy, locking_frequency,
177 CPUFREQ_RELATION_H);
178 mutex_lock(&cpufreq_lock);
179 }
180 break;
181
182 case PM_POST_SUSPEND:
183 if (saved_frequency) {
184 /*
185 * While frequency_locked, only locking_frequency
186 * is valid for target(). In order to use
187 * saved_frequency while keeping frequency_locked,
188 * we temporarly overwrite locking_frequency.
189 */
190 temp = locking_frequency;
191 locking_frequency = saved_frequency;
192
193 mutex_unlock(&cpufreq_lock);
194 exynos_target(policy, locking_frequency,
195 CPUFREQ_RELATION_H);
196 mutex_lock(&cpufreq_lock);
197
198 locking_frequency = temp;
199 }
200 frequency_locked = false;
201 break;
202 }
203out:
204 mutex_unlock(&cpufreq_lock);
205
206 return NOTIFY_OK;
207}
208
209static struct notifier_block exynos_cpufreq_nb = {
210 .notifier_call = exynos_cpufreq_pm_notifier,
211};
212
213static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
214{
215 policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu);
216
217 cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
218
219 /* set the transition latency value */
220 policy->cpuinfo.transition_latency = 100000;
221
222 /*
223 * EXYNOS4 multi-core processors has 2 cores
224 * that the frequency cannot be set independently.
225 * Each cpu is bound to the same speed.
226 * So the affected cpu is all of the cpus.
227 */
228 if (num_online_cpus() == 1) {
229 cpumask_copy(policy->related_cpus, cpu_possible_mask);
230 cpumask_copy(policy->cpus, cpu_online_mask);
231 } else {
232 cpumask_setall(policy->cpus);
233 }
234
235 return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
236}
237
238static struct cpufreq_driver exynos_driver = {
239 .flags = CPUFREQ_STICKY,
240 .verify = exynos_verify_speed,
241 .target = exynos_target,
242 .get = exynos_getspeed,
243 .init = exynos_cpufreq_cpu_init,
244 .name = "exynos_cpufreq",
245#ifdef CONFIG_PM
246 .suspend = exynos_cpufreq_suspend,
247 .resume = exynos_cpufreq_resume,
248#endif
249};
250
251static int __init exynos_cpufreq_init(void)
252{
253 int ret = -EINVAL;
254
255 exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL);
256 if (!exynos_info)
257 return -ENOMEM;
258
259 if (soc_is_exynos4210())
260 ret = exynos4210_cpufreq_init(exynos_info);
261 else
262 pr_err("%s: CPU type not found\n", __func__);
263
264 if (ret)
265 goto err_vdd_arm;
266
267 if (exynos_info->set_freq == NULL) {
268 pr_err("%s: No set_freq function (ERR)\n", __func__);
269 goto err_vdd_arm;
270 }
271
272 arm_regulator = regulator_get(NULL, "vdd_arm");
273 if (IS_ERR(arm_regulator)) {
274 pr_err("%s: failed to get resource vdd_arm\n", __func__);
275 goto err_vdd_arm;
276 }
277
278 register_pm_notifier(&exynos_cpufreq_nb);
279
280 if (cpufreq_register_driver(&exynos_driver)) {
281 pr_err("%s: failed to register cpufreq driver\n", __func__);
282 goto err_cpufreq;
283 }
284
285 return 0;
286err_cpufreq:
287 unregister_pm_notifier(&exynos_cpufreq_nb);
288
289 if (!IS_ERR(arm_regulator))
290 regulator_put(arm_regulator);
291err_vdd_arm:
292 kfree(exynos_info);
293 pr_debug("%s: failed initialization\n", __func__);
294 return -EINVAL;
295}
296late_initcall(exynos_cpufreq_init);
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
index a0af2d4448a9..6bc4ada56df1 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -2,7 +2,7 @@
2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. 2 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com 3 * http://www.samsung.com
4 * 4 *
5 * EXYNOS4 - CPU frequency scaling support 5 * EXYNOS4210 - CPU frequency scaling support
6 * 6 *
7 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as 8 * it under the terms of the GNU General Public License version 2 as
@@ -23,10 +23,16 @@
23#include <mach/map.h> 23#include <mach/map.h>
24#include <mach/regs-clock.h> 24#include <mach/regs-clock.h>
25#include <mach/regs-mem.h> 25#include <mach/regs-mem.h>
26#include <mach/cpufreq.h>
26 27
27#include <plat/clock.h> 28#include <plat/clock.h>
28#include <plat/pm.h> 29#include <plat/pm.h>
29 30
31#define CPUFREQ_LEVEL_END L5
32
33static int max_support_idx = L0;
34static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
35
30static struct clk *cpu_clk; 36static struct clk *cpu_clk;
31static struct clk *moutcore; 37static struct clk *moutcore;
32static struct clk *mout_mpll; 38static struct clk *mout_mpll;
@@ -37,20 +43,18 @@ static struct regulator *arm_regulator;
37static struct cpufreq_freqs freqs; 43static struct cpufreq_freqs freqs;
38 44
39struct cpufreq_clkdiv { 45struct cpufreq_clkdiv {
46 unsigned int index;
40 unsigned int clkdiv; 47 unsigned int clkdiv;
41}; 48};
42 49
43static unsigned int locking_frequency; 50static unsigned int exynos4210_volt_table[CPUFREQ_LEVEL_END] = {
44static bool frequency_locked; 51 1250000, 1150000, 1050000, 975000, 950000,
45static DEFINE_MUTEX(cpufreq_lock);
46
47enum cpufreq_level_index {
48 L0, L1, L2, L3, L4, CPUFREQ_LEVEL_END,
49}; 52};
50 53
51static struct cpufreq_clkdiv exynos4_clkdiv_table[CPUFREQ_LEVEL_END];
52 54
53static struct cpufreq_frequency_table exynos4_freq_table[] = { 55static struct cpufreq_clkdiv exynos4210_clkdiv_table[CPUFREQ_LEVEL_END];
56
57static struct cpufreq_frequency_table exynos4210_freq_table[] = {
54 {L0, 1200*1000}, 58 {L0, 1200*1000},
55 {L1, 1000*1000}, 59 {L1, 1000*1000},
56 {L2, 800*1000}, 60 {L2, 800*1000},
@@ -104,31 +108,7 @@ static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = {
104 { 3, 0 }, 108 { 3, 0 },
105}; 109};
106 110
107struct cpufreq_voltage_table { 111static unsigned int exynos4210_apll_pms_table[CPUFREQ_LEVEL_END] = {
108 unsigned int index; /* any */
109 unsigned int arm_volt; /* uV */
110};
111
112static struct cpufreq_voltage_table exynos4_volt_table[CPUFREQ_LEVEL_END] = {
113 {
114 .index = L0,
115 .arm_volt = 1350000,
116 }, {
117 .index = L1,
118 .arm_volt = 1300000,
119 }, {
120 .index = L2,
121 .arm_volt = 1200000,
122 }, {
123 .index = L3,
124 .arm_volt = 1100000,
125 }, {
126 .index = L4,
127 .arm_volt = 1050000,
128 },
129};
130
131static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = {
132 /* APLL FOUT L0: 1200MHz */ 112 /* APLL FOUT L0: 1200MHz */
133 ((150 << 16) | (3 << 8) | 1), 113 ((150 << 16) | (3 << 8) | 1),
134 114
@@ -145,23 +125,13 @@ static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = {
145 ((200 << 16) | (6 << 8) | 3), 125 ((200 << 16) | (6 << 8) | 3),
146}; 126};
147 127
148static int exynos4_verify_speed(struct cpufreq_policy *policy) 128static void exynos4210_set_clkdiv(unsigned int div_index)
149{
150 return cpufreq_frequency_table_verify(policy, exynos4_freq_table);
151}
152
153static unsigned int exynos4_getspeed(unsigned int cpu)
154{
155 return clk_get_rate(cpu_clk) / 1000;
156}
157
158static void exynos4_set_clkdiv(unsigned int div_index)
159{ 129{
160 unsigned int tmp; 130 unsigned int tmp;
161 131
162 /* Change Divider - CPU0 */ 132 /* Change Divider - CPU0 */
163 133
164 tmp = exynos4_clkdiv_table[div_index].clkdiv; 134 tmp = exynos4210_clkdiv_table[div_index].clkdiv;
165 135
166 __raw_writel(tmp, S5P_CLKDIV_CPU); 136 __raw_writel(tmp, S5P_CLKDIV_CPU);
167 137
@@ -185,7 +155,7 @@ static void exynos4_set_clkdiv(unsigned int div_index)
185 } while (tmp & 0x11); 155 } while (tmp & 0x11);
186} 156}
187 157
188static void exynos4_set_apll(unsigned int index) 158static void exynos4210_set_apll(unsigned int index)
189{ 159{
190 unsigned int tmp; 160 unsigned int tmp;
191 161
@@ -204,7 +174,7 @@ static void exynos4_set_apll(unsigned int index)
204 /* 3. Change PLL PMS values */ 174 /* 3. Change PLL PMS values */
205 tmp = __raw_readl(S5P_APLL_CON0); 175 tmp = __raw_readl(S5P_APLL_CON0);
206 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); 176 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
207 tmp |= exynos4_apll_pms_table[index]; 177 tmp |= exynos4210_apll_pms_table[index];
208 __raw_writel(tmp, S5P_APLL_CON0); 178 __raw_writel(tmp, S5P_APLL_CON0);
209 179
210 /* 4. wait_lock_time */ 180 /* 4. wait_lock_time */
@@ -221,305 +191,90 @@ static void exynos4_set_apll(unsigned int index)
221 } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT)); 191 } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
222} 192}
223 193
224static void exynos4_set_frequency(unsigned int old_index, unsigned int new_index) 194bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index)
195{
196 unsigned int old_pm = (exynos4210_apll_pms_table[old_index] >> 8);
197 unsigned int new_pm = (exynos4210_apll_pms_table[new_index] >> 8);
198
199 return (old_pm == new_pm) ? 0 : 1;
200}
201
202static void exynos4210_set_frequency(unsigned int old_index,
203 unsigned int new_index)
225{ 204{
226 unsigned int tmp; 205 unsigned int tmp;
227 206
228 if (old_index > new_index) { 207 if (old_index > new_index) {
229 /* 208 if (!exynos4210_pms_change(old_index, new_index)) {
230 * L1/L3, L2/L4 Level change require
231 * to only change s divider value
232 */
233 if (((old_index == L3) && (new_index == L1)) ||
234 ((old_index == L4) && (new_index == L2))) {
235 /* 1. Change the system clock divider values */ 209 /* 1. Change the system clock divider values */
236 exynos4_set_clkdiv(new_index); 210 exynos4210_set_clkdiv(new_index);
237 211
238 /* 2. Change just s value in apll m,p,s value */ 212 /* 2. Change just s value in apll m,p,s value */
239 tmp = __raw_readl(S5P_APLL_CON0); 213 tmp = __raw_readl(S5P_APLL_CON0);
240 tmp &= ~(0x7 << 0); 214 tmp &= ~(0x7 << 0);
241 tmp |= (exynos4_apll_pms_table[new_index] & 0x7); 215 tmp |= (exynos4210_apll_pms_table[new_index] & 0x7);
242 __raw_writel(tmp, S5P_APLL_CON0); 216 __raw_writel(tmp, S5P_APLL_CON0);
243 } else { 217 } else {
244 /* Clock Configuration Procedure */ 218 /* Clock Configuration Procedure */
245 /* 1. Change the system clock divider values */ 219 /* 1. Change the system clock divider values */
246 exynos4_set_clkdiv(new_index); 220 exynos4210_set_clkdiv(new_index);
247 /* 2. Change the apll m,p,s value */ 221 /* 2. Change the apll m,p,s value */
248 exynos4_set_apll(new_index); 222 exynos4210_set_apll(new_index);
249 } 223 }
250 } else if (old_index < new_index) { 224 } else if (old_index < new_index) {
251 /* 225 if (!exynos4210_pms_change(old_index, new_index)) {
252 * L1/L3, L2/L4 Level change require
253 * to only change s divider value
254 */
255 if (((old_index == L1) && (new_index == L3)) ||
256 ((old_index == L2) && (new_index == L4))) {
257 /* 1. Change just s value in apll m,p,s value */ 226 /* 1. Change just s value in apll m,p,s value */
258 tmp = __raw_readl(S5P_APLL_CON0); 227 tmp = __raw_readl(S5P_APLL_CON0);
259 tmp &= ~(0x7 << 0); 228 tmp &= ~(0x7 << 0);
260 tmp |= (exynos4_apll_pms_table[new_index] & 0x7); 229 tmp |= (exynos4210_apll_pms_table[new_index] & 0x7);
261 __raw_writel(tmp, S5P_APLL_CON0); 230 __raw_writel(tmp, S5P_APLL_CON0);
262 231
263 /* 2. Change the system clock divider values */ 232 /* 2. Change the system clock divider values */
264 exynos4_set_clkdiv(new_index); 233 exynos4210_set_clkdiv(new_index);
265 } else { 234 } else {
266 /* Clock Configuration Procedure */ 235 /* Clock Configuration Procedure */
267 /* 1. Change the apll m,p,s value */ 236 /* 1. Change the apll m,p,s value */
268 exynos4_set_apll(new_index); 237 exynos4210_set_apll(new_index);
269 /* 2. Change the system clock divider values */ 238 /* 2. Change the system clock divider values */
270 exynos4_set_clkdiv(new_index); 239 exynos4210_set_clkdiv(new_index);
271 } 240 }
272 } 241 }
273} 242}
274 243
275static int exynos4_target(struct cpufreq_policy *policy, 244int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
276 unsigned int target_freq,
277 unsigned int relation)
278{
279 unsigned int index, old_index;
280 unsigned int arm_volt;
281 int err = -EINVAL;
282
283 freqs.old = exynos4_getspeed(policy->cpu);
284
285 mutex_lock(&cpufreq_lock);
286
287 if (frequency_locked && target_freq != locking_frequency) {
288 err = -EAGAIN;
289 goto out;
290 }
291
292 if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
293 freqs.old, relation, &old_index))
294 goto out;
295
296 if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
297 target_freq, relation, &index))
298 goto out;
299
300 err = 0;
301
302 freqs.new = exynos4_freq_table[index].frequency;
303 freqs.cpu = policy->cpu;
304
305 if (freqs.new == freqs.old)
306 goto out;
307
308 /* get the voltage value */
309 arm_volt = exynos4_volt_table[index].arm_volt;
310
311 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
312
313 /* control regulator */
314 if (freqs.new > freqs.old) {
315 /* Voltage up */
316 regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
317 }
318
319 /* Clock Configuration Procedure */
320 exynos4_set_frequency(old_index, index);
321
322 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
323
324 /* control regulator */
325 if (freqs.new < freqs.old) {
326 /* Voltage down */
327 regulator_set_voltage(arm_regulator, arm_volt, arm_volt);
328 }
329
330out:
331 mutex_unlock(&cpufreq_lock);
332 return err;
333}
334
335#ifdef CONFIG_PM
336/*
337 * These suspend/resume are used as syscore_ops, it is already too
338 * late to set regulator voltages at this stage.
339 */
340static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
341{
342 return 0;
343}
344
345static int exynos4_cpufreq_resume(struct cpufreq_policy *policy)
346{
347 return 0;
348}
349#endif
350
351/**
352 * exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
353 * context
354 * @notifier
355 * @pm_event
356 * @v
357 *
358 * While frequency_locked == true, target() ignores every frequency but
359 * locking_frequency. The locking_frequency value is the initial frequency,
360 * which is set by the bootloader. In order to eliminate possible
361 * inconsistency in clock values, we save and restore frequencies during
362 * suspend and resume and block CPUFREQ activities. Note that the standard
363 * suspend/resume cannot be used as they are too deep (syscore_ops) for
364 * regulator actions.
365 */
366static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier,
367 unsigned long pm_event, void *v)
368{
369 struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
370 static unsigned int saved_frequency;
371 unsigned int temp;
372
373 mutex_lock(&cpufreq_lock);
374 switch (pm_event) {
375 case PM_SUSPEND_PREPARE:
376 if (frequency_locked)
377 goto out;
378 frequency_locked = true;
379
380 if (locking_frequency) {
381 saved_frequency = exynos4_getspeed(0);
382
383 mutex_unlock(&cpufreq_lock);
384 exynos4_target(policy, locking_frequency,
385 CPUFREQ_RELATION_H);
386 mutex_lock(&cpufreq_lock);
387 }
388
389 break;
390 case PM_POST_SUSPEND:
391
392 if (saved_frequency) {
393 /*
394 * While frequency_locked, only locking_frequency
395 * is valid for target(). In order to use
396 * saved_frequency while keeping frequency_locked,
397 * we temporarly overwrite locking_frequency.
398 */
399 temp = locking_frequency;
400 locking_frequency = saved_frequency;
401
402 mutex_unlock(&cpufreq_lock);
403 exynos4_target(policy, locking_frequency,
404 CPUFREQ_RELATION_H);
405 mutex_lock(&cpufreq_lock);
406
407 locking_frequency = temp;
408 }
409
410 frequency_locked = false;
411 break;
412 }
413out:
414 mutex_unlock(&cpufreq_lock);
415
416 return NOTIFY_OK;
417}
418
419static struct notifier_block exynos4_cpufreq_nb = {
420 .notifier_call = exynos4_cpufreq_pm_notifier,
421};
422
423static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
424{
425 int ret;
426
427 policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu);
428
429 cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
430
431 /* set the transition latency value */
432 policy->cpuinfo.transition_latency = 100000;
433
434 /*
435 * EXYNOS4 multi-core processors has 2 cores
436 * that the frequency cannot be set independently.
437 * Each cpu is bound to the same speed.
438 * So the affected cpu is all of the cpus.
439 */
440 if (!cpu_online(1)) {
441 cpumask_copy(policy->related_cpus, cpu_possible_mask);
442 cpumask_copy(policy->cpus, cpu_online_mask);
443 } else {
444 cpumask_setall(policy->cpus);
445 }
446
447 ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
448 if (ret)
449 return ret;
450
451 cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
452
453 return 0;
454}
455
456static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy)
457{
458 cpufreq_frequency_table_put_attr(policy->cpu);
459 return 0;
460}
461
462static struct freq_attr *exynos4_cpufreq_attr[] = {
463 &cpufreq_freq_attr_scaling_available_freqs,
464 NULL,
465};
466
467static struct cpufreq_driver exynos4_driver = {
468 .flags = CPUFREQ_STICKY,
469 .verify = exynos4_verify_speed,
470 .target = exynos4_target,
471 .get = exynos4_getspeed,
472 .init = exynos4_cpufreq_cpu_init,
473 .exit = exynos4_cpufreq_cpu_exit,
474 .name = "exynos4_cpufreq",
475 .attr = exynos4_cpufreq_attr,
476#ifdef CONFIG_PM
477 .suspend = exynos4_cpufreq_suspend,
478 .resume = exynos4_cpufreq_resume,
479#endif
480};
481
482static int __init exynos4_cpufreq_init(void)
483{ 245{
484 int i; 246 int i;
485 unsigned int tmp; 247 unsigned int tmp;
248 unsigned long rate;
486 249
487 cpu_clk = clk_get(NULL, "armclk"); 250 cpu_clk = clk_get(NULL, "armclk");
488 if (IS_ERR(cpu_clk)) 251 if (IS_ERR(cpu_clk))
489 return PTR_ERR(cpu_clk); 252 return PTR_ERR(cpu_clk);
490 253
491 locking_frequency = exynos4_getspeed(0);
492
493 moutcore = clk_get(NULL, "moutcore"); 254 moutcore = clk_get(NULL, "moutcore");
494 if (IS_ERR(moutcore)) 255 if (IS_ERR(moutcore))
495 goto out; 256 goto err_moutcore;
496 257
497 mout_mpll = clk_get(NULL, "mout_mpll"); 258 mout_mpll = clk_get(NULL, "mout_mpll");
498 if (IS_ERR(mout_mpll)) 259 if (IS_ERR(mout_mpll))
499 goto out; 260 goto err_mout_mpll;
261
262 rate = clk_get_rate(mout_mpll) / 1000;
500 263
501 mout_apll = clk_get(NULL, "mout_apll"); 264 mout_apll = clk_get(NULL, "mout_apll");
502 if (IS_ERR(mout_apll)) 265 if (IS_ERR(mout_apll))
503 goto out; 266 goto err_mout_apll;
504
505 arm_regulator = regulator_get(NULL, "vdd_arm");
506 if (IS_ERR(arm_regulator)) {
507 printk(KERN_ERR "failed to get resource %s\n", "vdd_arm");
508 goto out;
509 }
510
511 register_pm_notifier(&exynos4_cpufreq_nb);
512 267
513 tmp = __raw_readl(S5P_CLKDIV_CPU); 268 tmp = __raw_readl(S5P_CLKDIV_CPU);
514 269
515 for (i = L0; i < CPUFREQ_LEVEL_END; i++) { 270 for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
516 tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | 271 tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK |
517 S5P_CLKDIV_CPU0_COREM0_MASK | 272 S5P_CLKDIV_CPU0_COREM0_MASK |
518 S5P_CLKDIV_CPU0_COREM1_MASK | 273 S5P_CLKDIV_CPU0_COREM1_MASK |
519 S5P_CLKDIV_CPU0_PERIPH_MASK | 274 S5P_CLKDIV_CPU0_PERIPH_MASK |
520 S5P_CLKDIV_CPU0_ATB_MASK | 275 S5P_CLKDIV_CPU0_ATB_MASK |
521 S5P_CLKDIV_CPU0_PCLKDBG_MASK | 276 S5P_CLKDIV_CPU0_PCLKDBG_MASK |
522 S5P_CLKDIV_CPU0_APLL_MASK); 277 S5P_CLKDIV_CPU0_APLL_MASK);
523 278
524 tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | 279 tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) |
525 (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | 280 (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) |
@@ -529,29 +284,33 @@ static int __init exynos4_cpufreq_init(void)
529 (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | 284 (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) |
530 (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); 285 (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT));
531 286
532 exynos4_clkdiv_table[i].clkdiv = tmp; 287 exynos4210_clkdiv_table[i].clkdiv = tmp;
533 } 288 }
534 289
535 return cpufreq_register_driver(&exynos4_driver); 290 info->mpll_freq_khz = rate;
536 291 info->pm_lock_idx = L2;
537out: 292 info->pll_safe_idx = L2;
538 if (!IS_ERR(cpu_clk)) 293 info->max_support_idx = max_support_idx;
539 clk_put(cpu_clk); 294 info->min_support_idx = min_support_idx;
295 info->cpu_clk = cpu_clk;
296 info->volt_table = exynos4210_volt_table;
297 info->freq_table = exynos4210_freq_table;
298 info->set_freq = exynos4210_set_frequency;
299 info->need_apll_change = exynos4210_pms_change;
540 300
541 if (!IS_ERR(moutcore)) 301 return 0;
542 clk_put(moutcore);
543 302
303err_mout_apll:
544 if (!IS_ERR(mout_mpll)) 304 if (!IS_ERR(mout_mpll))
545 clk_put(mout_mpll); 305 clk_put(mout_mpll);
306err_mout_mpll:
307 if (!IS_ERR(moutcore))
308 clk_put(moutcore);
309err_moutcore:
310 if (!IS_ERR(cpu_clk))
311 clk_put(cpu_clk);
546 312
547 if (!IS_ERR(mout_apll)) 313 pr_debug("%s: failed initialization\n", __func__);
548 clk_put(mout_apll);
549
550 if (!IS_ERR(arm_regulator))
551 regulator_put(arm_regulator);
552
553 printk(KERN_ERR "%s: failed initialization\n", __func__);
554
555 return -EINVAL; 314 return -EINVAL;
556} 315}
557late_initcall(exynos4_cpufreq_init); 316EXPORT_SYMBOL(exynos4210_cpufreq_init);