diff options
Diffstat (limited to 'drivers/cpufreq')
-rw-r--r-- | drivers/cpufreq/Kconfig.arm | 42 | ||||
-rw-r--r-- | drivers/cpufreq/Makefile | 5 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq.c | 24 | ||||
-rw-r--r-- | drivers/cpufreq/cpufreq_ondemand.c | 58 | ||||
-rw-r--r-- | drivers/cpufreq/db8500-cpufreq.c | 13 | ||||
-rw-r--r-- | drivers/cpufreq/exynos-cpufreq.c | 6 | ||||
-rw-r--r-- | drivers/cpufreq/exynos4210-cpufreq.c | 70 | ||||
-rw-r--r-- | drivers/cpufreq/exynos4x12-cpufreq.c | 536 | ||||
-rw-r--r-- | drivers/cpufreq/exynos5250-cpufreq.c | 347 | ||||
-rw-r--r-- | drivers/cpufreq/omap-cpufreq.c | 72 | ||||
-rw-r--r-- | drivers/cpufreq/s3c2416-cpufreq.c | 542 | ||||
-rw-r--r-- | drivers/cpufreq/s3c64xx-cpufreq.c | 7 |
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 | ||
5 | config ARM_OMAP2PLUS_CPUFREQ | ||
6 | bool "TI OMAP2+" | ||
7 | default ARCH_OMAP2PLUS | ||
8 | select CPU_FREQ_TABLE | ||
9 | |||
10 | config 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 | |||
22 | config 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 | |||
5 | config ARM_S3C64XX_CPUFREQ | 32 | config 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 | ||
35 | config ARM_EXYNOS4210_CPUFREQ | 64 | config 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 | |||
71 | config ARM_EXYNOS4X12_CPUFREQ | ||
72 | bool "Samsung EXYNOS4X12" | ||
73 | help | ||
74 | This adds the CPUFreq driver for Samsung EXYNOS4X12 | ||
75 | SoC (EXYNOS4212 or EXYNOS4412). | ||
76 | |||
77 | config 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 |
42 | obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o | 42 | obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o |
43 | obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o | ||
43 | obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o | 44 | obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o |
44 | obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o | 45 | obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o |
45 | obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o | 46 | obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o |
46 | obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o | 47 | obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o |
47 | obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o | 48 | obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o |
49 | obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o | ||
50 | obj-$(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 | } |
127 | pure_initcall(init_cpufreq_transition_notifier_list); | 127 | pure_initcall(init_cpufreq_transition_notifier_list); |
128 | 128 | ||
129 | static int off __read_mostly; | ||
130 | int cpufreq_disabled(void) | ||
131 | { | ||
132 | return off; | ||
133 | } | ||
134 | void disable_cpufreq(void) | ||
135 | { | ||
136 | off = 1; | ||
137 | } | ||
129 | static LIST_HEAD(cpufreq_governor_list); | 138 | static LIST_HEAD(cpufreq_governor_list); |
130 | static DEFINE_MUTEX(cpufreq_governor_mutex); | 139 | static 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); | |||
257 | show_one(ignore_nice_load, ignore_nice); | 257 | show_one(ignore_nice_load, ignore_nice); |
258 | show_one(powersave_bias, powersave_bias); | 258 | show_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 | */ | ||
273 | static 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 | |||
260 | static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b, | 316 | static 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 | ||
182 | bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index) | 182 | bool 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 | |||
25 | static int max_support_idx; | ||
26 | static int min_support_idx = (CPUFREQ_LEVEL_END - 1); | ||
27 | |||
28 | static struct clk *cpu_clk; | ||
29 | static struct clk *moutcore; | ||
30 | static struct clk *mout_mpll; | ||
31 | static struct clk *mout_apll; | ||
32 | |||
33 | struct cpufreq_clkdiv { | ||
34 | unsigned int index; | ||
35 | unsigned int clkdiv; | ||
36 | unsigned int clkdiv1; | ||
37 | }; | ||
38 | |||
39 | static unsigned int exynos4x12_volt_table[CPUFREQ_LEVEL_END]; | ||
40 | |||
41 | static 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 | |||
59 | static struct cpufreq_clkdiv exynos4x12_clkdiv_table[CPUFREQ_LEVEL_END]; | ||
60 | |||
61 | static 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 | |||
110 | static 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 | |||
159 | static 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 | |||
206 | static 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 | |||
253 | static 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 | |||
297 | static 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 | |||
302 | static 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 | |||
329 | static 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 | |||
370 | bool 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 | |||
378 | static 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 | |||
419 | static 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 | |||
432 | int 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 | |||
526 | err_mout_apll: | ||
527 | clk_put(mout_mpll); | ||
528 | err_mout_mpll: | ||
529 | clk_put(moutcore); | ||
530 | err_moutcore: | ||
531 | clk_put(cpu_clk); | ||
532 | |||
533 | pr_debug("%s: failed initialization\n", __func__); | ||
534 | return -EINVAL; | ||
535 | } | ||
536 | EXPORT_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 | |||
26 | static int max_support_idx; | ||
27 | static int min_support_idx = (CPUFREQ_LEVEL_END - 1); | ||
28 | static struct clk *cpu_clk; | ||
29 | static struct clk *moutcore; | ||
30 | static struct clk *mout_mpll; | ||
31 | static struct clk *mout_apll; | ||
32 | |||
33 | struct cpufreq_clkdiv { | ||
34 | unsigned int index; | ||
35 | unsigned int clkdiv; | ||
36 | unsigned int clkdiv1; | ||
37 | }; | ||
38 | |||
39 | static unsigned int exynos5250_volt_table[CPUFREQ_LEVEL_END]; | ||
40 | |||
41 | static 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 | |||
61 | static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END]; | ||
62 | |||
63 | static 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 | |||
86 | static 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 | |||
108 | static 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 */ | ||
128 | static 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 | |||
134 | static 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 | |||
156 | static 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 | |||
198 | bool 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 | |||
206 | static 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 | |||
247 | static 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 | |||
265 | int 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 | |||
337 | err_mout_apll: | ||
338 | clk_put(mout_mpll); | ||
339 | err_mout_mpll: | ||
340 | clk_put(moutcore); | ||
341 | err_moutcore: | ||
342 | clk_put(cpu_clk); | ||
343 | |||
344 | pr_err("%s: failed initialization\n", __func__); | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | EXPORT_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 |
40 | struct lpj_info { | 44 | struct lpj_info { |
41 | unsigned long ref; | 45 | unsigned long ref; |
@@ -51,6 +55,7 @@ static atomic_t freq_table_users = ATOMIC_INIT(0); | |||
51 | static struct clk *mpu_clk; | 55 | static struct clk *mpu_clk; |
52 | static char *mpu_clk_name; | 56 | static char *mpu_clk_name; |
53 | static struct device *mpu_dev; | 57 | static struct device *mpu_dev; |
58 | static struct regulator *mpu_reg; | ||
54 | 59 | ||
55 | static int omap_verify_speed(struct cpufreq_policy *policy) | 60 | static 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 | ||
190 | done: | ||
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 | |||
25 | static DEFINE_MUTEX(cpufreq_lock); | ||
26 | |||
27 | struct 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 | |||
43 | static struct s3c2416_data s3c2416_cpufreq; | ||
44 | |||
45 | struct 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 | */ | ||
68 | static struct s3c2416_dvfs s3c2416_dvfs_table[] = { | ||
69 | [SOURCE_HCLK] = { 950000, 1250000 }, | ||
70 | [SOURCE_ARMDIV] = { 1250000, 1350000 }, | ||
71 | }; | ||
72 | #endif | ||
73 | |||
74 | static 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 | |||
82 | static 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 | |||
90 | static 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 | |||
100 | static 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 | |||
114 | static 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 | |||
131 | static 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 | |||
173 | static 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 | |||
229 | static 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 | |||
292 | out: | ||
293 | mutex_unlock(&cpufreq_lock); | ||
294 | |||
295 | return ret; | ||
296 | } | ||
297 | |||
298 | #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE | ||
299 | static 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 | |||
340 | static 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 | |||
367 | static struct notifier_block s3c2416_cpufreq_reboot_notifier = { | ||
368 | .notifier_call = s3c2416_cpufreq_reboot_notifier_evt, | ||
369 | }; | ||
370 | |||
371 | static 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 | |||
508 | err_freq_table: | ||
509 | #ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE | ||
510 | regulator_put(s3c_freq->vddarm); | ||
511 | err_vddarm: | ||
512 | #endif | ||
513 | clk_put(s3c_freq->armclk); | ||
514 | err_armclk: | ||
515 | clk_put(s3c_freq->hclk); | ||
516 | err_hclk: | ||
517 | clk_put(s3c_freq->armdiv); | ||
518 | |||
519 | return ret; | ||
520 | } | ||
521 | |||
522 | static struct freq_attr *s3c2416_cpufreq_attr[] = { | ||
523 | &cpufreq_freq_attr_scaling_available_freqs, | ||
524 | NULL, | ||
525 | }; | ||
526 | |||
527 | static 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 | |||
538 | static int __init s3c2416_cpufreq_init(void) | ||
539 | { | ||
540 | return cpufreq_register_driver(&s3c2416_cpufreq_driver); | ||
541 | } | ||
542 | module_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; |