diff options
Diffstat (limited to 'arch/arm/mach-s3c2410/clock.c')
| -rw-r--r-- | arch/arm/mach-s3c2410/clock.c | 178 |
1 files changed, 174 insertions, 4 deletions
diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index 08489efdaf06..fec02c92f95f 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c | |||
| @@ -38,12 +38,14 @@ | |||
| 38 | #include <linux/ioport.h> | 38 | #include <linux/ioport.h> |
| 39 | #include <linux/clk.h> | 39 | #include <linux/clk.h> |
| 40 | #include <linux/mutex.h> | 40 | #include <linux/mutex.h> |
| 41 | #include <linux/delay.h> | ||
| 41 | 42 | ||
| 42 | #include <asm/hardware.h> | 43 | #include <asm/hardware.h> |
| 43 | #include <asm/irq.h> | 44 | #include <asm/irq.h> |
| 44 | #include <asm/io.h> | 45 | #include <asm/io.h> |
| 45 | 46 | ||
| 46 | #include <asm/arch/regs-clock.h> | 47 | #include <asm/arch/regs-clock.h> |
| 48 | #include <asm/arch/regs-gpio.h> | ||
| 47 | 49 | ||
| 48 | #include "clock.h" | 50 | #include "clock.h" |
| 49 | #include "cpu.h" | 51 | #include "cpu.h" |
| @@ -51,7 +53,8 @@ | |||
| 51 | /* clock information */ | 53 | /* clock information */ |
| 52 | 54 | ||
| 53 | static LIST_HEAD(clocks); | 55 | static LIST_HEAD(clocks); |
| 54 | static DEFINE_MUTEX(clocks_mutex); | 56 | |
| 57 | DEFINE_MUTEX(clocks_mutex); | ||
| 55 | 58 | ||
| 56 | /* old functions */ | 59 | /* old functions */ |
| 57 | 60 | ||
| @@ -178,12 +181,24 @@ unsigned long clk_get_rate(struct clk *clk) | |||
| 178 | 181 | ||
| 179 | long clk_round_rate(struct clk *clk, unsigned long rate) | 182 | long clk_round_rate(struct clk *clk, unsigned long rate) |
| 180 | { | 183 | { |
| 184 | if (!IS_ERR(clk) && clk->round_rate) | ||
| 185 | return (clk->round_rate)(clk, rate); | ||
| 186 | |||
| 181 | return rate; | 187 | return rate; |
| 182 | } | 188 | } |
| 183 | 189 | ||
| 184 | int clk_set_rate(struct clk *clk, unsigned long rate) | 190 | int clk_set_rate(struct clk *clk, unsigned long rate) |
| 185 | { | 191 | { |
| 186 | return -EINVAL; | 192 | int ret; |
| 193 | |||
| 194 | if (IS_ERR(clk)) | ||
| 195 | return -EINVAL; | ||
| 196 | |||
| 197 | mutex_lock(&clocks_mutex); | ||
| 198 | ret = (clk->set_rate)(clk, rate); | ||
| 199 | mutex_unlock(&clocks_mutex); | ||
| 200 | |||
| 201 | return ret; | ||
| 187 | } | 202 | } |
| 188 | 203 | ||
| 189 | struct clk *clk_get_parent(struct clk *clk) | 204 | struct clk *clk_get_parent(struct clk *clk) |
| @@ -191,6 +206,23 @@ struct clk *clk_get_parent(struct clk *clk) | |||
| 191 | return clk->parent; | 206 | return clk->parent; |
| 192 | } | 207 | } |
| 193 | 208 | ||
| 209 | int clk_set_parent(struct clk *clk, struct clk *parent) | ||
| 210 | { | ||
| 211 | int ret = 0; | ||
| 212 | |||
| 213 | if (IS_ERR(clk)) | ||
| 214 | return -EINVAL; | ||
| 215 | |||
| 216 | mutex_lock(&clocks_mutex); | ||
| 217 | |||
| 218 | if (clk->set_parent) | ||
| 219 | ret = (clk->set_parent)(clk, parent); | ||
| 220 | |||
| 221 | mutex_unlock(&clocks_mutex); | ||
| 222 | |||
| 223 | return ret; | ||
| 224 | } | ||
| 225 | |||
| 194 | EXPORT_SYMBOL(clk_get); | 226 | EXPORT_SYMBOL(clk_get); |
| 195 | EXPORT_SYMBOL(clk_put); | 227 | EXPORT_SYMBOL(clk_put); |
| 196 | EXPORT_SYMBOL(clk_enable); | 228 | EXPORT_SYMBOL(clk_enable); |
| @@ -199,6 +231,29 @@ EXPORT_SYMBOL(clk_get_rate); | |||
| 199 | EXPORT_SYMBOL(clk_round_rate); | 231 | EXPORT_SYMBOL(clk_round_rate); |
| 200 | EXPORT_SYMBOL(clk_set_rate); | 232 | EXPORT_SYMBOL(clk_set_rate); |
| 201 | EXPORT_SYMBOL(clk_get_parent); | 233 | EXPORT_SYMBOL(clk_get_parent); |
| 234 | EXPORT_SYMBOL(clk_set_parent); | ||
| 235 | |||
| 236 | /* base clock enable */ | ||
| 237 | |||
| 238 | static int s3c24xx_upll_enable(struct clk *clk, int enable) | ||
| 239 | { | ||
| 240 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); | ||
| 241 | unsigned long orig = clkslow; | ||
| 242 | |||
| 243 | if (enable) | ||
| 244 | clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF; | ||
| 245 | else | ||
| 246 | clkslow |= S3C2410_CLKSLOW_UCLK_OFF; | ||
| 247 | |||
| 248 | __raw_writel(clkslow, S3C2410_CLKSLOW); | ||
| 249 | |||
| 250 | /* if we started the UPLL, then allow to settle */ | ||
| 251 | |||
| 252 | if (enable && !(orig & S3C2410_CLKSLOW_UCLK_OFF)) | ||
| 253 | udelay(200); | ||
| 254 | |||
| 255 | return 0; | ||
| 256 | } | ||
| 202 | 257 | ||
| 203 | /* base clocks */ | 258 | /* base clocks */ |
| 204 | 259 | ||
| @@ -210,6 +265,14 @@ static struct clk clk_xtal = { | |||
| 210 | .ctrlbit = 0, | 265 | .ctrlbit = 0, |
| 211 | }; | 266 | }; |
| 212 | 267 | ||
| 268 | static struct clk clk_upll = { | ||
| 269 | .name = "upll", | ||
| 270 | .id = -1, | ||
| 271 | .parent = NULL, | ||
| 272 | .enable = s3c24xx_upll_enable, | ||
| 273 | .ctrlbit = 0, | ||
| 274 | }; | ||
| 275 | |||
| 213 | static struct clk clk_f = { | 276 | static struct clk clk_f = { |
| 214 | .name = "fclk", | 277 | .name = "fclk", |
| 215 | .id = -1, | 278 | .id = -1, |
| @@ -234,26 +297,124 @@ static struct clk clk_p = { | |||
| 234 | .ctrlbit = 0, | 297 | .ctrlbit = 0, |
| 235 | }; | 298 | }; |
| 236 | 299 | ||
| 300 | struct clk clk_usb_bus = { | ||
| 301 | .name = "usb-bus", | ||
| 302 | .id = -1, | ||
| 303 | .rate = 0, | ||
| 304 | .parent = &clk_upll, | ||
| 305 | }; | ||
| 306 | |||
| 237 | /* clocks that could be registered by external code */ | 307 | /* clocks that could be registered by external code */ |
| 238 | 308 | ||
| 309 | static int s3c24xx_dclk_enable(struct clk *clk, int enable) | ||
| 310 | { | ||
| 311 | unsigned long dclkcon = __raw_readl(S3C2410_DCLKCON); | ||
| 312 | |||
| 313 | if (enable) | ||
| 314 | dclkcon |= clk->ctrlbit; | ||
| 315 | else | ||
| 316 | dclkcon &= ~clk->ctrlbit; | ||
| 317 | |||
| 318 | __raw_writel(dclkcon, S3C2410_DCLKCON); | ||
| 319 | |||
| 320 | return 0; | ||
| 321 | } | ||
| 322 | |||
| 323 | static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent) | ||
| 324 | { | ||
| 325 | unsigned long dclkcon; | ||
| 326 | unsigned int uclk; | ||
| 327 | |||
| 328 | if (parent == &clk_upll) | ||
| 329 | uclk = 1; | ||
| 330 | else if (parent == &clk_p) | ||
| 331 | uclk = 0; | ||
| 332 | else | ||
| 333 | return -EINVAL; | ||
| 334 | |||
| 335 | clk->parent = parent; | ||
| 336 | |||
| 337 | dclkcon = __raw_readl(S3C2410_DCLKCON); | ||
| 338 | |||
| 339 | if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) { | ||
| 340 | if (uclk) | ||
| 341 | dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK; | ||
| 342 | else | ||
| 343 | dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK; | ||
| 344 | } else { | ||
| 345 | if (uclk) | ||
| 346 | dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK; | ||
| 347 | else | ||
| 348 | dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK; | ||
| 349 | } | ||
| 350 | |||
| 351 | __raw_writel(dclkcon, S3C2410_DCLKCON); | ||
| 352 | |||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | |||
| 357 | static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) | ||
| 358 | { | ||
| 359 | unsigned long mask; | ||
| 360 | unsigned long source; | ||
| 361 | |||
| 362 | /* calculate the MISCCR setting for the clock */ | ||
| 363 | |||
| 364 | if (parent == &clk_xtal) | ||
| 365 | source = S3C2410_MISCCR_CLK0_MPLL; | ||
| 366 | else if (parent == &clk_upll) | ||
| 367 | source = S3C2410_MISCCR_CLK0_UPLL; | ||
| 368 | else if (parent == &clk_f) | ||
| 369 | source = S3C2410_MISCCR_CLK0_FCLK; | ||
| 370 | else if (parent == &clk_p) | ||
| 371 | source = S3C2410_MISCCR_CLK0_PCLK; | ||
| 372 | else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) | ||
| 373 | source = S3C2410_MISCCR_CLK0_DCLK0; | ||
| 374 | else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1) | ||
| 375 | source = S3C2410_MISCCR_CLK0_DCLK0; | ||
| 376 | else | ||
| 377 | return -EINVAL; | ||
| 378 | |||
| 379 | if (clk == &s3c24xx_dclk0) | ||
| 380 | mask = S3C2410_MISCCR_CLK0_MASK; | ||
| 381 | else { | ||
| 382 | source <<= 4; | ||
| 383 | mask = S3C2410_MISCCR_CLK1_MASK; | ||
| 384 | } | ||
| 385 | |||
| 386 | s3c2410_modify_misccr(mask, source); | ||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | |||
| 390 | /* external clock definitions */ | ||
| 391 | |||
| 239 | struct clk s3c24xx_dclk0 = { | 392 | struct clk s3c24xx_dclk0 = { |
| 240 | .name = "dclk0", | 393 | .name = "dclk0", |
| 241 | .id = -1, | 394 | .id = -1, |
| 395 | .ctrlbit = S3C2410_DCLKCON_DCLK0EN, | ||
| 396 | .enable = s3c24xx_dclk_enable, | ||
| 397 | .set_parent = s3c24xx_dclk_setparent, | ||
| 242 | }; | 398 | }; |
| 243 | 399 | ||
| 244 | struct clk s3c24xx_dclk1 = { | 400 | struct clk s3c24xx_dclk1 = { |
| 245 | .name = "dclk1", | 401 | .name = "dclk1", |
| 246 | .id = -1, | 402 | .id = -1, |
| 403 | .ctrlbit = S3C2410_DCLKCON_DCLK0EN, | ||
| 404 | .enable = s3c24xx_dclk_enable, | ||
| 405 | .set_parent = s3c24xx_dclk_setparent, | ||
| 247 | }; | 406 | }; |
| 248 | 407 | ||
| 249 | struct clk s3c24xx_clkout0 = { | 408 | struct clk s3c24xx_clkout0 = { |
| 250 | .name = "clkout0", | 409 | .name = "clkout0", |
| 251 | .id = -1, | 410 | .id = -1, |
| 411 | .set_parent = s3c24xx_clkout_setparent, | ||
| 252 | }; | 412 | }; |
| 253 | 413 | ||
| 254 | struct clk s3c24xx_clkout1 = { | 414 | struct clk s3c24xx_clkout1 = { |
| 255 | .name = "clkout1", | 415 | .name = "clkout1", |
| 256 | .id = -1, | 416 | .id = -1, |
| 417 | .set_parent = s3c24xx_clkout_setparent, | ||
| 257 | }; | 418 | }; |
| 258 | 419 | ||
| 259 | struct clk s3c24xx_uclk = { | 420 | struct clk s3c24xx_uclk = { |
| @@ -262,7 +423,7 @@ struct clk s3c24xx_uclk = { | |||
| 262 | }; | 423 | }; |
| 263 | 424 | ||
| 264 | 425 | ||
| 265 | /* clock definitions */ | 426 | /* standard clock definitions */ |
| 266 | 427 | ||
| 267 | static struct clk init_clocks[] = { | 428 | static struct clk init_clocks[] = { |
| 268 | { | 429 | { |
| @@ -374,7 +535,7 @@ int s3c24xx_register_clock(struct clk *clk) | |||
| 374 | 535 | ||
| 375 | /* if this is a standard clock, set the usage state */ | 536 | /* if this is a standard clock, set the usage state */ |
| 376 | 537 | ||
| 377 | if (clk->ctrlbit) { | 538 | if (clk->ctrlbit && clk->enable == s3c24xx_clkcon_enable) { |
| 378 | unsigned long clkcon = __raw_readl(S3C2410_CLKCON); | 539 | unsigned long clkcon = __raw_readl(S3C2410_CLKCON); |
| 379 | 540 | ||
| 380 | clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0; | 541 | clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0; |
| @@ -396,6 +557,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
| 396 | unsigned long hclk, | 557 | unsigned long hclk, |
| 397 | unsigned long pclk) | 558 | unsigned long pclk) |
| 398 | { | 559 | { |
| 560 | unsigned long upllcon = __raw_readl(S3C2410_UPLLCON); | ||
| 399 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); | 561 | unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW); |
| 400 | struct clk *clkp = init_clocks; | 562 | struct clk *clkp = init_clocks; |
| 401 | int ptr; | 563 | int ptr; |
| @@ -406,6 +568,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
| 406 | /* initialise the main system clocks */ | 568 | /* initialise the main system clocks */ |
| 407 | 569 | ||
| 408 | clk_xtal.rate = xtal; | 570 | clk_xtal.rate = xtal; |
| 571 | clk_upll.rate = s3c2410_get_pll(upllcon, xtal); | ||
| 409 | 572 | ||
| 410 | clk_h.rate = hclk; | 573 | clk_h.rate = hclk; |
| 411 | clk_p.rate = pclk; | 574 | clk_p.rate = pclk; |
| @@ -439,6 +602,9 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
| 439 | if (s3c24xx_register_clock(&clk_xtal) < 0) | 602 | if (s3c24xx_register_clock(&clk_xtal) < 0) |
| 440 | printk(KERN_ERR "failed to register master xtal\n"); | 603 | printk(KERN_ERR "failed to register master xtal\n"); |
| 441 | 604 | ||
| 605 | if (s3c24xx_register_clock(&clk_upll) < 0) | ||
| 606 | printk(KERN_ERR "failed to register upll clock\n"); | ||
| 607 | |||
| 442 | if (s3c24xx_register_clock(&clk_f) < 0) | 608 | if (s3c24xx_register_clock(&clk_f) < 0) |
| 443 | printk(KERN_ERR "failed to register cpu fclk\n"); | 609 | printk(KERN_ERR "failed to register cpu fclk\n"); |
| 444 | 610 | ||
| @@ -448,6 +614,10 @@ int __init s3c24xx_setup_clocks(unsigned long xtal, | |||
| 448 | if (s3c24xx_register_clock(&clk_p) < 0) | 614 | if (s3c24xx_register_clock(&clk_p) < 0) |
| 449 | printk(KERN_ERR "failed to register cpu pclk\n"); | 615 | printk(KERN_ERR "failed to register cpu pclk\n"); |
| 450 | 616 | ||
| 617 | |||
| 618 | if (s3c24xx_register_clock(&clk_usb_bus) < 0) | ||
| 619 | printk(KERN_ERR "failed to register usb bus clock\n"); | ||
| 620 | |||
| 451 | /* register clocks from clock array */ | 621 | /* register clocks from clock array */ |
| 452 | 622 | ||
| 453 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { | 623 | for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { |
