1/* linux/arch/arm/mach-s5p6442/clock.c 2 * 3 * Copyright (c) 2010 Samsung Electronics Co., Ltd. 4 * http://www.samsung.com/ 5 * 6 * S5P6442 - 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/err.h> 18#include <linux/clk.h> 19#include <linux/io.h> 20 21#include <mach/map.h> 22 23#include <plat/cpu-freq.h> 24#include <mach/regs-clock.h> 25#include <plat/clock.h> 26#include <plat/cpu.h> 27#include <plat/pll.h> 28#include <plat/s5p-clock.h> 29#include <plat/clock-clksrc.h> 30#include <plat/s5p6442.h> 31 32static struct clksrc_clk clk_mout_apll = { 33 .clk = { 34 .name = "mout_apll", 35 .id = -1, 36 }, 37 .sources = &clk_src_apll, 38 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 0, .size = 1 }, 39}; 40 41static struct clksrc_clk clk_mout_mpll = { 42 .clk = { 43 .name = "mout_mpll", 44 .id = -1, 45 }, 46 .sources = &clk_src_mpll, 47 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 4, .size = 1 }, 48}; 49 50static struct clksrc_clk clk_mout_epll = { 51 .clk = { 52 .name = "mout_epll", 53 .id = -1, 54 }, 55 .sources = &clk_src_epll, 56 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 8, .size = 1 }, 57}; 58 59/* Possible clock sources for ARM Mux */ 60static struct clk *clk_src_arm_list[] = { 61 [1] = &clk_mout_apll.clk, 62 [2] = &clk_mout_mpll.clk, 63}; 64 65static struct clksrc_sources clk_src_arm = { 66 .sources = clk_src_arm_list, 67 .nr_sources = ARRAY_SIZE(clk_src_arm_list), 68}; 69 70static struct clksrc_clk clk_mout_arm = { 71 .clk = { 72 .name = "mout_arm", 73 .id = -1, 74 }, 75 .sources = &clk_src_arm, 76 .reg_src = { .reg = S5P_CLK_MUX_STAT0, .shift = 16, .size = 3 }, 77}; 78 79static struct clk clk_dout_a2m = { 80 .name = "dout_a2m", 81 .id = -1, 82 .parent = &clk_mout_apll.clk, 83}; 84 85/* Possible clock sources for D0 Mux */ 86static struct clk *clk_src_d0_list[] = { 87 [1] = &clk_mout_mpll.clk, 88 [2] = &clk_dout_a2m, 89}; 90 91static struct clksrc_sources clk_src_d0 = { 92 .sources = clk_src_d0_list, 93 .nr_sources = ARRAY_SIZE(clk_src_d0_list), 94}; 95 96static struct clksrc_clk clk_mout_d0 = { 97 .clk = { 98 .name = "mout_d0", 99 .id = -1, 100 }, 101 .sources = &clk_src_d0, 102 .reg_src = { .reg = S5P_CLK_MUX_STAT0, .shift = 20, .size = 3 }, 103}; 104 105static struct clk clk_dout_apll = { 106 .name = "dout_apll", 107 .id = -1, 108 .parent = &clk_mout_arm.clk, 109}; 110 111/* Possible clock sources for D0SYNC Mux */ 112static struct clk *clk_src_d0sync_list[] = { 113 [1] = &clk_mout_d0.clk, 114 [2] = &clk_dout_apll, 115}; 116 117static struct clksrc_sources clk_src_d0sync = { 118 .sources = clk_src_d0sync_list, 119 .nr_sources = ARRAY_SIZE(clk_src_d0sync_list), 120}; 121 122static struct clksrc_clk clk_mout_d0sync = { 123 .clk = { 124 .name = "mout_d0sync", 125 .id = -1, 126 }, 127 .sources = &clk_src_d0sync, 128 .reg_src = { .reg = S5P_CLK_MUX_STAT1, .shift = 28, .size = 3 }, 129}; 130 131/* Possible clock sources for D1 Mux */ 132static struct clk *clk_src_d1_list[] = { 133 [1] = &clk_mout_mpll.clk, 134 [2] = &clk_dout_a2m, 135}; 136 137static struct clksrc_sources clk_src_d1 = { 138 .sources = clk_src_d1_list, 139 .nr_sources = ARRAY_SIZE(clk_src_d1_list), 140}; 141 142static struct clksrc_clk clk_mout_d1 = { 143 .clk = { 144 .name = "mout_d1", 145 .id = -1, 146 }, 147 .sources = &clk_src_d1, 148 .reg_src = { .reg = S5P_CLK_MUX_STAT0, .shift = 24, .size = 3 }, 149}; 150 151/* Possible clock sources for D1SYNC Mux */ 152static struct clk *clk_src_d1sync_list[] = { 153 [1] = &clk_mout_d1.clk, 154 [2] = &clk_dout_apll, 155}; 156 157static struct clksrc_sources clk_src_d1sync = { 158 .sources = clk_src_d1sync_list, 159 .nr_sources = ARRAY_SIZE(clk_src_d1sync_list), 160}; 161 162static struct clksrc_clk clk_mout_d1sync = { 163 .clk = { 164 .name = "mout_d1sync", 165 .id = -1, 166 }, 167 .sources = &clk_src_d1sync, 168 .reg_src = { .reg = S5P_CLK_MUX_STAT1, .shift = 24, .size = 3 }, 169}; 170 171static struct clk clk_hclkd0 = { 172 .name = "hclkd0", 173 .id = -1, 174 .parent = &clk_mout_d0sync.clk, 175}; 176 177static struct clk clk_hclkd1 = { 178 .name = "hclkd1", 179 .id = -1, 180 .parent = &clk_mout_d1sync.clk, 181}; 182 183static struct clk clk_pclkd0 = { 184 .name = "pclkd0", 185 .id = -1, 186 .parent = &clk_hclkd0, 187}; 188 189static struct clk clk_pclkd1 = { 190 .name = "pclkd1", 191 .id = -1, 192 .parent = &clk_hclkd1, 193}; 194 195int s5p6442_clk_ip3_ctrl(struct clk *clk, int enable) 196{ 197 return s5p_gatectrl(S5P_CLKGATE_IP3, clk, enable); 198} 199 200static struct clksrc_clk clksrcs[] = { 201 { 202 .clk = { 203 .name = "dout_a2m", 204 .id = -1, 205 .parent = &clk_mout_apll.clk, 206 }, 207 .sources = &clk_src_apll, 208 .reg_src = { .reg = S5P_CLK_SRC0, .shift = 0, .size = 1 }, 209 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 4, .size = 3 }, 210 }, { 211 .clk = { 212 .name = "dout_apll", 213 .id = -1, 214 .parent = &clk_mout_arm.clk, 215 }, 216 .sources = &clk_src_arm, 217 .reg_src = { .reg = S5P_CLK_MUX_STAT0, .shift = 16, .size = 3 }, 218 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 0, .size = 3 }, 219 }, { 220 .clk = { 221 .name = "hclkd1", 222 .id = -1, 223 .parent = &clk_mout_d1sync.clk, 224 }, 225 .sources = &clk_src_d1sync, 226 .reg_src = { .reg = S5P_CLK_MUX_STAT1, .shift = 24, .size = 3 }, 227 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 24, .size = 4 }, 228 }, { 229 .clk = { 230 .name = "hclkd0", 231 .id = -1, 232 .parent = &clk_mout_d0sync.clk, 233 }, 234 .sources = &clk_src_d0sync, 235 .reg_src = { .reg = S5P_CLK_MUX_STAT1, .shift = 28, .size = 3 }, 236 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 16, .size = 4 }, 237 }, { 238 .clk = { 239 .name = "pclkd0", 240 .id = -1, 241 .parent = &clk_hclkd0, 242 }, 243 .sources = &clk_src_d0sync, 244 .reg_src = { .reg = S5P_CLK_MUX_STAT1, .shift = 28, .size = 3 }, 245 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 20, .size = 3 }, 246 }, { 247 .clk = { 248 .name = "pclkd1", 249 .id = -1, 250 .parent = &clk_hclkd1, 251 }, 252 .sources = &clk_src_d1sync, 253 .reg_src = { .reg = S5P_CLK_MUX_STAT1, .shift = 24, .size = 3 }, 254 .reg_div = { .reg = S5P_CLK_DIV0, .shift = 28, .size = 3 }, 255 } 256}; 257 258/* Clock initialisation code */ 259static struct clksrc_clk *init_parents[] = { 260 &clk_mout_apll, 261 &clk_mout_mpll, 262 &clk_mout_epll, 263 &clk_mout_arm, 264 &clk_mout_d0, 265 &clk_mout_d0sync, 266 &clk_mout_d1, 267 &clk_mout_d1sync, 268}; 269 270void __init_or_cpufreq s5p6442_setup_clocks(void) 271{ 272 struct clk *pclkd0_clk; 273 struct clk *pclkd1_clk; 274 275 unsigned long xtal; 276 unsigned long arm; 277 unsigned long hclkd0 = 0; 278 unsigned long hclkd1 = 0; 279 unsigned long pclkd0 = 0; 280 unsigned long pclkd1 = 0; 281 282 unsigned long apll; 283 unsigned long mpll; 284 unsigned long epll; 285 unsigned int ptr; 286 287 printk(KERN_DEBUG "%s: registering clocks\n", __func__); 288 289 xtal = clk_get_rate(&clk_xtal); 290 291 printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal); 292 293 apll = s5p_get_pll45xx(xtal, __raw_readl(S5P_APLL_CON), pll_4508); 294 mpll = s5p_get_pll45xx(xtal, __raw_readl(S5P_MPLL_CON), pll_4502); 295 epll = s5p_get_pll45xx(xtal, __raw_readl(S5P_EPLL_CON), pll_4500); 296 297 printk(KERN_INFO "S5P6442: PLL settings, A=%ld, M=%ld, E=%ld", 298 apll, mpll, epll); 299 300 clk_fout_apll.rate = apll; 301 clk_fout_mpll.rate = mpll; 302 clk_fout_epll.rate = epll; 303 304 for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++) 305 s3c_set_clksrc(init_parents[ptr], true); 306 307 for (ptr = 0; ptr < ARRAY_SIZE(clksrcs); ptr++) 308 s3c_set_clksrc(&clksrcs[ptr], true); 309 310 arm = clk_get_rate(&clk_dout_apll); 311 hclkd0 = clk_get_rate(&clk_hclkd0); 312 hclkd1 = clk_get_rate(&clk_hclkd1); 313 314 pclkd0_clk = clk_get(NULL, "pclkd0"); 315 BUG_ON(IS_ERR(pclkd0_clk)); 316 317 pclkd0 = clk_get_rate(pclkd0_clk); 318 clk_put(pclkd0_clk); 319 320 pclkd1_clk = clk_get(NULL, "pclkd1"); 321 BUG_ON(IS_ERR(pclkd1_clk)); 322 323 pclkd1 = clk_get_rate(pclkd1_clk); 324 clk_put(pclkd1_clk); 325 326 printk(KERN_INFO "S5P6442: HCLKD0=%ld, HCLKD1=%ld, PCLKD0=%ld, PCLKD1=%ld\n", 327 hclkd0, hclkd1, pclkd0, pclkd1); 328 329 /* For backward compatibility */ 330 clk_f.rate = arm; 331 clk_h.rate = hclkd1; 332 clk_p.rate = pclkd1; 333 334 clk_pclkd0.rate = pclkd0; 335 clk_pclkd1.rate = pclkd1; 336} 337 338static struct clk init_clocks[] = { 339 { 340 .name = "systimer", 341 .id = -1, 342 .parent = &clk_pclkd1, 343 .enable = s5p6442_clk_ip3_ctrl, 344 .ctrlbit = (1<<16), 345 }, { 346 .name = "uart", 347 .id = 0, 348 .parent = &clk_pclkd1, 349 .enable = s5p6442_clk_ip3_ctrl, 350 .ctrlbit = (1<<17), 351 }, { 352 .name = "uart", 353 .id = 1, 354 .parent = &clk_pclkd1, 355 .enable = s5p6442_clk_ip3_ctrl, 356 .ctrlbit = (1<<18), 357 }, { 358 .name = "uart", 359 .id = 2, 360 .parent = &clk_pclkd1, 361 .enable = s5p6442_clk_ip3_ctrl, 362 .ctrlbit = (1<<19), 363 }, { 364 .name = "watchdog", 365 .id = -1, 366 .parent = &clk_pclkd1, 367 .enable = s5p6442_clk_ip3_ctrl, 368 .ctrlbit = (1 << 22), 369 }, { 370 .name = "timers", 371 .id = -1, 372 .parent = &clk_pclkd1, 373 .enable = s5p6442_clk_ip3_ctrl, 374 .ctrlbit = (1<<23), 375 }, 376}; 377 378static struct clk *clks[] __initdata = { 379 &clk_ext, 380 &clk_epll, 381 &clk_mout_apll.clk, 382 &clk_mout_mpll.clk, 383 &clk_mout_epll.clk, 384 &clk_mout_d0.clk, 385 &clk_mout_d0sync.clk, 386 &clk_mout_d1.clk, 387 &clk_mout_d1sync.clk, 388 &clk_hclkd0, 389 &clk_pclkd0, 390 &clk_hclkd1, 391 &clk_pclkd1, 392}; 393 394void __init s5p6442_register_clocks(void) 395{ 396 s3c24xx_register_clocks(clks, ARRAY_SIZE(clks)); 397 398 s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs)); 399 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks)); 400 401 s3c_pwmclk_init(); 402} 403