1/* linux/arch/arm/plat-s3c24xx/s3c2443-clock.c 2 * 3 * Copyright (c) 2007, 2010 Simtec Electronics 4 * Ben Dooks <ben@simtec.co.uk> 5 * 6 * S3C2443 Clock control suport - common code 7 */ 8 9#include <linux/init.h> 10#include <linux/clk.h> 11#include <linux/io.h> 12 13#include <mach/regs-s3c2443-clock.h> 14 15#include <plat/s3c2443.h> 16#include <plat/clock.h> 17#include <plat/clock-clksrc.h> 18#include <plat/cpu.h> 19 20#include <plat/cpu-freq.h> 21 22 23static int s3c2443_gate(void __iomem *reg, struct clk *clk, int enable) 24{ 25 u32 ctrlbit = clk->ctrlbit; 26 u32 con = __raw_readl(reg); 27 28 if (enable) 29 con |= ctrlbit; 30 else 31 con &= ~ctrlbit; 32 33 __raw_writel(con, reg); 34 return 0; 35} 36 37int s3c2443_clkcon_enable_h(struct clk *clk, int enable) 38{ 39 return s3c2443_gate(S3C2443_HCLKCON, clk, enable); 40} 41 42int s3c2443_clkcon_enable_p(struct clk *clk, int enable) 43{ 44 return s3c2443_gate(S3C2443_PCLKCON, clk, enable); 45} 46 47int s3c2443_clkcon_enable_s(struct clk *clk, int enable) 48{ 49 return s3c2443_gate(S3C2443_SCLKCON, clk, enable); 50} 51 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. 55 */ 56struct clk clk_mpllref = { 57 .name = "mpllref", 58 .parent = &clk_xtal, 59 .id = -1, 60}; 61 62static struct clk *clk_epllref_sources[] = { 63 [0] = &clk_mpllref, 64 [1] = &clk_mpllref, 65 [2] = &clk_xtal, 66 [3] = &clk_ext, 67}; 68 69struct clksrc_clk clk_epllref = { 70 .clk = { 71 .name = "epllref", 72 .id = -1, 73 }, 74 .sources = &(struct clksrc_sources) { 75 .sources = clk_epllref_sources, 76 .nr_sources = ARRAY_SIZE(clk_epllref_sources), 77 }, 78 .reg_src = { .reg = S3C2443_CLKSRC, .size = 2, .shift = 7 }, 79}; 80 81/* esysclk 82 * 83 * this is sourced from either the EPLL or the EPLLref clock 84*/ 85 86static struct clk *clk_sysclk_sources[] = { 87 [0] = &clk_epllref.clk, 88 [1] = &clk_epll, 89}; 90 91struct clksrc_clk clk_esysclk = { 92 .clk = { 93 .name = "esysclk", 94 .parent = &clk_epll, 95 .id = -1, 96 }, 97 .sources = &(struct clksrc_sources) { 98 .sources = clk_sysclk_sources, 99 .nr_sources = ARRAY_SIZE(clk_sysclk_sources), 100 }, 101 .reg_src = { .reg = S3C2443_CLKSRC, .size = 1, .shift = 6 }, 102}; 103 104static unsigned long s3c2443_getrate_mdivclk(struct clk *clk) 105{ 106 unsigned long parent_rate = clk_get_rate(clk->parent); 107 unsigned long div = __raw_readl(S3C2443_CLKDIV0); 108 109 div &= S3C2443_CLKDIV0_EXTDIV_MASK; 110 div >>= (S3C2443_CLKDIV0_EXTDIV_SHIFT-1); /* x2 */ 111 112 return parent_rate / (div + 1); 113} 114 115static struct clk clk_mdivclk = { 116 .name = "mdivclk", 117 .parent = &clk_mpllref, 118 .id = -1, 119 .ops = &(struct clk_ops) { 120 .get_rate = s3c2443_getrate_mdivclk, 121 }, 122}; 123 124static struct clk *clk_msysclk_sources[] = { 125 [0] = &clk_mpllref, 126 [1] = &clk_mpll, 127 [2] = &clk_mdivclk, 128 [3] = &clk_mpllref, 129}; 130 131struct clksrc_clk clk_msysclk = { 132 .clk = { 133 .name = "msysclk", 134 .parent = &clk_xtal, 135 .id = -1, 136 }, 137 .sources = &(struct clksrc_sources) { 138 .sources = clk_msysclk_sources, 139 .nr_sources = ARRAY_SIZE(clk_msysclk_sources), 140 }, 141 .reg_src = { .reg = S3C2443_CLKSRC, .size = 2, .shift = 3 }, 142}; 143 144/* prediv 145 * 146 * this divides the msysclk down to pass to h/p/etc. 147 */ 148 149static unsigned long s3c2443_prediv_getrate(struct clk *clk) 150{ 151 unsigned long rate = clk_get_rate(clk->parent); 152 unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0); 153 154 clkdiv0 &= S3C2443_CLKDIV0_PREDIV_MASK; 155 clkdiv0 >>= S3C2443_CLKDIV0_PREDIV_SHIFT; 156 157 return rate / (clkdiv0 + 1); 158} 159 160static struct clk clk_prediv = { 161 .name = "prediv", 162 .id = -1, 163 .parent = &clk_msysclk.clk, 164 .ops = &(struct clk_ops) { 165 .get_rate = s3c2443_prediv_getrate, 166 }, 167}; 168 169/* usbhost 170 * 171 * usb host bus-clock, usually 48MHz to provide USB bus clock timing 172*/ 173 174static struct clksrc_clk clk_usb_bus_host = { 175 .clk = { 176 .name = "usb-bus-host-parent", 177 .id = -1, 178 .parent = &clk_esysclk.clk, 179 .ctrlbit = S3C2443_SCLKCON_USBHOST, 180 .enable = s3c2443_clkcon_enable_s, 181 }, 182 .reg_div = { .reg = S3C2443_CLKDIV1, .size = 2, .shift = 4 }, 183}; 184 185/* common clksrc clocks */ 186 187static struct clksrc_clk clksrc_clks[] = { 188 { 189 /* ART baud-rate clock sourced from esysclk via a divisor */ 190 .clk = { 191 .name = "uartclk", 192 .id = -1, 193 .parent = &clk_esysclk.clk, 194 }, 195 .reg_div = { .reg = S3C2443_CLKDIV1, .size = 4, .shift = 8 }, 196 }, { 197 /* camera interface bus-clock, divided down from esysclk */ 198 .clk = { 199 .name = "camif-upll", /* same as 2440 name */ 200 .id = -1, 201 .parent = &clk_esysclk.clk, 202 .ctrlbit = S3C2443_SCLKCON_CAMCLK, 203 .enable = s3c2443_clkcon_enable_s, 204 }, 205 .reg_div = { .reg = S3C2443_CLKDIV1, .size = 4, .shift = 26 }, 206 }, { 207 .clk = { 208 .name = "display-if", 209 .id = -1, 210 .parent = &clk_esysclk.clk, 211 .ctrlbit = S3C2443_SCLKCON_DISPCLK, 212 .enable = s3c2443_clkcon_enable_s, 213 }, 214 .reg_div = { .reg = S3C2443_CLKDIV1, .size = 8, .shift = 16 }, 215 }, 216}; 217 218 219static struct clk init_clocks_off[] = { 220 { 221 .name = "adc", 222 .id = -1, 223 .parent = &clk_p, 224 .enable = s3c2443_clkcon_enable_p, 225 .ctrlbit = S3C2443_PCLKCON_ADC, 226 }, { 227 .name = "i2c", 228 .id = -1, 229 .parent = &clk_p, 230 .enable = s3c2443_clkcon_enable_p, 231 .ctrlbit = S3C2443_PCLKCON_IIC, 232 } 233}; 234 235static struct clk init_clocks[] = { 236 { 237 .name = "dma", 238 .id = 0, 239 .parent = &clk_h, 240 .enable = s3c2443_clkcon_enable_h, 241 .ctrlbit = S3C2443_HCLKCON_DMA0, 242 }, { 243 .name = "dma", 244 .id = 1, 245 .parent = &clk_h, 246 .enable = s3c2443_clkcon_enable_h, 247 .ctrlbit = S3C2443_HCLKCON_DMA1, 248 }, { 249 .name = "dma", 250 .id = 2, 251 .parent = &clk_h, 252 .enable = s3c2443_clkcon_enable_h, 253 .ctrlbit = S3C2443_HCLKCON_DMA2, 254 }, { 255 .name = "dma", 256 .id = 3, 257 .parent = &clk_h, 258 .enable = s3c2443_clkcon_enable_h, 259 .ctrlbit = S3C2443_HCLKCON_DMA3, 260 }, { 261 .name = "dma", 262 .id = 4, 263 .parent = &clk_h, 264 .enable = s3c2443_clkcon_enable_h, 265 .ctrlbit = S3C2443_HCLKCON_DMA4, 266 }, { 267 .name = "dma", 268 .id = 5, 269 .parent = &clk_h, 270 .enable = s3c2443_clkcon_enable_h, 271 .ctrlbit = S3C2443_HCLKCON_DMA5, 272 }, { 273 .name = "hsmmc", 274 .id = 0, 275 .parent = &clk_h, 276 .enable = s3c2443_clkcon_enable_h, 277 .ctrlbit = S3C2443_HCLKCON_HSMMC, 278 }, { 279 .name = "gpio", 280 .id = -1, 281 .parent = &clk_p, 282 .enable = s3c2443_clkcon_enable_p, 283 .ctrlbit = S3C2443_PCLKCON_GPIO, 284 }, { 285 .name = "usb-host", 286 .id = -1, 287 .parent = &clk_h, 288 .enable = s3c2443_clkcon_enable_h, 289 .ctrlbit = S3C2443_HCLKCON_USBH, 290 }, { 291 .name = "usb-device", 292 .id = -1, 293 .parent = &clk_h, 294 .enable = s3c2443_clkcon_enable_h, 295 .ctrlbit = S3C2443_HCLKCON_USBD, 296 }, { 297 .name = "lcd", 298 .id = -1, 299 .parent = &clk_h, 300 .enable = s3c2443_clkcon_enable_h, 301 .ctrlbit = S3C2443_HCLKCON_LCDC, 302 303 }, { 304 .name = "timers", 305 .id = -1, 306 .parent = &clk_p, 307 .enable = s3c2443_clkcon_enable_p, 308 .ctrlbit = S3C2443_PCLKCON_PWMT, 309 }, { 310 .name = "cfc", 311 .id = -1, 312 .parent = &clk_h, 313 .enable = s3c2443_clkcon_enable_h, 314 .ctrlbit = S3C2443_HCLKCON_CFC, 315 }, { 316 .name = "ssmc", 317 .id = -1, 318 .parent = &clk_h, 319 .enable = s3c2443_clkcon_enable_h, 320 .ctrlbit = S3C2443_HCLKCON_SSMC, 321 }, { 322 .name = "uart", 323 .id = 0, 324 .parent = &clk_p, 325 .enable = s3c2443_clkcon_enable_p, 326 .ctrlbit = S3C2443_PCLKCON_UART0, 327 }, { 328 .name = "uart", 329 .id = 1, 330 .parent = &clk_p, 331 .enable = s3c2443_clkcon_enable_p, 332 .ctrlbit = S3C2443_PCLKCON_UART1, 333 }, { 334 .name = "uart", 335 .id = 2, 336 .parent = &clk_p, 337 .enable = s3c2443_clkcon_enable_p, 338 .ctrlbit = S3C2443_PCLKCON_UART2, 339 }, { 340 .name = "uart", 341 .id = 3, 342 .parent = &clk_p, 343 .enable = s3c2443_clkcon_enable_p, 344 .ctrlbit = S3C2443_PCLKCON_UART3, 345 }, { 346 .name = "rtc", 347 .id = -1, 348 .parent = &clk_p, 349 .enable = s3c2443_clkcon_enable_p, 350 .ctrlbit = S3C2443_PCLKCON_RTC, 351 }, { 352 .name = "watchdog", 353 .id = -1, 354 .parent = &clk_p, 355 .ctrlbit = S3C2443_PCLKCON_WDT, 356 }, { 357 .name = "ac97", 358 .id = -1, 359 .parent = &clk_p, 360 .ctrlbit = S3C2443_PCLKCON_AC97, 361 }, { 362 .name = "nand", 363 .id = -1, 364 .parent = &clk_h, 365 }, { 366 .name = "usb-bus-host", 367 .id = -1, 368 .parent = &clk_usb_bus_host.clk, 369 } 370}; 371 372static inline unsigned long s3c2443_get_hdiv(unsigned long clkcon0) 373{ 374 clkcon0 &= S3C2443_CLKDIV0_HCLKDIV_MASK; 375 376 return clkcon0 + 1; 377} 378 379/* EPLLCON compatible enough to get on/off information */ 380 381void __init_or_cpufreq s3c2443_common_setup_clocks(pll_fn get_mpll, 382 fdiv_fn get_fdiv) 383{ 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; 388 unsigned long xtal; 389 unsigned long pll; 390 unsigned long fclk; 391 unsigned long hclk; 392 unsigned long pclk; 393 int ptr; 394 395 xtal_clk = clk_get(NULL, "xtal"); 396 xtal = clk_get_rate(xtal_clk); 397 clk_put(xtal_clk); 398 399 pll = get_mpll(mpllcon, xtal); 400 clk_msysclk.clk.rate = pll; 401 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); 406 407 s3c24xx_setup_clocks(fclk, hclk, pclk); 408 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)); 413 414 for (ptr = 0; ptr < ARRAY_SIZE(clksrc_clks); ptr++) 415 s3c_set_clksrc(&clksrc_clks[ptr], true); 416 417 /* ensure usb bus clock is within correct rate of 48MHz */ 418 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); 422 } 423 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))); 428} 429 430static struct clk *clks[] __initdata = { 431 &clk_prediv, 432 &clk_mpllref, 433 &clk_mdivclk, 434 &clk_ext, 435 &clk_epll, 436 &clk_usb_bus, 437}; 438 439static struct clksrc_clk *clksrcs[] __initdata = { 440 &clk_usb_bus_host, 441 &clk_epllref, 442 &clk_esysclk, 443 &clk_msysclk, 444}; 445 446void __init s3c2443_common_init_clocks(int xtal, pll_fn get_mpll, 447 fdiv_fn get_fdiv) 448{ 449 int ptr; 450 451 /* s3c2443 parents h and p clocks from prediv */ 452 clk_h.parent = &clk_prediv; 453 clk_p.parent = &clk_prediv; 454 455 clk_usb_bus.parent = &clk_usb_bus_host.clk; 456 clk_epll.parent = &clk_epllref.clk; 457 458 s3c24xx_register_baseclocks(xtal); 459 s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); 460 461 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) 462 s3c_register_clksrc(clksrcs[ptr], 1); 463 464 s3c_register_clksrc(clksrc_clks, ARRAY_SIZE(clksrc_clks)); 465 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); 466 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)); 470 471 s3c2443_common_setup_clocks(get_mpll, get_fdiv); 472} 473