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