aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s5p
diff options
context:
space:
mode:
authorKukjin Kim <kgene.kim@samsung.com>2010-01-13 18:13:37 -0500
committerBen Dooks <ben-linux@fluff.org>2010-01-15 05:16:16 -0500
commit1a0e8a52ad56075663d8e120e2468fc96fb6fa6c (patch)
tree1ff1246f8bfc638e6fc2fab1c3d25b1aba6bb8c1 /arch/arm/plat-s5p
parent209fecd1b8e65b8046efbbc8314d449e53c4c6b6 (diff)
ARM: S5P6440: Add Clock and PLL support
This patch adds clock and pll support for S5P6440. This patch are based on Harald Welte's patches and Ben's plat-samsung. Signed-off-by: Thomas Abraham <thomas.ab@samsung.com> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/plat-s5p')
-rw-r--r--arch/arm/plat-s5p/clock.c136
-rw-r--r--arch/arm/plat-s5p/include/plat/pll.h83
-rw-r--r--arch/arm/plat-s5p/include/plat/s5p-clock.h38
-rw-r--r--arch/arm/plat-s5p/s5p6440-clock.c698
4 files changed, 955 insertions, 0 deletions
diff --git a/arch/arm/plat-s5p/clock.c b/arch/arm/plat-s5p/clock.c
new file mode 100644
index 00000000000..3d3c0f1934f
--- /dev/null
+++ b/arch/arm/plat-s5p/clock.c
@@ -0,0 +1,136 @@
1/* linux/arch/arm/plat-s5p/clock.c
2 *
3 * Copyright 2009 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * S5P - Common 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#include <asm/div64.h>
23
24#include <plat/clock.h>
25#include <plat/clock-clksrc.h>
26#include <plat/s5p-clock.h>
27
28/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
29 * clk_ext_xtal_mux.
30*/
31struct clk clk_ext_xtal_mux = {
32 .name = "ext_xtal",
33 .id = -1,
34};
35
36/* 48MHz USB Phy clock output */
37struct clk clk_48m = {
38 .name = "clk_48m",
39 .id = -1,
40 .rate = 48000000,
41};
42
43/* APLL clock output
44 * No need .ctrlbit, this is always on
45*/
46struct clk clk_fout_apll = {
47 .name = "fout_apll",
48 .id = -1,
49};
50
51/* MPLL clock output
52 * No need .ctrlbit, this is always on
53*/
54struct clk clk_fout_mpll = {
55 .name = "fout_mpll",
56 .id = -1,
57};
58
59/* EPLL clock output */
60struct clk clk_fout_epll = {
61 .name = "fout_epll",
62 .id = -1,
63 .ctrlbit = (1 << 31),
64};
65
66/* ARM clock */
67struct clk clk_arm = {
68 .name = "armclk",
69 .id = -1,
70 .rate = 0,
71 .ctrlbit = 0,
72};
73
74/* Possible clock sources for APLL Mux */
75static struct clk *clk_src_apll_list[] = {
76 [0] = &clk_fin_apll,
77 [1] = &clk_fout_apll,
78};
79
80struct clksrc_sources clk_src_apll = {
81 .sources = clk_src_apll_list,
82 .nr_sources = ARRAY_SIZE(clk_src_apll_list),
83};
84
85/* Possible clock sources for MPLL Mux */
86static struct clk *clk_src_mpll_list[] = {
87 [0] = &clk_fin_mpll,
88 [1] = &clk_fout_mpll,
89};
90
91struct clksrc_sources clk_src_mpll = {
92 .sources = clk_src_mpll_list,
93 .nr_sources = ARRAY_SIZE(clk_src_mpll_list),
94};
95
96/* Possible clock sources for EPLL Mux */
97static struct clk *clk_src_epll_list[] = {
98 [0] = &clk_fin_epll,
99 [1] = &clk_fout_epll,
100};
101
102struct clksrc_sources clk_src_epll = {
103 .sources = clk_src_epll_list,
104 .nr_sources = ARRAY_SIZE(clk_src_epll_list),
105};
106
107int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable)
108{
109 unsigned int ctrlbit = clk->ctrlbit;
110 u32 con;
111
112 con = __raw_readl(reg);
113 con = enable ? (con | ctrlbit) : (con & ~ctrlbit);
114 __raw_writel(con, reg);
115 return 0;
116}
117
118static struct clk *s5p_clks[] __initdata = {
119 &clk_ext_xtal_mux,
120 &clk_48m,
121 &clk_fout_apll,
122 &clk_fout_mpll,
123 &clk_fout_epll,
124 &clk_arm,
125};
126
127void __init s5p_register_clocks(unsigned long xtal_freq)
128{
129 int ret;
130
131 clk_ext_xtal_mux.rate = xtal_freq;
132
133 ret = s3c24xx_register_clocks(s5p_clks, ARRAY_SIZE(s5p_clks));
134 if (ret > 0)
135 printk(KERN_ERR "Failed to register s5p clocks\n");
136}
diff --git a/arch/arm/plat-s5p/include/plat/pll.h b/arch/arm/plat-s5p/include/plat/pll.h
new file mode 100644
index 00000000000..d48325bb29e
--- /dev/null
+++ b/arch/arm/plat-s5p/include/plat/pll.h
@@ -0,0 +1,83 @@
1/* arch/arm/plat-s5p/include/plat/pll.h
2 *
3 * Copyright (c) 2009 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * S5P PLL code
7 *
8 * Based on arch/arm/plat-s3c64xx/include/plat/pll.h
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#define PLL45XX_MDIV_MASK (0x3FF)
16#define PLL45XX_PDIV_MASK (0x3F)
17#define PLL45XX_SDIV_MASK (0x7)
18#define PLL45XX_MDIV_SHIFT (16)
19#define PLL45XX_PDIV_SHIFT (8)
20#define PLL45XX_SDIV_SHIFT (0)
21
22#include <asm/div64.h>
23
24enum pll45xx_type_t {
25 pll_4500,
26 pll_4502,
27 pll_4508
28};
29
30static inline unsigned long s5p_get_pll45xx(unsigned long baseclk, u32 pll_con,
31 enum pll45xx_type_t pll_type)
32{
33 u32 mdiv, pdiv, sdiv;
34 u64 fvco = baseclk;
35
36 mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
37 pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
38 sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
39
40 if (pll_type == pll_4508)
41 sdiv = sdiv - 1;
42
43 fvco *= mdiv;
44 do_div(fvco, (pdiv << sdiv));
45
46 return (unsigned long)fvco;
47}
48
49#define PLL90XX_MDIV_MASK (0xFF)
50#define PLL90XX_PDIV_MASK (0x3F)
51#define PLL90XX_SDIV_MASK (0x7)
52#define PLL90XX_KDIV_MASK (0xffff)
53#define PLL90XX_MDIV_SHIFT (16)
54#define PLL90XX_PDIV_SHIFT (8)
55#define PLL90XX_SDIV_SHIFT (0)
56#define PLL90XX_KDIV_SHIFT (0)
57
58static inline unsigned long s5p_get_pll90xx(unsigned long baseclk,
59 u32 pll_con, u32 pll_conk)
60{
61 unsigned long result;
62 u32 mdiv, pdiv, sdiv, kdiv;
63 u64 tmp;
64
65 mdiv = (pll_con >> PLL90XX_MDIV_SHIFT) & PLL90XX_MDIV_MASK;
66 pdiv = (pll_con >> PLL90XX_PDIV_SHIFT) & PLL90XX_PDIV_MASK;
67 sdiv = (pll_con >> PLL90XX_SDIV_SHIFT) & PLL90XX_SDIV_MASK;
68 kdiv = pll_conk & PLL90XX_KDIV_MASK;
69
70 /* We need to multiple baseclk by mdiv (the integer part) and kdiv
71 * which is in 2^16ths, so shift mdiv up (does not overflow) and
72 * add kdiv before multiplying. The use of tmp is to avoid any
73 * overflows before shifting bac down into result when multipling
74 * by the mdiv and kdiv pair.
75 */
76
77 tmp = baseclk;
78 tmp *= (mdiv << 16) + kdiv;
79 do_div(tmp, (pdiv << sdiv));
80 result = tmp >> 16;
81
82 return result;
83}
diff --git a/arch/arm/plat-s5p/include/plat/s5p-clock.h b/arch/arm/plat-s5p/include/plat/s5p-clock.h
new file mode 100644
index 00000000000..e1a7444b882
--- /dev/null
+++ b/arch/arm/plat-s5p/include/plat/s5p-clock.h
@@ -0,0 +1,38 @@
1/* linux/arch/arm/plat-s5p/include/plat/s5p-clock.h
2 *
3 * Copyright 2009 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * Header file for s5p 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#ifndef __ASM_PLAT_S5P_CLOCK_H
14#define __ASM_PLAT_S5P_CLOCK_H __FILE__
15
16#include <linux/clk.h>
17
18#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
19
20#define clk_fin_apll clk_ext_xtal_mux
21#define clk_fin_mpll clk_ext_xtal_mux
22#define clk_fin_epll clk_ext_xtal_mux
23
24extern struct clk clk_ext_xtal_mux;
25extern struct clk clk_48m;
26extern struct clk clk_fout_apll;
27extern struct clk clk_fout_mpll;
28extern struct clk clk_fout_epll;
29extern struct clk clk_arm;
30
31extern struct clksrc_sources clk_src_apll;
32extern struct clksrc_sources clk_src_mpll;
33extern struct clksrc_sources clk_src_epll;
34
35extern int s5p6440_clk48m_ctrl(struct clk *clk, int enable);
36extern int s5p_gatectrl(void __iomem *reg, struct clk *clk, int enable);
37
38#endif /* __ASM_PLAT_S5P_CLOCK_H */
diff --git a/arch/arm/plat-s5p/s5p6440-clock.c b/arch/arm/plat-s5p/s5p6440-clock.c
new file mode 100644
index 00000000000..2f412f8c121
--- /dev/null
+++ b/arch/arm/plat-s5p/s5p6440-clock.c
@@ -0,0 +1,698 @@
1/* linux/arch/arm/plat-s5p/s5p6440-clock.c
2 *
3 * Copyright (c) 2009 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com/
5 *
6 * S5P6440 - 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
26#include <plat/cpu-freq.h>
27#include <mach/regs-clock.h>
28#include <plat/clock.h>
29#include <plat/cpu.h>
30#include <plat/clock-clksrc.h>
31#include <plat/s5p-clock.h>
32#include <plat/pll.h>
33#include <plat/s5p6440.h>
34
35/* APLL Mux output clock */
36static 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 = S5P_CLK_SRC0, .shift = 0, .size = 1 },
43};
44
45static int s5p6440_epll_enable(struct clk *clk, int enable)
46{
47 unsigned int ctrlbit = clk->ctrlbit;
48 unsigned int epll_con = __raw_readl(S5P_EPLL_CON) & ~ctrlbit;
49
50 if (enable)
51 __raw_writel(epll_con | ctrlbit, S5P_EPLL_CON);
52 else
53 __raw_writel(epll_con, S5P_EPLL_CON);
54
55 return 0;
56}
57
58static unsigned long s5p6440_epll_get_rate(struct clk *clk)
59{
60 return clk->rate;
61}
62
63static u32 epll_div[][5] = {
64 { 36000000, 0, 48, 1, 4 },
65 { 48000000, 0, 32, 1, 3 },
66 { 60000000, 0, 40, 1, 3 },
67 { 72000000, 0, 48, 1, 3 },
68 { 84000000, 0, 28, 1, 2 },
69 { 96000000, 0, 32, 1, 2 },
70 { 32768000, 45264, 43, 1, 4 },
71 { 45158000, 6903, 30, 1, 3 },
72 { 49152000, 50332, 32, 1, 3 },
73 { 67738000, 10398, 45, 1, 3 },
74 { 73728000, 9961, 49, 1, 3 }
75};
76
77static int s5p6440_epll_set_rate(struct clk *clk, unsigned long rate)
78{
79 unsigned int epll_con, epll_con_k;
80 unsigned int i;
81
82 if (clk->rate == rate) /* Return if nothing changed */
83 return 0;
84
85 epll_con = __raw_readl(S5P_EPLL_CON);
86 epll_con_k = __raw_readl(S5P_EPLL_CON_K);
87
88 epll_con_k &= ~(PLL90XX_KDIV_MASK);
89 epll_con &= ~(PLL90XX_MDIV_MASK | PLL90XX_PDIV_MASK | PLL90XX_SDIV_MASK);
90
91 for (i = 0; i < ARRAY_SIZE(epll_div); i++) {
92 if (epll_div[i][0] == rate) {
93 epll_con_k |= (epll_div[i][1] << PLL90XX_KDIV_SHIFT);
94 epll_con |= (epll_div[i][2] << PLL90XX_MDIV_SHIFT) |
95 (epll_div[i][3] << PLL90XX_PDIV_SHIFT) |
96 (epll_div[i][4] << PLL90XX_SDIV_SHIFT);
97 break;
98 }
99 }
100
101 if (i == ARRAY_SIZE(epll_div)) {
102 printk(KERN_ERR "%s: Invalid Clock EPLL Frequency\n", __func__);
103 return -EINVAL;
104 }
105
106 __raw_writel(epll_con, S5P_EPLL_CON);
107 __raw_writel(epll_con_k, S5P_EPLL_CON_K);
108
109 clk->rate = rate;
110
111 return 0;
112}
113
114static struct clk_ops s5p6440_epll_ops = {
115 .get_rate = s5p6440_epll_get_rate,
116 .set_rate = s5p6440_epll_set_rate,
117};
118
119static struct clksrc_clk clk_mout_epll = {
120 .clk = {
121 .name = "mout_epll",
122 .id = -1,
123 },
124 .sources = &clk_src_epll,
125 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 2, .size = 1 },
126};
127
128static struct clksrc_clk clk_mout_mpll = {
129 .clk = {
130 .name = "mout_mpll",
131 .id = -1,
132 },
133 .sources = &clk_src_mpll,
134 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 1, .size = 1 },
135};
136
137static struct clk clk_h_low = {
138 .name = "hclk_low",
139 .id = -1,
140 .rate = 0,
141 .parent = NULL,
142 .ctrlbit = 0,
143 .ops = &clk_ops_def_setrate,
144};
145
146static struct clk clk_p_low = {
147 .name = "pclk_low",
148 .id = -1,
149 .rate = 0,
150 .parent = NULL,
151 .ctrlbit = 0,
152 .ops = &clk_ops_def_setrate,
153};
154
155enum perf_level {
156 L0 = 532*1000,
157 L1 = 266*1000,
158 L2 = 133*1000,
159};
160
161static const u32 clock_table[][3] = {
162 /*{ARM_CLK, DIVarm, DIVhclk}*/
163 {L0 * 1000, (0 << ARM_DIV_RATIO_SHIFT), (3 << S5P_CLKDIV0_HCLK_SHIFT)},
164 {L1 * 1000, (1 << ARM_DIV_RATIO_SHIFT), (1 << S5P_CLKDIV0_HCLK_SHIFT)},
165 {L2 * 1000, (3 << ARM_DIV_RATIO_SHIFT), (0 << S5P_CLKDIV0_HCLK_SHIFT)},
166};
167
168static unsigned long s5p6440_armclk_get_rate(struct clk *clk)
169{
170 unsigned long rate = clk_get_rate(clk->parent);
171 u32 clkdiv;
172
173 /* divisor mask starts at bit0, so no need to shift */
174 clkdiv = __raw_readl(ARM_CLK_DIV) & ARM_DIV_MASK;
175
176 return rate / (clkdiv + 1);
177}
178
179static unsigned long s5p6440_armclk_round_rate(struct clk *clk,
180 unsigned long rate)
181{
182 u32 iter;
183
184 for (iter = 1 ; iter < ARRAY_SIZE(clock_table) ; iter++) {
185 if (rate > clock_table[iter][0])
186 return clock_table[iter-1][0];
187 }
188
189 return clock_table[ARRAY_SIZE(clock_table) - 1][0];
190}
191
192static int s5p6440_armclk_set_rate(struct clk *clk, unsigned long rate)
193{
194 u32 round_tmp;
195 u32 iter;
196 u32 clk_div0_tmp;
197 u32 cur_rate = clk->ops->get_rate(clk);
198 unsigned long flags;
199
200 round_tmp = clk->ops->round_rate(clk, rate);
201 if (round_tmp == cur_rate)
202 return 0;
203
204
205 for (iter = 0 ; iter < ARRAY_SIZE(clock_table) ; iter++) {
206 if (round_tmp == clock_table[iter][0])
207 break;
208 }
209
210 if (iter >= ARRAY_SIZE(clock_table))
211 iter = ARRAY_SIZE(clock_table) - 1;
212
213 local_irq_save(flags);
214 if (cur_rate > round_tmp) {
215 /* Frequency Down */
216 clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & ~(ARM_DIV_MASK);
217 clk_div0_tmp |= clock_table[iter][1];
218 __raw_writel(clk_div0_tmp, ARM_CLK_DIV);
219
220 clk_div0_tmp = __raw_readl(ARM_CLK_DIV) &
221 ~(S5P_CLKDIV0_HCLK_MASK);
222 clk_div0_tmp |= clock_table[iter][2];
223 __raw_writel(clk_div0_tmp, ARM_CLK_DIV);
224
225
226 } else {
227 /* Frequency Up */
228 clk_div0_tmp = __raw_readl(ARM_CLK_DIV) &
229 ~(S5P_CLKDIV0_HCLK_MASK);
230 clk_div0_tmp |= clock_table[iter][2];
231 __raw_writel(clk_div0_tmp, ARM_CLK_DIV);
232
233 clk_div0_tmp = __raw_readl(ARM_CLK_DIV) & ~(ARM_DIV_MASK);
234 clk_div0_tmp |= clock_table[iter][1];
235 __raw_writel(clk_div0_tmp, ARM_CLK_DIV);
236 }
237 local_irq_restore(flags);
238
239 clk->rate = clock_table[iter][0];
240
241 return 0;
242}
243
244static struct clk_ops s5p6440_clkarm_ops = {
245 .get_rate = s5p6440_armclk_get_rate,
246 .set_rate = s5p6440_armclk_set_rate,
247 .round_rate = s5p6440_armclk_round_rate,
248};
249
250static unsigned long s5p6440_clk_doutmpll_get_rate(struct clk *clk)
251{
252 unsigned long rate = clk_get_rate(clk->parent);
253
254 if (__raw_readl(S5P_CLK_DIV0) & S5P_CLKDIV0_MPLL_MASK)
255 rate /= 2;
256
257 return rate;
258}
259
260static struct clk clk_dout_mpll = {
261 .name = "dout_mpll",
262 .id = -1,
263 .parent = &clk_mout_mpll.clk,
264 .ops = &(struct clk_ops) {
265 .get_rate = s5p6440_clk_doutmpll_get_rate,
266 },
267};
268
269int s5p6440_clk48m_ctrl(struct clk *clk, int enable)
270{
271 unsigned long flags;
272 u32 val;
273
274 /* can't rely on clock lock, this register has other usages */
275 local_irq_save(flags);
276
277 val = __raw_readl(S5P_OTHERS);
278 if (enable)
279 val |= S5P_OTHERS_USB_SIG_MASK;
280 else
281 val &= ~S5P_OTHERS_USB_SIG_MASK;
282
283 __raw_writel(val, S5P_OTHERS);
284
285 local_irq_restore(flags);
286
287 return 0;
288}
289
290static int s5p6440_pclk_ctrl(struct clk *clk, int enable)
291{
292 return s5p_gatectrl(S5P_CLK_GATE_PCLK, clk, enable);
293}
294
295static int s5p6440_hclk0_ctrl(struct clk *clk, int enable)
296{
297 return s5p_gatectrl(S5P_CLK_GATE_HCLK0, clk, enable);
298}
299
300static int s5p6440_hclk1_ctrl(struct clk *clk, int enable)
301{
302 return s5p_gatectrl(S5P_CLK_GATE_HCLK1, clk, enable);
303}
304
305static int s5p6440_sclk_ctrl(struct clk *clk, int enable)
306{
307 return s5p_gatectrl(S5P_CLK_GATE_SCLK0, clk, enable);
308}
309
310static int s5p6440_mem_ctrl(struct clk *clk, int enable)
311{
312 return s5p_gatectrl(S5P_CLK_GATE_MEM0, clk, enable);
313}
314
315/*
316 * The following clocks will be disabled during clock initialization. It is
317 * recommended to keep the following clocks disabled until the driver requests
318 * for enabling the clock.
319 */
320static struct clk init_clocks_disable[] = {
321 {
322 .name = "nand",
323 .id = -1,
324 .parent = &clk_h,
325 .enable = s5p6440_mem_ctrl,
326 .ctrlbit = S5P_CLKCON_MEM0_HCLK_NFCON,
327 }, {
328 .name = "adc",
329 .id = -1,
330 .parent = &clk_p_low,
331 .enable = s5p6440_pclk_ctrl,
332 .ctrlbit = S5P_CLKCON_PCLK_TSADC,
333 }, {
334 .name = "i2c",
335 .id = -1,
336 .parent = &clk_p_low,
337 .enable = s5p6440_pclk_ctrl,
338 .ctrlbit = S5P_CLKCON_PCLK_IIC0,
339 }, {
340 .name = "i2s_v40",
341 .id = 0,
342 .parent = &clk_p_low,
343 .enable = s5p6440_pclk_ctrl,
344 .ctrlbit = S5P_CLKCON_PCLK_IIS2,
345 }, {
346 .name = "spi",
347 .id = 0,
348 .parent = &clk_p_low,
349 .enable = s5p6440_pclk_ctrl,
350 .ctrlbit = S5P_CLKCON_PCLK_SPI0,
351 }, {
352 .name = "spi",
353 .id = 1,
354 .parent = &clk_p_low,
355 .enable = s5p6440_pclk_ctrl,
356 .ctrlbit = S5P_CLKCON_PCLK_SPI1,
357 }, {
358 .name = "sclk_spi_48",
359 .id = 0,
360 .parent = &clk_48m,
361 .enable = s5p6440_sclk_ctrl,
362 .ctrlbit = S5P_CLKCON_SCLK0_SPI0_48,
363 }, {
364 .name = "sclk_spi_48",
365 .id = 1,
366 .parent = &clk_48m,
367 .enable = s5p6440_sclk_ctrl,
368 .ctrlbit = S5P_CLKCON_SCLK0_SPI1_48,
369 }, {
370 .name = "mmc_48m",
371 .id = 0,
372 .parent = &clk_48m,
373 .enable = s5p6440_sclk_ctrl,
374 .ctrlbit = S5P_CLKCON_SCLK0_MMC0_48,
375 }, {
376 .name = "mmc_48m",
377 .id = 1,
378 .parent = &clk_48m,
379 .enable = s5p6440_sclk_ctrl,
380 .ctrlbit = S5P_CLKCON_SCLK0_MMC1_48,
381 }, {
382 .name = "mmc_48m",
383 .id = 2,
384 .parent = &clk_48m,
385 .enable = s5p6440_sclk_ctrl,
386 .ctrlbit = S5P_CLKCON_SCLK0_MMC2_48,
387 }, {
388 .name = "otg",
389 .id = -1,
390 .parent = &clk_h_low,
391 .enable = s5p6440_hclk0_ctrl,
392 .ctrlbit = S5P_CLKCON_HCLK0_USB
393 }, {
394 .name = "post",
395 .id = -1,
396 .parent = &clk_h_low,
397 .enable = s5p6440_hclk0_ctrl,
398 .ctrlbit = S5P_CLKCON_HCLK0_POST0
399 }, {
400 .name = "lcd",
401 .id = -1,
402 .parent = &clk_h_low,
403 .enable = s5p6440_hclk1_ctrl,
404 .ctrlbit = S5P_CLKCON_HCLK1_DISPCON,
405 }, {
406 .name = "hsmmc",
407 .id = 0,
408 .parent = &clk_h_low,
409 .enable = s5p6440_hclk0_ctrl,
410 .ctrlbit = S5P_CLKCON_HCLK0_HSMMC0,
411 }, {
412 .name = "hsmmc",
413 .id = 1,
414 .parent = &clk_h_low,
415 .enable = s5p6440_hclk0_ctrl,
416 .ctrlbit = S5P_CLKCON_HCLK0_HSMMC1,
417 }, {
418 .name = "hsmmc",
419 .id = 2,
420 .parent = &clk_h_low,
421 .enable = s5p6440_hclk0_ctrl,
422 .ctrlbit = S5P_CLKCON_HCLK0_HSMMC2,
423 }, {
424 .name = "rtc",
425 .id = -1,
426 .parent = &clk_p_low,
427 .enable = s5p6440_pclk_ctrl,
428 .ctrlbit = S5P_CLKCON_PCLK_RTC,
429 }, {
430 .name = "watchdog",
431 .id = -1,
432 .parent = &clk_p_low,
433 .enable = s5p6440_pclk_ctrl,
434 .ctrlbit = S5P_CLKCON_PCLK_WDT,
435 }, {
436 .name = "timers",
437 .id = -1,
438 .parent = &clk_p_low,
439 .enable = s5p6440_pclk_ctrl,
440 .ctrlbit = S5P_CLKCON_PCLK_PWM,
441 }
442};
443
444/*
445 * The following clocks will be enabled during clock initialization.
446 */
447static struct clk init_clocks[] = {
448 {
449 .name = "gpio",
450 .id = -1,
451 .parent = &clk_p_low,
452 .enable = s5p6440_pclk_ctrl,
453 .ctrlbit = S5P_CLKCON_PCLK_GPIO,
454 }, {
455 .name = "uart",
456 .id = 0,
457 .parent = &clk_p_low,
458 .enable = s5p6440_pclk_ctrl,
459 .ctrlbit = S5P_CLKCON_PCLK_UART0,
460 }, {
461 .name = "uart",
462 .id = 1,
463 .parent = &clk_p_low,
464 .enable = s5p6440_pclk_ctrl,
465 .ctrlbit = S5P_CLKCON_PCLK_UART1,
466 }, {
467 .name = "uart",
468 .id = 2,
469 .parent = &clk_p_low,
470 .enable = s5p6440_pclk_ctrl,
471 .ctrlbit = S5P_CLKCON_PCLK_UART2,
472 }, {
473 .name = "uart",
474 .id = 3,
475 .parent = &clk_p_low,
476 .enable = s5p6440_pclk_ctrl,
477 .ctrlbit = S5P_CLKCON_PCLK_UART3,
478 }
479};
480
481static struct clk clk_iis_cd_v40 = {
482 .name = "iis_cdclk_v40",
483 .id = -1,
484};
485
486static struct clk clk_pcm_cd = {
487 .name = "pcm_cdclk",
488 .id = -1,
489};
490
491static struct clk *clkset_spi_mmc_list[] = {
492 &clk_mout_epll.clk,
493 &clk_dout_mpll,
494 &clk_fin_epll,
495};
496
497static struct clksrc_sources clkset_spi_mmc = {
498 .sources = clkset_spi_mmc_list,
499 .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list),
500};
501
502static struct clk *clkset_uart_list[] = {
503 &clk_mout_epll.clk,
504 &clk_dout_mpll
505};
506
507static struct clksrc_sources clkset_uart = {
508 .sources = clkset_uart_list,
509 .nr_sources = ARRAY_SIZE(clkset_uart_list),
510};
511
512static struct clksrc_clk clksrcs[] = {
513 {
514 .clk = {
515 .name = "mmc_bus",
516 .id = 0,
517 .ctrlbit = S5P_CLKCON_SCLK0_MMC0,
518 .enable = s5p6440_sclk_ctrl,
519 },
520 .sources = &clkset_spi_mmc,
521 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 18, .size = 2 },
522 .reg_div = { .reg = S5P_CLK_DIV1, .shift = 0, .size = 4 },
523 }, {
524 .clk = {
525 .name = "mmc_bus",
526 .id = 1,
527 .ctrlbit = S5P_CLKCON_SCLK0_MMC1,
528 .enable = s5p6440_sclk_ctrl,
529 },
530 .sources = &clkset_spi_mmc,
531 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 20, .size = 2 },
532 .reg_div = { .reg = S5P_CLK_DIV1, .shift = 4, .size = 4 },
533 }, {
534 .clk = {
535 .name = "mmc_bus",
536 .id = 2,
537 .ctrlbit = S5P_CLKCON_SCLK0_MMC2,
538 .enable = s5p6440_sclk_ctrl,
539 },
540 .sources = &clkset_spi_mmc,
541 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 22, .size = 2 },
542 .reg_div = { .reg = S5P_CLK_DIV1, .shift = 8, .size = 4 },
543 }, {
544 .clk = {
545 .name = "uclk1",
546 .id = -1,
547 .ctrlbit = S5P_CLKCON_SCLK0_UART,
548 .enable = s5p6440_sclk_ctrl,
549 },
550 .sources = &clkset_uart,
551 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 13, .size = 1 },
552 .reg_div = { .reg = S5P_CLK_DIV2, .shift = 16, .size = 4 },
553 }, {
554 .clk = {
555 .name = "spi_epll",
556 .id = 0,
557 .ctrlbit = S5P_CLKCON_SCLK0_SPI0,
558 .enable = s5p6440_sclk_ctrl,
559 },
560 .sources = &clkset_spi_mmc,
561 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 14, .size = 2 },
562 .reg_div = { .reg = S5P_CLK_DIV2, .shift = 0, .size = 4 },
563 }, {
564 .clk = {
565 .name = "spi_epll",
566 .id = 1,
567 .ctrlbit = S5P_CLKCON_SCLK0_SPI1,
568 .enable = s5p6440_sclk_ctrl,
569 },
570 .sources = &clkset_spi_mmc,
571 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 16, .size = 2 },
572 .reg_div = { .reg = S5P_CLK_DIV2, .shift = 4, .size = 4 },
573 }
574};
575
576/* Clock initialisation code */
577static struct clksrc_clk *init_parents[] = {
578 &clk_mout_apll,
579 &clk_mout_epll,
580 &clk_mout_mpll,
581};
582
583void __init_or_cpufreq s5p6440_setup_clocks(void)
584{
585 struct clk *xtal_clk;
586 unsigned long xtal;
587 unsigned long fclk;
588 unsigned long hclk;
589 unsigned long hclk_low;
590 unsigned long pclk;
591 unsigned long pclk_low;
592 unsigned long epll;
593 unsigned long apll;
594 unsigned long mpll;
595 unsigned int ptr;
596 u32 clkdiv0;
597 u32 clkdiv3;
598
599 /* Set S5P6440 functions for clk_fout_epll */
600 clk_fout_epll.enable = s5p6440_epll_enable;
601 clk_fout_epll.ops = &s5p6440_epll_ops;
602
603 /* Set S5P6440 functions for arm clock */
604 clk_arm.parent = &clk_mout_apll.clk;
605 clk_arm.ops = &s5p6440_clkarm_ops;
606 clk_48m.enable = s5p6440_clk48m_ctrl;
607
608 clkdiv0 = __raw_readl(S5P_CLK_DIV0);
609 clkdiv3 = __raw_readl(S5P_CLK_DIV3);
610
611 xtal_clk = clk_get(NULL, "ext_xtal");
612 BUG_ON(IS_ERR(xtal_clk));
613
614 xtal = clk_get_rate(xtal_clk);
615 clk_put(xtal_clk);
616
617 epll = s5p_get_pll90xx(xtal, __raw_readl(S5P_EPLL_CON),
618 __raw_readl(S5P_EPLL_CON_K));
619 mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502);
620 apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4502);
621
622 printk(KERN_INFO "S5P6440: PLL settings, A=%ld.%ldMHz, M=%ld.%ldMHz," \
623 " E=%ld.%ldMHz\n",
624 print_mhz(apll), print_mhz(mpll), print_mhz(epll));
625
626 fclk = apll / GET_DIV(clkdiv0, S5P_CLKDIV0_ARM);
627 hclk = fclk / GET_DIV(clkdiv0, S5P_CLKDIV0_HCLK);
628 pclk = hclk / GET_DIV(clkdiv0, S5P_CLKDIV0_PCLK);
629
630 if (__raw_readl(S5P_OTHERS) & S5P_OTHERS_HCLK_LOW_SEL_MPLL) {
631 /* Asynchronous mode */
632 hclk_low = mpll / GET_DIV(clkdiv3, S5P_CLKDIV3_HCLK_LOW);
633 } else {
634 /* Synchronous mode */
635 hclk_low = apll / GET_DIV(clkdiv3, S5P_CLKDIV3_HCLK_LOW);
636 }
637
638 pclk_low = hclk_low / GET_DIV(clkdiv3, S5P_CLKDIV3_PCLK_LOW);
639
640 printk(KERN_INFO "S5P6440: HCLK=%ld.%ldMHz, HCLK_LOW=%ld.%ldMHz," \
641 " PCLK=%ld.%ldMHz, PCLK_LOW=%ld.%ldMHz\n",
642 print_mhz(hclk), print_mhz(hclk_low),
643 print_mhz(pclk), print_mhz(pclk_low));
644
645 clk_fout_mpll.rate = mpll;
646 clk_fout_epll.rate = epll;
647 clk_fout_apll.rate = apll;
648
649 clk_f.rate = fclk;
650 clk_h.rate = hclk;
651 clk_p.rate = pclk;
652 clk_h_low.rate = hclk_low;
653 clk_p_low.rate = pclk_low;
654
655 for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++)
656 s3c_set_clksrc(init_parents[ptr], true);
657
658 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++)
659 s3c_set_clksrc(&clksrcs[ptr], true);
660}
661
662static struct clk *clks[] __initdata = {
663 &clk_ext,
664 &clk_mout_epll.clk,
665 &clk_mout_mpll.clk,
666 &clk_dout_mpll,
667 &clk_iis_cd_v40,
668 &clk_pcm_cd,
669 &clk_p_low,
670 &clk_h_low,
671};
672
673void __init s5p6440_register_clocks(void)
674{
675 struct clk *clkp;
676 int ret;
677 int ptr;
678
679 ret = s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
680 if (ret > 0)
681 printk(KERN_ERR "Failed to register %u clocks\n", ret);
682
683 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
684 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
685
686 clkp = init_clocks_disable;
687 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
688
689 ret = s3c24xx_register_clock(clkp);
690 if (ret < 0) {
691 printk(KERN_ERR "Failed to register clock %s (%d)\n",
692 clkp->name, ret);
693 }
694 (clkp->enable)(clkp, 0);
695 }
696
697 s3c_pwmclk_init();
698}