blob: 90663086d02307713b1575438ef9128d0fe51ea7 [file] [log] [blame]
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* EXYNOS5420 - CPU frequency scaling support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/cpufreq.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <plat/clock.h>
#include <mach/cpufreq.h>
#include <mach/asv-exynos.h>
#define CPUFREQ_NUM_LEVELS (L19 + 1)
#define CPUFREQ_NUM_LEVELS_CA7 (L13 + 1)
#define EXYNOS5_CLKDIV_STATCPU0_MASK 0x11111111
#define EXYNOS5_CLKDIV_STATCPU1_MASK 0x111
#define EXYNOS5420_EVT2_REV_NUM 0x20
static int max_support_idx;
static int max_support_idx_CA7;
static int min_support_idx = (CPUFREQ_NUM_LEVELS - 1);
static int min_support_idx_CA7 = (CPUFREQ_NUM_LEVELS_CA7 - 1);
static int arm_safe_idx;
static int kfc_safe_idx;
static struct clk *mout_cpu;
static struct clk *mout_mspll_cpu;
static struct clk *mout_apll;
static struct clk *fout_apll;
static struct clk *mout_kfc;
static struct clk *mout_mspll_kfc;
static struct clk *mout_kpll;
static struct clk *fout_kpll;
struct cpufreq_clkdiv {
unsigned int index;
unsigned int clkdiv;
unsigned int clkdiv1;
};
static unsigned int exynos5420_volt_table[CPUFREQ_NUM_LEVELS];
static struct cpufreq_frequency_table exynos5420_freq_table[] = {
{L0, 2100 * 1000},
{L1, 2000 * 1000},
{L2, 1900 * 1000},
{L3, 1800 * 1000},
{L4, 1700 * 1000},
{L5, 1600 * 1000},
{L6, 1500 * 1000},
{L7, 1400 * 1000},
{L8, 1300 * 1000},
{L9, 1200 * 1000},
{L10, 1100 * 1000},
{L11, 1000 * 1000},
{L12, 900 * 1000},
{L13, 800 * 1000},
{L14, 700 * 1000},
{L15, 600 * 1000},
{L16, 500 * 1000},
{L17, 400 * 1000},
{L18, 300 * 1000},
{L19, 200 * 1000},
{0, CPUFREQ_TABLE_END},
};
static unsigned int exynos5420_volt_table_CA7[CPUFREQ_NUM_LEVELS_CA7];
static struct cpufreq_frequency_table exynos5420_freq_table_CA7[] = {
{L0, 1500 * 1000},
{L1, 1400 * 1000},
{L2, 1300 * 1000},
{L3, 1200 * 1000},
{L4, 1100 * 1000},
{L5, 1000 * 1000},
{L6, 900 * 1000},
{L7, 800 * 1000},
{L8, 700 * 1000},
{L9, 600 * 1000},
{L10, 500 * 1000},
{L11, 400 * 1000},
{L12, 300 * 1000},
{L13, 200 * 1000},
{0, CPUFREQ_TABLE_END},
};
static struct cpufreq_clkdiv
exynos5420_clkdiv_table_CA7[CPUFREQ_NUM_LEVELS_CA7];
static struct cpufreq_clkdiv exynos5420_clkdiv_table[CPUFREQ_NUM_LEVELS];
static unsigned int clkdiv_cpu0_5420[CPUFREQ_NUM_LEVELS][6] = {
/*
* Clock divider values for
* {CPUD_5420, CPUD_5422, ATB, PCLK_DBG, APLL, ARM2}
*/
{ 4, 4, 7, 7, 3, 0 }, /* ARM L0: 2.1GHz */
{ 4, 4, 7, 7, 3, 0 }, /* ARM L1: 2.0GHz */
{ 4, 4, 7, 7, 3, 0 }, /* ARM L2: 1.9GHz */
{ 4, 4, 7, 7, 3, 0 }, /* ARM L3: 1.8GHz */
{ 3, 3, 7, 7, 3, 0 }, /* ARM L4: 1.7GHz */
{ 3, 3, 7, 7, 3, 0 }, /* ARM L5: 1.6GHz */
{ 3, 3, 7, 7, 3, 0 }, /* ARM L6: 1.5GHz */
{ 3, 3, 7, 7, 3, 0 }, /* ARM L7: 1.4GHz */
{ 2, 2, 7, 7, 3, 0 }, /* ARM L8: 1.3GHz */
{ 2, 2, 7, 7, 3, 0 }, /* ARM L9: 1.2GHz */
{ 2, 2, 7, 7, 3, 0 }, /* ARM L10: 1.1GHz */
{ 2, 2, 6, 6, 3, 0 }, /* ARM L11: 1.0GHz */
{ 2, 2, 6, 6, 3, 0 }, /* ARM L12: 900MHz */
{ 2, 2, 5, 5, 3, 0 }, /* ARM L13: 800MHz */
{ 2, 2, 5, 5, 3, 0 }, /* ARM L14: 700MHz */
{ 2, 2, 4, 4, 3, 0 }, /* ARM L15: 600MHz */
{ 2, 2, 3, 3, 3, 0 }, /* ARM L16: 500MHz */
{ 2, 2, 3, 3, 3, 0 }, /* ARM L17: 400MHz */
{ 2, 2, 3, 3, 3, 0 }, /* ARM L18: 300MHz */
{ 2, 2, 3, 3, 3, 0 }, /* ARM L19: 200MHz */
};
static unsigned int clkdiv_cpu0_5420_CA7[CPUFREQ_NUM_LEVELS_CA7][6] = {
/*
* Clock divider values for
* {KFC, ACLK_5420, ACLK_5422, HPM, PCLK, KPLL}
*/
{ 0, 2, 3, 7, 5, 3 }, /* KFC L0: 1.5GHz */
{ 0, 2, 3, 7, 5, 3 }, /* KFC L1: 1.4GHz */
{ 0, 2, 2, 7, 5, 3 }, /* KFC L2: 1.3GHz */
{ 0, 2, 2, 7, 5, 3 }, /* KFC L3: 1.2GHz */
{ 0, 2, 2, 7, 5, 3 }, /* KFC L4: 1.1GHz */
{ 0, 2, 2, 7, 5, 3 }, /* KFC L5: 1.0GHz */
{ 0, 2, 2, 7, 5, 3 }, /* KFC L6: 900MHz */
{ 0, 2, 2, 7, 5, 3 }, /* KFC L7: 800MHz */
{ 0, 2, 2, 7, 4, 3 }, /* KFC L8: 700MHz */
{ 0, 2, 2, 7, 4, 3 }, /* KFC L9: 600MHz */
{ 0, 2, 2, 7, 4, 3 }, /* KFC L10: 500MHz */
{ 0, 2, 2, 7, 3, 3 }, /* KFC L11: 400MHz */
{ 0, 2, 2, 7, 3, 3 }, /* KFC L12: 300MHz */
{ 0, 2, 2, 7, 3, 3 }, /* KFC L13: 200MHz */
};
unsigned int clkdiv_cpu1_5420[CPUFREQ_NUM_LEVELS][2] = {
/* Clock divider values for { copy, HPM } */
{ 7, 7 }, /* ARM L0: 2.1GHz */
{ 7, 7 }, /* ARM L1: 2.0GHz */
{ 7, 7 }, /* ARM L2: 1.9GHz */
{ 7, 7 }, /* ARM L3: 1.8GHz */
{ 7, 7 }, /* ARM L4: 1.7GHz */
{ 7, 7 }, /* ARM L5: 1.6GHz */
{ 7, 7 }, /* ARM L6: 1.5GHz */
{ 7, 7 }, /* ARM L7: 1.4GHz */
{ 7, 7 }, /* ARM L8: 1.3GHz */
{ 7, 7 }, /* ARM L9: 1.2GHz */
{ 7, 7 }, /* ARM L10: 1.1GHz */
{ 7, 7 }, /* ARM L11: 1.0GHz */
{ 7, 7 }, /* ARM L12: 900MHz */
{ 7, 7 }, /* ARM L13: 800MHz */
{ 7, 7 }, /* ARM L14: 700MHz */
{ 7, 7 }, /* ARM L15: 600MHz */
{ 7, 7 }, /* ARM L16: 500MHz */
{ 7, 7 }, /* ARM L17: 400MHz */
{ 7, 7 }, /* ARM L18: 300MHz */
{ 7, 7 }, /* ARM L19: 200MHz */
};
/*
* Default ASV table
*/
static const unsigned int asv_voltage_5420[CPUFREQ_NUM_LEVELS] = {
1362500, /* L0 2100 */
1312500, /* L1 2000 */
1275000, /* L2 1900 */
1225000, /* L3 1800 */
1200000, /* L4 1700 */
1200000, /* L5 1600 */
1175000, /* L6 1500 */
1150000, /* L7 1400 */
1125000, /* L8 1300 */
1100000, /* L9 1200 */
1075000, /* L10 1100 */
1050000, /* L11 1000 */
1000000, /* L12 900 */
950000, /* L13 800 */
925000, /* L14 700 */
900000, /* L15 600 */
900000, /* L16 500 */
900000, /* L17 400 */
900000, /* L18 300 */
900000, /* L19 200 */
};
static const unsigned int asv_voltage_5420_CA7[CPUFREQ_NUM_LEVELS_CA7] = {
1300000, /* L0 1500 */
1300000, /* L1 1400 */
1300000, /* L2 1300 */
1200000, /* L3 1200 */
1200000, /* L4 1100 */
1112500, /* L5 1000 */
1100000, /* L6 900 */
1100000, /* L7 800 */
1000000, /* L8 700 */
1000000, /* L9 600 */
1000000, /* L10 500 */
1000000, /* L11 400 */
900000, /* L12 300 */
900000, /* L13 200 */
};
static void exynos5420_set_clkdiv(unsigned int div_index)
{
unsigned int tmp;
/* Change Divider - CPU0 for CMU_CPU */
tmp = exynos5420_clkdiv_table[div_index].clkdiv;
__raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKDIV_STATCPU0);
} while (tmp & EXYNOS5_CLKDIV_STATCPU0_MASK);
pr_debug("DIV_CPU0[0x%x]\n", __raw_readl(EXYNOS5_CLKDIV_CPU0));
/* Change Divider - CPU1 for CMU_CPU */
tmp = exynos5420_clkdiv_table[div_index].clkdiv1;
__raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKDIV_STATCPU1);
} while (tmp & EXYNOS5_CLKDIV_STATCPU1_MASK);
pr_debug("DIV_CPU1[0x%x]\n", __raw_readl(EXYNOS5_CLKDIV_CPU1));
}
static void exynos5420_set_clkdiv_CA7(unsigned int div_index)
{
unsigned int tmp;
/* Change Divider - KFC0 */
tmp = exynos5420_clkdiv_table_CA7[div_index].clkdiv;
__raw_writel(tmp, EXYNOS5_CLKDIV_KFC0);
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKDIV_STAT_KFC0);
} while (tmp & EXYNOS5_CLKDIV_STATCPU0_MASK);
pr_debug("DIV_KFC0[0x%x]\n", __raw_readl(EXYNOS5_CLKDIV_KFC0));
}
static void exynos5420_set_apll(unsigned int new_index,
unsigned int old_index)
{
unsigned int tmp;
unsigned long rate;
/* 0. If switching is faster than old or new, give us a safe divider */
if ((new_index > arm_safe_idx) && (old_index > arm_safe_idx))
exynos5420_set_clkdiv(arm_safe_idx);
/* 1. MUX_CORE_SEL = MOUT_MSPLL; ARMCLK uses MOUT_MSPLL for lock time */
if (clk_set_parent(mout_cpu, mout_mspll_cpu)) {
pr_err(KERN_ERR "Unable to set parent %s of clock %s.\n",
mout_mspll_cpu->name, mout_cpu->name);
}
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
tmp &= EXYNOS5_CLKMUX_STATCPU_MUXCORE_MASK;
} while (tmp != (0x2 << EXYNOS5_CLKSRC_CPU_MUXCORE_SHIFT));
/* 2. Set APLL rate */
rate = exynos5420_freq_table[new_index].frequency * 1000;
if (clk_set_rate(fout_apll, rate))
pr_err("Unable to change apll rate to %lu\n", rate);
/* 3. MUX_CORE_SEL = APLL */
if (clk_set_parent(mout_cpu, mout_apll)) {
pr_err("Unable to set parent %s of clock %s.\n",
mout_apll->name, mout_cpu->name);
}
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
tmp &= EXYNOS5_CLKMUX_STATCPU_MUXCORE_MASK;
} while (tmp != (0x1 << EXYNOS5_CLKSRC_CPU_MUXCORE_SHIFT));
/* 4. restore original div value */
if ((new_index > arm_safe_idx) && (old_index > arm_safe_idx))
exynos5420_set_clkdiv(new_index);
}
static void exynos5420_set_kpll(unsigned int new_index,
unsigned int old_index)
{
unsigned int tmp;
unsigned long rate;
/* 0. If switching is faster than old or new, give us a safe divider */
if ((new_index > kfc_safe_idx) && (old_index > kfc_safe_idx))
exynos5420_set_clkdiv_CA7(kfc_safe_idx);
/* 1. MUX_CORE_SEL = MPLL, KFCCLK uses MPLL for lock time */
if (clk_set_parent(mout_kfc, mout_mspll_kfc))
pr_err("Unable to set mout_mspll_kfc as parent of mout_kfc\n");
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKMUX_STAT_KFC);
tmp &= EXYNOS5_CLKMUX_STATKFC_MUXCORE_MASK;
} while (tmp != (0x2 << EXYNOS5_CLKSRC_KFC_MUXCORE_SHIFT));
/* 2. Set KPLL rate */
rate = exynos5420_freq_table_CA7[new_index].frequency * 1000;
if (clk_set_rate(fout_kpll, rate))
pr_err("Unable to change kpll rate to %lu\n", rate);
/* 3. MUX_CORE_SEL = KPLL */
if (clk_set_parent(mout_kfc, mout_kpll))
pr_err("Unable to set mout_kpll as parent of mout_kfc\n");
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_CLKMUX_STAT_KFC);
tmp &= EXYNOS5_CLKMUX_STATKFC_MUXCORE_MASK;
} while (tmp != (0x1 << EXYNOS5_CLKSRC_KFC_MUXCORE_SHIFT));
/* 4. restore original div value */
if ((new_index > kfc_safe_idx) && (old_index > kfc_safe_idx))
exynos5420_set_clkdiv_CA7(new_index);
}
static bool exynos5420_pms_change(unsigned int old_index,
unsigned int new_index)
{
/*
* The Exynos cpufreq driver uses this to determine if it can
* avoid changing the CPU voltage and re-parenting the CPU clock
* while chaning the PLL rate. Because we're using CCF to change
* the PLL rate, we no longer have access to the PLL divider table,
* so we can't tell whether or not we can take the fast path from
* here and must always take the slow path. Since this only affects
* a few transitions, there should hopefully be no impact on
* performance.
*/
return (old_index != new_index);
}
static void exynos5420_set_frequency(unsigned int old_index,
unsigned int new_index)
{
if (old_index > new_index) {
/* 1. Change the system clock divider values */
exynos5420_set_clkdiv(new_index);
/* 2. Change the apll rate */
exynos5420_set_apll(new_index, old_index);
} else if (old_index < new_index) {
/* 1. Change the apll rate */
exynos5420_set_apll(new_index, old_index);
/* 2. Change the system clock divider values */
exynos5420_set_clkdiv(new_index);
}
}
static void exynos5420_set_frequency_CA7(unsigned int old_index,
unsigned int new_index)
{
if (old_index > new_index) {
/* 1. Change the system clock divider values */
exynos5420_set_clkdiv_CA7(new_index);
/* 2. Change the kpll rate */
exynos5420_set_kpll(new_index, old_index);
} else if (old_index < new_index) {
/* 1. Change the kpll rate */
exynos5420_set_kpll(new_index, old_index);
/* 2. Change the system clock divider values */
exynos5420_set_clkdiv_CA7(new_index);
}
}
static void __init set_volt_table(void)
{
unsigned int i;
unsigned int asv_volt = 0;
unsigned int exynos5420_rev_num;
for (i = 0; i < CPUFREQ_NUM_LEVELS; i++) {
#ifdef CONFIG_ARM_EXYNOS5420_ASV
asv_volt = get_match_volt
(ID_ARM, exynos5420_freq_table[i].frequency);
#endif
if (!asv_volt)
exynos5420_volt_table[i] = asv_voltage_5420[i];
else
exynos5420_volt_table[i] = asv_volt;
pr_debug("CPUFREQ of CA15 L%d : %d uV\n", i,
exynos5420_volt_table[i]);
}
if (soc_is_exynos5420()) {
exynos5420_rev_num = samsung_rev();
if (exynos5420_rev_num >= EXYNOS5420_EVT2_REV_NUM)
max_support_idx = L2;
else
max_support_idx = L3;
} else {
max_support_idx = L1;
}
min_support_idx = L14;
for (i = L0; i < max_support_idx; i++)
exynos5420_freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
for (i = (min_support_idx + 1); i < CPUFREQ_NUM_LEVELS; i++)
exynos5420_freq_table[i].frequency = CPUFREQ_ENTRY_INVALID;
}
static void __init set_volt_table_CA7(void)
{
unsigned int i;
unsigned int asv_volt = 0;
for (i = 0; i < CPUFREQ_NUM_LEVELS_CA7; i++) {
#ifdef CONFIG_ARM_EXYNOS5420_ASV
asv_volt = get_match_volt(ID_KFC,
exynos5420_freq_table_CA7[i].frequency);
#endif
if (!asv_volt)
exynos5420_volt_table_CA7[i] = asv_voltage_5420_CA7[i];
else
exynos5420_volt_table_CA7[i] = asv_volt;
pr_debug("CPUFREQ of CA7 L%d : %d uV\n", i,
exynos5420_volt_table_CA7[i]);
}
max_support_idx_CA7 = L2;
min_support_idx_CA7 = L9;
for (i = L0; i < max_support_idx_CA7; i++)
exynos5420_freq_table_CA7[i].frequency = CPUFREQ_ENTRY_INVALID;
for (i = (min_support_idx_CA7 + 1); i < CPUFREQ_NUM_LEVELS_CA7; i++)
exynos5420_freq_table_CA7[i].frequency = CPUFREQ_ENTRY_INVALID;
}
int exynos5420_cpufreq_CA7_init(struct exynos_dvfs_info *info)
{
int i;
unsigned int tmp;
unsigned long rate;
unsigned int aclk_index;
struct clk *mout_mspll_kfc_p;
set_volt_table_CA7();
mout_kfc = clk_get(NULL, "mout_kfc");
if (IS_ERR(mout_kfc))
goto err_mout_kfc;
mout_mspll_kfc = clk_get(NULL, "mout_mspll_kfc");
if (IS_ERR(mout_mspll_kfc))
goto err_mout_mspll_kfc;
if (soc_is_exynos5422())
mout_mspll_kfc_p = clk_get(NULL, "mout_cpll");
else
mout_mspll_kfc_p = clk_get(NULL, "mout_spll");
if (IS_ERR(mout_mspll_kfc_p)) {
pr_err("Clock mout_mspll_kfc_p not found\n");
goto err_mout_mspll_kfc_p;
}
clk_set_parent(mout_mspll_kfc, mout_mspll_kfc_p);
clk_put(mout_mspll_kfc_p);
rate = clk_get_rate(mout_mspll_kfc) / 1000;
mout_kpll = clk_get(NULL, "mout_kpll");
if (IS_ERR(mout_kpll))
goto err_mout_mspll_kfc_p;
fout_kpll = clk_get(NULL, "fout_kpll");
if (IS_ERR(fout_kpll))
goto err_fout_kpll;
for (i = L0; i < CPUFREQ_NUM_LEVELS_CA7; i++) {
exynos5420_clkdiv_table_CA7[i].index = i;
tmp = __raw_readl(EXYNOS5_CLKDIV_KFC0);
tmp &= ~(EXYNOS5_CLKDIV_KFC0_CORE_MASK |
EXYNOS5_CLKDIV_KFC0_ACLK_MASK |
EXYNOS5_CLKDIV_KFC0_HPM_MASK |
EXYNOS5_CLKDIV_KFC0_PCLK_MASK |
EXYNOS5_CLKDIV_KFC0_KPLL_MASK);
if (soc_is_exynos5420())
aclk_index = 1;
else
aclk_index = 2;
tmp |= ((clkdiv_cpu0_5420_CA7[i][0] <<
EXYNOS5_CLKDIV_KFC0_CORE_SHIFT) |
(clkdiv_cpu0_5420_CA7[i][aclk_index] <<
EXYNOS5_CLKDIV_KFC0_ACLK_SHIFT) |
(clkdiv_cpu0_5420_CA7[i][3] <<
EXYNOS5_CLKDIV_KFC0_HPM_SHIFT) |
(clkdiv_cpu0_5420_CA7[i][4] <<
EXYNOS5_CLKDIV_KFC0_PCLK_SHIFT) |
(clkdiv_cpu0_5420_CA7[i][5] <<
EXYNOS5_CLKDIV_KFC0_KPLL_SHIFT));
exynos5420_clkdiv_table_CA7[i].clkdiv = tmp;
}
for (i = min_support_idx_CA7; i > max_support_idx_CA7; i--) {
if (exynos5420_freq_table_CA7[i].frequency >= rate)
break;
}
kfc_safe_idx = info->pll_safe_idx = i;
pr_debug("%s: pll_safe_idx = %d (%d, %lu)\n",
__func__, i, exynos5420_freq_table_CA7[i].frequency, rate);
info->mpll_freq_khz = rate;
info->pm_lock_idx = L0;
info->max_support_idx = max_support_idx_CA7;
info->min_support_idx = min_support_idx_CA7;
info->cpu_clk = fout_kpll;
pr_debug("fout_kpll[%lu]\n", clk_get_rate(fout_kpll));
info->volt_table = exynos5420_volt_table_CA7;
info->freq_table = exynos5420_freq_table_CA7;
info->set_freq = exynos5420_set_frequency_CA7;
info->need_apll_change = exynos5420_pms_change;
return 0;
err_fout_kpll:
clk_put(mout_kpll);
err_mout_mspll_kfc_p:
clk_put(mout_mspll_kfc);
err_mout_mspll_kfc:
clk_put(mout_kfc);
err_mout_kfc:
pr_err("%s: failed initialization\n", __func__);
return -EINVAL;
}
int exynos5420_cpufreq_init(struct exynos_dvfs_info *info)
{
int i;
unsigned int tmp;
unsigned long rate;
unsigned int cpud_index;
struct clk *mout_mspll_cpu_p;
set_volt_table();
mout_cpu = clk_get(NULL, "mout_cpu");
if (IS_ERR(mout_cpu))
goto err_mout_cpu;
mout_mspll_cpu = clk_get(NULL, "mout_mspll_cpu");
if (IS_ERR(mout_mspll_cpu))
goto err_mout_mspll_cpu;
if (soc_is_exynos5422())
mout_mspll_cpu_p = clk_get(NULL, "mout_mpll");
else
mout_mspll_cpu_p = clk_get(NULL, "mout_spll");
if (IS_ERR(mout_mspll_cpu_p)) {
pr_err("Clock mout_mspll_cpu_p not found\n");
goto err_mout_mspll_cpu_p;
}
clk_set_parent(mout_mspll_cpu, mout_mspll_cpu_p);
clk_put(mout_mspll_cpu_p);
rate = clk_get_rate(mout_mspll_cpu) / 1000;
mout_apll = clk_get(NULL, "mout_apll");
if (IS_ERR(mout_apll))
goto err_mout_mspll_cpu_p;
fout_apll = clk_get(NULL, "fout_apll");
if (IS_ERR(fout_apll))
goto err_fout_apll;
for (i = L0; i < CPUFREQ_NUM_LEVELS; i++) {
exynos5420_clkdiv_table[i].index = i;
tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0);
tmp &= ~(EXYNOS5_CLKDIV_CPU0_CPUD_MASK |
EXYNOS5_CLKDIV_CPU0_ATB_MASK |
EXYNOS5_CLKDIV_CPU0_PCLKDBG_MASK |
EXYNOS5_CLKDIV_CPU0_APLL_MASK |
EXYNOS5_CLKDIV_CPU0_CORE2_MASK);
if (soc_is_exynos5420())
cpud_index = 0;
else
cpud_index = 1;
tmp |= ((clkdiv_cpu0_5420[i][cpud_index] <<
EXYNOS5_CLKDIV_CPU0_CPUD_SHIFT) |
(clkdiv_cpu0_5420[i][2] <<
EXYNOS5_CLKDIV_CPU0_ATB_SHIFT) |
(clkdiv_cpu0_5420[i][3] <<
EXYNOS5_CLKDIV_CPU0_PCLKDBG_SHIFT) |
(clkdiv_cpu0_5420[i][4] <<
EXYNOS5_CLKDIV_CPU0_APLL_SHIFT) |
(clkdiv_cpu0_5420[i][5] <<
EXYNOS5_CLKDIV_CPU0_CORE2_SHIFT));
exynos5420_clkdiv_table[i].clkdiv = tmp;
tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1);
tmp &= ~(EXYNOS5_CLKDIV_CPU1_COPY_MASK |
EXYNOS5_CLKDIV_CPU1_HPM_MASK);
tmp |= ((clkdiv_cpu1_5420[i][0] <<
EXYNOS5_CLKDIV_CPU1_COPY_SHIFT) |
(clkdiv_cpu1_5420[i][1] <<
EXYNOS5_CLKDIV_CPU1_HPM_SHIFT));
exynos5420_clkdiv_table[i].clkdiv1 = tmp;
}
for (i = min_support_idx; i > max_support_idx; i--) {
if (exynos5420_freq_table[i].frequency >= rate)
break;
}
arm_safe_idx = info->pll_safe_idx = i;
pr_debug("%s: pll_safe_idx = %d (%d, %lu)\n",
__func__, i, exynos5420_freq_table[i].frequency, rate);
info->mpll_freq_khz = rate;
info->pm_lock_idx = L0;
info->max_support_idx = max_support_idx;
info->min_support_idx = min_support_idx;
info->cpu_clk = fout_apll;
pr_debug("fout_apll[%lu]\n", clk_get_rate(fout_apll));
info->volt_table = exynos5420_volt_table;
info->freq_table = exynos5420_freq_table;
info->set_freq = exynos5420_set_frequency;
info->need_apll_change = exynos5420_pms_change;
tmp = __raw_readl(EXYNOS5_CLKOUT_CMU_CPU);
tmp &= ~0xffff;
tmp |= 0x1904;
__raw_writel(tmp, EXYNOS5_CLKOUT_CMU_CPU);
return 0;
err_fout_apll:
clk_put(mout_apll);
err_mout_mspll_cpu_p:
clk_put(mout_mspll_cpu);
err_mout_mspll_cpu:
clk_put(mout_cpu);
err_mout_cpu:
pr_err("%s: failed initialization\n", __func__);
return -EINVAL;
}