aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2008-10-21 09:07:02 -0400
committerBen Dooks <ben-linux@fluff.org>2008-12-15 16:54:36 -0500
commitcf18acf0e04260ff8ffa46dc245d3d2324ed41b0 (patch)
tree904cd7c007c7da92ae34ecfe5cea049ee4b08e57 /arch
parentc652d2ddb97ccdc4774e149ef998928263fd8886 (diff)
[ARM] S3C64XX: Clock support for S3C6400/S3C6410
Add the PLL clock initialisation and clock registration and include the clocks sourced via CLKDIVx for most of the on-chip peripherals. Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-s3c6410/Kconfig1
-rw-r--r--arch/arm/mach-s3c6410/cpu.c3
-rw-r--r--arch/arm/plat-s3c/include/plat/clock.h3
-rw-r--r--arch/arm/plat-s3c64xx/Kconfig6
-rw-r--r--arch/arm/plat-s3c64xx/Makefile1
-rw-r--r--arch/arm/plat-s3c64xx/clock.c2
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/regs-clock.h95
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/s3c6400.h2
-rw-r--r--arch/arm/plat-s3c64xx/s3c6400-clock.c654
9 files changed, 765 insertions, 2 deletions
diff --git a/arch/arm/mach-s3c6410/Kconfig b/arch/arm/mach-s3c6410/Kconfig
index eff8248439a4..d8377f737c40 100644
--- a/arch/arm/mach-s3c6410/Kconfig
+++ b/arch/arm/mach-s3c6410/Kconfig
@@ -10,6 +10,7 @@
10config CPU_S3C6410 10config CPU_S3C6410
11 bool 11 bool
12 select CPU_S3C6400_INIT 12 select CPU_S3C6400_INIT
13 select CPU_S3C6400_CLOCK
13 help 14 help
14 Enable S3C6410 CPU support 15 Enable S3C6410 CPU support
15 16
diff --git a/arch/arm/mach-s3c6410/cpu.c b/arch/arm/mach-s3c6410/cpu.c
index 94a6204ee55a..846f464c7673 100644
--- a/arch/arm/mach-s3c6410/cpu.c
+++ b/arch/arm/mach-s3c6410/cpu.c
@@ -35,6 +35,7 @@
35#include <plat/cpu.h> 35#include <plat/cpu.h>
36#include <plat/devs.h> 36#include <plat/devs.h>
37#include <plat/clock.h> 37#include <plat/clock.h>
38#include <plat/s3c6400.h>
38#include <plat/s3c6410.h> 39#include <plat/s3c6410.h>
39 40
40/* Initial IO mappings */ 41/* Initial IO mappings */
@@ -57,6 +58,8 @@ void __init s3c6410_init_clocks(int xtal)
57 printk(KERN_INFO "%s: initialising clocks\n", __func__); 58 printk(KERN_INFO "%s: initialising clocks\n", __func__);
58 s3c24xx_register_baseclocks(xtal); 59 s3c24xx_register_baseclocks(xtal);
59 s3c64xx_register_clocks(); 60 s3c64xx_register_clocks();
61 s3c6400_register_clocks();
62 s3c6400_setup_clocks();
60} 63}
61 64
62void __init s3c6410_init_irq(void) 65void __init s3c6410_init_irq(void)
diff --git a/arch/arm/plat-s3c/include/plat/clock.h b/arch/arm/plat-s3c/include/plat/clock.h
index 6a2c5af10009..ea1f3ffa9717 100644
--- a/arch/arm/plat-s3c/include/plat/clock.h
+++ b/arch/arm/plat-s3c/include/plat/clock.h
@@ -78,3 +78,6 @@ extern void s3c2412_setup_clocks(void);
78extern void s3c244x_setup_clocks(void); 78extern void s3c244x_setup_clocks(void);
79extern void s3c2443_setup_clocks(void); 79extern void s3c2443_setup_clocks(void);
80 80
81/* S3C64XX specific functions and clocks */
82
83extern int s3c64xx_sclk_ctrl(struct clk *clk, int enable);
diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig
index 8ed2f7b8611a..bd832ba0cf77 100644
--- a/arch/arm/plat-s3c64xx/Kconfig
+++ b/arch/arm/plat-s3c64xx/Kconfig
@@ -27,4 +27,10 @@ config CPU_S3C6400_INIT
27 Common initialisation code for the S3C6400 that is shared 27 Common initialisation code for the S3C6400 that is shared
28 by other CPUs in the series, such as the S3C6410. 28 by other CPUs in the series, such as the S3C6410.
29 29
30config CPU_S3C6400_CLOCK
31 bool
32 help
33 Common clock support code for the S3C6400 that is shared
34 by other CPUs in the series, such as the S3C6410.
35
30endif 36endif
diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile
index 15f717fd1483..9e055d48661d 100644
--- a/arch/arm/plat-s3c64xx/Makefile
+++ b/arch/arm/plat-s3c64xx/Makefile
@@ -20,3 +20,4 @@ obj-y += clock.o
20# CPU support 20# CPU support
21 21
22obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o 22obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o
23obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o
diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c
index e7c2994e2d32..2d2e83a036c4 100644
--- a/arch/arm/plat-s3c64xx/clock.c
+++ b/arch/arm/plat-s3c64xx/clock.c
@@ -67,7 +67,7 @@ static int s3c64xx_hclk_ctrl(struct clk *clk, int enable)
67 return s3c64xx_gate(S3C_HCLK_GATE, clk, enable); 67 return s3c64xx_gate(S3C_HCLK_GATE, clk, enable);
68} 68}
69 69
70static int s3c6xx_sclk_ctrl(struct clk *clk, int enable) 70int s3c64xx_sclk_ctrl(struct clk *clk, int enable)
71{ 71{
72 return s3c64xx_gate(S3C_SCLK_GATE, clk, enable); 72 return s3c64xx_gate(S3C_SCLK_GATE, clk, enable);
73} 73}
diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h
index 462558ec1af0..78938a5e1d20 100644
--- a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h
+++ b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h
@@ -33,6 +33,59 @@
33#define S3C_PCLK_GATE S3C_CLKREG(0x34) 33#define S3C_PCLK_GATE S3C_CLKREG(0x34)
34#define S3C_SCLK_GATE S3C_CLKREG(0x38) 34#define S3C_SCLK_GATE S3C_CLKREG(0x38)
35 35
36/* CLKDIV0 */
37#define S3C6400_CLKDIV0_MFC_MASK (0xf << 28)
38#define S3C6400_CLKDIV0_MFC_SHIFT (28)
39#define S3C6400_CLKDIV0_JPEG_MASK (0xf << 24)
40#define S3C6400_CLKDIV0_JPEG_SHIFT (24)
41#define S3C6400_CLKDIV0_CAM_MASK (0xf << 20)
42#define S3C6400_CLKDIV0_CAM_SHIFT (20)
43#define S3C6400_CLKDIV0_SECURITY_MASK (0x3 << 18)
44#define S3C6400_CLKDIV0_SECURITY_SHIFT (18)
45#define S3C6400_CLKDIV0_PCLK_MASK (0xf << 12)
46#define S3C6400_CLKDIV0_PCLK_SHIFT (12)
47#define S3C6400_CLKDIV0_HCLK2_MASK (0x7 << 9)
48#define S3C6400_CLKDIV0_HCLK2_SHIFT (9)
49#define S3C6400_CLKDIV0_HCLK_MASK (0x1 << 8)
50#define S3C6400_CLKDIV0_HCLK_SHIFT (8)
51#define S3C6400_CLKDIV0_MPLL_MASK (0x1 << 4)
52#define S3C6400_CLKDIV0_MPLL_SHIFT (4)
53#define S3C6400_CLKDIV0_ARM_MASK (0x3 << 0)
54#define S3C6410_CLKDIV0_ARM_MASK (0x7 << 0)
55#define S3C6400_CLKDIV0_ARM_SHIFT (0)
56
57/* CLKDIV1 */
58#define S3C6410_CLKDIV1_FIMC_MASK (0xf << 24)
59#define S3C6410_CLKDIV1_FIMC_SHIFT (24)
60#define S3C6400_CLKDIV1_UHOST_MASK (0xf << 20)
61#define S3C6400_CLKDIV1_UHOST_SHIFT (20)
62#define S3C6400_CLKDIV1_SCALER_MASK (0xf << 16)
63#define S3C6400_CLKDIV1_SCALER_SHIFT (16)
64#define S3C6400_CLKDIV1_LCD_MASK (0xf << 12)
65#define S3C6400_CLKDIV1_LCD_SHIFT (12)
66#define S3C6400_CLKDIV1_MMC2_MASK (0xf << 8)
67#define S3C6400_CLKDIV1_MMC2_SHIFT (8)
68#define S3C6400_CLKDIV1_MMC1_MASK (0xf << 4)
69#define S3C6400_CLKDIV1_MMC1_SHIFT (4)
70#define S3C6400_CLKDIV1_MMC0_MASK (0xf << 0)
71#define S3C6400_CLKDIV1_MMC0_SHIFT (0)
72
73/* CLKDIV2 */
74#define S3C6410_CLKDIV2_AUDIO2_MASK (0xf << 24)
75#define S3C6410_CLKDIV2_AUDIO2_SHIFT (24)
76#define S3C6400_CLKDIV2_IRDA_MASK (0xf << 20)
77#define S3C6400_CLKDIV2_IRDA_SHIFT (20)
78#define S3C6400_CLKDIV2_UART_MASK (0xf << 16)
79#define S3C6400_CLKDIV2_UART_SHIFT (16)
80#define S3C6400_CLKDIV2_AUDIO1_MASK (0xf << 12)
81#define S3C6400_CLKDIV2_AUDIO1_SHIFT (12)
82#define S3C6400_CLKDIV2_AUDIO0_MASK (0xf << 8)
83#define S3C6400_CLKDIV2_AUDIO0_SHIFT (8)
84#define S3C6400_CLKDIV2_SPI1_MASK (0xf << 4)
85#define S3C6400_CLKDIV2_SPI1_SHIFT (4)
86#define S3C6400_CLKDIV2_SPI0_MASK (0xf << 0)
87#define S3C6400_CLKDIV2_SPI0_SHIFT (0)
88
36/* HCLK GATE Registers */ 89/* HCLK GATE Registers */
37#define S3C_CLKCON_HCLK_BUS (1<<30) 90#define S3C_CLKCON_HCLK_BUS (1<<30)
38#define S3C_CLKCON_HCLK_SECUR (1<<29) 91#define S3C_CLKCON_HCLK_SECUR (1<<29)
@@ -128,4 +181,44 @@
128#define S3C_CLKCON_SCLK_CAM (1<<2) 181#define S3C_CLKCON_SCLK_CAM (1<<2)
129#define S3C_CLKCON_SCLK_JPEG (1<<1) 182#define S3C_CLKCON_SCLK_JPEG (1<<1)
130 183
131#endif /* __PLAT_REGS_CLOCK_H */ 184/* CLKSRC */
185
186#define S3C6400_CLKSRC_APLL_MOUT (1 << 0)
187#define S3C6400_CLKSRC_MPLL_MOUT (1 << 1)
188#define S3C6400_CLKSRC_EPLL_MOUT (1 << 2)
189#define S3C6400_CLKSRC_APLL_MOUT_SHIFT (0)
190#define S3C6400_CLKSRC_MPLL_MOUT_SHIFT (1)
191#define S3C6400_CLKSRC_EPLL_MOUT_SHIFT (2)
192#define S3C6400_CLKSRC_MFC (1 << 4)
193
194#define S3C6410_CLKSRC_TV27_MASK (0x1 << 31)
195#define S3C6410_CLKSRC_TV27_SHIFT (31)
196#define S3C6410_CLKSRC_DAC27_MASK (0x1 << 30)
197#define S3C6410_CLKSRC_DAC27_SHIFT (30)
198#define S3C6400_CLKSRC_SCALER_MASK (0x3 << 28)
199#define S3C6400_CLKSRC_SCALER_SHIFT (28)
200#define S3C6400_CLKSRC_LCD_MASK (0x3 << 26)
201#define S3C6400_CLKSRC_LCD_SHIFT (26)
202#define S3C6400_CLKSRC_IRDA_MASK (0x3 << 24)
203#define S3C6400_CLKSRC_IRDA_SHIFT (24)
204#define S3C6400_CLKSRC_MMC2_MASK (0x3 << 22)
205#define S3C6400_CLKSRC_MMC2_SHIFT (22)
206#define S3C6400_CLKSRC_MMC1_MASK (0x3 << 20)
207#define S3C6400_CLKSRC_MMC1_SHIFT (20)
208#define S3C6400_CLKSRC_MMC0_MASK (0xf << 1)
209#define S3C6400_CLKSRC_MMC0_SHIFT (1)
210#define S3C6400_CLKSRC_SPI1_MASK (0x3 << 16)
211#define S3C6400_CLKSRC_SPI1_SHIFT (16)
212#define S3C6400_CLKSRC_SPI0_MASK (0x3 << 14)
213#define S3C6400_CLKSRC_SPI0_SHIFT (14)
214#define S3C6400_CLKSRC_UART_MASK (0x1 << 13)
215#define S3C6400_CLKSRC_UART_SHIFT (13)
216#define S3C6400_CLKSRC_AUDIO1_MASK (0x7 << 10)
217#define S3C6400_CLKSRC_AUDIO1_SHIFT (10)
218#define S3C6400_CLKSRC_AUDIO0_MASK (0x7 << 7)
219#define S3C6400_CLKSRC_AUDIO0_SHIFT (7)
220#define S3C6400_CLKSRC_UHOST_MASK (0x3 << 5)
221#define S3C6400_CLKSRC_UHOST_SHIFT (5)
222
223
224#endif /* _PLAT_REGS_CLOCK_H */
diff --git a/arch/arm/plat-s3c64xx/include/plat/s3c6400.h b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
index 142bb3d18cdc..571eaa2e54f1 100644
--- a/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
+++ b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h
@@ -15,6 +15,8 @@
15/* Common init code for S3C6400 related SoCs */ 15/* Common init code for S3C6400 related SoCs */
16 16
17extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); 17extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
18extern void s3c6400_register_clocks(void);
19extern void s3c6400_setup_clocks(void);
18 20
19#ifdef CONFIG_CPU_S3C6400 21#ifdef CONFIG_CPU_S3C6400
20 22
diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c
new file mode 100644
index 000000000000..ff5d907f2fc4
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c
@@ -0,0 +1,654 @@
1/* linux/arch/arm/plat-s3c64xx/s3c6400-clock.c
2 *
3 * Copyright 2008 Openmoko, Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * S3C6400 based common clock support
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/init.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/list.h>
19#include <linux/errno.h>
20#include <linux/err.h>
21#include <linux/clk.h>
22#include <linux/sysdev.h>
23#include <linux/io.h>
24
25#include <mach/hardware.h>
26#include <mach/map.h>
27
28#include <plat/cpu-freq.h>
29
30#include <plat/regs-clock.h>
31#include <plat/clock.h>
32#include <plat/cpu.h>
33#include <plat/pll.h>
34
35/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
36 * ext_xtal_mux for want of an actual name from the manual.
37*/
38
39struct clk clk_ext_xtal_mux = {
40 .name = "ext_xtal",
41 .id = -1,
42};
43
44#define clk_fin_apll clk_ext_xtal_mux
45#define clk_fin_mpll clk_ext_xtal_mux
46#define clk_fin_epll clk_ext_xtal_mux
47
48#define clk_fout_mpll clk_mpll
49
50struct clk_sources {
51 unsigned int nr_sources;
52 struct clk **sources;
53};
54
55struct clksrc_clk {
56 struct clk clk;
57 unsigned int mask;
58 unsigned int shift;
59
60 struct clk_sources *sources;
61
62 unsigned int divider_shift;
63 void __iomem *reg_divider;
64};
65
66struct clk clk_fout_apll = {
67 .name = "fout_apll",
68 .id = -1,
69};
70
71static struct clk *clk_src_apll_list[] = {
72 [0] = &clk_fin_apll,
73 [1] = &clk_fout_apll,
74};
75
76static struct clk_sources clk_src_apll = {
77 .sources = clk_src_apll_list,
78 .nr_sources = ARRAY_SIZE(clk_src_apll_list),
79};
80
81struct clksrc_clk clk_mout_apll = {
82 .clk = {
83 .name = "mout_apll",
84 .id = -1,
85 },
86 .shift = S3C6400_CLKSRC_APLL_MOUT_SHIFT,
87 .mask = S3C6400_CLKSRC_APLL_MOUT,
88 .sources = &clk_src_apll,
89};
90
91struct clk clk_fout_epll = {
92 .name = "fout_epll",
93 .id = -1,
94};
95
96static struct clk *clk_src_epll_list[] = {
97 [0] = &clk_fin_epll,
98 [1] = &clk_fout_epll,
99};
100
101static struct clk_sources clk_src_epll = {
102 .sources = clk_src_epll_list,
103 .nr_sources = ARRAY_SIZE(clk_src_epll_list),
104};
105
106struct clksrc_clk clk_mout_epll = {
107 .clk = {
108 .name = "mout_epll",
109 .id = -1,
110 },
111 .shift = S3C6400_CLKSRC_EPLL_MOUT_SHIFT,
112 .mask = S3C6400_CLKSRC_EPLL_MOUT,
113 .sources = &clk_src_epll,
114};
115
116static struct clk *clk_src_mpll_list[] = {
117 [0] = &clk_fin_mpll,
118 [1] = &clk_fout_mpll,
119};
120
121static struct clk_sources clk_src_mpll = {
122 .sources = clk_src_mpll_list,
123 .nr_sources = ARRAY_SIZE(clk_src_mpll_list),
124};
125
126struct clksrc_clk clk_mout_mpll = {
127 .clk = {
128 .name = "mout_mpll",
129 .id = -1,
130 },
131 .shift = S3C6400_CLKSRC_MPLL_MOUT_SHIFT,
132 .mask = S3C6400_CLKSRC_MPLL_MOUT,
133 .sources = &clk_src_mpll,
134};
135
136static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk)
137{
138 unsigned long rate = clk_get_rate(clk->parent);
139
140 printk(KERN_INFO "%s: parent is %ld\n", __func__, rate);
141
142 if (__raw_readl(S3C_CLK_DIV0) & S3C6400_CLKDIV0_MPLL_MASK)
143 rate /= 2;
144
145 return rate;
146}
147
148struct clk clk_dout_mpll = {
149 .name = "dout_mpll",
150 .id = -1,
151 .parent = &clk_mout_mpll.clk,
152 .get_rate = s3c64xx_clk_doutmpll_get_rate,
153};
154
155static struct clk *clkset_spi_mmc_list[] = {
156 &clk_mout_epll.clk,
157 &clk_dout_mpll,
158 &clk_fin_epll,
159 &clk_27m,
160};
161
162static struct clk_sources clkset_spi_mmc = {
163 .sources = clkset_spi_mmc_list,
164 .nr_sources = ARRAY_SIZE(clkset_spi_mmc_list),
165};
166
167static struct clk *clkset_irda_list[] = {
168 &clk_mout_epll.clk,
169 &clk_dout_mpll,
170 NULL,
171 &clk_27m,
172};
173
174static struct clk_sources clkset_irda = {
175 .sources = clkset_irda_list,
176 .nr_sources = ARRAY_SIZE(clkset_irda_list),
177};
178
179static struct clk *clkset_uart_list[] = {
180 &clk_mout_epll.clk,
181 &clk_dout_mpll,
182 NULL,
183 NULL
184};
185
186static struct clk_sources clkset_uart = {
187 .sources = clkset_uart_list,
188 .nr_sources = ARRAY_SIZE(clkset_uart_list),
189};
190
191static struct clk *clkset_uhost_list[] = {
192 &clk_mout_epll.clk,
193 &clk_dout_mpll,
194 &clk_fin_epll,
195 &clk_48m,
196};
197
198static struct clk_sources clkset_uhost = {
199 .sources = clkset_uhost_list,
200 .nr_sources = ARRAY_SIZE(clkset_uhost_list),
201};
202
203
204/* The peripheral clocks are all controlled via clocksource followed
205 * by an optional divider and gate stage. We currently roll this into
206 * one clock which hides the intermediate clock from the mux.
207 *
208 * Note, the JPEG clock can only be an even divider...
209 *
210 * The scaler and LCD clocks depend on the S3C64XX version, and also
211 * have a common parent divisor so are not included here.
212 */
213
214static inline struct clksrc_clk *to_clksrc(struct clk *clk)
215{
216 return container_of(clk, struct clksrc_clk, clk);
217}
218
219static unsigned long s3c64xx_getrate_clksrc(struct clk *clk)
220{
221 struct clksrc_clk *sclk = to_clksrc(clk);
222 unsigned long rate = clk_get_rate(clk->parent);
223 u32 clkdiv = __raw_readl(sclk->reg_divider);
224
225 clkdiv >>= sclk->divider_shift;
226 clkdiv &= 0xf;
227 clkdiv++;
228
229 rate /= clkdiv;
230 return rate;
231}
232
233static int s3c64xx_setrate_clksrc(struct clk *clk, unsigned long rate)
234{
235 struct clksrc_clk *sclk = to_clksrc(clk);
236 void __iomem *reg = sclk->reg_divider;
237 unsigned int div;
238 u32 val;
239
240 rate = clk_round_rate(clk, rate);
241 div = clk_get_rate(clk->parent) / rate;
242
243 val = __raw_readl(reg);
244 val &= ~sclk->mask;
245 val |= (rate - 1) << sclk->shift;
246 __raw_writel(val, reg);
247
248 return 0;
249}
250
251static int s3c64xx_setparent_clksrc(struct clk *clk, struct clk *parent)
252{
253 struct clksrc_clk *sclk = to_clksrc(clk);
254 struct clk_sources *srcs = sclk->sources;
255 u32 clksrc = __raw_readl(S3C_CLK_SRC);
256 int src_nr = -1;
257 int ptr;
258
259 for (ptr = 0; ptr < srcs->nr_sources; ptr++)
260 if (srcs->sources[ptr] == parent) {
261 src_nr = ptr;
262 break;
263 }
264
265 if (src_nr >= 0) {
266 clksrc &= ~sclk->mask;
267 clksrc |= src_nr << sclk->shift;
268
269 __raw_writel(clksrc, S3C_CLK_SRC);
270 return 0;
271 }
272
273 return -EINVAL;
274}
275
276static unsigned long s3c64xx_roundrate_clksrc(struct clk *clk,
277 unsigned long rate)
278{
279 unsigned long parent_rate = clk_get_rate(clk->parent);
280 int div;
281
282 if (rate > parent_rate)
283 rate = parent_rate;
284 else {
285 div = rate / parent_rate;
286
287 if (div == 0)
288 div = 1;
289 if (div > 16)
290 div = 16;
291
292 rate = parent_rate / div;
293 }
294
295 return rate;
296}
297
298static struct clksrc_clk clk_mmc0 = {
299 .clk = {
300 .name = "mmc_bus",
301 .id = 0,
302 .ctrlbit = S3C_CLKCON_SCLK_MMC0,
303 .enable = s3c64xx_sclk_ctrl,
304 .set_parent = s3c64xx_setparent_clksrc,
305 .get_rate = s3c64xx_getrate_clksrc,
306 .set_rate = s3c64xx_setrate_clksrc,
307 .round_rate = s3c64xx_roundrate_clksrc,
308 },
309 .shift = S3C6400_CLKSRC_MMC0_SHIFT,
310 .mask = S3C6400_CLKSRC_MMC0_MASK,
311 .sources = &clkset_spi_mmc,
312 .divider_shift = S3C6400_CLKDIV1_MMC0_SHIFT,
313 .reg_divider = S3C_CLK_DIV1,
314};
315
316static struct clksrc_clk clk_mmc1 = {
317 .clk = {
318 .name = "mmc_bus",
319 .id = 1,
320 .ctrlbit = S3C_CLKCON_SCLK_MMC1,
321 .enable = s3c64xx_sclk_ctrl,
322 .get_rate = s3c64xx_getrate_clksrc,
323 .set_rate = s3c64xx_setrate_clksrc,
324 .set_parent = s3c64xx_setparent_clksrc,
325 .round_rate = s3c64xx_roundrate_clksrc,
326 },
327 .shift = S3C6400_CLKSRC_MMC1_SHIFT,
328 .mask = S3C6400_CLKSRC_MMC1_MASK,
329 .sources = &clkset_spi_mmc,
330 .divider_shift = S3C6400_CLKDIV1_MMC1_SHIFT,
331 .reg_divider = S3C_CLK_DIV1,
332};
333
334static struct clksrc_clk clk_mmc2 = {
335 .clk = {
336 .name = "mmc_bus",
337 .id = 2,
338 .ctrlbit = S3C_CLKCON_SCLK_MMC2,
339 .enable = s3c64xx_sclk_ctrl,
340 .get_rate = s3c64xx_getrate_clksrc,
341 .set_rate = s3c64xx_setrate_clksrc,
342 .set_parent = s3c64xx_setparent_clksrc,
343 .round_rate = s3c64xx_roundrate_clksrc,
344 },
345 .shift = S3C6400_CLKSRC_MMC2_SHIFT,
346 .mask = S3C6400_CLKSRC_MMC2_MASK,
347 .sources = &clkset_spi_mmc,
348 .divider_shift = S3C6400_CLKDIV1_MMC2_SHIFT,
349 .reg_divider = S3C_CLK_DIV1,
350};
351
352static struct clksrc_clk clk_usbhost = {
353 .clk = {
354 .name = "usb-host-bus",
355 .id = -1,
356 .ctrlbit = S3C_CLKCON_SCLK_UHOST,
357 .enable = s3c64xx_sclk_ctrl,
358 .set_parent = s3c64xx_setparent_clksrc,
359 .get_rate = s3c64xx_getrate_clksrc,
360 .set_rate = s3c64xx_setrate_clksrc,
361 .round_rate = s3c64xx_roundrate_clksrc,
362 },
363 .shift = S3C6400_CLKSRC_UHOST_SHIFT,
364 .mask = S3C6400_CLKSRC_UHOST_MASK,
365 .sources = &clkset_uhost,
366 .divider_shift = S3C6400_CLKDIV1_UHOST_SHIFT,
367 .reg_divider = S3C_CLK_DIV1,
368};
369
370static struct clksrc_clk clk_uart_uclk1 = {
371 .clk = {
372 .name = "uclk1",
373 .id = -1,
374 .ctrlbit = S3C_CLKCON_SCLK_UART,
375 .enable = s3c64xx_sclk_ctrl,
376 .set_parent = s3c64xx_setparent_clksrc,
377 .get_rate = s3c64xx_getrate_clksrc,
378 .set_rate = s3c64xx_setrate_clksrc,
379 .round_rate = s3c64xx_roundrate_clksrc,
380 },
381 .shift = S3C6400_CLKSRC_UART_SHIFT,
382 .mask = S3C6400_CLKSRC_UART_MASK,
383 .sources = &clkset_uart,
384 .divider_shift = S3C6400_CLKDIV2_UART_SHIFT,
385 .reg_divider = S3C_CLK_DIV2,
386};
387
388/* Where does UCLK0 come from? */
389
390static struct clksrc_clk clk_spi0 = {
391 .clk = {
392 .name = "spi-bus",
393 .id = 0,
394 .ctrlbit = S3C_CLKCON_SCLK_SPI0,
395 .enable = s3c64xx_sclk_ctrl,
396 .set_parent = s3c64xx_setparent_clksrc,
397 .get_rate = s3c64xx_getrate_clksrc,
398 .set_rate = s3c64xx_setrate_clksrc,
399 .round_rate = s3c64xx_roundrate_clksrc,
400 },
401 .shift = S3C6400_CLKSRC_SPI0_SHIFT,
402 .mask = S3C6400_CLKSRC_SPI0_MASK,
403 .sources = &clkset_spi_mmc,
404 .divider_shift = S3C6400_CLKDIV2_SPI0_SHIFT,
405 .reg_divider = S3C_CLK_DIV2,
406};
407
408static struct clksrc_clk clk_spi1 = {
409 .clk = {
410 .name = "spi-bus",
411 .id = 1,
412 .ctrlbit = S3C_CLKCON_SCLK_SPI1,
413 .enable = s3c64xx_sclk_ctrl,
414 .set_parent = s3c64xx_setparent_clksrc,
415 .get_rate = s3c64xx_getrate_clksrc,
416 .set_rate = s3c64xx_setrate_clksrc,
417 .round_rate = s3c64xx_roundrate_clksrc,
418 },
419 .shift = S3C6400_CLKSRC_SPI1_SHIFT,
420 .mask = S3C6400_CLKSRC_SPI1_MASK,
421 .sources = &clkset_spi_mmc,
422 .divider_shift = S3C6400_CLKDIV2_SPI1_SHIFT,
423 .reg_divider = S3C_CLK_DIV2,
424};
425
426static struct clk clk_iis_cd0 = {
427 .name = "iis_cdclk0",
428 .id = -1,
429};
430
431static struct clk clk_iis_cd1 = {
432 .name = "iis_cdclk1",
433 .id = -1,
434};
435
436static struct clk clk_pcm_cd = {
437 .name = "pcm_cdclk",
438 .id = -1,
439};
440
441static struct clk *clkset_audio0_list[] = {
442 [0] = &clk_mout_epll.clk,
443 [1] = &clk_dout_mpll,
444 [2] = &clk_fin_epll,
445 [3] = &clk_iis_cd0,
446 [4] = &clk_pcm_cd,
447};
448
449static struct clk_sources clkset_audio0 = {
450 .sources = clkset_audio0_list,
451 .nr_sources = ARRAY_SIZE(clkset_audio0_list),
452};
453
454static struct clksrc_clk clk_audio0 = {
455 .clk = {
456 .name = "audio-bus",
457 .id = 0,
458 .ctrlbit = S3C_CLKCON_SCLK_AUDIO0,
459 .enable = s3c64xx_sclk_ctrl,
460 .set_parent = s3c64xx_setparent_clksrc,
461 .get_rate = s3c64xx_getrate_clksrc,
462 .set_rate = s3c64xx_setrate_clksrc,
463 .round_rate = s3c64xx_roundrate_clksrc,
464 },
465 .shift = S3C6400_CLKSRC_AUDIO0_SHIFT,
466 .mask = S3C6400_CLKSRC_AUDIO0_MASK,
467 .sources = &clkset_audio0,
468 .divider_shift = S3C6400_CLKDIV2_AUDIO0_SHIFT,
469 .reg_divider = S3C_CLK_DIV2,
470};
471
472static struct clk *clkset_audio1_list[] = {
473 [0] = &clk_mout_epll.clk,
474 [1] = &clk_dout_mpll,
475 [2] = &clk_fin_epll,
476 [3] = &clk_iis_cd1,
477 [4] = &clk_pcm_cd,
478};
479
480static struct clk_sources clkset_audio1 = {
481 .sources = clkset_audio1_list,
482 .nr_sources = ARRAY_SIZE(clkset_audio1_list),
483};
484
485static struct clksrc_clk clk_audio1 = {
486 .clk = {
487 .name = "audio-bus",
488 .id = 1,
489 .ctrlbit = S3C_CLKCON_SCLK_AUDIO1,
490 .enable = s3c64xx_sclk_ctrl,
491 .set_parent = s3c64xx_setparent_clksrc,
492 .get_rate = s3c64xx_getrate_clksrc,
493 .set_rate = s3c64xx_setrate_clksrc,
494 .round_rate = s3c64xx_roundrate_clksrc,
495 },
496 .shift = S3C6400_CLKSRC_AUDIO1_SHIFT,
497 .mask = S3C6400_CLKSRC_AUDIO1_MASK,
498 .sources = &clkset_audio1,
499 .divider_shift = S3C6400_CLKDIV2_AUDIO1_SHIFT,
500 .reg_divider = S3C_CLK_DIV2,
501};
502
503static struct clksrc_clk clk_irda = {
504 .clk = {
505 .name = "irda-bus",
506 .id = 0,
507 .ctrlbit = S3C_CLKCON_SCLK_IRDA,
508 .enable = s3c64xx_sclk_ctrl,
509 .set_parent = s3c64xx_setparent_clksrc,
510 .get_rate = s3c64xx_getrate_clksrc,
511 .set_rate = s3c64xx_setrate_clksrc,
512 .round_rate = s3c64xx_roundrate_clksrc,
513 },
514 .shift = S3C6400_CLKSRC_IRDA_SHIFT,
515 .mask = S3C6400_CLKSRC_IRDA_MASK,
516 .sources = &clkset_irda,
517 .divider_shift = S3C6400_CLKDIV2_IRDA_SHIFT,
518 .reg_divider = S3C_CLK_DIV2,
519};
520
521/* Clock initialisation code */
522
523static struct clksrc_clk *init_parents[] = {
524 &clk_mout_apll,
525 &clk_mout_epll,
526 &clk_mout_mpll,
527 &clk_mmc0,
528 &clk_mmc1,
529 &clk_mmc2,
530 &clk_usbhost,
531 &clk_uart_uclk1,
532 &clk_spi0,
533 &clk_spi1,
534 &clk_audio0,
535 &clk_audio1,
536 &clk_irda,
537};
538
539static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk)
540{
541 struct clk_sources *srcs = clk->sources;
542 u32 clksrc = __raw_readl(S3C_CLK_SRC);
543
544 clksrc &= clk->mask;
545 clksrc >>= clk->shift;
546
547 if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) {
548 printk(KERN_ERR "%s: bad source %d\n",
549 clk->clk.name, clksrc);
550 return;
551 }
552
553 clk->clk.parent = srcs->sources[clksrc];
554
555 printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n",
556 clk->clk.name, clk->clk.parent->name, clksrc,
557 clk_get_rate(&clk->clk));
558}
559
560#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
561
562void __init_or_cpufreq s3c6400_setup_clocks(void)
563{
564 struct clk *xtal_clk;
565 unsigned long xtal;
566 unsigned long fclk;
567 unsigned long hclk;
568 unsigned long hclk2;
569 unsigned long pclk;
570 unsigned long epll;
571 unsigned long apll;
572 unsigned long mpll;
573 unsigned int ptr;
574 u32 clkdiv0;
575
576 printk(KERN_INFO "%s: registering clocks\n", __func__);
577
578 clkdiv0 = __raw_readl(S3C_CLK_DIV0);
579 printk(KERN_INFO "%s: clkdiv0 = %08x\n", __func__, clkdiv0);
580
581 xtal_clk = clk_get(NULL, "xtal");
582 BUG_ON(IS_ERR(xtal_clk));
583
584 xtal = clk_get_rate(xtal_clk);
585 clk_put(xtal_clk);
586
587 printk(KERN_INFO "%s: xtal is %ld\n", __func__, xtal);
588
589 epll = s3c6400_get_epll(xtal);
590 mpll = s3c6400_get_pll(xtal, __raw_readl(S3C_MPLL_CON));
591 apll = s3c6400_get_pll(xtal, __raw_readl(S3C_APLL_CON));
592
593 fclk = mpll;
594
595 printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n",
596 apll, mpll, epll);
597
598 hclk2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2);
599 hclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK);
600 pclk = hclk2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK);
601
602 printk(KERN_INFO "S3C64XX: HCLK2=%ld, HCLK=%ld, PCLK=%ld\n",
603 hclk2, hclk, pclk);
604
605 clk_fout_mpll.rate = mpll;
606 clk_fout_epll.rate = epll;
607 clk_fout_apll.rate = apll;
608
609 clk_h.rate = hclk;
610 clk_p.rate = pclk;
611 clk_f.rate = fclk;
612
613 for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++)
614 s3c6400_set_clksrc(init_parents[ptr]);
615}
616
617static struct clk *clks[] __initdata = {
618 &clk_ext_xtal_mux,
619 &clk_iis_cd0,
620 &clk_iis_cd1,
621 &clk_pcm_cd,
622 &clk_mout_epll.clk,
623 &clk_mout_mpll.clk,
624 &clk_dout_mpll,
625 &clk_mmc0.clk,
626 &clk_mmc1.clk,
627 &clk_mmc2.clk,
628 &clk_usbhost.clk,
629 &clk_uart_uclk1.clk,
630 &clk_spi0.clk,
631 &clk_spi1.clk,
632 &clk_audio0.clk,
633 &clk_audio1.clk,
634 &clk_irda.clk,
635};
636
637void __init s3c6400_register_clocks(void)
638{
639 struct clk *clkp;
640 int ret;
641 int ptr;
642
643 for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
644 clkp = clks[ptr];
645 ret = s3c24xx_register_clock(clkp);
646 if (ret < 0) {
647 printk(KERN_ERR "Failed to register clock %s (%d)\n",
648 clkp->name, ret);
649 }
650 }
651
652 clk_mpll.parent = &clk_mout_mpll.clk;
653 clk_epll.parent = &clk_mout_epll.clk;
654}