diff options
Diffstat (limited to 'arch/arm/mach-s5p64x0/clock.c')
-rw-r--r-- | arch/arm/mach-s5p64x0/clock.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/arch/arm/mach-s5p64x0/clock.c b/arch/arm/mach-s5p64x0/clock.c new file mode 100644 index 000000000000..523ba8039ac2 --- /dev/null +++ b/arch/arm/mach-s5p64x0/clock.c | |||
@@ -0,0 +1,253 @@ | |||
1 | /* linux/arch/arm/mach-s5p64x0/clock.c | ||
2 | * | ||
3 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com | ||
5 | * | ||
6 | * S5P64X0 - Clock support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/init.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/list.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <linux/clk.h> | ||
20 | #include <linux/sysdev.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include <mach/hardware.h> | ||
24 | #include <mach/map.h> | ||
25 | #include <mach/regs-clock.h> | ||
26 | |||
27 | #include <plat/cpu-freq.h> | ||
28 | #include <plat/clock.h> | ||
29 | #include <plat/cpu.h> | ||
30 | #include <plat/pll.h> | ||
31 | #include <plat/s5p-clock.h> | ||
32 | #include <plat/clock-clksrc.h> | ||
33 | #include <plat/s5p6440.h> | ||
34 | #include <plat/s5p6450.h> | ||
35 | |||
36 | struct clksrc_clk clk_mout_apll = { | ||
37 | .clk = { | ||
38 | .name = "mout_apll", | ||
39 | .id = -1, | ||
40 | }, | ||
41 | .sources = &clk_src_apll, | ||
42 | .reg_src = { .reg = S5P64X0_CLK_SRC0, .shift = 0, .size = 1 }, | ||
43 | }; | ||
44 | |||
45 | struct clksrc_clk clk_mout_mpll = { | ||
46 | .clk = { | ||
47 | .name = "mout_mpll", | ||
48 | .id = -1, | ||
49 | }, | ||
50 | .sources = &clk_src_mpll, | ||
51 | .reg_src = { .reg = S5P64X0_CLK_SRC0, .shift = 1, .size = 1 }, | ||
52 | }; | ||
53 | |||
54 | struct clksrc_clk clk_mout_epll = { | ||
55 | .clk = { | ||
56 | .name = "mout_epll", | ||
57 | .id = -1, | ||
58 | }, | ||
59 | .sources = &clk_src_epll, | ||
60 | .reg_src = { .reg = S5P64X0_CLK_SRC0, .shift = 2, .size = 1 }, | ||
61 | }; | ||
62 | |||
63 | enum perf_level { | ||
64 | L0 = 532*1000, | ||
65 | L1 = 266*1000, | ||
66 | L2 = 133*1000, | ||
67 | }; | ||
68 | |||
69 | static const u32 clock_table[][3] = { | ||
70 | /*{ARM_CLK, DIVarm, DIVhclk}*/ | ||
71 | {L0 * 1000, (0 << ARM_DIV_RATIO_SHIFT), (3 << S5P64X0_CLKDIV0_HCLK_SHIFT)}, | ||
72 | {L1 * 1000, (1 << ARM_DIV_RATIO_SHIFT), (1 << S5P64X0_CLKDIV0_HCLK_SHIFT)}, | ||
73 | {L2 * 1000, (3 << ARM_DIV_RATIO_SHIFT), (0 << S5P64X0_CLKDIV0_HCLK_SHIFT)}, | ||
74 | }; | ||
75 | |||
76 | int s5p64x0_epll_enable(struct clk *clk, int enable) | ||
77 | { | ||
78 | unsigned int ctrlbit = clk->ctrlbit; | ||
79 | unsigned int epll_con = __raw_readl(S5P64X0_EPLL_CON) & ~ctrlbit; | ||
80 | |||
81 | if (enable) | ||
82 | __raw_writel(epll_con | ctrlbit, S5P64X0_EPLL_CON); | ||
83 | else | ||
84 | __raw_writel(epll_con, S5P64X0_EPLL_CON); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | unsigned long s5p64x0_epll_get_rate(struct clk *clk) | ||
90 | { | ||
91 | return clk->rate; | ||
92 | } | ||
93 | |||
94 | unsigned long s5p64x0_armclk_get_rate(struct clk *clk) | ||
95 | { | ||
96 | unsigned long rate = clk_get_rate(clk->parent); | ||
97 | u32 clkdiv; | ||
98 | |||
99 | /* divisor mask starts at bit0, so no need to shift */ | ||
100 | clkdiv = __raw_readl(ARM_CLK_DIV) & ARM_DIV_MASK; | ||
101 | |||
102 | return rate / (clkdiv + 1); | ||
103 | } | ||
104 | |||
105 | unsigned long s5p64x0_armclk_round_rate(struct clk *clk, unsigned long rate) | ||
106 | { | ||
107 | u32 iter; | ||
108 | |||
109 | for (iter = 1 ; iter < ARRAY_SIZE(clock_table) ; iter++) { | ||
110 | if (rate > clock_table[iter][0]) | ||
111 | return clock_table[iter-1][0]; | ||
112 | } | ||
113 | |||
114 | return clock_table[ARRAY_SIZE(clock_table) - 1][0]; | ||
115 | } | ||
116 | |||
117 | int s5p64x0_armclk_set_rate(struct clk *clk, unsigned long rate) | ||
118 | { | ||
119 | u32 round_tmp; | ||
120 | u32 iter; | ||
121 | u32 clk_div0_tmp; | ||
122 | u32 cur_rate = clk->ops->get_rate(clk); | ||
123 | unsigned long flags; | ||
124 | |||
125 | round_tmp = clk->ops->round_rate(clk, rate); | ||
126 | if (round_tmp == cur_rate) | ||
127 | return 0; | ||
128 | |||
129 | |||
130 | for (iter = 0 ; iter < ARRAY_SIZE(clock_table) ; iter++) { | ||
131 | if (round_tmp == clock_table[iter][0]) | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | if (iter >= ARRAY_SIZE(clock_table)) | ||
136 | iter = ARRAY_SIZE(clock_table) - 1; | ||
137 | |||
138 | local_irq_save(flags); | ||
139 | if (cur_rate > round_tmp) { | ||
140 | /* Frequency Down */ | ||
141 | clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & ~(ARM_DIV_MASK); | ||
142 | clk_div0_tmp |= clock_table[iter][1]; | ||
143 | __raw_writel(clk_div0_tmp, ARM_CLK_DIV); | ||
144 | |||
145 | clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & | ||
146 | ~(S5P64X0_CLKDIV0_HCLK_MASK); | ||
147 | clk_div0_tmp |= clock_table[iter][2]; | ||
148 | __raw_writel(clk_div0_tmp, ARM_CLK_DIV); | ||
149 | |||
150 | |||
151 | } else { | ||
152 | /* Frequency Up */ | ||
153 | clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & | ||
154 | ~(S5P64X0_CLKDIV0_HCLK_MASK); | ||
155 | clk_div0_tmp |= clock_table[iter][2]; | ||
156 | __raw_writel(clk_div0_tmp, ARM_CLK_DIV); | ||
157 | |||
158 | clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & ~(ARM_DIV_MASK); | ||
159 | clk_div0_tmp |= clock_table[iter][1]; | ||
160 | __raw_writel(clk_div0_tmp, ARM_CLK_DIV); | ||
161 | } | ||
162 | local_irq_restore(flags); | ||
163 | |||
164 | clk->rate = clock_table[iter][0]; | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | struct clk_ops s5p64x0_clkarm_ops = { | ||
170 | .get_rate = s5p64x0_armclk_get_rate, | ||
171 | .set_rate = s5p64x0_armclk_set_rate, | ||
172 | .round_rate = s5p64x0_armclk_round_rate, | ||
173 | }; | ||
174 | |||
175 | struct clksrc_clk clk_armclk = { | ||
176 | .clk = { | ||
177 | .name = "armclk", | ||
178 | .id = 1, | ||
179 | .parent = &clk_mout_apll.clk, | ||
180 | .ops = &s5p64x0_clkarm_ops, | ||
181 | }, | ||
182 | .reg_div = { .reg = S5P64X0_CLK_DIV0, .shift = 0, .size = 4 }, | ||
183 | }; | ||
184 | |||
185 | struct clksrc_clk clk_dout_mpll = { | ||
186 | .clk = { | ||
187 | .name = "dout_mpll", | ||
188 | .id = -1, | ||
189 | .parent = &clk_mout_mpll.clk, | ||
190 | }, | ||
191 | .reg_div = { .reg = S5P64X0_CLK_DIV0, .shift = 4, .size = 1 }, | ||
192 | }; | ||
193 | |||
194 | struct clk *clkset_hclk_low_list[] = { | ||
195 | &clk_mout_apll.clk, | ||
196 | &clk_mout_mpll.clk, | ||
197 | }; | ||
198 | |||
199 | struct clksrc_sources clkset_hclk_low = { | ||
200 | .sources = clkset_hclk_low_list, | ||
201 | .nr_sources = ARRAY_SIZE(clkset_hclk_low_list), | ||
202 | }; | ||
203 | |||
204 | int s5p64x0_pclk_ctrl(struct clk *clk, int enable) | ||
205 | { | ||
206 | return s5p_gatectrl(S5P64X0_CLK_GATE_PCLK, clk, enable); | ||
207 | } | ||
208 | |||
209 | int s5p64x0_hclk0_ctrl(struct clk *clk, int enable) | ||
210 | { | ||
211 | return s5p_gatectrl(S5P64X0_CLK_GATE_HCLK0, clk, enable); | ||
212 | } | ||
213 | |||
214 | int s5p64x0_hclk1_ctrl(struct clk *clk, int enable) | ||
215 | { | ||
216 | return s5p_gatectrl(S5P64X0_CLK_GATE_HCLK1, clk, enable); | ||
217 | } | ||
218 | |||
219 | int s5p64x0_sclk_ctrl(struct clk *clk, int enable) | ||
220 | { | ||
221 | return s5p_gatectrl(S5P64X0_CLK_GATE_SCLK0, clk, enable); | ||
222 | } | ||
223 | |||
224 | int s5p64x0_sclk1_ctrl(struct clk *clk, int enable) | ||
225 | { | ||
226 | return s5p_gatectrl(S5P64X0_CLK_GATE_SCLK1, clk, enable); | ||
227 | } | ||
228 | |||
229 | int s5p64x0_mem_ctrl(struct clk *clk, int enable) | ||
230 | { | ||
231 | return s5p_gatectrl(S5P64X0_CLK_GATE_MEM0, clk, enable); | ||
232 | } | ||
233 | |||
234 | int s5p64x0_clk48m_ctrl(struct clk *clk, int enable) | ||
235 | { | ||
236 | unsigned long flags; | ||
237 | u32 val; | ||
238 | |||
239 | /* can't rely on clock lock, this register has other usages */ | ||
240 | local_irq_save(flags); | ||
241 | |||
242 | val = __raw_readl(S5P64X0_OTHERS); | ||
243 | if (enable) | ||
244 | val |= S5P64X0_OTHERS_USB_SIG_MASK; | ||
245 | else | ||
246 | val &= ~S5P64X0_OTHERS_USB_SIG_MASK; | ||
247 | |||
248 | __raw_writel(val, S5P64X0_OTHERS); | ||
249 | |||
250 | local_irq_restore(flags); | ||
251 | |||
252 | return 0; | ||
253 | } | ||