linux/arch/arm/mach-omap2/clock24xx.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/arm/mach-omap2/clock.c
   3 *
   4 *  Copyright (C) 2005-2008 Texas Instruments, Inc.
   5 *  Copyright (C) 2004-2008 Nokia Corporation
   6 *
   7 *  Contacts:
   8 *  Richard Woodruff <r-woodruff2@ti.com>
   9 *  Paul Walmsley
  10 *
  11 *  Based on earlier work by Tuukka Tikkanen, Tony Lindgren,
  12 *  Gordon McNutt and RidgeRun, Inc.
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License version 2 as
  16 * published by the Free Software Foundation.
  17 */
  18#undef DEBUG
  19
  20#include <linux/module.h>
  21#include <linux/kernel.h>
  22#include <linux/device.h>
  23#include <linux/list.h>
  24#include <linux/errno.h>
  25#include <linux/delay.h>
  26#include <linux/clk.h>
  27
  28#include <linux/io.h>
  29#include <linux/cpufreq.h>
  30
  31#include <asm/arch/clock.h>
  32#include <asm/arch/sram.h>
  33#include <asm/div64.h>
  34#include <asm/bitops.h>
  35
  36#include "memory.h"
  37#include "clock.h"
  38#include "clock24xx.h"
  39#include "prm.h"
  40#include "prm-regbits-24xx.h"
  41#include "cm.h"
  42#include "cm-regbits-24xx.h"
  43
  44/* CM_CLKEN_PLL.EN_{54,96}M_PLL options (24XX) */
  45#define EN_APLL_STOPPED                 0
  46#define EN_APLL_LOCKED                  3
  47
  48/* CM_CLKSEL1_PLL.APLLS_CLKIN options (24XX) */
  49#define APLLS_CLKIN_19_2MHZ             0
  50#define APLLS_CLKIN_13MHZ               2
  51#define APLLS_CLKIN_12MHZ               3
  52
  53/* #define DOWN_VARIABLE_DPLL 1 */              /* Experimental */
  54
  55static struct prcm_config *curr_prcm_set;
  56static struct clk *vclk;
  57static struct clk *sclk;
  58
  59/*-------------------------------------------------------------------------
  60 * Omap24xx specific clock functions
  61 *-------------------------------------------------------------------------*/
  62
  63/* This actually returns the rate of core_ck, not dpll_ck. */
  64static u32 omap2_get_dpll_rate_24xx(struct clk *tclk)
  65{
  66        long long dpll_clk;
  67        u8 amult;
  68
  69        dpll_clk = omap2_get_dpll_rate(tclk);
  70
  71        amult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
  72        amult &= OMAP24XX_CORE_CLK_SRC_MASK;
  73        dpll_clk *= amult;
  74
  75        return dpll_clk;
  76}
  77
  78static int omap2_enable_osc_ck(struct clk *clk)
  79{
  80        u32 pcc;
  81
  82        pcc = __raw_readl(OMAP24XX_PRCM_CLKSRC_CTRL);
  83
  84        __raw_writel(pcc & ~OMAP_AUTOEXTCLKMODE_MASK,
  85                      OMAP24XX_PRCM_CLKSRC_CTRL);
  86
  87        return 0;
  88}
  89
  90static void omap2_disable_osc_ck(struct clk *clk)
  91{
  92        u32 pcc;
  93
  94        pcc = __raw_readl(OMAP24XX_PRCM_CLKSRC_CTRL);
  95
  96        __raw_writel(pcc | OMAP_AUTOEXTCLKMODE_MASK,
  97                      OMAP24XX_PRCM_CLKSRC_CTRL);
  98}
  99
 100#ifdef OLD_CK
 101/* Recalculate SYST_CLK */
 102static void omap2_sys_clk_recalc(struct clk * clk)
 103{
 104        u32 div = PRCM_CLKSRC_CTRL;
 105        div &= (1 << 7) | (1 << 6);     /* Test if ext clk divided by 1 or 2 */
 106        div >>= clk->rate_offset;
 107        clk->rate = (clk->parent->rate / div);
 108        propagate_rate(clk);
 109}
 110#endif  /* OLD_CK */
 111
 112/* Enable an APLL if off */
 113static int omap2_clk_fixed_enable(struct clk *clk)
 114{
 115        u32 cval, apll_mask;
 116
 117        apll_mask = EN_APLL_LOCKED << clk->enable_bit;
 118
 119        cval = cm_read_mod_reg(PLL_MOD, CM_CLKEN);
 120
 121        if ((cval & apll_mask) == apll_mask)
 122                return 0;   /* apll already enabled */
 123
 124        cval &= ~apll_mask;
 125        cval |= apll_mask;
 126        cm_write_mod_reg(cval, PLL_MOD, CM_CLKEN);
 127
 128        if (clk == &apll96_ck)
 129                cval = OMAP24XX_ST_96M_APLL;
 130        else if (clk == &apll54_ck)
 131                cval = OMAP24XX_ST_54M_APLL;
 132
 133        omap2_wait_clock_ready(OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST), cval,
 134                            clk->name);
 135
 136        /*
 137         * REVISIT: Should we return an error code if omap2_wait_clock_ready()
 138         * fails?
 139         */
 140        return 0;
 141}
 142
 143/* Stop APLL */
 144static void omap2_clk_fixed_disable(struct clk *clk)
 145{
 146        u32 cval;
 147
 148        cval = cm_read_mod_reg(PLL_MOD, CM_CLKEN);
 149        cval &= ~(EN_APLL_LOCKED << clk->enable_bit);
 150        cm_write_mod_reg(cval, PLL_MOD, CM_CLKEN);
 151}
 152
 153/*
 154 * Uses the current prcm set to tell if a rate is valid.
 155 * You can go slower, but not faster within a given rate set.
 156 */
 157static u32 omap2_dpll_round_rate(unsigned long target_rate)
 158{
 159        u32 high, low, core_clk_src;
 160
 161        core_clk_src = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
 162        core_clk_src &= OMAP24XX_CORE_CLK_SRC_MASK;
 163
 164        if (core_clk_src == CORE_CLK_SRC_DPLL) {        /* DPLL clockout */
 165                high = curr_prcm_set->dpll_speed * 2;
 166                low = curr_prcm_set->dpll_speed;
 167        } else {                                /* DPLL clockout x 2 */
 168                high = curr_prcm_set->dpll_speed;
 169                low = curr_prcm_set->dpll_speed / 2;
 170        }
 171
 172#ifdef DOWN_VARIABLE_DPLL
 173        if (target_rate > high)
 174                return high;
 175        else
 176                return target_rate;
 177#else
 178        if (target_rate > low)
 179                return high;
 180        else
 181                return low;
 182#endif
 183
 184}
 185
 186static void omap2_dpll_recalc(struct clk *clk)
 187{
 188        clk->rate = omap2_get_dpll_rate_24xx(clk);
 189
 190        propagate_rate(clk);
 191}
 192
 193static int omap2_reprogram_dpll(struct clk *clk, unsigned long rate)
 194{
 195        u32 cur_rate, low, mult, div, valid_rate, done_rate;
 196        u32 bypass = 0;
 197        struct prcm_config tmpset;
 198        const struct dpll_data *dd;
 199        unsigned long flags;
 200        int ret = -EINVAL;
 201
 202        local_irq_save(flags);
 203        cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck);
 204        mult = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
 205        mult &= OMAP24XX_CORE_CLK_SRC_MASK;
 206
 207        if ((rate == (cur_rate / 2)) && (mult == 2)) {
 208                omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL, 1);
 209        } else if ((rate == (cur_rate * 2)) && (mult == 1)) {
 210                omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1);
 211        } else if (rate != cur_rate) {
 212                valid_rate = omap2_dpll_round_rate(rate);
 213                if (valid_rate != rate)
 214                        goto dpll_exit;
 215
 216                if (mult == 1)
 217                        low = curr_prcm_set->dpll_speed;
 218                else
 219                        low = curr_prcm_set->dpll_speed / 2;
 220
 221                dd = clk->dpll_data;
 222                if (!dd)
 223                        goto dpll_exit;
 224
 225                tmpset.cm_clksel1_pll = __raw_readl(dd->mult_div1_reg);
 226                tmpset.cm_clksel1_pll &= ~(dd->mult_mask |
 227                                           dd->div1_mask);
 228                div = ((curr_prcm_set->xtal_speed / 1000000) - 1);
 229                tmpset.cm_clksel2_pll = cm_read_mod_reg(PLL_MOD, CM_CLKSEL2);
 230                tmpset.cm_clksel2_pll &= ~OMAP24XX_CORE_CLK_SRC_MASK;
 231                if (rate > low) {
 232                        tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL_X2;
 233                        mult = ((rate / 2) / 1000000);
 234                        done_rate = CORE_CLK_SRC_DPLL_X2;
 235                } else {
 236                        tmpset.cm_clksel2_pll |= CORE_CLK_SRC_DPLL;
 237                        mult = (rate / 1000000);
 238                        done_rate = CORE_CLK_SRC_DPLL;
 239                }
 240                tmpset.cm_clksel1_pll |= (div << __ffs(dd->mult_mask));
 241                tmpset.cm_clksel1_pll |= (mult << __ffs(dd->div1_mask));
 242
 243                /* Worst case */
 244                tmpset.base_sdrc_rfr = SDRC_RFR_CTRL_BYPASS;
 245
 246                if (rate == curr_prcm_set->xtal_speed)  /* If asking for 1-1 */
 247                        bypass = 1;
 248
 249                omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1); /* For init_mem */
 250
 251                /* Force dll lock mode */
 252                omap2_set_prcm(tmpset.cm_clksel1_pll, tmpset.base_sdrc_rfr,
 253                               bypass);
 254
 255                /* Errata: ret dll entry state */
 256                omap2_init_memory_params(omap2_dll_force_needed());
 257                omap2_reprogram_sdrc(done_rate, 0);
 258        }
 259        omap2_dpll_recalc(&dpll_ck);
 260        ret = 0;
 261
 262dpll_exit:
 263        local_irq_restore(flags);
 264        return(ret);
 265}
 266
 267/**
 268 * omap2_table_mpu_recalc - just return the MPU speed
 269 * @clk: virt_prcm_set struct clk
 270 *
 271 * Set virt_prcm_set's rate to the mpu_speed field of the current PRCM set.
 272 */
 273static void omap2_table_mpu_recalc(struct clk *clk)
 274{
 275        clk->rate = curr_prcm_set->mpu_speed;
 276}
 277
 278/*
 279 * Look for a rate equal or less than the target rate given a configuration set.
 280 *
 281 * What's not entirely clear is "which" field represents the key field.
 282 * Some might argue L3-DDR, others ARM, others IVA. This code is simple and
 283 * just uses the ARM rates.
 284 */
 285static long omap2_round_to_table_rate(struct clk *clk, unsigned long rate)
 286{
 287        struct prcm_config *ptr;
 288        long highest_rate;
 289
 290        if (clk != &virt_prcm_set)
 291                return -EINVAL;
 292
 293        highest_rate = -EINVAL;
 294
 295        for (ptr = rate_table; ptr->mpu_speed; ptr++) {
 296                if (!(ptr->flags & cpu_mask))
 297                        continue;
 298                if (ptr->xtal_speed != sys_ck.rate)
 299                        continue;
 300
 301                highest_rate = ptr->mpu_speed;
 302
 303                /* Can check only after xtal frequency check */
 304                if (ptr->mpu_speed <= rate)
 305                        break;
 306        }
 307        return highest_rate;
 308}
 309
 310/* Sets basic clocks based on the specified rate */
 311static int omap2_select_table_rate(struct clk *clk, unsigned long rate)
 312{
 313        u32 cur_rate, done_rate, bypass = 0, tmp;
 314        struct prcm_config *prcm;
 315        unsigned long found_speed = 0;
 316        unsigned long flags;
 317
 318        if (clk != &virt_prcm_set)
 319                return -EINVAL;
 320
 321        for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 322                if (!(prcm->flags & cpu_mask))
 323                        continue;
 324
 325                if (prcm->xtal_speed != sys_ck.rate)
 326                        continue;
 327
 328                if (prcm->mpu_speed <= rate) {
 329                        found_speed = prcm->mpu_speed;
 330                        break;
 331                }
 332        }
 333
 334        if (!found_speed) {
 335                printk(KERN_INFO "Could not set MPU rate to %luMHz\n",
 336                       rate / 1000000);
 337                return -EINVAL;
 338        }
 339
 340        curr_prcm_set = prcm;
 341        cur_rate = omap2_get_dpll_rate_24xx(&dpll_ck);
 342
 343        if (prcm->dpll_speed == cur_rate / 2) {
 344                omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL, 1);
 345        } else if (prcm->dpll_speed == cur_rate * 2) {
 346                omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1);
 347        } else if (prcm->dpll_speed != cur_rate) {
 348                local_irq_save(flags);
 349
 350                if (prcm->dpll_speed == prcm->xtal_speed)
 351                        bypass = 1;
 352
 353                if ((prcm->cm_clksel2_pll & OMAP24XX_CORE_CLK_SRC_MASK) ==
 354                    CORE_CLK_SRC_DPLL_X2)
 355                        done_rate = CORE_CLK_SRC_DPLL_X2;
 356                else
 357                        done_rate = CORE_CLK_SRC_DPLL;
 358
 359                /* MPU divider */
 360                cm_write_mod_reg(prcm->cm_clksel_mpu, MPU_MOD, CM_CLKSEL);
 361
 362                /* dsp + iva1 div(2420), iva2.1(2430) */
 363                cm_write_mod_reg(prcm->cm_clksel_dsp,
 364                                 OMAP24XX_DSP_MOD, CM_CLKSEL);
 365
 366                cm_write_mod_reg(prcm->cm_clksel_gfx, GFX_MOD, CM_CLKSEL);
 367
 368                /* Major subsystem dividers */
 369                tmp = cm_read_mod_reg(CORE_MOD, CM_CLKSEL1) & OMAP24XX_CLKSEL_DSS2_MASK;
 370                cm_write_mod_reg(prcm->cm_clksel1_core | tmp, CORE_MOD, CM_CLKSEL1);
 371                if (cpu_is_omap2430())
 372                        cm_write_mod_reg(prcm->cm_clksel_mdm,
 373                                         OMAP2430_MDM_MOD, CM_CLKSEL);
 374
 375                /* x2 to enter init_mem */
 376                omap2_reprogram_sdrc(CORE_CLK_SRC_DPLL_X2, 1);
 377
 378                omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr,
 379                               bypass);
 380
 381                omap2_init_memory_params(omap2_dll_force_needed());
 382                omap2_reprogram_sdrc(done_rate, 0);
 383
 384                local_irq_restore(flags);
 385        }
 386        omap2_dpll_recalc(&dpll_ck);
 387
 388        return 0;
 389}
 390
 391static struct clk_functions omap2_clk_functions = {
 392        .clk_enable             = omap2_clk_enable,
 393        .clk_disable            = omap2_clk_disable,
 394        .clk_round_rate         = omap2_clk_round_rate,
 395        .clk_set_rate           = omap2_clk_set_rate,
 396        .clk_set_parent         = omap2_clk_set_parent,
 397        .clk_disable_unused     = omap2_clk_disable_unused,
 398};
 399
 400static u32 omap2_get_apll_clkin(void)
 401{
 402        u32 aplls, sclk = 0;
 403
 404        aplls = cm_read_mod_reg(PLL_MOD, CM_CLKSEL1);
 405        aplls &= OMAP24XX_APLLS_CLKIN_MASK;
 406        aplls >>= OMAP24XX_APLLS_CLKIN_SHIFT;
 407
 408        if (aplls == APLLS_CLKIN_19_2MHZ)
 409                sclk = 19200000;
 410        else if (aplls == APLLS_CLKIN_13MHZ)
 411                sclk = 13000000;
 412        else if (aplls == APLLS_CLKIN_12MHZ)
 413                sclk = 12000000;
 414
 415        return sclk;
 416}
 417
 418static u32 omap2_get_sysclkdiv(void)
 419{
 420        u32 div;
 421
 422        div = __raw_readl(OMAP24XX_PRCM_CLKSRC_CTRL);
 423        div &= OMAP_SYSCLKDIV_MASK;
 424        div >>= OMAP_SYSCLKDIV_SHIFT;
 425
 426        return div;
 427}
 428
 429static void omap2_osc_clk_recalc(struct clk *clk)
 430{
 431        clk->rate = omap2_get_apll_clkin() * omap2_get_sysclkdiv();
 432        propagate_rate(clk);
 433}
 434
 435static void omap2_sys_clk_recalc(struct clk *clk)
 436{
 437        clk->rate = clk->parent->rate / omap2_get_sysclkdiv();
 438        propagate_rate(clk);
 439}
 440
 441/*
 442 * Set clocks for bypass mode for reboot to work.
 443 */
 444void omap2_clk_prepare_for_reboot(void)
 445{
 446        u32 rate;
 447
 448        if (vclk == NULL || sclk == NULL)
 449                return;
 450
 451        rate = clk_get_rate(sclk);
 452        clk_set_rate(vclk, rate);
 453}
 454
 455/*
 456 * Switch the MPU rate if specified on cmdline.
 457 * We cannot do this early until cmdline is parsed.
 458 */
 459static int __init omap2_clk_arch_init(void)
 460{
 461        if (!mpurate)
 462                return -EINVAL;
 463
 464        if (omap2_select_table_rate(&virt_prcm_set, mpurate))
 465                printk(KERN_ERR "Could not find matching MPU rate\n");
 466
 467        recalculate_root_clocks();
 468
 469        printk(KERN_INFO "Switched to new clocking rate (Crystal/DPLL/MPU): "
 470               "%ld.%01ld/%ld/%ld MHz\n",
 471               (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10,
 472               (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ;
 473
 474        return 0;
 475}
 476arch_initcall(omap2_clk_arch_init);
 477
 478int __init omap2_clk_init(void)
 479{
 480        struct prcm_config *prcm;
 481        struct clk **clkp;
 482        u32 clkrate;
 483
 484        if (cpu_is_omap242x())
 485                cpu_mask = RATE_IN_242X;
 486        else if (cpu_is_omap2430())
 487                cpu_mask = RATE_IN_243X;
 488
 489        clk_init(&omap2_clk_functions);
 490
 491        omap2_osc_clk_recalc(&osc_ck);
 492        omap2_sys_clk_recalc(&sys_ck);
 493
 494        for (clkp = onchip_24xx_clks;
 495             clkp < onchip_24xx_clks + ARRAY_SIZE(onchip_24xx_clks);
 496             clkp++) {
 497
 498                if ((*clkp)->flags & CLOCK_IN_OMAP242X && cpu_is_omap2420()) {
 499                        clk_register(*clkp);
 500                        continue;
 501                }
 502
 503                if ((*clkp)->flags & CLOCK_IN_OMAP243X && cpu_is_omap2430()) {
 504                        clk_register(*clkp);
 505                        continue;
 506                }
 507        }
 508
 509        /* Check the MPU rate set by bootloader */
 510        clkrate = omap2_get_dpll_rate_24xx(&dpll_ck);
 511        for (prcm = rate_table; prcm->mpu_speed; prcm++) {
 512                if (!(prcm->flags & cpu_mask))
 513                        continue;
 514                if (prcm->xtal_speed != sys_ck.rate)
 515                        continue;
 516                if (prcm->dpll_speed <= clkrate)
 517                         break;
 518        }
 519        curr_prcm_set = prcm;
 520
 521        recalculate_root_clocks();
 522
 523        printk(KERN_INFO "Clocking rate (Crystal/DPLL/MPU): "
 524               "%ld.%01ld/%ld/%ld MHz\n",
 525               (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10,
 526               (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ;
 527
 528        /*
 529         * Only enable those clocks we will need, let the drivers
 530         * enable other clocks as necessary
 531         */
 532        clk_enable_init_clocks();
 533
 534        /* Avoid sleeping sleeping during omap2_clk_prepare_for_reboot() */
 535        vclk = clk_get(NULL, "virt_prcm_set");
 536        sclk = clk_get(NULL, "sys_ck");
 537
 538        return 0;
 539}
 540
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.