linux/drivers/cpufreq/exynos4x12-cpufreq.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
   3 *              http://www.samsung.com
   4 *
   5 * EXYNOS4X12 - CPU frequency scaling support
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10*/
  11
  12#include <linux/module.h>
  13#include <linux/kernel.h>
  14#include <linux/err.h>
  15#include <linux/clk.h>
  16#include <linux/io.h>
  17#include <linux/slab.h>
  18#include <linux/cpufreq.h>
  19
  20#include <mach/regs-clock.h>
  21#include <mach/cpufreq.h>
  22
  23#define CPUFREQ_LEVEL_END       (L13 + 1)
  24
  25static int max_support_idx;
  26static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
  27
  28static struct clk *cpu_clk;
  29static struct clk *moutcore;
  30static struct clk *mout_mpll;
  31static struct clk *mout_apll;
  32
  33struct cpufreq_clkdiv {
  34        unsigned int    index;
  35        unsigned int    clkdiv;
  36        unsigned int    clkdiv1;
  37};
  38
  39static unsigned int exynos4x12_volt_table[CPUFREQ_LEVEL_END];
  40
  41static struct cpufreq_frequency_table exynos4x12_freq_table[] = {
  42        {L0, 1500 * 1000},
  43        {L1, 1400 * 1000},
  44        {L2, 1300 * 1000},
  45        {L3, 1200 * 1000},
  46        {L4, 1100 * 1000},
  47        {L5, 1000 * 1000},
  48        {L6,  900 * 1000},
  49        {L7,  800 * 1000},
  50        {L8,  700 * 1000},
  51        {L9,  600 * 1000},
  52        {L10, 500 * 1000},
  53        {L11, 400 * 1000},
  54        {L12, 300 * 1000},
  55        {L13, 200 * 1000},
  56        {0, CPUFREQ_TABLE_END},
  57};
  58
  59static struct cpufreq_clkdiv exynos4x12_clkdiv_table[CPUFREQ_LEVEL_END];
  60
  61static unsigned int clkdiv_cpu0_4212[CPUFREQ_LEVEL_END][8] = {
  62        /*
  63         * Clock divider value for following
  64         * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
  65         *              DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 }
  66         */
  67        /* ARM L0: 1500 MHz */
  68        { 0, 3, 7, 0, 6, 1, 2, 0 },
  69
  70        /* ARM L1: 1400 MHz */
  71        { 0, 3, 7, 0, 6, 1, 2, 0 },
  72
  73        /* ARM L2: 1300 MHz */
  74        { 0, 3, 7, 0, 5, 1, 2, 0 },
  75
  76        /* ARM L3: 1200 MHz */
  77        { 0, 3, 7, 0, 5, 1, 2, 0 },
  78
  79        /* ARM L4: 1100 MHz */
  80        { 0, 3, 6, 0, 4, 1, 2, 0 },
  81
  82        /* ARM L5: 1000 MHz */
  83        { 0, 2, 5, 0, 4, 1, 1, 0 },
  84
  85        /* ARM L6: 900 MHz */
  86        { 0, 2, 5, 0, 3, 1, 1, 0 },
  87
  88        /* ARM L7: 800 MHz */
  89        { 0, 2, 5, 0, 3, 1, 1, 0 },
  90
  91        /* ARM L8: 700 MHz */
  92        { 0, 2, 4, 0, 3, 1, 1, 0 },
  93
  94        /* ARM L9: 600 MHz */
  95        { 0, 2, 4, 0, 3, 1, 1, 0 },
  96
  97        /* ARM L10: 500 MHz */
  98        { 0, 2, 4, 0, 3, 1, 1, 0 },
  99
 100        /* ARM L11: 400 MHz */
 101        { 0, 2, 4, 0, 3, 1, 1, 0 },
 102
 103        /* ARM L12: 300 MHz */
 104        { 0, 2, 4, 0, 2, 1, 1, 0 },
 105
 106        /* ARM L13: 200 MHz */
 107        { 0, 1, 3, 0, 1, 1, 1, 0 },
 108};
 109
 110static unsigned int clkdiv_cpu0_4412[CPUFREQ_LEVEL_END][8] = {
 111        /*
 112         * Clock divider value for following
 113         * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
 114         *              DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 }
 115         */
 116        /* ARM L0: 1500 MHz */
 117        { 0, 3, 7, 0, 6, 1, 2, 0 },
 118
 119        /* ARM L1: 1400 MHz */
 120        { 0, 3, 7, 0, 6, 1, 2, 0 },
 121
 122        /* ARM L2: 1300 MHz */
 123        { 0, 3, 7, 0, 5, 1, 2, 0 },
 124
 125        /* ARM L3: 1200 MHz */
 126        { 0, 3, 7, 0, 5, 1, 2, 0 },
 127
 128        /* ARM L4: 1100 MHz */
 129        { 0, 3, 6, 0, 4, 1, 2, 0 },
 130
 131        /* ARM L5: 1000 MHz */
 132        { 0, 2, 5, 0, 4, 1, 1, 0 },
 133
 134        /* ARM L6: 900 MHz */
 135        { 0, 2, 5, 0, 3, 1, 1, 0 },
 136
 137        /* ARM L7: 800 MHz */
 138        { 0, 2, 5, 0, 3, 1, 1, 0 },
 139
 140        /* ARM L8: 700 MHz */
 141        { 0, 2, 4, 0, 3, 1, 1, 0 },
 142
 143        /* ARM L9: 600 MHz */
 144        { 0, 2, 4, 0, 3, 1, 1, 0 },
 145
 146        /* ARM L10: 500 MHz */
 147        { 0, 2, 4, 0, 3, 1, 1, 0 },
 148
 149        /* ARM L11: 400 MHz */
 150        { 0, 2, 4, 0, 3, 1, 1, 0 },
 151
 152        /* ARM L12: 300 MHz */
 153        { 0, 2, 4, 0, 2, 1, 1, 0 },
 154
 155        /* ARM L13: 200 MHz */
 156        { 0, 1, 3, 0, 1, 1, 1, 0 },
 157};
 158
 159static unsigned int clkdiv_cpu1_4212[CPUFREQ_LEVEL_END][2] = {
 160        /* Clock divider value for following
 161         * { DIVCOPY, DIVHPM }
 162         */
 163        /* ARM L0: 1500 MHz */
 164        { 6, 0 },
 165
 166        /* ARM L1: 1400 MHz */
 167        { 6, 0 },
 168
 169        /* ARM L2: 1300 MHz */
 170        { 5, 0 },
 171
 172        /* ARM L3: 1200 MHz */
 173        { 5, 0 },
 174
 175        /* ARM L4: 1100 MHz */
 176        { 4, 0 },
 177
 178        /* ARM L5: 1000 MHz */
 179        { 4, 0 },
 180
 181        /* ARM L6: 900 MHz */
 182        { 3, 0 },
 183
 184        /* ARM L7: 800 MHz */
 185        { 3, 0 },
 186
 187        /* ARM L8: 700 MHz */
 188        { 3, 0 },
 189
 190        /* ARM L9: 600 MHz */
 191        { 3, 0 },
 192
 193        /* ARM L10: 500 MHz */
 194        { 3, 0 },
 195
 196        /* ARM L11: 400 MHz */
 197        { 3, 0 },
 198
 199        /* ARM L12: 300 MHz */
 200        { 3, 0 },
 201
 202        /* ARM L13: 200 MHz */
 203        { 3, 0 },
 204};
 205
 206static unsigned int clkdiv_cpu1_4412[CPUFREQ_LEVEL_END][3] = {
 207        /* Clock divider value for following
 208         * { DIVCOPY, DIVHPM, DIVCORES }
 209         */
 210        /* ARM L0: 1500 MHz */
 211        { 6, 0, 7 },
 212
 213        /* ARM L1: 1400 MHz */
 214        { 6, 0, 6 },
 215
 216        /* ARM L2: 1300 MHz */
 217        { 5, 0, 6 },
 218
 219        /* ARM L3: 1200 MHz */
 220        { 5, 0, 5 },
 221
 222        /* ARM L4: 1100 MHz */
 223        { 4, 0, 5 },
 224
 225        /* ARM L5: 1000 MHz */
 226        { 4, 0, 4 },
 227
 228        /* ARM L6: 900 MHz */
 229        { 3, 0, 4 },
 230
 231        /* ARM L7: 800 MHz */
 232        { 3, 0, 3 },
 233
 234        /* ARM L8: 700 MHz */
 235        { 3, 0, 3 },
 236
 237        /* ARM L9: 600 MHz */
 238        { 3, 0, 2 },
 239
 240        /* ARM L10: 500 MHz */
 241        { 3, 0, 2 },
 242
 243        /* ARM L11: 400 MHz */
 244        { 3, 0, 1 },
 245
 246        /* ARM L12: 300 MHz */
 247        { 3, 0, 1 },
 248
 249        /* ARM L13: 200 MHz */
 250        { 3, 0, 0 },
 251};
 252
 253static unsigned int exynos4x12_apll_pms_table[CPUFREQ_LEVEL_END] = {
 254        /* APLL FOUT L0: 1500 MHz */
 255        ((250 << 16) | (4 << 8) | (0x0)),
 256
 257        /* APLL FOUT L1: 1400 MHz */
 258        ((175 << 16) | (3 << 8) | (0x0)),
 259
 260        /* APLL FOUT L2: 1300 MHz */
 261        ((325 << 16) | (6 << 8) | (0x0)),
 262
 263        /* APLL FOUT L3: 1200 MHz */
 264        ((200 << 16) | (4 << 8) | (0x0)),
 265
 266        /* APLL FOUT L4: 1100 MHz */
 267        ((275 << 16) | (6 << 8) | (0x0)),
 268
 269        /* APLL FOUT L5: 1000 MHz */
 270        ((125 << 16) | (3 << 8) | (0x0)),
 271
 272        /* APLL FOUT L6: 900 MHz */
 273        ((150 << 16) | (4 << 8) | (0x0)),
 274
 275        /* APLL FOUT L7: 800 MHz */
 276        ((100 << 16) | (3 << 8) | (0x0)),
 277
 278        /* APLL FOUT L8: 700 MHz */
 279        ((175 << 16) | (3 << 8) | (0x1)),
 280
 281        /* APLL FOUT L9: 600 MHz */
 282        ((200 << 16) | (4 << 8) | (0x1)),
 283
 284        /* APLL FOUT L10: 500 MHz */
 285        ((125 << 16) | (3 << 8) | (0x1)),
 286
 287        /* APLL FOUT L11 400 MHz */
 288        ((100 << 16) | (3 << 8) | (0x1)),
 289
 290        /* APLL FOUT L12: 300 MHz */
 291        ((200 << 16) | (4 << 8) | (0x2)),
 292
 293        /* APLL FOUT L13: 200 MHz */
 294        ((100 << 16) | (3 << 8) | (0x2)),
 295};
 296
 297static const unsigned int asv_voltage_4x12[CPUFREQ_LEVEL_END] = {
 298        1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500,
 299        1000000,  987500,  975000,  950000,  925000,  900000,  900000
 300};
 301
 302static void exynos4x12_set_clkdiv(unsigned int div_index)
 303{
 304        unsigned int tmp;
 305        unsigned int stat_cpu1;
 306
 307        /* Change Divider - CPU0 */
 308
 309        tmp = exynos4x12_clkdiv_table[div_index].clkdiv;
 310
 311        __raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
 312
 313        while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111)
 314                cpu_relax();
 315
 316        /* Change Divider - CPU1 */
 317        tmp = exynos4x12_clkdiv_table[div_index].clkdiv1;
 318
 319        __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
 320        if (soc_is_exynos4212())
 321                stat_cpu1 = 0x11;
 322        else
 323                stat_cpu1 = 0x111;
 324
 325        while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1)
 326                cpu_relax();
 327}
 328
 329static void exynos4x12_set_apll(unsigned int index)
 330{
 331        unsigned int tmp, pdiv;
 332
 333        /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
 334        clk_set_parent(moutcore, mout_mpll);
 335
 336        do {
 337                cpu_relax();
 338                tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
 339                        >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
 340                tmp &= 0x7;
 341        } while (tmp != 0x2);
 342
 343        /* 2. Set APLL Lock time */
 344        pdiv = ((exynos4x12_apll_pms_table[index] >> 8) & 0x3f);
 345
 346        __raw_writel((pdiv * 250), EXYNOS4_APLL_LOCK);
 347
 348        /* 3. Change PLL PMS values */
 349        tmp = __raw_readl(EXYNOS4_APLL_CON0);
 350        tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
 351        tmp |= exynos4x12_apll_pms_table[index];
 352        __raw_writel(tmp, EXYNOS4_APLL_CON0);
 353
 354        /* 4. wait_lock_time */
 355        do {
 356                cpu_relax();
 357                tmp = __raw_readl(EXYNOS4_APLL_CON0);
 358        } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT)));
 359
 360        /* 5. MUX_CORE_SEL = APLL */
 361        clk_set_parent(moutcore, mout_apll);
 362
 363        do {
 364                cpu_relax();
 365                tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
 366                tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
 367        } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
 368}
 369
 370bool exynos4x12_pms_change(unsigned int old_index, unsigned int new_index)
 371{
 372        unsigned int old_pm = exynos4x12_apll_pms_table[old_index] >> 8;
 373        unsigned int new_pm = exynos4x12_apll_pms_table[new_index] >> 8;
 374
 375        return (old_pm == new_pm) ? 0 : 1;
 376}
 377
 378static void exynos4x12_set_frequency(unsigned int old_index,
 379                                  unsigned int new_index)
 380{
 381        unsigned int tmp;
 382
 383        if (old_index > new_index) {
 384                if (!exynos4x12_pms_change(old_index, new_index)) {
 385                        /* 1. Change the system clock divider values */
 386                        exynos4x12_set_clkdiv(new_index);
 387                        /* 2. Change just s value in apll m,p,s value */
 388                        tmp = __raw_readl(EXYNOS4_APLL_CON0);
 389                        tmp &= ~(0x7 << 0);
 390                        tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7);
 391                        __raw_writel(tmp, EXYNOS4_APLL_CON0);
 392
 393                } else {
 394                        /* Clock Configuration Procedure */
 395                        /* 1. Change the system clock divider values */
 396                        exynos4x12_set_clkdiv(new_index);
 397                        /* 2. Change the apll m,p,s value */
 398                        exynos4x12_set_apll(new_index);
 399                }
 400        } else if (old_index < new_index) {
 401                if (!exynos4x12_pms_change(old_index, new_index)) {
 402                        /* 1. Change just s value in apll m,p,s value */
 403                        tmp = __raw_readl(EXYNOS4_APLL_CON0);
 404                        tmp &= ~(0x7 << 0);
 405                        tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7);
 406                        __raw_writel(tmp, EXYNOS4_APLL_CON0);
 407                        /* 2. Change the system clock divider values */
 408                        exynos4x12_set_clkdiv(new_index);
 409                } else {
 410                        /* Clock Configuration Procedure */
 411                        /* 1. Change the apll m,p,s value */
 412                        exynos4x12_set_apll(new_index);
 413                        /* 2. Change the system clock divider values */
 414                        exynos4x12_set_clkdiv(new_index);
 415                }
 416        }
 417}
 418
 419static void __init set_volt_table(void)
 420{
 421        unsigned int i;
 422
 423        max_support_idx = L1;
 424
 425        /* Not supported */
 426        exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
 427
 428        for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
 429                exynos4x12_volt_table[i] = asv_voltage_4x12[i];
 430}
 431
 432int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
 433{
 434        int i;
 435        unsigned int tmp;
 436        unsigned long rate;
 437
 438        set_volt_table();
 439
 440        cpu_clk = clk_get(NULL, "armclk");
 441        if (IS_ERR(cpu_clk))
 442                return PTR_ERR(cpu_clk);
 443
 444        moutcore = clk_get(NULL, "moutcore");
 445        if (IS_ERR(moutcore))
 446                goto err_moutcore;
 447
 448        mout_mpll = clk_get(NULL, "mout_mpll");
 449        if (IS_ERR(mout_mpll))
 450                goto err_mout_mpll;
 451
 452        rate = clk_get_rate(mout_mpll) / 1000;
 453
 454        mout_apll = clk_get(NULL, "mout_apll");
 455        if (IS_ERR(mout_apll))
 456                goto err_mout_apll;
 457
 458        for (i = L0; i <  CPUFREQ_LEVEL_END; i++) {
 459
 460                exynos4x12_clkdiv_table[i].index = i;
 461
 462                tmp = __raw_readl(EXYNOS4_CLKDIV_CPU);
 463
 464                tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK |
 465                        EXYNOS4_CLKDIV_CPU0_COREM0_MASK |
 466                        EXYNOS4_CLKDIV_CPU0_COREM1_MASK |
 467                        EXYNOS4_CLKDIV_CPU0_PERIPH_MASK |
 468                        EXYNOS4_CLKDIV_CPU0_ATB_MASK |
 469                        EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK |
 470                        EXYNOS4_CLKDIV_CPU0_APLL_MASK);
 471
 472                if (soc_is_exynos4212()) {
 473                        tmp |= ((clkdiv_cpu0_4212[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
 474                                (clkdiv_cpu0_4212[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
 475                                (clkdiv_cpu0_4212[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
 476                                (clkdiv_cpu0_4212[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
 477                                (clkdiv_cpu0_4212[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
 478                                (clkdiv_cpu0_4212[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
 479                                (clkdiv_cpu0_4212[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT));
 480                } else {
 481                        tmp &= ~EXYNOS4_CLKDIV_CPU0_CORE2_MASK;
 482
 483                        tmp |= ((clkdiv_cpu0_4412[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
 484                                (clkdiv_cpu0_4412[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
 485                                (clkdiv_cpu0_4412[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
 486                                (clkdiv_cpu0_4412[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
 487                                (clkdiv_cpu0_4412[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
 488                                (clkdiv_cpu0_4412[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
 489                                (clkdiv_cpu0_4412[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT) |
 490                                (clkdiv_cpu0_4412[i][7] << EXYNOS4_CLKDIV_CPU0_CORE2_SHIFT));
 491                }
 492
 493                exynos4x12_clkdiv_table[i].clkdiv = tmp;
 494
 495                tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1);
 496
 497                if (soc_is_exynos4212()) {
 498                        tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK |
 499                                EXYNOS4_CLKDIV_CPU1_HPM_MASK);
 500                        tmp |= ((clkdiv_cpu1_4212[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) |
 501                                (clkdiv_cpu1_4212[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT));
 502                } else {
 503                        tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK |
 504                                EXYNOS4_CLKDIV_CPU1_HPM_MASK |
 505                                EXYNOS4_CLKDIV_CPU1_CORES_MASK);
 506                        tmp |= ((clkdiv_cpu1_4412[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) |
 507                                (clkdiv_cpu1_4412[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT) |
 508                                (clkdiv_cpu1_4412[i][2] << EXYNOS4_CLKDIV_CPU1_CORES_SHIFT));
 509                }
 510                exynos4x12_clkdiv_table[i].clkdiv1 = tmp;
 511        }
 512
 513        info->mpll_freq_khz = rate;
 514        info->pm_lock_idx = L5;
 515        info->pll_safe_idx = L7;
 516        info->max_support_idx = max_support_idx;
 517        info->min_support_idx = min_support_idx;
 518        info->cpu_clk = cpu_clk;
 519        info->volt_table = exynos4x12_volt_table;
 520        info->freq_table = exynos4x12_freq_table;
 521        info->set_freq = exynos4x12_set_frequency;
 522        info->need_apll_change = exynos4x12_pms_change;
 523
 524        return 0;
 525
 526err_mout_apll:
 527        clk_put(mout_mpll);
 528err_mout_mpll:
 529        clk_put(moutcore);
 530err_moutcore:
 531        clk_put(cpu_clk);
 532
 533        pr_debug("%s: failed initialization\n", __func__);
 534        return -EINVAL;
 535}
 536EXPORT_SYMBOL(exynos4x12_cpufreq_init);
 537
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.