linux/arch/arm/mach-s3c2412/clock.c
<<
>>
Prefs
   1/* linux/arch/arm/mach-s3c2412/clock.c
   2 *
   3 * Copyright (c) 2006 Simtec Electronics
   4 *      Ben Dooks <ben@simtec.co.uk>
   5 *
   6 * S3C2412,S3C2413 Clock control 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 as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21*/
  22
  23#include <linux/init.h>
  24#include <linux/module.h>
  25#include <linux/kernel.h>
  26#include <linux/list.h>
  27#include <linux/errno.h>
  28#include <linux/err.h>
  29#include <linux/sysdev.h>
  30#include <linux/clk.h>
  31#include <linux/mutex.h>
  32#include <linux/delay.h>
  33#include <linux/serial_core.h>
  34#include <linux/io.h>
  35
  36#include <asm/mach/map.h>
  37
  38#include <mach/hardware.h>
  39
  40#include <plat/regs-serial.h>
  41#include <mach/regs-clock.h>
  42#include <mach/regs-gpio.h>
  43
  44#include <plat/s3c2412.h>
  45#include <plat/clock.h>
  46#include <plat/cpu.h>
  47
  48/* We currently have to assume that the system is running
  49 * from the XTPll input, and that all ***REFCLKs are being
  50 * fed from it, as we cannot read the state of OM[4] from
  51 * software.
  52 *
  53 * It would be possible for each board initialisation to
  54 * set the correct muxing at initialisation
  55*/
  56
  57static int s3c2412_clkcon_enable(struct clk *clk, int enable)
  58{
  59        unsigned int clocks = clk->ctrlbit;
  60        unsigned long clkcon;
  61
  62        clkcon = __raw_readl(S3C2410_CLKCON);
  63
  64        if (enable)
  65                clkcon |= clocks;
  66        else
  67                clkcon &= ~clocks;
  68
  69        __raw_writel(clkcon, S3C2410_CLKCON);
  70
  71        return 0;
  72}
  73
  74static int s3c2412_upll_enable(struct clk *clk, int enable)
  75{
  76        unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
  77        unsigned long orig = upllcon;
  78
  79        if (!enable)
  80                upllcon |= S3C2412_PLLCON_OFF;
  81        else
  82                upllcon &= ~S3C2412_PLLCON_OFF;
  83
  84        __raw_writel(upllcon, S3C2410_UPLLCON);
  85
  86        /* allow ~150uS for the PLL to settle and lock */
  87
  88        if (enable && (orig & S3C2412_PLLCON_OFF))
  89                udelay(150);
  90
  91        return 0;
  92}
  93
  94/* clock selections */
  95
  96static struct clk clk_erefclk = {
  97        .name           = "erefclk",
  98        .id             = -1,
  99};
 100
 101static struct clk clk_urefclk = {
 102        .name           = "urefclk",
 103        .id             = -1,
 104};
 105
 106static int s3c2412_setparent_usysclk(struct clk *clk, struct clk *parent)
 107{
 108        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 109
 110        if (parent == &clk_urefclk)
 111                clksrc &= ~S3C2412_CLKSRC_USYSCLK_UPLL;
 112        else if (parent == &clk_upll)
 113                clksrc |= S3C2412_CLKSRC_USYSCLK_UPLL;
 114        else
 115                return -EINVAL;
 116
 117        clk->parent = parent;
 118
 119        __raw_writel(clksrc, S3C2412_CLKSRC);
 120        return 0;
 121}
 122
 123static struct clk clk_usysclk = {
 124        .name           = "usysclk",
 125        .id             = -1,
 126        .parent         = &clk_xtal,
 127        .ops            = &(struct clk_ops) {
 128                .set_parent     = s3c2412_setparent_usysclk,
 129        },
 130};
 131
 132static struct clk clk_mrefclk = {
 133        .name           = "mrefclk",
 134        .parent         = &clk_xtal,
 135        .id             = -1,
 136};
 137
 138static struct clk clk_mdivclk = {
 139        .name           = "mdivclk",
 140        .parent         = &clk_xtal,
 141        .id             = -1,
 142};
 143
 144static int s3c2412_setparent_usbsrc(struct clk *clk, struct clk *parent)
 145{
 146        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 147
 148        if (parent == &clk_usysclk)
 149                clksrc &= ~S3C2412_CLKSRC_USBCLK_HCLK;
 150        else if (parent == &clk_h)
 151                clksrc |= S3C2412_CLKSRC_USBCLK_HCLK;
 152        else
 153                return -EINVAL;
 154
 155        clk->parent = parent;
 156
 157        __raw_writel(clksrc, S3C2412_CLKSRC);
 158        return 0;
 159}
 160
 161static unsigned long s3c2412_roundrate_usbsrc(struct clk *clk,
 162                                              unsigned long rate)
 163{
 164        unsigned long parent_rate = clk_get_rate(clk->parent);
 165        int div;
 166
 167        if (rate > parent_rate)
 168                return parent_rate;
 169
 170        div = parent_rate / rate;
 171        if (div > 2)
 172                div = 2;
 173
 174        return parent_rate / div;
 175}
 176
 177static unsigned long s3c2412_getrate_usbsrc(struct clk *clk)
 178{
 179        unsigned long parent_rate = clk_get_rate(clk->parent);
 180        unsigned long div = __raw_readl(S3C2410_CLKDIVN);
 181
 182        return parent_rate / ((div & S3C2412_CLKDIVN_USB48DIV) ? 2 : 1);
 183}
 184
 185static int s3c2412_setrate_usbsrc(struct clk *clk, unsigned long rate)
 186{
 187        unsigned long parent_rate = clk_get_rate(clk->parent);
 188        unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
 189
 190        rate = s3c2412_roundrate_usbsrc(clk, rate);
 191
 192        if ((parent_rate / rate) == 2)
 193                clkdivn |= S3C2412_CLKDIVN_USB48DIV;
 194        else
 195                clkdivn &= ~S3C2412_CLKDIVN_USB48DIV;
 196
 197        __raw_writel(clkdivn, S3C2410_CLKDIVN);
 198        return 0;
 199}
 200
 201static struct clk clk_usbsrc = {
 202        .name           = "usbsrc",
 203        .id             = -1,
 204        .ops            = &(struct clk_ops) {
 205                .get_rate       = s3c2412_getrate_usbsrc,
 206                .set_rate       = s3c2412_setrate_usbsrc,
 207                .round_rate     = s3c2412_roundrate_usbsrc,
 208                .set_parent     = s3c2412_setparent_usbsrc,
 209        },
 210};
 211
 212static int s3c2412_setparent_msysclk(struct clk *clk, struct clk *parent)
 213{
 214        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 215
 216        if (parent == &clk_mdivclk)
 217                clksrc &= ~S3C2412_CLKSRC_MSYSCLK_MPLL;
 218        else if (parent == &clk_mpll)
 219                clksrc |= S3C2412_CLKSRC_MSYSCLK_MPLL;
 220        else
 221                return -EINVAL;
 222
 223        clk->parent = parent;
 224
 225        __raw_writel(clksrc, S3C2412_CLKSRC);
 226        return 0;
 227}
 228
 229static struct clk clk_msysclk = {
 230        .name           = "msysclk",
 231        .id             = -1,
 232        .ops            = &(struct clk_ops) {
 233                .set_parent     = s3c2412_setparent_msysclk,
 234        },
 235};
 236
 237static int s3c2412_setparent_armclk(struct clk *clk, struct clk *parent)
 238{
 239        unsigned long flags;
 240        unsigned long clkdiv;
 241        unsigned long dvs;
 242
 243        /* Note, we current equate fclk andf msysclk for S3C2412 */
 244
 245        if (parent == &clk_msysclk || parent == &clk_f)
 246                dvs = 0;
 247        else if (parent == &clk_h)
 248                dvs = S3C2412_CLKDIVN_DVSEN;
 249        else
 250                return -EINVAL;
 251
 252        clk->parent = parent;
 253
 254        /* update this under irq lockdown, clkdivn is not protected
 255         * by the clock system. */
 256
 257        local_irq_save(flags);
 258
 259        clkdiv  = __raw_readl(S3C2410_CLKDIVN);
 260        clkdiv &= ~S3C2412_CLKDIVN_DVSEN;
 261        clkdiv |= dvs;
 262        __raw_writel(clkdiv, S3C2410_CLKDIVN);
 263
 264        local_irq_restore(flags);
 265
 266        return 0;
 267}
 268
 269static struct clk clk_armclk = {
 270        .name           = "armclk",
 271        .id             = -1,
 272        .parent         = &clk_msysclk,
 273        .ops            = &(struct clk_ops) {
 274                .set_parent     = s3c2412_setparent_armclk,
 275        },
 276};
 277
 278/* these next clocks have an divider immediately after them,
 279 * so we can register them with their divider and leave out the
 280 * intermediate clock stage
 281*/
 282static unsigned long s3c2412_roundrate_clksrc(struct clk *clk,
 283                                              unsigned long rate)
 284{
 285        unsigned long parent_rate = clk_get_rate(clk->parent);
 286        int div;
 287
 288        if (rate > parent_rate)
 289                return parent_rate;
 290
 291        /* note, we remove the +/- 1 calculations as they cancel out */
 292
 293        div = (rate / parent_rate);
 294
 295        if (div < 1)
 296                div = 1;
 297        else if (div > 16)
 298                div = 16;
 299
 300        return parent_rate / div;
 301}
 302
 303static int s3c2412_setparent_uart(struct clk *clk, struct clk *parent)
 304{
 305        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 306
 307        if (parent == &clk_erefclk)
 308                clksrc &= ~S3C2412_CLKSRC_UARTCLK_MPLL;
 309        else if (parent == &clk_mpll)
 310                clksrc |= S3C2412_CLKSRC_UARTCLK_MPLL;
 311        else
 312                return -EINVAL;
 313
 314        clk->parent = parent;
 315
 316        __raw_writel(clksrc, S3C2412_CLKSRC);
 317        return 0;
 318}
 319
 320static unsigned long s3c2412_getrate_uart(struct clk *clk)
 321{
 322        unsigned long parent_rate = clk_get_rate(clk->parent);
 323        unsigned long div = __raw_readl(S3C2410_CLKDIVN);
 324
 325        div &= S3C2412_CLKDIVN_UARTDIV_MASK;
 326        div >>= S3C2412_CLKDIVN_UARTDIV_SHIFT;
 327
 328        return parent_rate / (div + 1);
 329}
 330
 331static int s3c2412_setrate_uart(struct clk *clk, unsigned long rate)
 332{
 333        unsigned long parent_rate = clk_get_rate(clk->parent);
 334        unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
 335
 336        rate = s3c2412_roundrate_clksrc(clk, rate);
 337
 338        clkdivn &= ~S3C2412_CLKDIVN_UARTDIV_MASK;
 339        clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_UARTDIV_SHIFT;
 340
 341        __raw_writel(clkdivn, S3C2410_CLKDIVN);
 342        return 0;
 343}
 344
 345static struct clk clk_uart = {
 346        .name           = "uartclk",
 347        .id             = -1,
 348        .ops            = &(struct clk_ops) {
 349                .get_rate       = s3c2412_getrate_uart,
 350                .set_rate       = s3c2412_setrate_uart,
 351                .set_parent     = s3c2412_setparent_uart,
 352                .round_rate     = s3c2412_roundrate_clksrc,
 353        },
 354};
 355
 356static int s3c2412_setparent_i2s(struct clk *clk, struct clk *parent)
 357{
 358        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 359
 360        if (parent == &clk_erefclk)
 361                clksrc &= ~S3C2412_CLKSRC_I2SCLK_MPLL;
 362        else if (parent == &clk_mpll)
 363                clksrc |= S3C2412_CLKSRC_I2SCLK_MPLL;
 364        else
 365                return -EINVAL;
 366
 367        clk->parent = parent;
 368
 369        __raw_writel(clksrc, S3C2412_CLKSRC);
 370        return 0;
 371}
 372
 373static unsigned long s3c2412_getrate_i2s(struct clk *clk)
 374{
 375        unsigned long parent_rate = clk_get_rate(clk->parent);
 376        unsigned long div = __raw_readl(S3C2410_CLKDIVN);
 377
 378        div &= S3C2412_CLKDIVN_I2SDIV_MASK;
 379        div >>= S3C2412_CLKDIVN_I2SDIV_SHIFT;
 380
 381        return parent_rate / (div + 1);
 382}
 383
 384static int s3c2412_setrate_i2s(struct clk *clk, unsigned long rate)
 385{
 386        unsigned long parent_rate = clk_get_rate(clk->parent);
 387        unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
 388
 389        rate = s3c2412_roundrate_clksrc(clk, rate);
 390
 391        clkdivn &= ~S3C2412_CLKDIVN_I2SDIV_MASK;
 392        clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_I2SDIV_SHIFT;
 393
 394        __raw_writel(clkdivn, S3C2410_CLKDIVN);
 395        return 0;
 396}
 397
 398static struct clk clk_i2s = {
 399        .name           = "i2sclk",
 400        .id             = -1,
 401        .ops            = &(struct clk_ops) {
 402                .get_rate       = s3c2412_getrate_i2s,
 403                .set_rate       = s3c2412_setrate_i2s,
 404                .set_parent     = s3c2412_setparent_i2s,
 405                .round_rate     = s3c2412_roundrate_clksrc,
 406        },
 407};
 408
 409static int s3c2412_setparent_cam(struct clk *clk, struct clk *parent)
 410{
 411        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 412
 413        if (parent == &clk_usysclk)
 414                clksrc &= ~S3C2412_CLKSRC_CAMCLK_HCLK;
 415        else if (parent == &clk_h)
 416                clksrc |= S3C2412_CLKSRC_CAMCLK_HCLK;
 417        else
 418                return -EINVAL;
 419
 420        clk->parent = parent;
 421
 422        __raw_writel(clksrc, S3C2412_CLKSRC);
 423        return 0;
 424}
 425static unsigned long s3c2412_getrate_cam(struct clk *clk)
 426{
 427        unsigned long parent_rate = clk_get_rate(clk->parent);
 428        unsigned long div = __raw_readl(S3C2410_CLKDIVN);
 429
 430        div &= S3C2412_CLKDIVN_CAMDIV_MASK;
 431        div >>= S3C2412_CLKDIVN_CAMDIV_SHIFT;
 432
 433        return parent_rate / (div + 1);
 434}
 435
 436static int s3c2412_setrate_cam(struct clk *clk, unsigned long rate)
 437{
 438        unsigned long parent_rate = clk_get_rate(clk->parent);
 439        unsigned long clkdivn = __raw_readl(S3C2410_CLKDIVN);
 440
 441        rate = s3c2412_roundrate_clksrc(clk, rate);
 442
 443        clkdivn &= ~S3C2412_CLKDIVN_CAMDIV_MASK;
 444        clkdivn |= ((parent_rate / rate) - 1) << S3C2412_CLKDIVN_CAMDIV_SHIFT;
 445
 446        __raw_writel(clkdivn, S3C2410_CLKDIVN);
 447        return 0;
 448}
 449
 450static struct clk clk_cam = {
 451        .name           = "camif-upll", /* same as 2440 name */
 452        .id             = -1,
 453        .ops            = &(struct clk_ops) {
 454                .get_rate       = s3c2412_getrate_cam,
 455                .set_rate       = s3c2412_setrate_cam,
 456                .set_parent     = s3c2412_setparent_cam,
 457                .round_rate     = s3c2412_roundrate_clksrc,
 458        },
 459};
 460
 461/* standard clock definitions */
 462
 463static struct clk init_clocks_disable[] = {
 464        {
 465                .name           = "nand",
 466                .id             = -1,
 467                .parent         = &clk_h,
 468                .enable         = s3c2412_clkcon_enable,
 469                .ctrlbit        = S3C2412_CLKCON_NAND,
 470        }, {
 471                .name           = "sdi",
 472                .id             = -1,
 473                .parent         = &clk_p,
 474                .enable         = s3c2412_clkcon_enable,
 475                .ctrlbit        = S3C2412_CLKCON_SDI,
 476        }, {
 477                .name           = "adc",
 478                .id             = -1,
 479                .parent         = &clk_p,
 480                .enable         = s3c2412_clkcon_enable,
 481                .ctrlbit        = S3C2412_CLKCON_ADC,
 482        }, {
 483                .name           = "i2c",
 484                .id             = -1,
 485                .parent         = &clk_p,
 486                .enable         = s3c2412_clkcon_enable,
 487                .ctrlbit        = S3C2412_CLKCON_IIC,
 488        }, {
 489                .name           = "iis",
 490                .id             = -1,
 491                .parent         = &clk_p,
 492                .enable         = s3c2412_clkcon_enable,
 493                .ctrlbit        = S3C2412_CLKCON_IIS,
 494        }, {
 495                .name           = "spi",
 496                .id             = -1,
 497                .parent         = &clk_p,
 498                .enable         = s3c2412_clkcon_enable,
 499                .ctrlbit        = S3C2412_CLKCON_SPI,
 500        }
 501};
 502
 503static struct clk init_clocks[] = {
 504        {
 505                .name           = "dma",
 506                .id             = 0,
 507                .parent         = &clk_h,
 508                .enable         = s3c2412_clkcon_enable,
 509                .ctrlbit        = S3C2412_CLKCON_DMA0,
 510        }, {
 511                .name           = "dma",
 512                .id             = 1,
 513                .parent         = &clk_h,
 514                .enable         = s3c2412_clkcon_enable,
 515                .ctrlbit        = S3C2412_CLKCON_DMA1,
 516        }, {
 517                .name           = "dma",
 518                .id             = 2,
 519                .parent         = &clk_h,
 520                .enable         = s3c2412_clkcon_enable,
 521                .ctrlbit        = S3C2412_CLKCON_DMA2,
 522        }, {
 523                .name           = "dma",
 524                .id             = 3,
 525                .parent         = &clk_h,
 526                .enable         = s3c2412_clkcon_enable,
 527                .ctrlbit        = S3C2412_CLKCON_DMA3,
 528        }, {
 529                .name           = "lcd",
 530                .id             = -1,
 531                .parent         = &clk_h,
 532                .enable         = s3c2412_clkcon_enable,
 533                .ctrlbit        = S3C2412_CLKCON_LCDC,
 534        }, {
 535                .name           = "gpio",
 536                .id             = -1,
 537                .parent         = &clk_p,
 538                .enable         = s3c2412_clkcon_enable,
 539                .ctrlbit        = S3C2412_CLKCON_GPIO,
 540        }, {
 541                .name           = "usb-host",
 542                .id             = -1,
 543                .parent         = &clk_h,
 544                .enable         = s3c2412_clkcon_enable,
 545                .ctrlbit        = S3C2412_CLKCON_USBH,
 546        }, {
 547                .name           = "usb-device",
 548                .id             = -1,
 549                .parent         = &clk_h,
 550                .enable         = s3c2412_clkcon_enable,
 551                .ctrlbit        = S3C2412_CLKCON_USBD,
 552        }, {
 553                .name           = "timers",
 554                .id             = -1,
 555                .parent         = &clk_p,
 556                .enable         = s3c2412_clkcon_enable,
 557                .ctrlbit        = S3C2412_CLKCON_PWMT,
 558        }, {
 559                .name           = "uart",
 560                .id             = 0,
 561                .parent         = &clk_p,
 562                .enable         = s3c2412_clkcon_enable,
 563                .ctrlbit        = S3C2412_CLKCON_UART0,
 564        }, {
 565                .name           = "uart",
 566                .id             = 1,
 567                .parent         = &clk_p,
 568                .enable         = s3c2412_clkcon_enable,
 569                .ctrlbit        = S3C2412_CLKCON_UART1,
 570        }, {
 571                .name           = "uart",
 572                .id             = 2,
 573                .parent         = &clk_p,
 574                .enable         = s3c2412_clkcon_enable,
 575                .ctrlbit        = S3C2412_CLKCON_UART2,
 576        }, {
 577                .name           = "rtc",
 578                .id             = -1,
 579                .parent         = &clk_p,
 580                .enable         = s3c2412_clkcon_enable,
 581                .ctrlbit        = S3C2412_CLKCON_RTC,
 582        }, {
 583                .name           = "watchdog",
 584                .id             = -1,
 585                .parent         = &clk_p,
 586                .ctrlbit        = 0,
 587        }, {
 588                .name           = "usb-bus-gadget",
 589                .id             = -1,
 590                .parent         = &clk_usb_bus,
 591                .enable         = s3c2412_clkcon_enable,
 592                .ctrlbit        = S3C2412_CLKCON_USB_DEV48,
 593        }, {
 594                .name           = "usb-bus-host",
 595                .id             = -1,
 596                .parent         = &clk_usb_bus,
 597                .enable         = s3c2412_clkcon_enable,
 598                .ctrlbit        = S3C2412_CLKCON_USB_HOST48,
 599        }
 600};
 601
 602/* clocks to add where we need to check their parentage */
 603
 604struct clk_init {
 605        struct clk      *clk;
 606        unsigned int     bit;
 607        struct clk      *src_0;
 608        struct clk      *src_1;
 609};
 610
 611static struct clk_init clks_src[] __initdata = {
 612        {
 613                .clk    = &clk_usysclk,
 614                .bit    = S3C2412_CLKSRC_USBCLK_HCLK,
 615                .src_0  = &clk_urefclk,
 616                .src_1  = &clk_upll,
 617        }, {
 618                .clk    = &clk_i2s,
 619                .bit    = S3C2412_CLKSRC_I2SCLK_MPLL,
 620                .src_0  = &clk_erefclk,
 621                .src_1  = &clk_mpll,
 622        }, {
 623                .clk    = &clk_cam,
 624                .bit    = S3C2412_CLKSRC_CAMCLK_HCLK,
 625                .src_0  = &clk_usysclk,
 626                .src_1  = &clk_h,
 627        }, {
 628                .clk    = &clk_msysclk,
 629                .bit    = S3C2412_CLKSRC_MSYSCLK_MPLL,
 630                .src_0  = &clk_mdivclk,
 631                .src_1  = &clk_mpll,
 632        }, {
 633                .clk    = &clk_uart,
 634                .bit    = S3C2412_CLKSRC_UARTCLK_MPLL,
 635                .src_0  = &clk_erefclk,
 636                .src_1  = &clk_mpll,
 637        }, {
 638                .clk    = &clk_usbsrc,
 639                .bit    = S3C2412_CLKSRC_USBCLK_HCLK,
 640                .src_0  = &clk_usysclk,
 641                .src_1  = &clk_h,
 642        /* here we assume  OM[4] select xtal */
 643        }, {
 644                .clk    = &clk_erefclk,
 645                .bit    = S3C2412_CLKSRC_EREFCLK_EXTCLK,
 646                .src_0  = &clk_xtal,
 647                .src_1  = &clk_ext,
 648        }, {
 649                .clk    = &clk_urefclk,
 650                .bit    = S3C2412_CLKSRC_UREFCLK_EXTCLK,
 651                .src_0  = &clk_xtal,
 652                .src_1  = &clk_ext,
 653        },
 654};
 655
 656/* s3c2412_clk_initparents
 657 *
 658 * Initialise the parents for the clocks that we get at start-time
 659*/
 660
 661static void __init s3c2412_clk_initparents(void)
 662{
 663        unsigned long clksrc = __raw_readl(S3C2412_CLKSRC);
 664        struct clk_init *cip = clks_src;
 665        struct clk *src;
 666        int ptr;
 667        int ret;
 668
 669        for (ptr = 0; ptr < ARRAY_SIZE(clks_src); ptr++, cip++) {
 670                ret = s3c24xx_register_clock(cip->clk);
 671                if (ret < 0) {
 672                        printk(KERN_ERR "Failed to register clock %s (%d)\n",
 673                               cip->clk->name, ret);
 674                }
 675
 676                src = (clksrc & cip->bit) ? cip->src_1 : cip->src_0;
 677
 678                printk(KERN_INFO "%s: parent %s\n", cip->clk->name, src->name);
 679                clk_set_parent(cip->clk, src);
 680        }
 681}
 682
 683/* clocks to add straight away */
 684
 685static struct clk *clks[] __initdata = {
 686        &clk_ext,
 687        &clk_usb_bus,
 688        &clk_mrefclk,
 689        &clk_armclk,
 690};
 691
 692int __init s3c2412_baseclk_add(void)
 693{
 694        unsigned long clkcon  = __raw_readl(S3C2410_CLKCON);
 695        unsigned int dvs;
 696        struct clk *clkp;
 697        int ret;
 698        int ptr;
 699
 700        clk_upll.enable = s3c2412_upll_enable;
 701        clk_usb_bus.parent = &clk_usbsrc;
 702        clk_usb_bus.rate = 0x0;
 703
 704        clk_f.parent = &clk_msysclk;
 705
 706        s3c2412_clk_initparents();
 707
 708        for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
 709                clkp = clks[ptr];
 710
 711                ret = s3c24xx_register_clock(clkp);
 712                if (ret < 0) {
 713                        printk(KERN_ERR "Failed to register clock %s (%d)\n",
 714                               clkp->name, ret);
 715                }
 716        }
 717
 718        /* set the dvs state according to what we got at boot time */
 719
 720        dvs = __raw_readl(S3C2410_CLKDIVN) & S3C2412_CLKDIVN_DVSEN;
 721
 722        if (dvs)
 723                clk_armclk.parent = &clk_h;
 724
 725        printk(KERN_INFO "S3C2412: DVS is %s\n", dvs ? "on" : "off");
 726
 727        /* ensure usb bus clock is within correct rate of 48MHz */
 728
 729        if (clk_get_rate(&clk_usb_bus) != (48 * 1000 * 1000)) {
 730                printk(KERN_INFO "Warning: USB bus clock not at 48MHz\n");
 731
 732                /* for the moment, let's use the UPLL, and see if we can
 733                 * get 48MHz */
 734
 735                clk_set_parent(&clk_usysclk, &clk_upll);
 736                clk_set_parent(&clk_usbsrc, &clk_usysclk);
 737                clk_set_rate(&clk_usbsrc, 48*1000*1000);
 738        }
 739
 740        printk("S3C2412: upll %s, %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n",
 741               (__raw_readl(S3C2410_UPLLCON) & S3C2412_PLLCON_OFF) ? "off":"on",
 742               print_mhz(clk_get_rate(&clk_upll)),
 743               print_mhz(clk_get_rate(&clk_usb_bus)));
 744
 745        /* register clocks from clock array */
 746
 747        clkp = init_clocks;
 748        for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
 749                /* ensure that we note the clock state */
 750
 751                clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
 752
 753                ret = s3c24xx_register_clock(clkp);
 754                if (ret < 0) {
 755                        printk(KERN_ERR "Failed to register clock %s (%d)\n",
 756                               clkp->name, ret);
 757                }
 758        }
 759
 760        /* We must be careful disabling the clocks we are not intending to
 761         * be using at boot time, as subsystems such as the LCD which do
 762         * their own DMA requests to the bus can cause the system to lockup
 763         * if they where in the middle of requesting bus access.
 764         *
 765         * Disabling the LCD clock if the LCD is active is very dangerous,
 766         * and therefore the bootloader should be careful to not enable
 767         * the LCD clock if it is not needed.
 768        */
 769
 770        /* install (and disable) the clocks we do not need immediately */
 771
 772        clkp = init_clocks_disable;
 773        for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
 774
 775                ret = s3c24xx_register_clock(clkp);
 776                if (ret < 0) {
 777                        printk(KERN_ERR "Failed to register clock %s (%d)\n",
 778                               clkp->name, ret);
 779                }
 780
 781                s3c2412_clkcon_enable(clkp, 0);
 782        }
 783
 784        s3c_pwmclk_init();
 785        return 0;
 786}
 787