1 /* linux/arch/arm/plat-s3c24xx/s3c2443-clock.c
3 * Copyright (c) 2007, 2010 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
6 * S3C2443 Clock control suport - common code
9 #include <linux/init.h>
10 #include <linux/clk.h>
13 #include <mach/regs-s3c2443-clock.h>
15 #include <plat/s3c2443.h>
16 #include <plat/clock.h>
17 #include <plat/clock-clksrc.h>
20 #include <plat/cpu-freq.h>
23 static int s3c2443_gate(void __iomem
*reg
, struct clk
*clk
, int enable
)
25 u32 ctrlbit
= clk
->ctrlbit
;
26 u32 con
= __raw_readl(reg
);
33 __raw_writel(con
, reg
);
37 int s3c2443_clkcon_enable_h(struct clk
*clk
, int enable
)
39 return s3c2443_gate(S3C2443_HCLKCON
, clk
, enable
);
42 int s3c2443_clkcon_enable_p(struct clk
*clk
, int enable
)
44 return s3c2443_gate(S3C2443_PCLKCON
, clk
, enable
);
47 int s3c2443_clkcon_enable_s(struct clk
*clk
, int enable
)
49 return s3c2443_gate(S3C2443_SCLKCON
, clk
, enable
);
52 /* mpllref is a direct descendant of clk_xtal by default, but it is not
53 * elided as the EPLL can be either sourced by the XTAL or EXTCLK and as
54 * such directly equating the two source clocks is impossible.
56 struct clk clk_mpllref
= {
62 static struct clk
*clk_epllref_sources
[] = {
69 struct clksrc_clk clk_epllref
= {
74 .sources
= &(struct clksrc_sources
) {
75 .sources
= clk_epllref_sources
,
76 .nr_sources
= ARRAY_SIZE(clk_epllref_sources
),
78 .reg_src
= { .reg
= S3C2443_CLKSRC
, .size
= 2, .shift
= 7 },
83 * this is sourced from either the EPLL or the EPLLref clock
86 static struct clk
*clk_sysclk_sources
[] = {
87 [0] = &clk_epllref
.clk
,
91 struct clksrc_clk clk_esysclk
= {
97 .sources
= &(struct clksrc_sources
) {
98 .sources
= clk_sysclk_sources
,
99 .nr_sources
= ARRAY_SIZE(clk_sysclk_sources
),
101 .reg_src
= { .reg
= S3C2443_CLKSRC
, .size
= 1, .shift
= 6 },
104 static unsigned long s3c2443_getrate_mdivclk(struct clk
*clk
)
106 unsigned long parent_rate
= clk_get_rate(clk
->parent
);
107 unsigned long div
= __raw_readl(S3C2443_CLKDIV0
);
109 div
&= S3C2443_CLKDIV0_EXTDIV_MASK
;
110 div
>>= (S3C2443_CLKDIV0_EXTDIV_SHIFT
-1); /* x2 */
112 return parent_rate
/ (div
+ 1);
115 static struct clk clk_mdivclk
= {
117 .parent
= &clk_mpllref
,
119 .ops
= &(struct clk_ops
) {
120 .get_rate
= s3c2443_getrate_mdivclk
,
124 static struct clk
*clk_msysclk_sources
[] = {
131 struct clksrc_clk clk_msysclk
= {
137 .sources
= &(struct clksrc_sources
) {
138 .sources
= clk_msysclk_sources
,
139 .nr_sources
= ARRAY_SIZE(clk_msysclk_sources
),
141 .reg_src
= { .reg
= S3C2443_CLKSRC
, .size
= 2, .shift
= 3 },
146 * this divides the msysclk down to pass to h/p/etc.
149 static unsigned long s3c2443_prediv_getrate(struct clk
*clk
)
151 unsigned long rate
= clk_get_rate(clk
->parent
);
152 unsigned long clkdiv0
= __raw_readl(S3C2443_CLKDIV0
);
154 clkdiv0
&= S3C2443_CLKDIV0_PREDIV_MASK
;
155 clkdiv0
>>= S3C2443_CLKDIV0_PREDIV_SHIFT
;
157 return rate
/ (clkdiv0
+ 1);
160 static struct clk clk_prediv
= {
163 .parent
= &clk_msysclk
.clk
,
164 .ops
= &(struct clk_ops
) {
165 .get_rate
= s3c2443_prediv_getrate
,
171 * usb host bus-clock, usually 48MHz to provide USB bus clock timing
174 static struct clksrc_clk clk_usb_bus_host
= {
176 .name
= "usb-bus-host-parent",
178 .parent
= &clk_esysclk
.clk
,
179 .ctrlbit
= S3C2443_SCLKCON_USBHOST
,
180 .enable
= s3c2443_clkcon_enable_s
,
182 .reg_div
= { .reg
= S3C2443_CLKDIV1
, .size
= 2, .shift
= 4 },
185 /* common clksrc clocks */
187 static struct clksrc_clk clksrc_clks
[] = {
189 /* ART baud-rate clock sourced from esysclk via a divisor */
193 .parent
= &clk_esysclk
.clk
,
195 .reg_div
= { .reg
= S3C2443_CLKDIV1
, .size
= 4, .shift
= 8 },
197 /* camera interface bus-clock, divided down from esysclk */
199 .name
= "camif-upll", /* same as 2440 name */
201 .parent
= &clk_esysclk
.clk
,
202 .ctrlbit
= S3C2443_SCLKCON_CAMCLK
,
203 .enable
= s3c2443_clkcon_enable_s
,
205 .reg_div
= { .reg
= S3C2443_CLKDIV1
, .size
= 4, .shift
= 26 },
208 .name
= "display-if",
210 .parent
= &clk_esysclk
.clk
,
211 .ctrlbit
= S3C2443_SCLKCON_DISPCLK
,
212 .enable
= s3c2443_clkcon_enable_s
,
214 .reg_div
= { .reg
= S3C2443_CLKDIV1
, .size
= 8, .shift
= 16 },
219 static struct clk init_clocks_off
[] = {
224 .enable
= s3c2443_clkcon_enable_p
,
225 .ctrlbit
= S3C2443_PCLKCON_ADC
,
230 .enable
= s3c2443_clkcon_enable_p
,
231 .ctrlbit
= S3C2443_PCLKCON_IIC
,
235 static struct clk init_clocks
[] = {
240 .enable
= s3c2443_clkcon_enable_h
,
241 .ctrlbit
= S3C2443_HCLKCON_DMA0
,
246 .enable
= s3c2443_clkcon_enable_h
,
247 .ctrlbit
= S3C2443_HCLKCON_DMA1
,
252 .enable
= s3c2443_clkcon_enable_h
,
253 .ctrlbit
= S3C2443_HCLKCON_DMA2
,
258 .enable
= s3c2443_clkcon_enable_h
,
259 .ctrlbit
= S3C2443_HCLKCON_DMA3
,
264 .enable
= s3c2443_clkcon_enable_h
,
265 .ctrlbit
= S3C2443_HCLKCON_DMA4
,
270 .enable
= s3c2443_clkcon_enable_h
,
271 .ctrlbit
= S3C2443_HCLKCON_DMA5
,
276 .enable
= s3c2443_clkcon_enable_h
,
277 .ctrlbit
= S3C2443_HCLKCON_HSMMC
,
282 .enable
= s3c2443_clkcon_enable_p
,
283 .ctrlbit
= S3C2443_PCLKCON_GPIO
,
288 .enable
= s3c2443_clkcon_enable_h
,
289 .ctrlbit
= S3C2443_HCLKCON_USBH
,
291 .name
= "usb-device",
294 .enable
= s3c2443_clkcon_enable_h
,
295 .ctrlbit
= S3C2443_HCLKCON_USBD
,
300 .enable
= s3c2443_clkcon_enable_h
,
301 .ctrlbit
= S3C2443_HCLKCON_LCDC
,
307 .enable
= s3c2443_clkcon_enable_p
,
308 .ctrlbit
= S3C2443_PCLKCON_PWMT
,
313 .enable
= s3c2443_clkcon_enable_h
,
314 .ctrlbit
= S3C2443_HCLKCON_CFC
,
319 .enable
= s3c2443_clkcon_enable_h
,
320 .ctrlbit
= S3C2443_HCLKCON_SSMC
,
325 .enable
= s3c2443_clkcon_enable_p
,
326 .ctrlbit
= S3C2443_PCLKCON_UART0
,
331 .enable
= s3c2443_clkcon_enable_p
,
332 .ctrlbit
= S3C2443_PCLKCON_UART1
,
337 .enable
= s3c2443_clkcon_enable_p
,
338 .ctrlbit
= S3C2443_PCLKCON_UART2
,
343 .enable
= s3c2443_clkcon_enable_p
,
344 .ctrlbit
= S3C2443_PCLKCON_UART3
,
349 .enable
= s3c2443_clkcon_enable_p
,
350 .ctrlbit
= S3C2443_PCLKCON_RTC
,
355 .ctrlbit
= S3C2443_PCLKCON_WDT
,
360 .ctrlbit
= S3C2443_PCLKCON_AC97
,
366 .name
= "usb-bus-host",
368 .parent
= &clk_usb_bus_host
.clk
,
372 static inline unsigned long s3c2443_get_hdiv(unsigned long clkcon0
)
374 clkcon0
&= S3C2443_CLKDIV0_HCLKDIV_MASK
;
379 /* EPLLCON compatible enough to get on/off information */
381 void __init_or_cpufreq
s3c2443_common_setup_clocks(pll_fn get_mpll
,
384 unsigned long epllcon
= __raw_readl(S3C2443_EPLLCON
);
385 unsigned long mpllcon
= __raw_readl(S3C2443_MPLLCON
);
386 unsigned long clkdiv0
= __raw_readl(S3C2443_CLKDIV0
);
387 struct clk
*xtal_clk
;
395 xtal_clk
= clk_get(NULL
, "xtal");
396 xtal
= clk_get_rate(xtal_clk
);
399 pll
= get_mpll(mpllcon
, xtal
);
400 clk_msysclk
.clk
.rate
= pll
;
402 fclk
= pll
/ get_fdiv(clkdiv0
);
403 hclk
= s3c2443_prediv_getrate(&clk_prediv
);
404 hclk
/= s3c2443_get_hdiv(clkdiv0
);
405 pclk
= hclk
/ ((clkdiv0
& S3C2443_CLKDIV0_HALF_PCLK
) ? 2 : 1);
407 s3c24xx_setup_clocks(fclk
, hclk
, pclk
);
409 printk("CPU: MPLL %s %ld.%03ld MHz, cpu %ld.%03ld MHz, mem %ld.%03ld MHz, pclk %ld.%03ld MHz\n",
410 (mpllcon
& S3C2443_PLLCON_OFF
) ? "off":"on",
411 print_mhz(pll
), print_mhz(fclk
),
412 print_mhz(hclk
), print_mhz(pclk
));
414 for (ptr
= 0; ptr
< ARRAY_SIZE(clksrc_clks
); ptr
++)
415 s3c_set_clksrc(&clksrc_clks
[ptr
], true);
417 /* ensure usb bus clock is within correct rate of 48MHz */
419 if (clk_get_rate(&clk_usb_bus_host
.clk
) != (48 * 1000 * 1000)) {
420 printk(KERN_INFO
"Warning: USB host bus not at 48MHz\n");
421 clk_set_rate(&clk_usb_bus_host
.clk
, 48*1000*1000);
424 printk("CPU: EPLL %s %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n",
425 (epllcon
& S3C2443_PLLCON_OFF
) ? "off":"on",
426 print_mhz(clk_get_rate(&clk_epll
)),
427 print_mhz(clk_get_rate(&clk_usb_bus
)));
430 static struct clk
*clks
[] __initdata
= {
439 static struct clksrc_clk
*clksrcs
[] __initdata
= {
446 void __init
s3c2443_common_init_clocks(int xtal
, pll_fn get_mpll
,
451 /* s3c2443 parents h and p clocks from prediv */
452 clk_h
.parent
= &clk_prediv
;
453 clk_p
.parent
= &clk_prediv
;
455 clk_usb_bus
.parent
= &clk_usb_bus_host
.clk
;
456 clk_epll
.parent
= &clk_epllref
.clk
;
458 s3c24xx_register_baseclocks(xtal
);
459 s3c24xx_register_clocks(clks
, ARRAY_SIZE(clks
));
461 for (ptr
= 0; ptr
< ARRAY_SIZE(clksrcs
); ptr
++)
462 s3c_register_clksrc(clksrcs
[ptr
], 1);
464 s3c_register_clksrc(clksrc_clks
, ARRAY_SIZE(clksrc_clks
));
465 s3c_register_clocks(init_clocks
, ARRAY_SIZE(init_clocks
));
467 /* See s3c2443/etc notes on disabling clocks at init time */
468 s3c_register_clocks(init_clocks_off
, ARRAY_SIZE(init_clocks_off
));
469 s3c_disable_clocks(init_clocks_off
, ARRAY_SIZE(init_clocks_off
));
471 s3c2443_common_setup_clocks(get_mpll
, get_fdiv
);