diff options
Diffstat (limited to 'arch/arm/plat-s3c24xx/s3c2443-clock.c')
| -rw-r--r-- | arch/arm/plat-s3c24xx/s3c2443-clock.c | 192 |
1 files changed, 187 insertions, 5 deletions
diff --git a/arch/arm/plat-s3c24xx/s3c2443-clock.c b/arch/arm/plat-s3c24xx/s3c2443-clock.c index 59552c0ea5fb..5a21b15b2a97 100644 --- a/arch/arm/plat-s3c24xx/s3c2443-clock.c +++ b/arch/arm/plat-s3c24xx/s3c2443-clock.c | |||
| @@ -160,6 +160,124 @@ static struct clk clk_prediv = { | |||
| 160 | }, | 160 | }, |
| 161 | }; | 161 | }; |
| 162 | 162 | ||
| 163 | /* armdiv | ||
| 164 | * | ||
| 165 | * this clock is sourced from msysclk and can have a number of | ||
| 166 | * divider values applied to it to then be fed into armclk. | ||
| 167 | */ | ||
| 168 | |||
| 169 | static unsigned int *armdiv; | ||
| 170 | static int nr_armdiv; | ||
| 171 | static int armdivmask; | ||
| 172 | |||
| 173 | static unsigned long s3c2443_armclk_roundrate(struct clk *clk, | ||
| 174 | unsigned long rate) | ||
| 175 | { | ||
| 176 | unsigned long parent = clk_get_rate(clk->parent); | ||
| 177 | unsigned long calc; | ||
| 178 | unsigned best = 256; /* bigger than any value */ | ||
| 179 | unsigned div; | ||
| 180 | int ptr; | ||
| 181 | |||
| 182 | if (!nr_armdiv) | ||
| 183 | return -EINVAL; | ||
| 184 | |||
| 185 | for (ptr = 0; ptr < nr_armdiv; ptr++) { | ||
| 186 | div = armdiv[ptr]; | ||
| 187 | if (div) { | ||
| 188 | /* cpufreq provides 266mhz as 266666000 not 266666666 */ | ||
| 189 | calc = (parent / div / 1000) * 1000; | ||
| 190 | if (calc <= rate && div < best) | ||
| 191 | best = div; | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | return parent / best; | ||
| 196 | } | ||
| 197 | |||
| 198 | static unsigned long s3c2443_armclk_getrate(struct clk *clk) | ||
| 199 | { | ||
| 200 | unsigned long rate = clk_get_rate(clk->parent); | ||
| 201 | unsigned long clkcon0; | ||
| 202 | int val; | ||
| 203 | |||
| 204 | if (!nr_armdiv || !armdivmask) | ||
| 205 | return -EINVAL; | ||
| 206 | |||
| 207 | clkcon0 = __raw_readl(S3C2443_CLKDIV0); | ||
| 208 | clkcon0 &= armdivmask; | ||
| 209 | val = clkcon0 >> S3C2443_CLKDIV0_ARMDIV_SHIFT; | ||
| 210 | |||
| 211 | return rate / armdiv[val]; | ||
| 212 | } | ||
| 213 | |||
| 214 | static int s3c2443_armclk_setrate(struct clk *clk, unsigned long rate) | ||
| 215 | { | ||
| 216 | unsigned long parent = clk_get_rate(clk->parent); | ||
| 217 | unsigned long calc; | ||
| 218 | unsigned div; | ||
| 219 | unsigned best = 256; /* bigger than any value */ | ||
| 220 | int ptr; | ||
| 221 | int val = -1; | ||
| 222 | |||
| 223 | if (!nr_armdiv || !armdivmask) | ||
| 224 | return -EINVAL; | ||
| 225 | |||
| 226 | for (ptr = 0; ptr < nr_armdiv; ptr++) { | ||
| 227 | div = armdiv[ptr]; | ||
| 228 | if (div) { | ||
| 229 | /* cpufreq provides 266mhz as 266666000 not 266666666 */ | ||
| 230 | calc = (parent / div / 1000) * 1000; | ||
| 231 | if (calc <= rate && div < best) { | ||
| 232 | best = div; | ||
| 233 | val = ptr; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | if (val >= 0) { | ||
| 239 | unsigned long clkcon0; | ||
| 240 | |||
| 241 | clkcon0 = __raw_readl(S3C2443_CLKDIV0); | ||
| 242 | clkcon0 &= ~armdivmask; | ||
| 243 | clkcon0 |= val << S3C2443_CLKDIV0_ARMDIV_SHIFT; | ||
| 244 | __raw_writel(clkcon0, S3C2443_CLKDIV0); | ||
| 245 | } | ||
| 246 | |||
| 247 | return (val == -1) ? -EINVAL : 0; | ||
| 248 | } | ||
| 249 | |||
| 250 | static struct clk clk_armdiv = { | ||
| 251 | .name = "armdiv", | ||
| 252 | .parent = &clk_msysclk.clk, | ||
| 253 | .ops = &(struct clk_ops) { | ||
| 254 | .round_rate = s3c2443_armclk_roundrate, | ||
| 255 | .get_rate = s3c2443_armclk_getrate, | ||
| 256 | .set_rate = s3c2443_armclk_setrate, | ||
| 257 | }, | ||
| 258 | }; | ||
| 259 | |||
| 260 | /* armclk | ||
| 261 | * | ||
| 262 | * this is the clock fed into the ARM core itself, from armdiv or from hclk. | ||
| 263 | */ | ||
| 264 | |||
| 265 | static struct clk *clk_arm_sources[] = { | ||
| 266 | [0] = &clk_armdiv, | ||
| 267 | [1] = &clk_h, | ||
| 268 | }; | ||
| 269 | |||
| 270 | static struct clksrc_clk clk_arm = { | ||
| 271 | .clk = { | ||
| 272 | .name = "armclk", | ||
| 273 | }, | ||
| 274 | .sources = &(struct clksrc_sources) { | ||
| 275 | .sources = clk_arm_sources, | ||
| 276 | .nr_sources = ARRAY_SIZE(clk_arm_sources), | ||
| 277 | }, | ||
| 278 | .reg_src = { .reg = S3C2443_CLKDIV0, .size = 1, .shift = 13 }, | ||
| 279 | }; | ||
| 280 | |||
| 163 | /* usbhost | 281 | /* usbhost |
| 164 | * | 282 | * |
| 165 | * usb host bus-clock, usually 48MHz to provide USB bus clock timing | 283 | * usb host bus-clock, usually 48MHz to provide USB bus clock timing |
| @@ -205,9 +323,64 @@ static struct clksrc_clk clksrc_clks[] = { | |||
| 205 | }, | 323 | }, |
| 206 | }; | 324 | }; |
| 207 | 325 | ||
| 326 | static struct clk clk_i2s_ext = { | ||
| 327 | .name = "i2s-ext", | ||
| 328 | }; | ||
| 329 | |||
| 330 | /* i2s_eplldiv | ||
| 331 | * | ||
| 332 | * This clock is the output from the I2S divisor of ESYSCLK, and is separate | ||
| 333 | * from the mux that comes after it (cannot merge into one single clock) | ||
| 334 | */ | ||
| 335 | |||
| 336 | static struct clksrc_clk clk_i2s_eplldiv = { | ||
| 337 | .clk = { | ||
| 338 | .name = "i2s-eplldiv", | ||
| 339 | .parent = &clk_esysclk.clk, | ||
| 340 | }, | ||
| 341 | .reg_div = { .reg = S3C2443_CLKDIV1, .size = 4, .shift = 12, }, | ||
| 342 | }; | ||
| 343 | |||
| 344 | /* i2s-ref | ||
| 345 | * | ||
| 346 | * i2s bus reference clock, selectable from external, esysclk or epllref | ||
| 347 | * | ||
| 348 | * Note, this used to be two clocks, but was compressed into one. | ||
| 349 | */ | ||
| 350 | |||
| 351 | static struct clk *clk_i2s_srclist[] = { | ||
| 352 | [0] = &clk_i2s_eplldiv.clk, | ||
| 353 | [1] = &clk_i2s_ext, | ||
| 354 | [2] = &clk_epllref.clk, | ||
| 355 | [3] = &clk_epllref.clk, | ||
| 356 | }; | ||
| 357 | |||
| 358 | static struct clksrc_clk clk_i2s = { | ||
| 359 | .clk = { | ||
| 360 | .name = "i2s-if", | ||
| 361 | .ctrlbit = S3C2443_SCLKCON_I2SCLK, | ||
| 362 | .enable = s3c2443_clkcon_enable_s, | ||
| 363 | |||
| 364 | }, | ||
| 365 | .sources = &(struct clksrc_sources) { | ||
| 366 | .sources = clk_i2s_srclist, | ||
| 367 | .nr_sources = ARRAY_SIZE(clk_i2s_srclist), | ||
| 368 | }, | ||
| 369 | .reg_src = { .reg = S3C2443_CLKSRC, .size = 2, .shift = 14 }, | ||
| 370 | }; | ||
| 208 | 371 | ||
| 209 | static struct clk init_clocks_off[] = { | 372 | static struct clk init_clocks_off[] = { |
| 210 | { | 373 | { |
| 374 | .name = "iis", | ||
| 375 | .parent = &clk_p, | ||
| 376 | .enable = s3c2443_clkcon_enable_p, | ||
| 377 | .ctrlbit = S3C2443_PCLKCON_IIS, | ||
| 378 | }, { | ||
| 379 | .name = "hsspi", | ||
| 380 | .parent = &clk_p, | ||
| 381 | .enable = s3c2443_clkcon_enable_p, | ||
| 382 | .ctrlbit = S3C2443_PCLKCON_HSSPI, | ||
| 383 | }, { | ||
| 211 | .name = "adc", | 384 | .name = "adc", |
| 212 | .parent = &clk_p, | 385 | .parent = &clk_p, |
| 213 | .enable = s3c2443_clkcon_enable_p, | 386 | .enable = s3c2443_clkcon_enable_p, |
| @@ -253,6 +426,7 @@ static struct clk init_clocks[] = { | |||
| 253 | .ctrlbit = S3C2443_HCLKCON_DMA5, | 426 | .ctrlbit = S3C2443_HCLKCON_DMA5, |
| 254 | }, { | 427 | }, { |
| 255 | .name = "hsmmc", | 428 | .name = "hsmmc", |
| 429 | .devname = "s3c-sdhci.1", | ||
| 256 | .parent = &clk_h, | 430 | .parent = &clk_h, |
| 257 | .enable = s3c2443_clkcon_enable_h, | 431 | .enable = s3c2443_clkcon_enable_h, |
| 258 | .ctrlbit = S3C2443_HCLKCON_HSMMC, | 432 | .ctrlbit = S3C2443_HCLKCON_HSMMC, |
| @@ -347,8 +521,7 @@ static inline unsigned long s3c2443_get_hdiv(unsigned long clkcon0) | |||
| 347 | 521 | ||
| 348 | /* EPLLCON compatible enough to get on/off information */ | 522 | /* EPLLCON compatible enough to get on/off information */ |
| 349 | 523 | ||
| 350 | void __init_or_cpufreq s3c2443_common_setup_clocks(pll_fn get_mpll, | 524 | void __init_or_cpufreq s3c2443_common_setup_clocks(pll_fn get_mpll) |
| 351 | fdiv_fn get_fdiv) | ||
| 352 | { | 525 | { |
| 353 | unsigned long epllcon = __raw_readl(S3C2443_EPLLCON); | 526 | unsigned long epllcon = __raw_readl(S3C2443_EPLLCON); |
| 354 | unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON); | 527 | unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON); |
| @@ -368,7 +541,7 @@ void __init_or_cpufreq s3c2443_common_setup_clocks(pll_fn get_mpll, | |||
| 368 | pll = get_mpll(mpllcon, xtal); | 541 | pll = get_mpll(mpllcon, xtal); |
| 369 | clk_msysclk.clk.rate = pll; | 542 | clk_msysclk.clk.rate = pll; |
| 370 | 543 | ||
| 371 | fclk = pll / get_fdiv(clkdiv0); | 544 | fclk = clk_get_rate(&clk_armdiv); |
| 372 | hclk = s3c2443_prediv_getrate(&clk_prediv); | 545 | hclk = s3c2443_prediv_getrate(&clk_prediv); |
| 373 | hclk /= s3c2443_get_hdiv(clkdiv0); | 546 | hclk /= s3c2443_get_hdiv(clkdiv0); |
| 374 | pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1); | 547 | pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1); |
| @@ -403,20 +576,29 @@ static struct clk *clks[] __initdata = { | |||
| 403 | &clk_ext, | 576 | &clk_ext, |
| 404 | &clk_epll, | 577 | &clk_epll, |
| 405 | &clk_usb_bus, | 578 | &clk_usb_bus, |
| 579 | &clk_armdiv, | ||
| 406 | }; | 580 | }; |
| 407 | 581 | ||
| 408 | static struct clksrc_clk *clksrcs[] __initdata = { | 582 | static struct clksrc_clk *clksrcs[] __initdata = { |
| 583 | &clk_i2s_eplldiv, | ||
| 584 | &clk_i2s, | ||
| 409 | &clk_usb_bus_host, | 585 | &clk_usb_bus_host, |
| 410 | &clk_epllref, | 586 | &clk_epllref, |
| 411 | &clk_esysclk, | 587 | &clk_esysclk, |
| 412 | &clk_msysclk, | 588 | &clk_msysclk, |
| 589 | &clk_arm, | ||
| 413 | }; | 590 | }; |
| 414 | 591 | ||
| 415 | void __init s3c2443_common_init_clocks(int xtal, pll_fn get_mpll, | 592 | void __init s3c2443_common_init_clocks(int xtal, pll_fn get_mpll, |
| 416 | fdiv_fn get_fdiv) | 593 | unsigned int *divs, int nr_divs, |
| 594 | int divmask) | ||
| 417 | { | 595 | { |
| 418 | int ptr; | 596 | int ptr; |
| 419 | 597 | ||
| 598 | armdiv = divs; | ||
| 599 | nr_armdiv = nr_divs; | ||
| 600 | armdivmask = divmask; | ||
| 601 | |||
| 420 | /* s3c2443 parents h and p clocks from prediv */ | 602 | /* s3c2443 parents h and p clocks from prediv */ |
| 421 | clk_h.parent = &clk_prediv; | 603 | clk_h.parent = &clk_prediv; |
| 422 | clk_p.parent = &clk_prediv; | 604 | clk_p.parent = &clk_prediv; |
| @@ -437,5 +619,5 @@ void __init s3c2443_common_init_clocks(int xtal, pll_fn get_mpll, | |||
| 437 | s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); | 619 | s3c_register_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); |
| 438 | s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); | 620 | s3c_disable_clocks(init_clocks_off, ARRAY_SIZE(init_clocks_off)); |
| 439 | 621 | ||
| 440 | s3c2443_common_setup_clocks(get_mpll, get_fdiv); | 622 | s3c2443_common_setup_clocks(get_mpll); |
| 441 | } | 623 | } |
