Commit | Line | Data |
---|---|---|
562a6cbe JL |
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> | |
c4aaa295 KK |
21 | |
22 | #include "exynos-cpufreq.h" | |
562a6cbe | 23 | |
562a6cbe JL |
24 | static struct clk *cpu_clk; |
25 | static struct clk *moutcore; | |
26 | static struct clk *mout_mpll; | |
27 | static struct clk *mout_apll; | |
28 | ||
9d0554ff JC |
29 | static unsigned int exynos5250_volt_table[] = { |
30 | 1300000, 1250000, 1225000, 1200000, 1150000, | |
31 | 1125000, 1100000, 1075000, 1050000, 1025000, | |
32 | 1012500, 1000000, 975000, 950000, 937500, | |
33 | 925000 | |
562a6cbe JL |
34 | }; |
35 | ||
562a6cbe | 36 | static struct cpufreq_frequency_table exynos5250_freq_table[] = { |
7f4b0461 VK |
37 | {0, L0, 1700 * 1000}, |
38 | {0, L1, 1600 * 1000}, | |
39 | {0, L2, 1500 * 1000}, | |
40 | {0, L3, 1400 * 1000}, | |
41 | {0, L4, 1300 * 1000}, | |
42 | {0, L5, 1200 * 1000}, | |
43 | {0, L6, 1100 * 1000}, | |
44 | {0, L7, 1000 * 1000}, | |
45 | {0, L8, 900 * 1000}, | |
46 | {0, L9, 800 * 1000}, | |
47 | {0, L10, 700 * 1000}, | |
48 | {0, L11, 600 * 1000}, | |
49 | {0, L12, 500 * 1000}, | |
50 | {0, L13, 400 * 1000}, | |
51 | {0, L14, 300 * 1000}, | |
52 | {0, L15, 200 * 1000}, | |
53 | {0, 0, CPUFREQ_TABLE_END}, | |
562a6cbe JL |
54 | }; |
55 | ||
9d0554ff | 56 | static struct apll_freq apll_freq_5250[] = { |
562a6cbe | 57 | /* |
9d0554ff JC |
58 | * values: |
59 | * freq | |
60 | * clock divider for ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 | |
61 | * clock divider for COPY, HPM, RESERVED | |
62 | * PLL M, P, S | |
562a6cbe | 63 | */ |
9d0554ff JC |
64 | APLL_FREQ(1700, 0, 3, 7, 7, 7, 3, 5, 0, 0, 2, 0, 425, 6, 0), |
65 | APLL_FREQ(1600, 0, 3, 7, 7, 7, 1, 4, 0, 0, 2, 0, 200, 3, 0), | |
66 | APLL_FREQ(1500, 0, 2, 7, 7, 7, 1, 4, 0, 0, 2, 0, 250, 4, 0), | |
67 | APLL_FREQ(1400, 0, 2, 7, 7, 6, 1, 4, 0, 0, 2, 0, 175, 3, 0), | |
68 | APLL_FREQ(1300, 0, 2, 7, 7, 6, 1, 3, 0, 0, 2, 0, 325, 6, 0), | |
69 | APLL_FREQ(1200, 0, 2, 7, 7, 5, 1, 3, 0, 0, 2, 0, 200, 4, 0), | |
70 | APLL_FREQ(1100, 0, 3, 7, 7, 5, 1, 3, 0, 0, 2, 0, 275, 6, 0), | |
71 | APLL_FREQ(1000, 0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 125, 3, 0), | |
72 | APLL_FREQ(900, 0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 150, 4, 0), | |
73 | APLL_FREQ(800, 0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 100, 3, 0), | |
74 | APLL_FREQ(700, 0, 1, 7, 7, 3, 1, 1, 0, 0, 2, 0, 175, 3, 1), | |
75 | APLL_FREQ(600, 0, 1, 7, 7, 3, 1, 1, 0, 0, 2, 0, 200, 4, 1), | |
76 | APLL_FREQ(500, 0, 1, 7, 7, 2, 1, 1, 0, 0, 2, 0, 125, 3, 1), | |
77 | APLL_FREQ(400, 0, 1, 7, 7, 2, 1, 1, 0, 0, 2, 0, 100, 3, 1), | |
78 | APLL_FREQ(300, 0, 1, 7, 7, 1, 1, 1, 0, 0, 2, 0, 200, 4, 2), | |
79 | APLL_FREQ(200, 0, 1, 7, 7, 1, 1, 1, 0, 0, 2, 0, 100, 3, 2), | |
562a6cbe JL |
80 | }; |
81 | ||
82 | static void set_clkdiv(unsigned int div_index) | |
83 | { | |
84 | unsigned int tmp; | |
85 | ||
86 | /* Change Divider - CPU0 */ | |
87 | ||
9d0554ff | 88 | tmp = apll_freq_5250[div_index].clk_div_cpu0; |
562a6cbe JL |
89 | |
90 | __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0); | |
91 | ||
92 | while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111) | |
93 | cpu_relax(); | |
94 | ||
95 | /* Change Divider - CPU1 */ | |
9d0554ff | 96 | tmp = apll_freq_5250[div_index].clk_div_cpu1; |
562a6cbe JL |
97 | |
98 | __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1); | |
99 | ||
100 | while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11) | |
101 | cpu_relax(); | |
102 | } | |
103 | ||
26ab1c62 | 104 | static void set_apll(unsigned int index) |
562a6cbe | 105 | { |
26ab1c62 SK |
106 | unsigned int tmp; |
107 | unsigned int freq = apll_freq_5250[index].freq; | |
562a6cbe | 108 | |
26ab1c62 | 109 | /* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ |
562a6cbe JL |
110 | clk_set_parent(moutcore, mout_mpll); |
111 | ||
112 | do { | |
113 | cpu_relax(); | |
114 | tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16); | |
115 | tmp &= 0x7; | |
116 | } while (tmp != 0x2); | |
117 | ||
26ab1c62 | 118 | clk_set_rate(mout_apll, freq * 1000); |
562a6cbe | 119 | |
26ab1c62 | 120 | /* MUX_CORE_SEL = APLL */ |
562a6cbe JL |
121 | clk_set_parent(moutcore, mout_apll); |
122 | ||
123 | do { | |
124 | cpu_relax(); | |
125 | tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); | |
126 | tmp &= (0x7 << 16); | |
127 | } while (tmp != (0x1 << 16)); | |
562a6cbe JL |
128 | } |
129 | ||
130 | static void exynos5250_set_frequency(unsigned int old_index, | |
131 | unsigned int new_index) | |
132 | { | |
562a6cbe | 133 | if (old_index > new_index) { |
26ab1c62 SK |
134 | set_clkdiv(new_index); |
135 | set_apll(new_index); | |
562a6cbe | 136 | } else if (old_index < new_index) { |
26ab1c62 SK |
137 | set_apll(new_index); |
138 | set_clkdiv(new_index); | |
562a6cbe JL |
139 | } |
140 | } | |
141 | ||
562a6cbe JL |
142 | int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) |
143 | { | |
562a6cbe JL |
144 | unsigned long rate; |
145 | ||
562a6cbe JL |
146 | cpu_clk = clk_get(NULL, "armclk"); |
147 | if (IS_ERR(cpu_clk)) | |
148 | return PTR_ERR(cpu_clk); | |
149 | ||
150 | moutcore = clk_get(NULL, "mout_cpu"); | |
151 | if (IS_ERR(moutcore)) | |
152 | goto err_moutcore; | |
153 | ||
154 | mout_mpll = clk_get(NULL, "mout_mpll"); | |
155 | if (IS_ERR(mout_mpll)) | |
156 | goto err_mout_mpll; | |
157 | ||
158 | rate = clk_get_rate(mout_mpll) / 1000; | |
159 | ||
160 | mout_apll = clk_get(NULL, "mout_apll"); | |
161 | if (IS_ERR(mout_apll)) | |
162 | goto err_mout_apll; | |
163 | ||
562a6cbe | 164 | info->mpll_freq_khz = rate; |
562a6cbe JL |
165 | /* 800Mhz */ |
166 | info->pll_safe_idx = L9; | |
562a6cbe JL |
167 | info->cpu_clk = cpu_clk; |
168 | info->volt_table = exynos5250_volt_table; | |
169 | info->freq_table = exynos5250_freq_table; | |
170 | info->set_freq = exynos5250_set_frequency; | |
562a6cbe JL |
171 | |
172 | return 0; | |
173 | ||
174 | err_mout_apll: | |
175 | clk_put(mout_mpll); | |
176 | err_mout_mpll: | |
177 | clk_put(moutcore); | |
178 | err_moutcore: | |
179 | clk_put(cpu_clk); | |
180 | ||
181 | pr_err("%s: failed initialization\n", __func__); | |
182 | return -EINVAL; | |
183 | } |