aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpufreq
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r--drivers/cpufreq/Kconfig.arm42
-rw-r--r--drivers/cpufreq/Makefile5
-rw-r--r--drivers/cpufreq/cpufreq.c24
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c58
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c13
-rw-r--r--drivers/cpufreq/exynos-cpufreq.c6
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c70
-rw-r--r--drivers/cpufreq/exynos4x12-cpufreq.c536
-rw-r--r--drivers/cpufreq/exynos5250-cpufreq.c347
-rw-r--r--drivers/cpufreq/omap-cpufreq.c72
-rw-r--r--drivers/cpufreq/s3c2416-cpufreq.c542
-rw-r--r--drivers/cpufreq/s3c64xx-cpufreq.c7
12 files changed, 1665 insertions, 57 deletions
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index e0664fed018a..32d790dd8180 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -2,6 +2,33 @@
2# ARM CPU Frequency scaling drivers 2# ARM CPU Frequency scaling drivers
3# 3#
4 4
5config ARM_OMAP2PLUS_CPUFREQ
6 bool "TI OMAP2+"
7 default ARCH_OMAP2PLUS
8 select CPU_FREQ_TABLE
9
10config ARM_S3C2416_CPUFREQ
11 bool "S3C2416 CPU Frequency scaling support"
12 depends on CPU_S3C2416
13 help
14 This adds the CPUFreq driver for the Samsung S3C2416 and
15 S3C2450 SoC. The S3C2416 supports changing the rate of the
16 armdiv clock source and also entering a so called dynamic
17 voltage scaling mode in which it is possible to reduce the
18 core voltage of the cpu.
19
20 If in doubt, say N.
21
22config ARM_S3C2416_CPUFREQ_VCORESCALE
23 bool "Allow voltage scaling for S3C2416 arm core (EXPERIMENTAL)"
24 depends on ARM_S3C2416_CPUFREQ && REGULATOR && EXPERIMENTAL
25 help
26 Enable CPU voltage scaling when entering the dvs mode.
27 It uses information gathered through existing hardware and
28 tests but not documented in any datasheet.
29
30 If in doubt, say N.
31
5config ARM_S3C64XX_CPUFREQ 32config ARM_S3C64XX_CPUFREQ
6 bool "Samsung S3C64XX" 33 bool "Samsung S3C64XX"
7 depends on CPU_S3C6410 34 depends on CPU_S3C6410
@@ -25,6 +52,8 @@ config ARM_EXYNOS_CPUFREQ
25 bool "SAMSUNG EXYNOS SoCs" 52 bool "SAMSUNG EXYNOS SoCs"
26 depends on ARCH_EXYNOS 53 depends on ARCH_EXYNOS
27 select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210 54 select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210
55 select ARM_EXYNOS4X12_CPUFREQ if (SOC_EXYNOS4212 || SOC_EXYNOS4412)
56 select ARM_EXYNOS5250_CPUFREQ if SOC_EXYNOS5250
28 default y 57 default y
29 help 58 help
30 This adds the CPUFreq driver common part for Samsung 59 This adds the CPUFreq driver common part for Samsung
@@ -34,6 +63,19 @@ config ARM_EXYNOS_CPUFREQ
34 63
35config ARM_EXYNOS4210_CPUFREQ 64config ARM_EXYNOS4210_CPUFREQ
36 bool "Samsung EXYNOS4210" 65 bool "Samsung EXYNOS4210"
66 depends on ARCH_EXYNOS
37 help 67 help
38 This adds the CPUFreq driver for Samsung EXYNOS4210 68 This adds the CPUFreq driver for Samsung EXYNOS4210
39 SoC (S5PV310 or S5PC210). 69 SoC (S5PV310 or S5PC210).
70
71config ARM_EXYNOS4X12_CPUFREQ
72 bool "Samsung EXYNOS4X12"
73 help
74 This adds the CPUFreq driver for Samsung EXYNOS4X12
75 SoC (EXYNOS4212 or EXYNOS4412).
76
77config ARM_EXYNOS5250_CPUFREQ
78 bool "Samsung EXYNOS5250"
79 help
80 This adds the CPUFreq driver for Samsung EXYNOS5250
81 SoC.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ac000fa76bbb..9531fc2eda22 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -40,11 +40,14 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
40################################################################################## 40##################################################################################
41# ARM SoC drivers 41# ARM SoC drivers
42obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o 42obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
43obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
43obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o 44obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
44obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o 45obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
45obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o 46obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o
46obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o 47obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
47obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o 48obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
49obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
50obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
48 51
49################################################################################## 52##################################################################################
50# PowerPC platform drivers 53# PowerPC platform drivers
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 622013fb7890..7f2f149ae40f 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -126,6 +126,15 @@ static int __init init_cpufreq_transition_notifier_list(void)
126} 126}
127pure_initcall(init_cpufreq_transition_notifier_list); 127pure_initcall(init_cpufreq_transition_notifier_list);
128 128
129static int off __read_mostly;
130int cpufreq_disabled(void)
131{
132 return off;
133}
134void disable_cpufreq(void)
135{
136 off = 1;
137}
129static LIST_HEAD(cpufreq_governor_list); 138static LIST_HEAD(cpufreq_governor_list);
130static DEFINE_MUTEX(cpufreq_governor_mutex); 139static DEFINE_MUTEX(cpufreq_governor_mutex);
131 140
@@ -1441,6 +1450,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
1441{ 1450{
1442 int retval = -EINVAL; 1451 int retval = -EINVAL;
1443 1452
1453 if (cpufreq_disabled())
1454 return -ENODEV;
1455
1444 pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu, 1456 pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
1445 target_freq, relation); 1457 target_freq, relation);
1446 if (cpu_online(policy->cpu) && cpufreq_driver->target) 1458 if (cpu_online(policy->cpu) && cpufreq_driver->target)
@@ -1549,6 +1561,9 @@ int cpufreq_register_governor(struct cpufreq_governor *governor)
1549 if (!governor) 1561 if (!governor)
1550 return -EINVAL; 1562 return -EINVAL;
1551 1563
1564 if (cpufreq_disabled())
1565 return -ENODEV;
1566
1552 mutex_lock(&cpufreq_governor_mutex); 1567 mutex_lock(&cpufreq_governor_mutex);
1553 1568
1554 err = -EBUSY; 1569 err = -EBUSY;
@@ -1572,6 +1587,9 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
1572 if (!governor) 1587 if (!governor)
1573 return; 1588 return;
1574 1589
1590 if (cpufreq_disabled())
1591 return;
1592
1575#ifdef CONFIG_HOTPLUG_CPU 1593#ifdef CONFIG_HOTPLUG_CPU
1576 for_each_present_cpu(cpu) { 1594 for_each_present_cpu(cpu) {
1577 if (cpu_online(cpu)) 1595 if (cpu_online(cpu))
@@ -1814,6 +1832,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
1814 unsigned long flags; 1832 unsigned long flags;
1815 int ret; 1833 int ret;
1816 1834
1835 if (cpufreq_disabled())
1836 return -ENODEV;
1837
1817 if (!driver_data || !driver_data->verify || !driver_data->init || 1838 if (!driver_data || !driver_data->verify || !driver_data->init ||
1818 ((!driver_data->setpolicy) && (!driver_data->target))) 1839 ((!driver_data->setpolicy) && (!driver_data->target)))
1819 return -EINVAL; 1840 return -EINVAL;
@@ -1901,6 +1922,9 @@ static int __init cpufreq_core_init(void)
1901{ 1922{
1902 int cpu; 1923 int cpu;
1903 1924
1925 if (cpufreq_disabled())
1926 return -ENODEV;
1927
1904 for_each_possible_cpu(cpu) { 1928 for_each_possible_cpu(cpu) {
1905 per_cpu(cpufreq_policy_cpu, cpu) = -1; 1929 per_cpu(cpufreq_policy_cpu, cpu) = -1;
1906 init_rwsem(&per_cpu(cpu_policy_rwsem, cpu)); 1930 init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index c3e0652520a1..836e9b062e5e 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -257,6 +257,62 @@ show_one(sampling_down_factor, sampling_down_factor);
257show_one(ignore_nice_load, ignore_nice); 257show_one(ignore_nice_load, ignore_nice);
258show_one(powersave_bias, powersave_bias); 258show_one(powersave_bias, powersave_bias);
259 259
260/**
261 * update_sampling_rate - update sampling rate effective immediately if needed.
262 * @new_rate: new sampling rate
263 *
264 * If new rate is smaller than the old, simply updaing
265 * dbs_tuners_int.sampling_rate might not be appropriate. For example,
266 * if the original sampling_rate was 1 second and the requested new sampling
267 * rate is 10 ms because the user needs immediate reaction from ondemand
268 * governor, but not sure if higher frequency will be required or not,
269 * then, the governor may change the sampling rate too late; up to 1 second
270 * later. Thus, if we are reducing the sampling rate, we need to make the
271 * new value effective immediately.
272 */
273static void update_sampling_rate(unsigned int new_rate)
274{
275 int cpu;
276
277 dbs_tuners_ins.sampling_rate = new_rate
278 = max(new_rate, min_sampling_rate);
279
280 for_each_online_cpu(cpu) {
281 struct cpufreq_policy *policy;
282 struct cpu_dbs_info_s *dbs_info;
283 unsigned long next_sampling, appointed_at;
284
285 policy = cpufreq_cpu_get(cpu);
286 if (!policy)
287 continue;
288 dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu);
289 cpufreq_cpu_put(policy);
290
291 mutex_lock(&dbs_info->timer_mutex);
292
293 if (!delayed_work_pending(&dbs_info->work)) {
294 mutex_unlock(&dbs_info->timer_mutex);
295 continue;
296 }
297
298 next_sampling = jiffies + usecs_to_jiffies(new_rate);
299 appointed_at = dbs_info->work.timer.expires;
300
301
302 if (time_before(next_sampling, appointed_at)) {
303
304 mutex_unlock(&dbs_info->timer_mutex);
305 cancel_delayed_work_sync(&dbs_info->work);
306 mutex_lock(&dbs_info->timer_mutex);
307
308 schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work,
309 usecs_to_jiffies(new_rate));
310
311 }
312 mutex_unlock(&dbs_info->timer_mutex);
313 }
314}
315
260static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, 316static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
261 const char *buf, size_t count) 317 const char *buf, size_t count)
262{ 318{
@@ -265,7 +321,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
265 ret = sscanf(buf, "%u", &input); 321 ret = sscanf(buf, "%u", &input);
266 if (ret != 1) 322 if (ret != 1)
267 return -EINVAL; 323 return -EINVAL;
268 dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate); 324 update_sampling_rate(input);
269 return count; 325 return count;
270} 326}
271 327
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
index f5002015d82e..a22ffa5bff9f 100644
--- a/drivers/cpufreq/db8500-cpufreq.c
+++ b/drivers/cpufreq/db8500-cpufreq.c
@@ -22,11 +22,11 @@ static struct cpufreq_frequency_table freq_table[] = {
22 }, 22 },
23 [1] = { 23 [1] = {
24 .index = 1, 24 .index = 1,
25 .frequency = 300000, 25 .frequency = 400000,
26 }, 26 },
27 [2] = { 27 [2] = {
28 .index = 2, 28 .index = 2,
29 .frequency = 600000, 29 .frequency = 800000,
30 }, 30 },
31 [3] = { 31 [3] = {
32 /* Used for MAX_OPP, if available */ 32 /* Used for MAX_OPP, if available */
@@ -113,12 +113,9 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
113 113
114 BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); 114 BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
115 115
116 if (!prcmu_is_u8400()) { 116 if (prcmu_has_arm_maxopp())
117 freq_table[1].frequency = 400000; 117 freq_table[3].frequency = 1000000;
118 freq_table[2].frequency = 800000; 118
119 if (prcmu_has_arm_maxopp())
120 freq_table[3].frequency = 1000000;
121 }
122 pr_info("db8500-cpufreq : Available frequencies:\n"); 119 pr_info("db8500-cpufreq : Available frequencies:\n");
123 for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) 120 for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
124 pr_info(" %d Mhz\n", freq_table[i].frequency/1000); 121 pr_info(" %d Mhz\n", freq_table[i].frequency/1000);
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
index 5467879ea07d..b243a7ee01f6 100644
--- a/drivers/cpufreq/exynos-cpufreq.c
+++ b/drivers/cpufreq/exynos-cpufreq.c
@@ -210,6 +210,8 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
210 210
211 cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); 211 cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
212 212
213 locking_frequency = exynos_getspeed(0);
214
213 /* set the transition latency value */ 215 /* set the transition latency value */
214 policy->cpuinfo.transition_latency = 100000; 216 policy->cpuinfo.transition_latency = 100000;
215 217
@@ -252,6 +254,10 @@ static int __init exynos_cpufreq_init(void)
252 254
253 if (soc_is_exynos4210()) 255 if (soc_is_exynos4210())
254 ret = exynos4210_cpufreq_init(exynos_info); 256 ret = exynos4210_cpufreq_init(exynos_info);
257 else if (soc_is_exynos4212() || soc_is_exynos4412())
258 ret = exynos4x12_cpufreq_init(exynos_info);
259 else if (soc_is_exynos5250())
260 ret = exynos5250_cpufreq_init(exynos_info);
255 else 261 else
256 pr_err("%s: CPU type not found\n", __func__); 262 pr_err("%s: CPU type not found\n", __func__);
257 263
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
index 065da5b702f1..fb148fa27678 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -121,25 +121,25 @@ static void exynos4210_set_clkdiv(unsigned int div_index)
121 121
122 tmp = exynos4210_clkdiv_table[div_index].clkdiv; 122 tmp = exynos4210_clkdiv_table[div_index].clkdiv;
123 123
124 __raw_writel(tmp, S5P_CLKDIV_CPU); 124 __raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
125 125
126 do { 126 do {
127 tmp = __raw_readl(S5P_CLKDIV_STATCPU); 127 tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU);
128 } while (tmp & 0x1111111); 128 } while (tmp & 0x1111111);
129 129
130 /* Change Divider - CPU1 */ 130 /* Change Divider - CPU1 */
131 131
132 tmp = __raw_readl(S5P_CLKDIV_CPU1); 132 tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1);
133 133
134 tmp &= ~((0x7 << 4) | 0x7); 134 tmp &= ~((0x7 << 4) | 0x7);
135 135
136 tmp |= ((clkdiv_cpu1[div_index][0] << 4) | 136 tmp |= ((clkdiv_cpu1[div_index][0] << 4) |
137 (clkdiv_cpu1[div_index][1] << 0)); 137 (clkdiv_cpu1[div_index][1] << 0));
138 138
139 __raw_writel(tmp, S5P_CLKDIV_CPU1); 139 __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
140 140
141 do { 141 do {
142 tmp = __raw_readl(S5P_CLKDIV_STATCPU1); 142 tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1);
143 } while (tmp & 0x11); 143 } while (tmp & 0x11);
144} 144}
145 145
@@ -151,32 +151,32 @@ static void exynos4210_set_apll(unsigned int index)
151 clk_set_parent(moutcore, mout_mpll); 151 clk_set_parent(moutcore, mout_mpll);
152 152
153 do { 153 do {
154 tmp = (__raw_readl(S5P_CLKMUX_STATCPU) 154 tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
155 >> S5P_CLKSRC_CPU_MUXCORE_SHIFT); 155 >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
156 tmp &= 0x7; 156 tmp &= 0x7;
157 } while (tmp != 0x2); 157 } while (tmp != 0x2);
158 158
159 /* 2. Set APLL Lock time */ 159 /* 2. Set APLL Lock time */
160 __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK); 160 __raw_writel(EXYNOS4_APLL_LOCKTIME, EXYNOS4_APLL_LOCK);
161 161
162 /* 3. Change PLL PMS values */ 162 /* 3. Change PLL PMS values */
163 tmp = __raw_readl(S5P_APLL_CON0); 163 tmp = __raw_readl(EXYNOS4_APLL_CON0);
164 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); 164 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
165 tmp |= exynos4210_apll_pms_table[index]; 165 tmp |= exynos4210_apll_pms_table[index];
166 __raw_writel(tmp, S5P_APLL_CON0); 166 __raw_writel(tmp, EXYNOS4_APLL_CON0);
167 167
168 /* 4. wait_lock_time */ 168 /* 4. wait_lock_time */
169 do { 169 do {
170 tmp = __raw_readl(S5P_APLL_CON0); 170 tmp = __raw_readl(EXYNOS4_APLL_CON0);
171 } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT))); 171 } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT)));
172 172
173 /* 5. MUX_CORE_SEL = APLL */ 173 /* 5. MUX_CORE_SEL = APLL */
174 clk_set_parent(moutcore, mout_apll); 174 clk_set_parent(moutcore, mout_apll);
175 175
176 do { 176 do {
177 tmp = __raw_readl(S5P_CLKMUX_STATCPU); 177 tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
178 tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK; 178 tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
179 } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT)); 179 } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
180} 180}
181 181
182bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index) 182bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index)
@@ -198,10 +198,10 @@ static void exynos4210_set_frequency(unsigned int old_index,
198 exynos4210_set_clkdiv(new_index); 198 exynos4210_set_clkdiv(new_index);
199 199
200 /* 2. Change just s value in apll m,p,s value */ 200 /* 2. Change just s value in apll m,p,s value */
201 tmp = __raw_readl(S5P_APLL_CON0); 201 tmp = __raw_readl(EXYNOS4_APLL_CON0);
202 tmp &= ~(0x7 << 0); 202 tmp &= ~(0x7 << 0);
203 tmp |= (exynos4210_apll_pms_table[new_index] & 0x7); 203 tmp |= (exynos4210_apll_pms_table[new_index] & 0x7);
204 __raw_writel(tmp, S5P_APLL_CON0); 204 __raw_writel(tmp, EXYNOS4_APLL_CON0);
205 } else { 205 } else {
206 /* Clock Configuration Procedure */ 206 /* Clock Configuration Procedure */
207 /* 1. Change the system clock divider values */ 207 /* 1. Change the system clock divider values */
@@ -212,10 +212,10 @@ static void exynos4210_set_frequency(unsigned int old_index,
212 } else if (old_index < new_index) { 212 } else if (old_index < new_index) {
213 if (!exynos4210_pms_change(old_index, new_index)) { 213 if (!exynos4210_pms_change(old_index, new_index)) {
214 /* 1. Change just s value in apll m,p,s value */ 214 /* 1. Change just s value in apll m,p,s value */
215 tmp = __raw_readl(S5P_APLL_CON0); 215 tmp = __raw_readl(EXYNOS4_APLL_CON0);
216 tmp &= ~(0x7 << 0); 216 tmp &= ~(0x7 << 0);
217 tmp |= (exynos4210_apll_pms_table[new_index] & 0x7); 217 tmp |= (exynos4210_apll_pms_table[new_index] & 0x7);
218 __raw_writel(tmp, S5P_APLL_CON0); 218 __raw_writel(tmp, EXYNOS4_APLL_CON0);
219 219
220 /* 2. Change the system clock divider values */ 220 /* 2. Change the system clock divider values */
221 exynos4210_set_clkdiv(new_index); 221 exynos4210_set_clkdiv(new_index);
@@ -253,24 +253,24 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
253 if (IS_ERR(mout_apll)) 253 if (IS_ERR(mout_apll))
254 goto err_mout_apll; 254 goto err_mout_apll;
255 255
256 tmp = __raw_readl(S5P_CLKDIV_CPU); 256 tmp = __raw_readl(EXYNOS4_CLKDIV_CPU);
257 257
258 for (i = L0; i < CPUFREQ_LEVEL_END; i++) { 258 for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
259 tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | 259 tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK |
260 S5P_CLKDIV_CPU0_COREM0_MASK | 260 EXYNOS4_CLKDIV_CPU0_COREM0_MASK |
261 S5P_CLKDIV_CPU0_COREM1_MASK | 261 EXYNOS4_CLKDIV_CPU0_COREM1_MASK |
262 S5P_CLKDIV_CPU0_PERIPH_MASK | 262 EXYNOS4_CLKDIV_CPU0_PERIPH_MASK |
263 S5P_CLKDIV_CPU0_ATB_MASK | 263 EXYNOS4_CLKDIV_CPU0_ATB_MASK |
264 S5P_CLKDIV_CPU0_PCLKDBG_MASK | 264 EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK |
265 S5P_CLKDIV_CPU0_APLL_MASK); 265 EXYNOS4_CLKDIV_CPU0_APLL_MASK);
266 266
267 tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | 267 tmp |= ((clkdiv_cpu0[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
268 (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | 268 (clkdiv_cpu0[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
269 (clkdiv_cpu0[i][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) | 269 (clkdiv_cpu0[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
270 (clkdiv_cpu0[i][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) | 270 (clkdiv_cpu0[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
271 (clkdiv_cpu0[i][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) | 271 (clkdiv_cpu0[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
272 (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | 272 (clkdiv_cpu0[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
273 (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); 273 (clkdiv_cpu0[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT));
274 274
275 exynos4210_clkdiv_table[i].clkdiv = tmp; 275 exynos4210_clkdiv_table[i].clkdiv = tmp;
276 } 276 }
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c
new file mode 100644
index 000000000000..8c5a7afa5b0b
--- /dev/null
+++ b/drivers/cpufreq/exynos4x12-cpufreq.c
@@ -0,0 +1,536 @@
1/*
2 * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * EXYNOS4X12 - CPU frequency scaling support
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/module.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/cpufreq.h>
19
20#include <mach/regs-clock.h>
21#include <mach/cpufreq.h>
22
23#define CPUFREQ_LEVEL_END (L13 + 1)
24
25static int max_support_idx;
26static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
27
28static struct clk *cpu_clk;
29static struct clk *moutcore;
30static struct clk *mout_mpll;
31static struct clk *mout_apll;
32
33struct cpufreq_clkdiv {
34 unsigned int index;
35 unsigned int clkdiv;
36 unsigned int clkdiv1;
37};
38
39static unsigned int exynos4x12_volt_table[CPUFREQ_LEVEL_END];
40
41static struct cpufreq_frequency_table exynos4x12_freq_table[] = {
42 {L0, 1500 * 1000},
43 {L1, 1400 * 1000},
44 {L2, 1300 * 1000},
45 {L3, 1200 * 1000},
46 {L4, 1100 * 1000},
47 {L5, 1000 * 1000},
48 {L6, 900 * 1000},
49 {L7, 800 * 1000},
50 {L8, 700 * 1000},
51 {L9, 600 * 1000},
52 {L10, 500 * 1000},
53 {L11, 400 * 1000},
54 {L12, 300 * 1000},
55 {L13, 200 * 1000},
56 {0, CPUFREQ_TABLE_END},
57};
58
59static struct cpufreq_clkdiv exynos4x12_clkdiv_table[CPUFREQ_LEVEL_END];
60
61static unsigned int clkdiv_cpu0_4212[CPUFREQ_LEVEL_END][8] = {
62 /*
63 * Clock divider value for following
64 * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
65 * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 }
66 */
67 /* ARM L0: 1500 MHz */
68 { 0, 3, 7, 0, 6, 1, 2, 0 },
69
70 /* ARM L1: 1400 MHz */
71 { 0, 3, 7, 0, 6, 1, 2, 0 },
72
73 /* ARM L2: 1300 MHz */
74 { 0, 3, 7, 0, 5, 1, 2, 0 },
75
76 /* ARM L3: 1200 MHz */
77 { 0, 3, 7, 0, 5, 1, 2, 0 },
78
79 /* ARM L4: 1100 MHz */
80 { 0, 3, 6, 0, 4, 1, 2, 0 },
81
82 /* ARM L5: 1000 MHz */
83 { 0, 2, 5, 0, 4, 1, 1, 0 },
84
85 /* ARM L6: 900 MHz */
86 { 0, 2, 5, 0, 3, 1, 1, 0 },
87
88 /* ARM L7: 800 MHz */
89 { 0, 2, 5, 0, 3, 1, 1, 0 },
90
91 /* ARM L8: 700 MHz */
92 { 0, 2, 4, 0, 3, 1, 1, 0 },
93
94 /* ARM L9: 600 MHz */
95 { 0, 2, 4, 0, 3, 1, 1, 0 },
96
97 /* ARM L10: 500 MHz */
98 { 0, 2, 4, 0, 3, 1, 1, 0 },
99
100 /* ARM L11: 400 MHz */
101 { 0, 2, 4, 0, 3, 1, 1, 0 },
102
103 /* ARM L12: 300 MHz */
104 { 0, 2, 4, 0, 2, 1, 1, 0 },
105
106 /* ARM L13: 200 MHz */
107 { 0, 1, 3, 0, 1, 1, 1, 0 },
108};
109
110static unsigned int clkdiv_cpu0_4412[CPUFREQ_LEVEL_END][8] = {
111 /*
112 * Clock divider value for following
113 * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
114 * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 }
115 */
116 /* ARM L0: 1500 MHz */
117 { 0, 3, 7, 0, 6, 1, 2, 0 },
118
119 /* ARM L1: 1400 MHz */
120 { 0, 3, 7, 0, 6, 1, 2, 0 },
121
122 /* ARM L2: 1300 MHz */
123 { 0, 3, 7, 0, 5, 1, 2, 0 },
124
125 /* ARM L3: 1200 MHz */
126 { 0, 3, 7, 0, 5, 1, 2, 0 },
127
128 /* ARM L4: 1100 MHz */
129 { 0, 3, 6, 0, 4, 1, 2, 0 },
130
131 /* ARM L5: 1000 MHz */
132 { 0, 2, 5, 0, 4, 1, 1, 0 },
133
134 /* ARM L6: 900 MHz */
135 { 0, 2, 5, 0, 3, 1, 1, 0 },
136
137 /* ARM L7: 800 MHz */
138 { 0, 2, 5, 0, 3, 1, 1, 0 },
139
140 /* ARM L8: 700 MHz */
141 { 0, 2, 4, 0, 3, 1, 1, 0 },
142
143 /* ARM L9: 600 MHz */
144 { 0, 2, 4, 0, 3, 1, 1, 0 },
145
146 /* ARM L10: 500 MHz */
147 { 0, 2, 4, 0, 3, 1, 1, 0 },
148
149 /* ARM L11: 400 MHz */
150 { 0, 2, 4, 0, 3, 1, 1, 0 },
151
152 /* ARM L12: 300 MHz */
153 { 0, 2, 4, 0, 2, 1, 1, 0 },
154
155 /* ARM L13: 200 MHz */
156 { 0, 1, 3, 0, 1, 1, 1, 0 },
157};
158
159static unsigned int clkdiv_cpu1_4212[CPUFREQ_LEVEL_END][2] = {
160 /* Clock divider value for following
161 * { DIVCOPY, DIVHPM }
162 */
163 /* ARM L0: 1500 MHz */
164 { 6, 0 },
165
166 /* ARM L1: 1400 MHz */
167 { 6, 0 },
168
169 /* ARM L2: 1300 MHz */
170 { 5, 0 },
171
172 /* ARM L3: 1200 MHz */
173 { 5, 0 },
174
175 /* ARM L4: 1100 MHz */
176 { 4, 0 },
177
178 /* ARM L5: 1000 MHz */
179 { 4, 0 },
180
181 /* ARM L6: 900 MHz */
182 { 3, 0 },
183
184 /* ARM L7: 800 MHz */
185 { 3, 0 },
186
187 /* ARM L8: 700 MHz */
188 { 3, 0 },
189
190 /* ARM L9: 600 MHz */
191 { 3, 0 },
192
193 /* ARM L10: 500 MHz */
194 { 3, 0 },
195
196 /* ARM L11: 400 MHz */
197 { 3, 0 },
198
199 /* ARM L12: 300 MHz */
200 { 3, 0 },
201
202 /* ARM L13: 200 MHz */
203 { 3, 0 },
204};
205
206static unsigned int clkdiv_cpu1_4412[CPUFREQ_LEVEL_END][3] = {
207 /* Clock divider value for following
208 * { DIVCOPY, DIVHPM, DIVCORES }
209 */
210 /* ARM L0: 1500 MHz */
211 { 6, 0, 7 },
212
213 /* ARM L1: 1400 MHz */
214 { 6, 0, 6 },
215
216 /* ARM L2: 1300 MHz */
217 { 5, 0, 6 },
218
219 /* ARM L3: 1200 MHz */
220 { 5, 0, 5 },
221
222 /* ARM L4: 1100 MHz */
223 { 4, 0, 5 },
224
225 /* ARM L5: 1000 MHz */
226 { 4, 0, 4 },
227
228 /* ARM L6: 900 MHz */
229 { 3, 0, 4 },
230
231 /* ARM L7: 800 MHz */
232 { 3, 0, 3 },
233
234 /* ARM L8: 700 MHz */
235 { 3, 0, 3 },
236
237 /* ARM L9: 600 MHz */
238 { 3, 0, 2 },
239
240 /* ARM L10: 500 MHz */
241 { 3, 0, 2 },
242
243 /* ARM L11: 400 MHz */
244 { 3, 0, 1 },
245
246 /* ARM L12: 300 MHz */
247 { 3, 0, 1 },
248
249 /* ARM L13: 200 MHz */
250 { 3, 0, 0 },
251};
252
253static unsigned int exynos4x12_apll_pms_table[CPUFREQ_LEVEL_END] = {
254 /* APLL FOUT L0: 1500 MHz */
255 ((250 << 16) | (4 << 8) | (0x0)),
256
257 /* APLL FOUT L1: 1400 MHz */
258 ((175 << 16) | (3 << 8) | (0x0)),
259
260 /* APLL FOUT L2: 1300 MHz */
261 ((325 << 16) | (6 << 8) | (0x0)),
262
263 /* APLL FOUT L3: 1200 MHz */
264 ((200 << 16) | (4 << 8) | (0x0)),
265
266 /* APLL FOUT L4: 1100 MHz */
267 ((275 << 16) | (6 << 8) | (0x0)),
268
269 /* APLL FOUT L5: 1000 MHz */
270 ((125 << 16) | (3 << 8) | (0x0)),
271
272 /* APLL FOUT L6: 900 MHz */
273 ((150 << 16) | (4 << 8) | (0x0)),
274
275 /* APLL FOUT L7: 800 MHz */
276 ((100 << 16) | (3 << 8) | (0x0)),
277
278 /* APLL FOUT L8: 700 MHz */
279 ((175 << 16) | (3 << 8) | (0x1)),
280
281 /* APLL FOUT L9: 600 MHz */
282 ((200 << 16) | (4 << 8) | (0x1)),
283
284 /* APLL FOUT L10: 500 MHz */
285 ((125 << 16) | (3 << 8) | (0x1)),
286
287 /* APLL FOUT L11 400 MHz */
288 ((100 << 16) | (3 << 8) | (0x1)),
289
290 /* APLL FOUT L12: 300 MHz */
291 ((200 << 16) | (4 << 8) | (0x2)),
292
293 /* APLL FOUT L13: 200 MHz */
294 ((100 << 16) | (3 << 8) | (0x2)),
295};
296
297static const unsigned int asv_voltage_4x12[CPUFREQ_LEVEL_END] = {
298 1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500,
299 1000000, 987500, 975000, 950000, 925000, 900000, 900000
300};
301
302static void exynos4x12_set_clkdiv(unsigned int div_index)
303{
304 unsigned int tmp;
305 unsigned int stat_cpu1;
306
307 /* Change Divider - CPU0 */
308
309 tmp = exynos4x12_clkdiv_table[div_index].clkdiv;
310
311 __raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
312
313 while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111)
314 cpu_relax();
315
316 /* Change Divider - CPU1 */
317 tmp = exynos4x12_clkdiv_table[div_index].clkdiv1;
318
319 __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
320 if (soc_is_exynos4212())
321 stat_cpu1 = 0x11;
322 else
323 stat_cpu1 = 0x111;
324
325 while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1)
326 cpu_relax();
327}
328
329static void exynos4x12_set_apll(unsigned int index)
330{
331 unsigned int tmp, pdiv;
332
333 /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
334 clk_set_parent(moutcore, mout_mpll);
335
336 do {
337 cpu_relax();
338 tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
339 >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
340 tmp &= 0x7;
341 } while (tmp != 0x2);
342
343 /* 2. Set APLL Lock time */
344 pdiv = ((exynos4x12_apll_pms_table[index] >> 8) & 0x3f);
345
346 __raw_writel((pdiv * 250), EXYNOS4_APLL_LOCK);
347
348 /* 3. Change PLL PMS values */
349 tmp = __raw_readl(EXYNOS4_APLL_CON0);
350 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
351 tmp |= exynos4x12_apll_pms_table[index];
352 __raw_writel(tmp, EXYNOS4_APLL_CON0);
353
354 /* 4. wait_lock_time */
355 do {
356 cpu_relax();
357 tmp = __raw_readl(EXYNOS4_APLL_CON0);
358 } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT)));
359
360 /* 5. MUX_CORE_SEL = APLL */
361 clk_set_parent(moutcore, mout_apll);
362
363 do {
364 cpu_relax();
365 tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
366 tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
367 } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
368}
369
370bool exynos4x12_pms_change(unsigned int old_index, unsigned int new_index)
371{
372 unsigned int old_pm = exynos4x12_apll_pms_table[old_index] >> 8;
373 unsigned int new_pm = exynos4x12_apll_pms_table[new_index] >> 8;
374
375 return (old_pm == new_pm) ? 0 : 1;
376}
377
378static void exynos4x12_set_frequency(unsigned int old_index,
379 unsigned int new_index)
380{
381 unsigned int tmp;
382
383 if (old_index > new_index) {
384 if (!exynos4x12_pms_change(old_index, new_index)) {
385 /* 1. Change the system clock divider values */
386 exynos4x12_set_clkdiv(new_index);
387 /* 2. Change just s value in apll m,p,s value */
388 tmp = __raw_readl(EXYNOS4_APLL_CON0);
389 tmp &= ~(0x7 << 0);
390 tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7);
391 __raw_writel(tmp, EXYNOS4_APLL_CON0);
392
393 } else {
394 /* Clock Configuration Procedure */
395 /* 1. Change the system clock divider values */
396 exynos4x12_set_clkdiv(new_index);
397 /* 2. Change the apll m,p,s value */
398 exynos4x12_set_apll(new_index);
399 }
400 } else if (old_index < new_index) {
401 if (!exynos4x12_pms_change(old_index, new_index)) {
402 /* 1. Change just s value in apll m,p,s value */
403 tmp = __raw_readl(EXYNOS4_APLL_CON0);
404 tmp &= ~(0x7 << 0);
405 tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7);
406 __raw_writel(tmp, EXYNOS4_APLL_CON0);
407 /* 2. Change the system clock divider values */
408 exynos4x12_set_clkdiv(new_index);
409 } else {
410 /* Clock Configuration Procedure */
411 /* 1. Change the apll m,p,s value */
412 exynos4x12_set_apll(new_index);
413 /* 2. Change the system clock divider values */
414 exynos4x12_set_clkdiv(new_index);
415 }
416 }
417}
418
419static void __init set_volt_table(void)
420{
421 unsigned int i;
422
423 max_support_idx = L1;
424
425 /* Not supported */
426 exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
427
428 for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
429 exynos4x12_volt_table[i] = asv_voltage_4x12[i];
430}
431
432int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
433{
434 int i;
435 unsigned int tmp;
436 unsigned long rate;
437
438 set_volt_table();
439
440 cpu_clk = clk_get(NULL, "armclk");
441 if (IS_ERR(cpu_clk))
442 return PTR_ERR(cpu_clk);
443
444 moutcore = clk_get(NULL, "moutcore");
445 if (IS_ERR(moutcore))
446 goto err_moutcore;
447
448 mout_mpll = clk_get(NULL, "mout_mpll");
449 if (IS_ERR(mout_mpll))
450 goto err_mout_mpll;
451
452 rate = clk_get_rate(mout_mpll) / 1000;
453
454 mout_apll = clk_get(NULL, "mout_apll");
455 if (IS_ERR(mout_apll))
456 goto err_mout_apll;
457
458 for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
459
460 exynos4x12_clkdiv_table[i].index = i;
461
462 tmp = __raw_readl(EXYNOS4_CLKDIV_CPU);
463
464 tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK |
465 EXYNOS4_CLKDIV_CPU0_COREM0_MASK |
466 EXYNOS4_CLKDIV_CPU0_COREM1_MASK |
467 EXYNOS4_CLKDIV_CPU0_PERIPH_MASK |
468 EXYNOS4_CLKDIV_CPU0_ATB_MASK |
469 EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK |
470 EXYNOS4_CLKDIV_CPU0_APLL_MASK);
471
472 if (soc_is_exynos4212()) {
473 tmp |= ((clkdiv_cpu0_4212[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
474 (clkdiv_cpu0_4212[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
475 (clkdiv_cpu0_4212[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
476 (clkdiv_cpu0_4212[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
477 (clkdiv_cpu0_4212[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
478 (clkdiv_cpu0_4212[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
479 (clkdiv_cpu0_4212[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT));
480 } else {
481 tmp &= ~EXYNOS4_CLKDIV_CPU0_CORE2_MASK;
482
483 tmp |= ((clkdiv_cpu0_4412[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
484 (clkdiv_cpu0_4412[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
485 (clkdiv_cpu0_4412[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
486 (clkdiv_cpu0_4412[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
487 (clkdiv_cpu0_4412[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
488 (clkdiv_cpu0_4412[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
489 (clkdiv_cpu0_4412[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT) |
490 (clkdiv_cpu0_4412[i][7] << EXYNOS4_CLKDIV_CPU0_CORE2_SHIFT));
491 }
492
493 exynos4x12_clkdiv_table[i].clkdiv = tmp;
494
495 tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1);
496
497 if (soc_is_exynos4212()) {
498 tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK |
499 EXYNOS4_CLKDIV_CPU1_HPM_MASK);
500 tmp |= ((clkdiv_cpu1_4212[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) |
501 (clkdiv_cpu1_4212[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT));
502 } else {
503 tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK |
504 EXYNOS4_CLKDIV_CPU1_HPM_MASK |
505 EXYNOS4_CLKDIV_CPU1_CORES_MASK);
506 tmp |= ((clkdiv_cpu1_4412[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) |
507 (clkdiv_cpu1_4412[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT) |
508 (clkdiv_cpu1_4412[i][2] << EXYNOS4_CLKDIV_CPU1_CORES_SHIFT));
509 }
510 exynos4x12_clkdiv_table[i].clkdiv1 = tmp;
511 }
512
513 info->mpll_freq_khz = rate;
514 info->pm_lock_idx = L5;
515 info->pll_safe_idx = L7;
516 info->max_support_idx = max_support_idx;
517 info->min_support_idx = min_support_idx;
518 info->cpu_clk = cpu_clk;
519 info->volt_table = exynos4x12_volt_table;
520 info->freq_table = exynos4x12_freq_table;
521 info->set_freq = exynos4x12_set_frequency;
522 info->need_apll_change = exynos4x12_pms_change;
523
524 return 0;
525
526err_mout_apll:
527 clk_put(mout_mpll);
528err_mout_mpll:
529 clk_put(moutcore);
530err_moutcore:
531 clk_put(cpu_clk);
532
533 pr_debug("%s: failed initialization\n", __func__);
534 return -EINVAL;
535}
536EXPORT_SYMBOL(exynos4x12_cpufreq_init);
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
new file mode 100644
index 000000000000..a88331644ebf
--- /dev/null
+++ b/drivers/cpufreq/exynos5250-cpufreq.c
@@ -0,0 +1,347 @@
1/*
2 * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
3 * http://www.samsung.com
4 *
5 * EXYNOS5250 - CPU frequency scaling support
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/module.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/cpufreq.h>
19
20#include <mach/map.h>
21#include <mach/regs-clock.h>
22#include <mach/cpufreq.h>
23
24#define CPUFREQ_LEVEL_END (L15 + 1)
25
26static int max_support_idx;
27static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
28static struct clk *cpu_clk;
29static struct clk *moutcore;
30static struct clk *mout_mpll;
31static struct clk *mout_apll;
32
33struct cpufreq_clkdiv {
34 unsigned int index;
35 unsigned int clkdiv;
36 unsigned int clkdiv1;
37};
38
39static unsigned int exynos5250_volt_table[CPUFREQ_LEVEL_END];
40
41static struct cpufreq_frequency_table exynos5250_freq_table[] = {
42 {L0, 1700 * 1000},
43 {L1, 1600 * 1000},
44 {L2, 1500 * 1000},
45 {L3, 1400 * 1000},
46 {L4, 1300 * 1000},
47 {L5, 1200 * 1000},
48 {L6, 1100 * 1000},
49 {L7, 1000 * 1000},
50 {L8, 900 * 1000},
51 {L9, 800 * 1000},
52 {L10, 700 * 1000},
53 {L11, 600 * 1000},
54 {L12, 500 * 1000},
55 {L13, 400 * 1000},
56 {L14, 300 * 1000},
57 {L15, 200 * 1000},
58 {0, CPUFREQ_TABLE_END},
59};
60
61static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END];
62
63static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
64 /*
65 * Clock divider value for following
66 * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
67 */
68 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1700 MHz - N/A */
69 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1600 MHz - N/A */
70 { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1500 MHz - N/A */
71 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1400 MHz */
72 { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */
73 { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */
74 { 0, 2, 7, 7, 5, 1, 2, 0 }, /* 1100 MHz */
75 { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */
76 { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */
77 { 0, 2, 7, 7, 3, 1, 1, 0 }, /* 800 MHz */
78 { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 700 MHz */
79 { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 600 MHz */
80 { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 500 MHz */
81 { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 400 MHz */
82 { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 300 MHz */
83 { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 200 MHz */
84};
85
86static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
87 /* Clock divider value for following
88 * { COPY, HPM }
89 */
90 { 0, 2 }, /* 1700 MHz - N/A */
91 { 0, 2 }, /* 1600 MHz - N/A */
92 { 0, 2 }, /* 1500 MHz - N/A */
93 { 0, 2 }, /* 1400 MHz */
94 { 0, 2 }, /* 1300 MHz */
95 { 0, 2 }, /* 1200 MHz */
96 { 0, 2 }, /* 1100 MHz */
97 { 0, 2 }, /* 1000 MHz */
98 { 0, 2 }, /* 900 MHz */
99 { 0, 2 }, /* 800 MHz */
100 { 0, 2 }, /* 700 MHz */
101 { 0, 2 }, /* 600 MHz */
102 { 0, 2 }, /* 500 MHz */
103 { 0, 2 }, /* 400 MHz */
104 { 0, 2 }, /* 300 MHz */
105 { 0, 2 }, /* 200 MHz */
106};
107
108static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
109 (0), /* 1700 MHz - N/A */
110 (0), /* 1600 MHz - N/A */
111 (0), /* 1500 MHz - N/A */
112 (0), /* 1400 MHz */
113 ((325 << 16) | (6 << 8) | 0), /* 1300 MHz */
114 ((200 << 16) | (4 << 8) | 0), /* 1200 MHz */
115 ((275 << 16) | (6 << 8) | 0), /* 1100 MHz */
116 ((125 << 16) | (3 << 8) | 0), /* 1000 MHz */
117 ((150 << 16) | (4 << 8) | 0), /* 900 MHz */
118 ((100 << 16) | (3 << 8) | 0), /* 800 MHz */
119 ((175 << 16) | (3 << 8) | 1), /* 700 MHz */
120 ((200 << 16) | (4 << 8) | 1), /* 600 MHz */
121 ((125 << 16) | (3 << 8) | 1), /* 500 MHz */
122 ((100 << 16) | (3 << 8) | 1), /* 400 MHz */
123 ((200 << 16) | (4 << 8) | 2), /* 300 MHz */
124 ((100 << 16) | (3 << 8) | 2), /* 200 MHz */
125};
126
127/* ASV group voltage table */
128static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
129 0, 0, 0, 0, 0, 0, 0, /* 1700 MHz ~ 1100 MHz Not supported */
130 1175000, 1125000, 1075000, 1050000, 1000000,
131 950000, 925000, 925000, 900000
132};
133
134static void set_clkdiv(unsigned int div_index)
135{
136 unsigned int tmp;
137
138 /* Change Divider - CPU0 */
139
140 tmp = exynos5250_clkdiv_table[div_index].clkdiv;
141
142 __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);
143
144 while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111)
145 cpu_relax();
146
147 /* Change Divider - CPU1 */
148 tmp = exynos5250_clkdiv_table[div_index].clkdiv1;
149
150 __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);
151
152 while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11)
153 cpu_relax();
154}
155
156static void set_apll(unsigned int new_index,
157 unsigned int old_index)
158{
159 unsigned int tmp, pdiv;
160
161 /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
162 clk_set_parent(moutcore, mout_mpll);
163
164 do {
165 cpu_relax();
166 tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16);
167 tmp &= 0x7;
168 } while (tmp != 0x2);
169
170 /* 2. Set APLL Lock time */
171 pdiv = ((exynos5_apll_pms_table[new_index] >> 8) & 0x3f);
172
173 __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK);
174
175 /* 3. Change PLL PMS values */
176 tmp = __raw_readl(EXYNOS5_APLL_CON0);
177 tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
178 tmp |= exynos5_apll_pms_table[new_index];
179 __raw_writel(tmp, EXYNOS5_APLL_CON0);
180
181 /* 4. wait_lock_time */
182 do {
183 cpu_relax();
184 tmp = __raw_readl(EXYNOS5_APLL_CON0);
185 } while (!(tmp & (0x1 << 29)));
186
187 /* 5. MUX_CORE_SEL = APLL */
188 clk_set_parent(moutcore, mout_apll);
189
190 do {
191 cpu_relax();
192 tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
193 tmp &= (0x7 << 16);
194 } while (tmp != (0x1 << 16));
195
196}
197
198bool exynos5250_pms_change(unsigned int old_index, unsigned int new_index)
199{
200 unsigned int old_pm = (exynos5_apll_pms_table[old_index] >> 8);
201 unsigned int new_pm = (exynos5_apll_pms_table[new_index] >> 8);
202
203 return (old_pm == new_pm) ? 0 : 1;
204}
205
206static void exynos5250_set_frequency(unsigned int old_index,
207 unsigned int new_index)
208{
209 unsigned int tmp;
210
211 if (old_index > new_index) {
212 if (!exynos5250_pms_change(old_index, new_index)) {
213 /* 1. Change the system clock divider values */
214 set_clkdiv(new_index);
215 /* 2. Change just s value in apll m,p,s value */
216 tmp = __raw_readl(EXYNOS5_APLL_CON0);
217 tmp &= ~(0x7 << 0);
218 tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
219 __raw_writel(tmp, EXYNOS5_APLL_CON0);
220
221 } else {
222 /* Clock Configuration Procedure */
223 /* 1. Change the system clock divider values */
224 set_clkdiv(new_index);
225 /* 2. Change the apll m,p,s value */
226 set_apll(new_index, old_index);
227 }
228 } else if (old_index < new_index) {
229 if (!exynos5250_pms_change(old_index, new_index)) {
230 /* 1. Change just s value in apll m,p,s value */
231 tmp = __raw_readl(EXYNOS5_APLL_CON0);
232 tmp &= ~(0x7 << 0);
233 tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
234 __raw_writel(tmp, EXYNOS5_APLL_CON0);
235 /* 2. Change the system clock divider values */
236 set_clkdiv(new_index);
237 } else {
238 /* Clock Configuration Procedure */
239 /* 1. Change the apll m,p,s value */
240 set_apll(new_index, old_index);
241 /* 2. Change the system clock divider values */
242 set_clkdiv(new_index);
243 }
244 }
245}
246
247static void __init set_volt_table(void)
248{
249 unsigned int i;
250
251 exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
252 exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID;
253 exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID;
254 exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID;
255 exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID;
256 exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID;
257 exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID;
258
259 max_support_idx = L7;
260
261 for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
262 exynos5250_volt_table[i] = asv_voltage_5250[i];
263}
264
265int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
266{
267 int i;
268 unsigned int tmp;
269 unsigned long rate;
270
271 set_volt_table();
272
273 cpu_clk = clk_get(NULL, "armclk");
274 if (IS_ERR(cpu_clk))
275 return PTR_ERR(cpu_clk);
276
277 moutcore = clk_get(NULL, "mout_cpu");
278 if (IS_ERR(moutcore))
279 goto err_moutcore;
280
281 mout_mpll = clk_get(NULL, "mout_mpll");
282 if (IS_ERR(mout_mpll))
283 goto err_mout_mpll;
284
285 rate = clk_get_rate(mout_mpll) / 1000;
286
287 mout_apll = clk_get(NULL, "mout_apll");
288 if (IS_ERR(mout_apll))
289 goto err_mout_apll;
290
291 for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
292
293 exynos5250_clkdiv_table[i].index = i;
294
295 tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0);
296
297 tmp &= ~((0x7 << 0) | (0x7 << 4) | (0x7 << 8) |
298 (0x7 << 12) | (0x7 << 16) | (0x7 << 20) |
299 (0x7 << 24) | (0x7 << 28));
300
301 tmp |= ((clkdiv_cpu0_5250[i][0] << 0) |
302 (clkdiv_cpu0_5250[i][1] << 4) |
303 (clkdiv_cpu0_5250[i][2] << 8) |
304 (clkdiv_cpu0_5250[i][3] << 12) |
305 (clkdiv_cpu0_5250[i][4] << 16) |
306 (clkdiv_cpu0_5250[i][5] << 20) |
307 (clkdiv_cpu0_5250[i][6] << 24) |
308 (clkdiv_cpu0_5250[i][7] << 28));
309
310 exynos5250_clkdiv_table[i].clkdiv = tmp;
311
312 tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1);
313
314 tmp &= ~((0x7 << 0) | (0x7 << 4));
315
316 tmp |= ((clkdiv_cpu1_5250[i][0] << 0) |
317 (clkdiv_cpu1_5250[i][1] << 4));
318
319 exynos5250_clkdiv_table[i].clkdiv1 = tmp;
320 }
321
322 info->mpll_freq_khz = rate;
323 /* 1000Mhz */
324 info->pm_lock_idx = L7;
325 /* 800Mhz */
326 info->pll_safe_idx = L9;
327 info->max_support_idx = max_support_idx;
328 info->min_support_idx = min_support_idx;
329 info->cpu_clk = cpu_clk;
330 info->volt_table = exynos5250_volt_table;
331 info->freq_table = exynos5250_freq_table;
332 info->set_freq = exynos5250_set_frequency;
333 info->need_apll_change = exynos5250_pms_change;
334
335 return 0;
336
337err_mout_apll:
338 clk_put(mout_mpll);
339err_mout_mpll:
340 clk_put(moutcore);
341err_moutcore:
342 clk_put(cpu_clk);
343
344 pr_err("%s: failed initialization\n", __func__);
345 return -EINVAL;
346}
347EXPORT_SYMBOL(exynos5250_cpufreq_init);
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 3093ca6152c9..17fa04d08be9 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -25,6 +25,7 @@
25#include <linux/opp.h> 25#include <linux/opp.h>
26#include <linux/cpu.h> 26#include <linux/cpu.h>
27#include <linux/module.h> 27#include <linux/module.h>
28#include <linux/regulator/consumer.h>
28 29
29#include <asm/smp_plat.h> 30#include <asm/smp_plat.h>
30#include <asm/cpu.h> 31#include <asm/cpu.h>
@@ -36,6 +37,9 @@
36 37
37#include <mach/hardware.h> 38#include <mach/hardware.h>
38 39
40/* OPP tolerance in percentage */
41#define OPP_TOLERANCE 4
42
39#ifdef CONFIG_SMP 43#ifdef CONFIG_SMP
40struct lpj_info { 44struct lpj_info {
41 unsigned long ref; 45 unsigned long ref;
@@ -51,6 +55,7 @@ static atomic_t freq_table_users = ATOMIC_INIT(0);
51static struct clk *mpu_clk; 55static struct clk *mpu_clk;
52static char *mpu_clk_name; 56static char *mpu_clk_name;
53static struct device *mpu_dev; 57static struct device *mpu_dev;
58static struct regulator *mpu_reg;
54 59
55static int omap_verify_speed(struct cpufreq_policy *policy) 60static int omap_verify_speed(struct cpufreq_policy *policy)
56{ 61{
@@ -75,8 +80,10 @@ static int omap_target(struct cpufreq_policy *policy,
75 unsigned int relation) 80 unsigned int relation)
76{ 81{
77 unsigned int i; 82 unsigned int i;
78 int ret = 0; 83 int r, ret = 0;
79 struct cpufreq_freqs freqs; 84 struct cpufreq_freqs freqs;
85 struct opp *opp;
86 unsigned long freq, volt = 0, volt_old = 0, tol = 0;
80 87
81 if (!freq_table) { 88 if (!freq_table) {
82 dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__, 89 dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__,
@@ -110,13 +117,50 @@ static int omap_target(struct cpufreq_policy *policy,
110 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); 117 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
111 } 118 }
112 119
113#ifdef CONFIG_CPU_FREQ_DEBUG 120 freq = freqs.new * 1000;
114 pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); 121
115#endif 122 if (mpu_reg) {
123 opp = opp_find_freq_ceil(mpu_dev, &freq);
124 if (IS_ERR(opp)) {
125 dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
126 __func__, freqs.new);
127 return -EINVAL;
128 }
129 volt = opp_get_voltage(opp);
130 tol = volt * OPP_TOLERANCE / 100;
131 volt_old = regulator_get_voltage(mpu_reg);
132 }
133
134 dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n",
135 freqs.old / 1000, volt_old ? volt_old / 1000 : -1,
136 freqs.new / 1000, volt ? volt / 1000 : -1);
137
138 /* scaling up? scale voltage before frequency */
139 if (mpu_reg && (freqs.new > freqs.old)) {
140 r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
141 if (r < 0) {
142 dev_warn(mpu_dev, "%s: unable to scale voltage up.\n",
143 __func__);
144 freqs.new = freqs.old;
145 goto done;
146 }
147 }
116 148
117 ret = clk_set_rate(mpu_clk, freqs.new * 1000); 149 ret = clk_set_rate(mpu_clk, freqs.new * 1000);
118 freqs.new = omap_getspeed(policy->cpu);
119 150
151 /* scaling down? scale voltage after frequency */
152 if (mpu_reg && (freqs.new < freqs.old)) {
153 r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
154 if (r < 0) {
155 dev_warn(mpu_dev, "%s: unable to scale voltage down.\n",
156 __func__);
157 ret = clk_set_rate(mpu_clk, freqs.old * 1000);
158 freqs.new = freqs.old;
159 goto done;
160 }
161 }
162
163 freqs.new = omap_getspeed(policy->cpu);
120#ifdef CONFIG_SMP 164#ifdef CONFIG_SMP
121 /* 165 /*
122 * Note that loops_per_jiffy is not updated on SMP systems in 166 * Note that loops_per_jiffy is not updated on SMP systems in
@@ -143,6 +187,7 @@ static int omap_target(struct cpufreq_policy *policy,
143 freqs.new); 187 freqs.new);
144#endif 188#endif
145 189
190done:
146 /* notifiers */ 191 /* notifiers */
147 for_each_cpu(i, policy->cpus) { 192 for_each_cpu(i, policy->cpus) {
148 freqs.cpu = i; 193 freqs.cpu = i;
@@ -259,6 +304,23 @@ static int __init omap_cpufreq_init(void)
259 return -EINVAL; 304 return -EINVAL;
260 } 305 }
261 306
307 mpu_reg = regulator_get(mpu_dev, "vcc");
308 if (IS_ERR(mpu_reg)) {
309 pr_warning("%s: unable to get MPU regulator\n", __func__);
310 mpu_reg = NULL;
311 } else {
312 /*
313 * Ensure physical regulator is present.
314 * (e.g. could be dummy regulator.)
315 */
316 if (regulator_get_voltage(mpu_reg) < 0) {
317 pr_warn("%s: physical regulator not present for MPU\n",
318 __func__);
319 regulator_put(mpu_reg);
320 mpu_reg = NULL;
321 }
322 }
323
262 return cpufreq_register_driver(&omap_driver); 324 return cpufreq_register_driver(&omap_driver);
263} 325}
264 326
diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c
new file mode 100644
index 000000000000..50d2f15a3c8a
--- /dev/null
+++ b/drivers/cpufreq/s3c2416-cpufreq.c
@@ -0,0 +1,542 @@
1/*
2 * S3C2416/2450 CPUfreq Support
3 *
4 * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
5 *
6 * based on s3c64xx_cpufreq.c
7 *
8 * Copyright 2009 Wolfson Microelectronics plc
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13 */
14
15#include <linux/kernel.h>
16#include <linux/types.h>
17#include <linux/init.h>
18#include <linux/cpufreq.h>
19#include <linux/clk.h>
20#include <linux/err.h>
21#include <linux/regulator/consumer.h>
22#include <linux/reboot.h>
23#include <linux/module.h>
24
25static DEFINE_MUTEX(cpufreq_lock);
26
27struct s3c2416_data {
28 struct clk *armdiv;
29 struct clk *armclk;
30 struct clk *hclk;
31
32 unsigned long regulator_latency;
33#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
34 struct regulator *vddarm;
35#endif
36
37 struct cpufreq_frequency_table *freq_table;
38
39 bool is_dvs;
40 bool disable_dvs;
41};
42
43static struct s3c2416_data s3c2416_cpufreq;
44
45struct s3c2416_dvfs {
46 unsigned int vddarm_min;
47 unsigned int vddarm_max;
48};
49
50/* pseudo-frequency for dvs mode */
51#define FREQ_DVS 132333
52
53/* frequency to sleep and reboot in
54 * it's essential to leave dvs, as some boards do not reconfigure the
55 * regulator on reboot
56 */
57#define FREQ_SLEEP 133333
58
59/* Sources for the ARMCLK */
60#define SOURCE_HCLK 0
61#define SOURCE_ARMDIV 1
62
63#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
64/* S3C2416 only supports changing the voltage in the dvs-mode.
65 * Voltages down to 1.0V seem to work, so we take what the regulator
66 * can get us.
67 */
68static struct s3c2416_dvfs s3c2416_dvfs_table[] = {
69 [SOURCE_HCLK] = { 950000, 1250000 },
70 [SOURCE_ARMDIV] = { 1250000, 1350000 },
71};
72#endif
73
74static struct cpufreq_frequency_table s3c2416_freq_table[] = {
75 { SOURCE_HCLK, FREQ_DVS },
76 { SOURCE_ARMDIV, 133333 },
77 { SOURCE_ARMDIV, 266666 },
78 { SOURCE_ARMDIV, 400000 },
79 { 0, CPUFREQ_TABLE_END },
80};
81
82static struct cpufreq_frequency_table s3c2450_freq_table[] = {
83 { SOURCE_HCLK, FREQ_DVS },
84 { SOURCE_ARMDIV, 133500 },
85 { SOURCE_ARMDIV, 267000 },
86 { SOURCE_ARMDIV, 534000 },
87 { 0, CPUFREQ_TABLE_END },
88};
89
90static int s3c2416_cpufreq_verify_speed(struct cpufreq_policy *policy)
91{
92 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
93
94 if (policy->cpu != 0)
95 return -EINVAL;
96
97 return cpufreq_frequency_table_verify(policy, s3c_freq->freq_table);
98}
99
100static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu)
101{
102 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
103
104 if (cpu != 0)
105 return 0;
106
107 /* return our pseudo-frequency when in dvs mode */
108 if (s3c_freq->is_dvs)
109 return FREQ_DVS;
110
111 return clk_get_rate(s3c_freq->armclk) / 1000;
112}
113
114static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq,
115 unsigned int freq)
116{
117 int ret;
118
119 if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) {
120 ret = clk_set_rate(s3c_freq->armdiv, freq * 1000);
121 if (ret < 0) {
122 pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n",
123 freq, ret);
124 return ret;
125 }
126 }
127
128 return 0;
129}
130
131static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx)
132{
133#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
134 struct s3c2416_dvfs *dvfs;
135#endif
136 int ret;
137
138 if (s3c_freq->is_dvs) {
139 pr_debug("cpufreq: already in dvs mode, nothing to do\n");
140 return 0;
141 }
142
143 pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n",
144 clk_get_rate(s3c_freq->hclk) / 1000);
145 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk);
146 if (ret < 0) {
147 pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret);
148 return ret;
149 }
150
151#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
152 /* changing the core voltage is only allowed when in dvs mode */
153 if (s3c_freq->vddarm) {
154 dvfs = &s3c2416_dvfs_table[idx];
155
156 pr_debug("cpufreq: setting regultor to %d-%d\n",
157 dvfs->vddarm_min, dvfs->vddarm_max);
158 ret = regulator_set_voltage(s3c_freq->vddarm,
159 dvfs->vddarm_min,
160 dvfs->vddarm_max);
161
162 /* when lowering the voltage failed, there is nothing to do */
163 if (ret != 0)
164 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
165 }
166#endif
167
168 s3c_freq->is_dvs = 1;
169
170 return 0;
171}
172
173static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx)
174{
175#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
176 struct s3c2416_dvfs *dvfs;
177#endif
178 int ret;
179
180 if (!s3c_freq->is_dvs) {
181 pr_debug("cpufreq: not in dvs mode, so can't leave\n");
182 return 0;
183 }
184
185#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
186 if (s3c_freq->vddarm) {
187 dvfs = &s3c2416_dvfs_table[idx];
188
189 pr_debug("cpufreq: setting regultor to %d-%d\n",
190 dvfs->vddarm_min, dvfs->vddarm_max);
191 ret = regulator_set_voltage(s3c_freq->vddarm,
192 dvfs->vddarm_min,
193 dvfs->vddarm_max);
194 if (ret != 0) {
195 pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
196 return ret;
197 }
198 }
199#endif
200
201 /* force armdiv to hclk frequency for transition from dvs*/
202 if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) {
203 pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n",
204 clk_get_rate(s3c_freq->hclk) / 1000);
205 ret = s3c2416_cpufreq_set_armdiv(s3c_freq,
206 clk_get_rate(s3c_freq->hclk) / 1000);
207 if (ret < 0) {
208 pr_err("cpufreq: Failed to to set the armdiv to %lukHz: %d\n",
209 clk_get_rate(s3c_freq->hclk) / 1000, ret);
210 return ret;
211 }
212 }
213
214 pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n",
215 clk_get_rate(s3c_freq->armdiv) / 1000);
216
217 ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv);
218 if (ret < 0) {
219 pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n",
220 ret);
221 return ret;
222 }
223
224 s3c_freq->is_dvs = 0;
225
226 return 0;
227}
228
229static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
230 unsigned int target_freq,
231 unsigned int relation)
232{
233 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
234 struct cpufreq_freqs freqs;
235 int idx, ret, to_dvs = 0;
236 unsigned int i;
237
238 mutex_lock(&cpufreq_lock);
239
240 pr_debug("cpufreq: to %dKHz, relation %d\n", target_freq, relation);
241
242 ret = cpufreq_frequency_table_target(policy, s3c_freq->freq_table,
243 target_freq, relation, &i);
244 if (ret != 0)
245 goto out;
246
247 idx = s3c_freq->freq_table[i].index;
248
249 if (idx == SOURCE_HCLK)
250 to_dvs = 1;
251
252 /* switching to dvs when it's not allowed */
253 if (to_dvs && s3c_freq->disable_dvs) {
254 pr_debug("cpufreq: entering dvs mode not allowed\n");
255 ret = -EINVAL;
256 goto out;
257 }
258
259 freqs.cpu = 0;
260 freqs.flags = 0;
261 freqs.old = s3c_freq->is_dvs ? FREQ_DVS
262 : clk_get_rate(s3c_freq->armclk) / 1000;
263
264 /* When leavin dvs mode, always switch the armdiv to the hclk rate
265 * The S3C2416 has stability issues when switching directly to
266 * higher frequencies.
267 */
268 freqs.new = (s3c_freq->is_dvs && !to_dvs)
269 ? clk_get_rate(s3c_freq->hclk) / 1000
270 : s3c_freq->freq_table[i].frequency;
271
272 pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
273
274 if (!to_dvs && freqs.old == freqs.new)
275 goto out;
276
277 cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
278
279 if (to_dvs) {
280 pr_debug("cpufreq: enter dvs\n");
281 ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx);
282 } else if (s3c_freq->is_dvs) {
283 pr_debug("cpufreq: leave dvs\n");
284 ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx);
285 } else {
286 pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new);
287 ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new);
288 }
289
290 cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
291
292out:
293 mutex_unlock(&cpufreq_lock);
294
295 return ret;
296}
297
298#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
299static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
300{
301 int count, v, i, found;
302 struct cpufreq_frequency_table *freq;
303 struct s3c2416_dvfs *dvfs;
304
305 count = regulator_count_voltages(s3c_freq->vddarm);
306 if (count < 0) {
307 pr_err("cpufreq: Unable to check supported voltages\n");
308 return;
309 }
310
311 freq = s3c_freq->freq_table;
312 while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
313 if (freq->frequency == CPUFREQ_ENTRY_INVALID)
314 continue;
315
316 dvfs = &s3c2416_dvfs_table[freq->index];
317 found = 0;
318
319 /* Check only the min-voltage, more is always ok on S3C2416 */
320 for (i = 0; i < count; i++) {
321 v = regulator_list_voltage(s3c_freq->vddarm, i);
322 if (v >= dvfs->vddarm_min)
323 found = 1;
324 }
325
326 if (!found) {
327 pr_debug("cpufreq: %dkHz unsupported by regulator\n",
328 freq->frequency);
329 freq->frequency = CPUFREQ_ENTRY_INVALID;
330 }
331
332 freq++;
333 }
334
335 /* Guessed */
336 s3c_freq->regulator_latency = 1 * 1000 * 1000;
337}
338#endif
339
340static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this,
341 unsigned long event, void *ptr)
342{
343 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
344 int ret;
345
346 mutex_lock(&cpufreq_lock);
347
348 /* disable further changes */
349 s3c_freq->disable_dvs = 1;
350
351 mutex_unlock(&cpufreq_lock);
352
353 /* some boards don't reconfigure the regulator on reboot, which
354 * could lead to undervolting the cpu when the clock is reset.
355 * Therefore we always leave the DVS mode on reboot.
356 */
357 if (s3c_freq->is_dvs) {
358 pr_debug("cpufreq: leave dvs on reboot\n");
359 ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0);
360 if (ret < 0)
361 return NOTIFY_BAD;
362 }
363
364 return NOTIFY_DONE;
365}
366
367static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
368 .notifier_call = s3c2416_cpufreq_reboot_notifier_evt,
369};
370
371static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
372{
373 struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
374 struct cpufreq_frequency_table *freq;
375 struct clk *msysclk;
376 unsigned long rate;
377 int ret;
378
379 if (policy->cpu != 0)
380 return -EINVAL;
381
382 msysclk = clk_get(NULL, "msysclk");
383 if (IS_ERR(msysclk)) {
384 ret = PTR_ERR(msysclk);
385 pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret);
386 return ret;
387 }
388
389 /*
390 * S3C2416 and S3C2450 share the same processor-ID and also provide no
391 * other means to distinguish them other than through the rate of
392 * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz.
393 */
394 rate = clk_get_rate(msysclk);
395 if (rate == 800 * 1000 * 1000) {
396 pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n",
397 rate / 1000);
398 s3c_freq->freq_table = s3c2416_freq_table;
399 policy->cpuinfo.max_freq = 400000;
400 } else if (rate / 1000 == 534000) {
401 pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n",
402 rate / 1000);
403 s3c_freq->freq_table = s3c2450_freq_table;
404 policy->cpuinfo.max_freq = 534000;
405 }
406
407 /* not needed anymore */
408 clk_put(msysclk);
409
410 if (s3c_freq->freq_table == NULL) {
411 pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n",
412 rate / 1000);
413 return -ENODEV;
414 }
415
416 s3c_freq->is_dvs = 0;
417
418 s3c_freq->armdiv = clk_get(NULL, "armdiv");
419 if (IS_ERR(s3c_freq->armdiv)) {
420 ret = PTR_ERR(s3c_freq->armdiv);
421 pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret);
422 return ret;
423 }
424
425 s3c_freq->hclk = clk_get(NULL, "hclk");
426 if (IS_ERR(s3c_freq->hclk)) {
427 ret = PTR_ERR(s3c_freq->hclk);
428 pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret);
429 goto err_hclk;
430 }
431
432 /* chech hclk rate, we only support the common 133MHz for now
433 * hclk could also run at 66MHz, but this not often used
434 */
435 rate = clk_get_rate(s3c_freq->hclk);
436 if (rate < 133 * 1000 * 1000) {
437 pr_err("cpufreq: HCLK not at 133MHz\n");
438 clk_put(s3c_freq->hclk);
439 ret = -EINVAL;
440 goto err_armclk;
441 }
442
443 s3c_freq->armclk = clk_get(NULL, "armclk");
444 if (IS_ERR(s3c_freq->armclk)) {
445 ret = PTR_ERR(s3c_freq->armclk);
446 pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret);
447 goto err_armclk;
448 }
449
450#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
451 s3c_freq->vddarm = regulator_get(NULL, "vddarm");
452 if (IS_ERR(s3c_freq->vddarm)) {
453 ret = PTR_ERR(s3c_freq->vddarm);
454 pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
455 goto err_vddarm;
456 }
457
458 s3c2416_cpufreq_cfg_regulator(s3c_freq);
459#else
460 s3c_freq->regulator_latency = 0;
461#endif
462
463 freq = s3c_freq->freq_table;
464 while (freq->frequency != CPUFREQ_TABLE_END) {
465 /* special handling for dvs mode */
466 if (freq->index == 0) {
467 if (!s3c_freq->hclk) {
468 pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
469 freq->frequency);
470 freq->frequency = CPUFREQ_ENTRY_INVALID;
471 } else {
472 freq++;
473 continue;
474 }
475 }
476
477 /* Check for frequencies we can generate */
478 rate = clk_round_rate(s3c_freq->armdiv,
479 freq->frequency * 1000);
480 rate /= 1000;
481 if (rate != freq->frequency) {
482 pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
483 freq->frequency, rate);
484 freq->frequency = CPUFREQ_ENTRY_INVALID;
485 }
486
487 freq++;
488 }
489
490 policy->cur = clk_get_rate(s3c_freq->armclk) / 1000;
491
492 /* Datasheet says PLL stabalisation time must be at least 300us,
493 * so but add some fudge. (reference in LOCKCON0 register description)
494 */
495 policy->cpuinfo.transition_latency = (500 * 1000) +
496 s3c_freq->regulator_latency;
497
498 ret = cpufreq_frequency_table_cpuinfo(policy, s3c_freq->freq_table);
499 if (ret)
500 goto err_freq_table;
501
502 cpufreq_frequency_table_get_attr(s3c_freq->freq_table, 0);
503
504 register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier);
505
506 return 0;
507
508err_freq_table:
509#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
510 regulator_put(s3c_freq->vddarm);
511err_vddarm:
512#endif
513 clk_put(s3c_freq->armclk);
514err_armclk:
515 clk_put(s3c_freq->hclk);
516err_hclk:
517 clk_put(s3c_freq->armdiv);
518
519 return ret;
520}
521
522static struct freq_attr *s3c2416_cpufreq_attr[] = {
523 &cpufreq_freq_attr_scaling_available_freqs,
524 NULL,
525};
526
527static struct cpufreq_driver s3c2416_cpufreq_driver = {
528 .owner = THIS_MODULE,
529 .flags = 0,
530 .verify = s3c2416_cpufreq_verify_speed,
531 .target = s3c2416_cpufreq_set_target,
532 .get = s3c2416_cpufreq_get_speed,
533 .init = s3c2416_cpufreq_driver_init,
534 .name = "s3c2416",
535 .attr = s3c2416_cpufreq_attr,
536};
537
538static int __init s3c2416_cpufreq_init(void)
539{
540 return cpufreq_register_driver(&s3c2416_cpufreq_driver);
541}
542module_init(s3c2416_cpufreq_init);
diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c
index a5e72cb5f53c..6f9490b3c356 100644
--- a/drivers/cpufreq/s3c64xx-cpufreq.c
+++ b/drivers/cpufreq/s3c64xx-cpufreq.c
@@ -217,13 +217,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
217 } else { 217 } else {
218 s3c64xx_cpufreq_config_regulator(); 218 s3c64xx_cpufreq_config_regulator();
219 } 219 }
220
221 vddint = regulator_get(NULL, "vddint");
222 if (IS_ERR(vddint)) {
223 ret = PTR_ERR(vddint);
224 pr_err("Failed to obtain VDDINT: %d\n", ret);
225 vddint = NULL;
226 }
227#endif 220#endif
228 221
229 freq = s3c64xx_freq_table; 222 freq = s3c64xx_freq_table;