exynos5250-cpufreq.c 4.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
 *		http://www.samsung.com
 *
 * EXYNOS5250 - 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/module.h>
#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>
22 23

#include "exynos-cpufreq.h"
24 25 26 27 28 29

static struct clk *cpu_clk;
static struct clk *moutcore;
static struct clk *mout_mpll;
static struct clk *mout_apll;

30 31 32 33 34
static unsigned int exynos5250_volt_table[] = {
	1300000, 1250000, 1225000, 1200000, 1150000,
	1125000, 1100000, 1075000, 1050000, 1025000,
	1012500, 1000000,  975000,  950000,  937500,
	925000
35 36 37 38 39 40 41 42 43 44 45
};

static struct cpufreq_frequency_table exynos5250_freq_table[] = {
	{L0, 1700 * 1000},
	{L1, 1600 * 1000},
	{L2, 1500 * 1000},
	{L3, 1400 * 1000},
	{L4, 1300 * 1000},
	{L5, 1200 * 1000},
	{L6, 1100 * 1000},
	{L7, 1000 * 1000},
46 47
	{L8,  900 * 1000},
	{L9,  800 * 1000},
48 49 50 51 52 53 54 55 56
	{L10, 700 * 1000},
	{L11, 600 * 1000},
	{L12, 500 * 1000},
	{L13, 400 * 1000},
	{L14, 300 * 1000},
	{L15, 200 * 1000},
	{0, CPUFREQ_TABLE_END},
};

57
static struct apll_freq apll_freq_5250[] = {
58
	/*
59 60 61 62 63
	 * values:
	 * freq
	 * clock divider for ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2
	 * clock divider for COPY, HPM, RESERVED
	 * PLL M, P, S
64
	 */
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
	APLL_FREQ(1700, 0, 3, 7, 7, 7, 3, 5, 0, 0, 2, 0, 425, 6, 0),
	APLL_FREQ(1600, 0, 3, 7, 7, 7, 1, 4, 0, 0, 2, 0, 200, 3, 0),
	APLL_FREQ(1500, 0, 2, 7, 7, 7, 1, 4, 0, 0, 2, 0, 250, 4, 0),
	APLL_FREQ(1400, 0, 2, 7, 7, 6, 1, 4, 0, 0, 2, 0, 175, 3, 0),
	APLL_FREQ(1300, 0, 2, 7, 7, 6, 1, 3, 0, 0, 2, 0, 325, 6, 0),
	APLL_FREQ(1200, 0, 2, 7, 7, 5, 1, 3, 0, 0, 2, 0, 200, 4, 0),
	APLL_FREQ(1100, 0, 3, 7, 7, 5, 1, 3, 0, 0, 2, 0, 275, 6, 0),
	APLL_FREQ(1000, 0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 125, 3, 0),
	APLL_FREQ(900,  0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 150, 4, 0),
	APLL_FREQ(800,  0, 1, 7, 7, 4, 1, 2, 0, 0, 2, 0, 100, 3, 0),
	APLL_FREQ(700,  0, 1, 7, 7, 3, 1, 1, 0, 0, 2, 0, 175, 3, 1),
	APLL_FREQ(600,  0, 1, 7, 7, 3, 1, 1, 0, 0, 2, 0, 200, 4, 1),
	APLL_FREQ(500,  0, 1, 7, 7, 2, 1, 1, 0, 0, 2, 0, 125, 3, 1),
	APLL_FREQ(400,  0, 1, 7, 7, 2, 1, 1, 0, 0, 2, 0, 100, 3, 1),
	APLL_FREQ(300,  0, 1, 7, 7, 1, 1, 1, 0, 0, 2, 0, 200, 4, 2),
	APLL_FREQ(200,  0, 1, 7, 7, 1, 1, 1, 0, 0, 2, 0, 100, 3, 2),
81 82 83 84 85 86 87 88
};

static void set_clkdiv(unsigned int div_index)
{
	unsigned int tmp;

	/* Change Divider - CPU0 */

89
	tmp = apll_freq_5250[div_index].clk_div_cpu0;
90 91 92 93 94 95 96

	__raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);

	while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111)
		cpu_relax();

	/* Change Divider - CPU1 */
97
	tmp = apll_freq_5250[div_index].clk_div_cpu1;
98 99 100 101 102 103 104

	__raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);

	while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11)
		cpu_relax();
}

105
static void set_apll(unsigned int index)
106
{
107 108
	unsigned int tmp;
	unsigned int freq = apll_freq_5250[index].freq;
109

110
	/* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
111 112 113 114 115 116 117 118
	clk_set_parent(moutcore, mout_mpll);

	do {
		cpu_relax();
		tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16);
		tmp &= 0x7;
	} while (tmp != 0x2);

119
	clk_set_rate(mout_apll, freq * 1000);
120

121
	/* MUX_CORE_SEL = APLL */
122 123 124 125 126 127 128 129 130 131 132 133 134
	clk_set_parent(moutcore, mout_apll);

	do {
		cpu_relax();
		tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
		tmp &= (0x7 << 16);
	} while (tmp != (0x1 << 16));
}

static void exynos5250_set_frequency(unsigned int old_index,
				  unsigned int new_index)
{
	if (old_index > new_index) {
135 136
		set_clkdiv(new_index);
		set_apll(new_index);
137
	} else if (old_index < new_index) {
138 139
		set_apll(new_index);
		set_clkdiv(new_index);
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	}
}

int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
{
	unsigned long rate;

	cpu_clk = clk_get(NULL, "armclk");
	if (IS_ERR(cpu_clk))
		return PTR_ERR(cpu_clk);

	moutcore = clk_get(NULL, "mout_cpu");
	if (IS_ERR(moutcore))
		goto err_moutcore;

	mout_mpll = clk_get(NULL, "mout_mpll");
	if (IS_ERR(mout_mpll))
		goto err_mout_mpll;

	rate = clk_get_rate(mout_mpll) / 1000;

	mout_apll = clk_get(NULL, "mout_apll");
	if (IS_ERR(mout_apll))
		goto err_mout_apll;

	info->mpll_freq_khz = rate;
	/* 800Mhz */
	info->pll_safe_idx = L9;
	info->cpu_clk = cpu_clk;
	info->volt_table = exynos5250_volt_table;
	info->freq_table = exynos5250_freq_table;
	info->set_freq = exynos5250_set_frequency;

	return 0;

err_mout_apll:
	clk_put(mout_mpll);
err_mout_mpll:
	clk_put(moutcore);
err_moutcore:
	clk_put(cpu_clk);

	pr_err("%s: failed initialization\n", __func__);
	return -EINVAL;
}