linux/drivers/cpufreq/exynos5250-cpufreq.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
   3 *              http://www.samsung.com
   4 *
   5 * EXYNOS5250 - 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/map.h>
  21#include <mach/regs-clock.h>
  22#include <mach/cpufreq.h>
  23
  24#define CPUFREQ_LEVEL_END       (L15 + 1)
  25
  26static int max_support_idx;
  27static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
  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 exynos5250_volt_table[CPUFREQ_LEVEL_END];
  40
  41static struct cpufreq_frequency_table exynos5250_freq_table[] = {
  42        {L0, 1700 * 1000},
  43        {L1, 1600 * 1000},
  44        {L2, 1500 * 1000},
  45        {L3, 1400 * 1000},
  46        {L4, 1300 * 1000},
  47        {L5, 1200 * 1000},
  48        {L6, 1100 * 1000},
  49        {L7, 1000 * 1000},
  50        {L8, 900 * 1000},
  51        {L9, 800 * 1000},
  52        {L10, 700 * 1000},
  53        {L11, 600 * 1000},
  54        {L12, 500 * 1000},
  55        {L13, 400 * 1000},
  56        {L14, 300 * 1000},
  57        {L15, 200 * 1000},
  58        {0, CPUFREQ_TABLE_END},
  59};
  60
  61static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END];
  62
  63static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
  64        /*
  65         * Clock divider value for following
  66         * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
  67         */
  68        { 0, 3, 7, 7, 7, 3, 5, 0 },     /* 1700 MHz */
  69        { 0, 3, 7, 7, 7, 1, 4, 0 },     /* 1600 MHz */
  70        { 0, 2, 7, 7, 7, 1, 4, 0 },     /* 1500 MHz */
  71        { 0, 2, 7, 7, 6, 1, 4, 0 },     /* 1400 MHz */
  72        { 0, 2, 7, 7, 6, 1, 3, 0 },     /* 1300 MHz */
  73        { 0, 2, 7, 7, 5, 1, 3, 0 },     /* 1200 MHz */
  74        { 0, 3, 7, 7, 5, 1, 3, 0 },     /* 1100 MHz */
  75        { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 1000 MHz */
  76        { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 900 MHz */
  77        { 0, 1, 7, 7, 4, 1, 2, 0 },     /* 800 MHz */
  78        { 0, 1, 7, 7, 3, 1, 1, 0 },     /* 700 MHz */
  79        { 0, 1, 7, 7, 3, 1, 1, 0 },     /* 600 MHz */
  80        { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 500 MHz */
  81        { 0, 1, 7, 7, 2, 1, 1, 0 },     /* 400 MHz */
  82        { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 300 MHz */
  83        { 0, 1, 7, 7, 1, 1, 1, 0 },     /* 200 MHz */
  84};
  85
  86static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
  87        /* Clock divider value for following
  88         * { COPY, HPM }
  89         */
  90        { 0, 2 },       /* 1700 MHz */
  91        { 0, 2 },       /* 1600 MHz */
  92        { 0, 2 },       /* 1500 MHz */
  93        { 0, 2 },       /* 1400 MHz */
  94        { 0, 2 },       /* 1300 MHz */
  95        { 0, 2 },       /* 1200 MHz */
  96        { 0, 2 },       /* 1100 MHz */
  97        { 0, 2 },       /* 1000 MHz */
  98        { 0, 2 },       /* 900 MHz */
  99        { 0, 2 },       /* 800 MHz */
 100        { 0, 2 },       /* 700 MHz */
 101        { 0, 2 },       /* 600 MHz */
 102        { 0, 2 },       /* 500 MHz */
 103        { 0, 2 },       /* 400 MHz */
 104        { 0, 2 },       /* 300 MHz */
 105        { 0, 2 },       /* 200 MHz */
 106};
 107
 108static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
 109        ((425 << 16) | (6 << 8) | 0),   /* 1700 MHz */
 110        ((200 << 16) | (3 << 8) | 0),   /* 1600 MHz */
 111        ((250 << 16) | (4 << 8) | 0),   /* 1500 MHz */
 112        ((175 << 16) | (3 << 8) | 0),   /* 1400 MHz */
 113        ((325 << 16) | (6 << 8) | 0),   /* 1300 MHz */
 114        ((200 << 16) | (4 << 8) | 0),   /* 1200 MHz */
 115        ((275 << 16) | (6 << 8) | 0),   /* 1100 MHz */
 116        ((125 << 16) | (3 << 8) | 0),   /* 1000 MHz */
 117        ((150 << 16) | (4 << 8) | 0),   /* 900 MHz */
 118        ((100 << 16) | (3 << 8) | 0),   /* 800 MHz */
 119        ((175 << 16) | (3 << 8) | 1),   /* 700 MHz */
 120        ((200 << 16) | (4 << 8) | 1),   /* 600 MHz */
 121        ((125 << 16) | (3 << 8) | 1),   /* 500 MHz */
 122        ((100 << 16) | (3 << 8) | 1),   /* 400 MHz */
 123        ((200 << 16) | (4 << 8) | 2),   /* 300 MHz */
 124        ((100 << 16) | (3 << 8) | 2),   /* 200 MHz */
 125};
 126
 127/* ASV group voltage table */
 128static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
 129        1300000, 1250000, 1225000, 1200000, 1150000,
 130        1125000, 1100000, 1075000, 1050000, 1025000,
 131        1012500, 1000000,  975000,  950000,  937500,
 132        925000
 133};
 134
 135static void set_clkdiv(unsigned int div_index)
 136{
 137        unsigned int tmp;
 138
 139        /* Change Divider - CPU0 */
 140
 141        tmp = exynos5250_clkdiv_table[div_index].clkdiv;
 142
 143        __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);
 144
 145        while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111)
 146                cpu_relax();
 147
 148        /* Change Divider - CPU1 */
 149        tmp = exynos5250_clkdiv_table[div_index].clkdiv1;
 150
 151        __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);
 152
 153        while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11)
 154                cpu_relax();
 155}
 156
 157static void set_apll(unsigned int new_index,
 158                             unsigned int old_index)
 159{
 160        unsigned int tmp, pdiv;
 161
 162        /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
 163        clk_set_parent(moutcore, mout_mpll);
 164
 165        do {
 166                cpu_relax();
 167                tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16);
 168                tmp &= 0x7;
 169        } while (tmp != 0x2);
 170
 171        /* 2. Set APLL Lock time */
 172        pdiv = ((exynos5_apll_pms_table[new_index] >> 8) & 0x3f);
 173
 174        __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK);
 175
 176        /* 3. Change PLL PMS values */
 177        tmp = __raw_readl(EXYNOS5_APLL_CON0);
 178        tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
 179        tmp |= exynos5_apll_pms_table[new_index];
 180        __raw_writel(tmp, EXYNOS5_APLL_CON0);
 181
 182        /* 4. wait_lock_time */
 183        do {
 184                cpu_relax();
 185                tmp = __raw_readl(EXYNOS5_APLL_CON0);
 186        } while (!(tmp & (0x1 << 29)));
 187
 188        /* 5. MUX_CORE_SEL = APLL */
 189        clk_set_parent(moutcore, mout_apll);
 190
 191        do {
 192                cpu_relax();
 193                tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
 194                tmp &= (0x7 << 16);
 195        } while (tmp != (0x1 << 16));
 196
 197}
 198
 199bool exynos5250_pms_change(unsigned int old_index, unsigned int new_index)
 200{
 201        unsigned int old_pm = (exynos5_apll_pms_table[old_index] >> 8);
 202        unsigned int new_pm = (exynos5_apll_pms_table[new_index] >> 8);
 203
 204        return (old_pm == new_pm) ? 0 : 1;
 205}
 206
 207static void exynos5250_set_frequency(unsigned int old_index,
 208                                  unsigned int new_index)
 209{
 210        unsigned int tmp;
 211
 212        if (old_index > new_index) {
 213                if (!exynos5250_pms_change(old_index, new_index)) {
 214                        /* 1. Change the system clock divider values */
 215                        set_clkdiv(new_index);
 216                        /* 2. Change just s value in apll m,p,s value */
 217                        tmp = __raw_readl(EXYNOS5_APLL_CON0);
 218                        tmp &= ~(0x7 << 0);
 219                        tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
 220                        __raw_writel(tmp, EXYNOS5_APLL_CON0);
 221
 222                } else {
 223                        /* Clock Configuration Procedure */
 224                        /* 1. Change the system clock divider values */
 225                        set_clkdiv(new_index);
 226                        /* 2. Change the apll m,p,s value */
 227                        set_apll(new_index, old_index);
 228                }
 229        } else if (old_index < new_index) {
 230                if (!exynos5250_pms_change(old_index, new_index)) {
 231                        /* 1. Change just s value in apll m,p,s value */
 232                        tmp = __raw_readl(EXYNOS5_APLL_CON0);
 233                        tmp &= ~(0x7 << 0);
 234                        tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
 235                        __raw_writel(tmp, EXYNOS5_APLL_CON0);
 236                        /* 2. Change the system clock divider values */
 237                        set_clkdiv(new_index);
 238                } else {
 239                        /* Clock Configuration Procedure */
 240                        /* 1. Change the apll m,p,s value */
 241                        set_apll(new_index, old_index);
 242                        /* 2. Change the system clock divider values */
 243                        set_clkdiv(new_index);
 244                }
 245        }
 246}
 247
 248static void __init set_volt_table(void)
 249{
 250        unsigned int i;
 251
 252        max_support_idx = L0;
 253
 254        for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
 255                exynos5250_volt_table[i] = asv_voltage_5250[i];
 256}
 257
 258int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
 259{
 260        int i;
 261        unsigned int tmp;
 262        unsigned long rate;
 263
 264        set_volt_table();
 265
 266        cpu_clk = clk_get(NULL, "armclk");
 267        if (IS_ERR(cpu_clk))
 268                return PTR_ERR(cpu_clk);
 269
 270        moutcore = clk_get(NULL, "mout_cpu");
 271        if (IS_ERR(moutcore))
 272                goto err_moutcore;
 273
 274        mout_mpll = clk_get(NULL, "mout_mpll");
 275        if (IS_ERR(mout_mpll))
 276                goto err_mout_mpll;
 277
 278        rate = clk_get_rate(mout_mpll) / 1000;
 279
 280        mout_apll = clk_get(NULL, "mout_apll");
 281        if (IS_ERR(mout_apll))
 282                goto err_mout_apll;
 283
 284        for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
 285
 286                exynos5250_clkdiv_table[i].index = i;
 287
 288                tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0);
 289
 290                tmp &= ~((0x7 << 0) | (0x7 << 4) | (0x7 << 8) |
 291                        (0x7 << 12) | (0x7 << 16) | (0x7 << 20) |
 292                        (0x7 << 24) | (0x7 << 28));
 293
 294                tmp |= ((clkdiv_cpu0_5250[i][0] << 0) |
 295                        (clkdiv_cpu0_5250[i][1] << 4) |
 296                        (clkdiv_cpu0_5250[i][2] << 8) |
 297                        (clkdiv_cpu0_5250[i][3] << 12) |
 298                        (clkdiv_cpu0_5250[i][4] << 16) |
 299                        (clkdiv_cpu0_5250[i][5] << 20) |
 300                        (clkdiv_cpu0_5250[i][6] << 24) |
 301                        (clkdiv_cpu0_5250[i][7] << 28));
 302
 303                exynos5250_clkdiv_table[i].clkdiv = tmp;
 304
 305                tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1);
 306
 307                tmp &= ~((0x7 << 0) | (0x7 << 4));
 308
 309                tmp |= ((clkdiv_cpu1_5250[i][0] << 0) |
 310                        (clkdiv_cpu1_5250[i][1] << 4));
 311
 312                exynos5250_clkdiv_table[i].clkdiv1 = tmp;
 313        }
 314
 315        info->mpll_freq_khz = rate;
 316        /* 1000Mhz */
 317        info->pm_lock_idx = L7;
 318        /* 800Mhz */
 319        info->pll_safe_idx = L9;
 320        info->max_support_idx = max_support_idx;
 321        info->min_support_idx = min_support_idx;
 322        info->cpu_clk = cpu_clk;
 323        info->volt_table = exynos5250_volt_table;
 324        info->freq_table = exynos5250_freq_table;
 325        info->set_freq = exynos5250_set_frequency;
 326        info->need_apll_change = exynos5250_pms_change;
 327
 328        return 0;
 329
 330err_mout_apll:
 331        clk_put(mout_mpll);
 332err_mout_mpll:
 333        clk_put(moutcore);
 334err_moutcore:
 335        clk_put(cpu_clk);
 336
 337        pr_err("%s: failed initialization\n", __func__);
 338        return -EINVAL;
 339}
 340EXPORT_SYMBOL(exynos5250_cpufreq_init);
 341
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.