diff options
author | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-05-22 20:39:00 +0400 |
---|---|---|
committer | Andrey Konovalov <andrey.konovalov@linaro.org> | 2012-05-22 20:39:00 +0400 |
commit | 0f832ec0abce4400dffafe77927722b7c65c4377 (patch) | |
tree | 6bc06b8bc1bbd1cc6c76b96db47eb7d168761695 | |
parent | f59c68d96739439b1e84553425be9c9f5fc99866 (diff) | |
parent | fd43b478a7679a31658300b047c38448dfc5ead8 (diff) |
Merge branch 'rebase-samslt-asv_cpufreq' into merge-linux-linaro
-rw-r--r-- | arch/arm/mach-exynos/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-exynos/asv-4210.c | 339 | ||||
-rw-r--r-- | arch/arm/mach-exynos/asv.c | 71 | ||||
-rw-r--r-- | arch/arm/mach-exynos/clock-exynos4.c | 35 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/asv.h | 43 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/cpufreq.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/map.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/regs-clock.h | 18 | ||||
-rw-r--r-- | arch/arm/mach-exynos/include/mach/regs-iem.h | 27 | ||||
-rw-r--r-- | drivers/cpufreq/exynos-cpufreq.c | 62 | ||||
-rw-r--r-- | drivers/cpufreq/exynos4210-cpufreq.c | 61 |
11 files changed, 650 insertions, 11 deletions
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index b4fd5349423..a0555cffb31 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_SOC_EXYNOS4212) += clock-exynos4212.o obj-$(CONFIG_PM) += pm.o obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o +obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += asv.o asv-4210.o obj-$(CONFIG_ARCH_EXYNOS4) += pmu.o diff --git a/arch/arm/mach-exynos/asv-4210.c b/arch/arm/mach-exynos/asv-4210.c new file mode 100644 index 00000000000..8f08cb2898f --- /dev/null +++ b/arch/arm/mach-exynos/asv-4210.c @@ -0,0 +1,339 @@ +/* linux/arch/arm/mach-exynos/asv-4210.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS4210 - ASV(Adaptive Support Voltage) driver + * + * 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/init.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <plat/clock.h> + +#include <mach/regs-iem.h> +#include <mach/regs-clock.h> +#include <mach/asv.h> + +/* + * exynos_result_of_asv is result of ASV group. + * Using by this value, other driver can adjust voltage. + */ +unsigned int exynos_result_of_asv; + +enum target_asv { + EXYNOS4210_1200, + EXYNOS4210_1400, + EXYNOS4210_SINGLE_1200, +}; + +struct asv_judge_table exynos4210_1200_limit[] = { + /* HPM , IDS */ + {8 , 4}, + {11 , 8}, + {14 , 12}, + {18 , 17}, + {21 , 27}, + {23 , 45}, + {25 , 55}, +}; + +static struct asv_judge_table exynos4210_1400_limit[] = { + /* HPM , IDS */ + {13 , 8}, + {17 , 12}, + {22 , 32}, + {26 , 52}, +}; + +static struct asv_judge_table exynos4210_single_1200_limit[] = { + /* HPM , IDS */ + {8 , 4}, + {14 , 12}, + {21 , 27}, + {25 , 55}, +}; + +static int exynos4210_asv_pre_clock_init(void) +{ + struct clk *clk_hpm; + struct clk *clk_copy; + struct clk *clk_parent; + + /* PWI clock setting */ + clk_copy = clk_get(NULL, "sclk_pwi"); + if (IS_ERR(clk_copy)) + goto clock_fail; + else { + clk_parent = clk_get(NULL, "xusbxti"); + + if (IS_ERR(clk_parent)) { + clk_put(clk_copy); + + goto clock_fail; + } + if (clk_set_parent(clk_copy, clk_parent)) + goto clock_fail; + + clk_put(clk_parent); + } + clk_set_rate(clk_copy, (48 * MHZ)); + + clk_put(clk_copy); + + /* HPM clock setting */ + clk_copy = clk_get(NULL, "dout_copy"); + if (IS_ERR(clk_copy)) + goto clock_fail; + else { + clk_parent = clk_get(NULL, "mout_mpll"); + if (IS_ERR(clk_parent)) { + clk_put(clk_copy); + + goto clock_fail; + } + if (clk_set_parent(clk_copy, clk_parent)) + goto clock_fail; + + clk_put(clk_parent); + } + + clk_set_rate(clk_copy, (400 * MHZ)); + + clk_put(clk_copy); + + clk_hpm = clk_get(NULL, "sclk_hpm"); + if (IS_ERR(clk_hpm)) + goto clock_fail; + + clk_set_rate(clk_hpm, (200 * MHZ)); + + clk_put(clk_hpm); + + return 0; + +clock_fail: + pr_err("EXYNOS4210: ASV: Clock init fail\n"); + + return -EBUSY; +} + +static int exynos4210_asv_pre_clock_setup(void) +{ + /* APLL_CON0 level register */ + __raw_writel(0x80FA0601, EXYNOS4_APLL_CON0L8); + __raw_writel(0x80C80601, EXYNOS4_APLL_CON0L7); + __raw_writel(0x80C80602, EXYNOS4_APLL_CON0L6); + __raw_writel(0x80C80604, EXYNOS4_APLL_CON0L5); + __raw_writel(0x80C80601, EXYNOS4_APLL_CON0L4); + __raw_writel(0x80C80601, EXYNOS4_APLL_CON0L3); + __raw_writel(0x80C80601, EXYNOS4_APLL_CON0L2); + __raw_writel(0x80C80601, EXYNOS4_APLL_CON0L1); + + /* IEM Divider register */ + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L8); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L7); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L6); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L5); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L4); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L3); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L2); + __raw_writel(0x00500000, EXYNOS4_CLKDIV_IEM_L1); + + return 0; +} + +static int exynos4210_find_group(struct samsung_asv *asv_info, + enum target_asv exynos4_target) +{ + unsigned int ret = 0; + unsigned int i; + + if (exynos4_target == EXYNOS4210_1200) { + ret = ARRAY_SIZE(exynos4210_1200_limit); + + for (i = 0; i < ARRAY_SIZE(exynos4210_1200_limit); i++) { + if (asv_info->hpm_result <= exynos4210_1200_limit[i].hpm_limit || + asv_info->ids_result <= exynos4210_1200_limit[i].ids_limit) { + ret = i; + break; + } + } + } else if (exynos4_target == EXYNOS4210_1400) { + ret = ARRAY_SIZE(exynos4210_1400_limit); + + for (i = 0; i < ARRAY_SIZE(exynos4210_1400_limit); i++) { + if (asv_info->hpm_result <= exynos4210_1400_limit[i].hpm_limit || + asv_info->ids_result <= exynos4210_1400_limit[i].ids_limit) { + ret = i; + break; + } + } + } else if (exynos4_target == EXYNOS4210_SINGLE_1200) { + ret = ARRAY_SIZE(exynos4210_single_1200_limit); + + for (i = 0; i < ARRAY_SIZE(exynos4210_single_1200_limit); i++) { + if (asv_info->hpm_result <= exynos4210_single_1200_limit[i].hpm_limit || + asv_info->ids_result <= exynos4210_single_1200_limit[i].ids_limit) { + ret = i; + break; + } + } + } + + return ret; +} + +#define PACK_ID 8 +#define PACK_MASK 0x3 + +#define SUPPORT_1400MHZ (1 << 31) +#define SUPPORT_1200MHZ (1 << 30) +#define SUPPORT_1000MHZ (1 << 29) + +static int exynos4210_get_hpm(struct samsung_asv *asv_info) +{ + unsigned int i; + unsigned int tmp; + unsigned int hpm_delay = 0; + void __iomem *iem_base; + + iem_base = ioremap(EXYNOS4_PA_IEM, SZ_128K); + + if (!iem_base) { + pr_err("EXYNOS4210: ASV: ioremap fail\n"); + return -EPERM; + } + + /* Clock setting to get asv value */ + if (!asv_info->pre_clock_init) + goto err; + else { + if (asv_info->pre_clock_init()) + goto err; + else { + /* HPM enable */ + tmp = __raw_readl(iem_base + EXYNOS4_APC_CONTROL); + tmp |= APC_HPM_EN; + __raw_writel(tmp, (iem_base + EXYNOS4_APC_CONTROL)); + + asv_info->pre_clock_setup(); + + /* IEM enable */ + tmp = __raw_readl(iem_base + EXYNOS4_IECDPCCR); + tmp |= IEC_EN; + __raw_writel(tmp, (iem_base + EXYNOS4_IECDPCCR)); + } + } + + /* Get HPM Delay value */ + for (i = 0; i < EXYNOS4_LOOP_CNT; i++) { + tmp = __raw_readb(iem_base + EXYNOS4_APC_DBG_DLYCODE); + hpm_delay += tmp; + } + + hpm_delay /= EXYNOS4_LOOP_CNT; + + /* Store result of hpm value */ + asv_info->hpm_result = hpm_delay; + + return 0; + +err: + pr_err("EXYNOS4210: ASV: Failt to get hpm function\n"); + + iounmap(iem_base); + + return -EPERM; +} + +static int exynos4210_get_ids(struct samsung_asv *asv_info) +{ + unsigned int pkg_id_val; + + if (!asv_info->ids_offset || !asv_info->ids_mask) { + pr_err("EXYNOS4210: ASV: No ids_offset or No ids_mask\n"); + + return -EPERM; + } + + pkg_id_val = __raw_readl(S5P_VA_CHIPID + 0x4); + asv_info->pkg_id = pkg_id_val; + asv_info->ids_result = ((pkg_id_val >> asv_info->ids_offset) & + asv_info->ids_mask); + + return 0; +} + +static int exynos4210_asv_store_result(struct samsung_asv *asv_info) +{ + unsigned int result_grp; + char *support_freq; + unsigned int exynos_idcode = 0x0; + + exynos_result_of_asv = 0; + + exynos_idcode = __raw_readl(S5P_VA_CHIPID); + + /* Single chip is only support 1.2GHz */ + if (!((exynos_idcode >> PACK_ID) & PACK_MASK)) { + result_grp = exynos4210_find_group(asv_info, EXYNOS4210_SINGLE_1200); + result_grp |= SUPPORT_1200MHZ; + support_freq = "1.2GHz"; + + goto set_reg; + } + + /* Check support freq */ + switch (asv_info->pkg_id & 0x7) { + /* Support 1.2GHz */ + case 1: + case 7: + result_grp = exynos4210_find_group(asv_info, EXYNOS4210_1200); + result_grp |= SUPPORT_1200MHZ; + support_freq = "1.2GHz"; + break; + /* Support 1.4GHz */ + case 5: + result_grp = exynos4210_find_group(asv_info, EXYNOS4210_1400); + result_grp |= SUPPORT_1200MHZ; + support_freq = "1.4GHz"; + break; + /* Defalut support 1.0GHz */ + default: + result_grp = exynos4210_find_group(asv_info, EXYNOS4210_1200); + result_grp |= SUPPORT_1000MHZ; + support_freq = "1.0GHz"; + break; + } + +set_reg: + exynos_result_of_asv = result_grp; + + pr_info("EXYNOS4: ASV: Support %s and Group is 0x%x\n", + support_freq, result_grp); + + return 0; +} + +void exynos4210_asv_init(struct samsung_asv *asv_info) +{ + pr_info("EXYNOS4210: Adaptive Support Voltage init\n"); + + asv_info->ids_offset = 24; + asv_info->ids_mask = 0xFF; + + asv_info->get_ids = exynos4210_get_ids; + asv_info->get_hpm = exynos4210_get_hpm; + asv_info->pre_clock_init = exynos4210_asv_pre_clock_init; + asv_info->pre_clock_setup = exynos4210_asv_pre_clock_setup; + asv_info->store_result = exynos4210_asv_store_result; +} diff --git a/arch/arm/mach-exynos/asv.c b/arch/arm/mach-exynos/asv.c new file mode 100644 index 00000000000..d0a33d3cecf --- /dev/null +++ b/arch/arm/mach-exynos/asv.c @@ -0,0 +1,71 @@ +/* linux/arch/arm/mach-exynos/asv.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS4 - ASV(Adaptive Support Voltage) driver + * + * 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/init.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include <plat/cpu.h> + +#include <mach/map.h> +#include <mach/asv.h> + +static struct samsung_asv *exynos_asv; + +static int __init exynos_asv_init(void) +{ + exynos_asv = kzalloc(sizeof(struct samsung_asv), GFP_KERNEL); + if (!exynos_asv) + goto out; + + if (soc_is_exynos4210()) + exynos4210_asv_init(exynos_asv); + else + goto out; + + if (exynos_asv->check_vdd_arm) { + if (exynos_asv->check_vdd_arm()) + goto out; + } + + /* Get HPM Delay value */ + if (exynos_asv->get_hpm) { + if (exynos_asv->get_hpm(exynos_asv)) + goto out; + } else + goto out; + + /* Get IDS ARM Value */ + if (exynos_asv->get_ids) { + if (exynos_asv->get_ids(exynos_asv)) + goto out; + } else + goto out; + + if (exynos_asv->store_result) { + if (exynos_asv->store_result(exynos_asv)) + goto out; + } else + goto out; + + return 0; +out: + pr_err("EXYNOS : Fail to initialize ASV\n"); + + kfree(exynos_asv); + + return -EINVAL; +} +device_initcall_sync(exynos_asv_init); diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index 288d06f98ff..bb40358d2f9 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -308,6 +308,25 @@ static struct clksrc_clk exynos4_clk_periphclk = { .reg_div = { .reg = EXYNOS4_CLKDIV_CPU, .shift = 12, .size = 3 }, }; +static struct clk *exynos4_clkset_mout_hpm_list[] = { + [0] = &exynos4_clk_mout_apll.clk, + [1] = &exynos4_clk_mout_mpll.clk, +}; + +static struct clksrc_sources exynos4_clkset_sclk_hpm = { + .sources = exynos4_clkset_mout_hpm_list, + .nr_sources = ARRAY_SIZE(exynos4_clkset_mout_hpm_list), +}; + +static struct clksrc_clk exynos4_clk_dout_copy = { + .clk = { + .name = "dout_copy", + }, + .sources = &exynos4_clkset_sclk_hpm, + .reg_src = { .reg = EXYNOS4_CLKSRC_CPU, .shift = 20, .size = 1 }, + .reg_div = { .reg = EXYNOS4_CLKDIV_CPU1, .shift = 0, .size = 3 }, +}; + /* Core list of CMU_CORE side */ static struct clk *exynos4_clkset_corebus_list[] = { @@ -1152,7 +1171,20 @@ static struct clksrc_clk exynos4_clksrcs[] = { .ctrlbit = (1 << 16), }, .reg_div = { .reg = EXYNOS4_CLKDIV_FSYS3, .shift = 8, .size = 8 }, - } + }, { + .clk = { + .name = "sclk_hpm", + .parent = &exynos4_clk_dout_copy.clk, + }, + .reg_div = { .reg = EXYNOS4_CLKDIV_CPU1, .shift = 4, .size = 3 }, + }, { + .clk = { + .name = "sclk_pwi", + }, + .sources = &exynos4_clkset_group, + .reg_src = { .reg = EXYNOS4_CLKSRC_DMC, .shift = 16, .size = 4 }, + .reg_div = { .reg = EXYNOS4_CLKDIV_DMC1, .shift = 8, .size = 4 }, + }, }; static struct clksrc_clk exynos4_clk_sclk_uart0 = { @@ -1294,6 +1326,7 @@ static struct clksrc_clk *exynos4_sysclks[] = { &exynos4_clk_armclk, &exynos4_clk_aclk_corem0, &exynos4_clk_aclk_cores, + &exynos4_clk_dout_copy, &exynos4_clk_aclk_corem1, &exynos4_clk_periphclk, &exynos4_clk_mout_corebus, diff --git a/arch/arm/mach-exynos/include/mach/asv.h b/arch/arm/mach-exynos/include/mach/asv.h new file mode 100644 index 00000000000..5b48b74811e --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/asv.h @@ -0,0 +1,43 @@ +/* linux/arch/arm/mach-exynos/include/mach/asv.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS4 - Adaptive Support Voltage Header file + * + * 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. +*/ + +#ifndef __ASM_ARCH_ASV_H +#define __ASM_ARCH_ASV_H __FILE__ + +#define JUDGE_TABLE_END NULL +#define EXYNOS4_LOOP_CNT 10 + +struct asv_judge_table { + unsigned int hpm_limit; /* HPM value to decide group of target */ + unsigned int ids_limit; /* IDS value to decide group of target */ +}; + +struct samsung_asv { + unsigned int pkg_id; /* fused value for chip */ + unsigned int ids_offset; /* ids_offset of chip */ + unsigned int ids_mask; /* ids_mask of chip */ + unsigned int hpm_result; /* hpm value of chip */ + unsigned int ids_result; /* ids value of chip */ + int (*check_vdd_arm)(void); /* check vdd_arm value, this function is selectable */ + int (*pre_clock_init)(void); /* clock init function to get hpm */ + int (*pre_clock_setup)(void); /* clock setup function to get hpm */ + /* specific get ids function */ + int (*get_ids)(struct samsung_asv *asv_info); + /* specific get hpm function */ + int (*get_hpm)(struct samsung_asv *asv_info); + /* store into some repository to send result of asv */ + int (*store_result)(struct samsung_asv *asv_info); +}; + +extern void exynos4210_asv_init(struct samsung_asv *asv_info); + +#endif /* __ASM_ARCH_ASV_H */ diff --git a/arch/arm/mach-exynos/include/mach/cpufreq.h b/arch/arm/mach-exynos/include/mach/cpufreq.h index 7517c3f417a..be80017eace 100644 --- a/arch/arm/mach-exynos/include/mach/cpufreq.h +++ b/arch/arm/mach-exynos/include/mach/cpufreq.h @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +extern unsigned int exynos_result_of_asv; + enum cpufreq_level_index { L0, L1, L2, L3, L4, L5, L6, L7, L8, L9, diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 4195089b7fc..32087c88f4a 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -76,6 +76,8 @@ #define EXYNOS4_PA_COMBINER 0x10440000 #define EXYNOS5_PA_COMBINER 0x10440000 +#define EXYNOS4_PA_IEM 0x10460000 + #define EXYNOS4_PA_GIC_CPU 0x10480000 #define EXYNOS4_PA_GIC_DIST 0x10490000 #define EXYNOS5_PA_GIC_CPU 0x10480000 diff --git a/arch/arm/mach-exynos/include/mach/regs-clock.h b/arch/arm/mach-exynos/include/mach/regs-clock.h index d9578a58ae7..cff0c718ffe 100644 --- a/arch/arm/mach-exynos/include/mach/regs-clock.h +++ b/arch/arm/mach-exynos/include/mach/regs-clock.h @@ -135,6 +135,24 @@ #define EXYNOS4_CLKGATE_SCLKCPU EXYNOS_CLKREG(0x14800) #define EXYNOS4_CLKGATE_IP_CPU EXYNOS_CLKREG(0x14900) +#define EXYNOS4_APLL_CON0L8 EXYNOS_CLKREG(0x15100) +#define EXYNOS4_APLL_CON0L7 EXYNOS_CLKREG(0x15104) +#define EXYNOS4_APLL_CON0L6 EXYNOS_CLKREG(0x15108) +#define EXYNOS4_APLL_CON0L5 EXYNOS_CLKREG(0x1510C) +#define EXYNOS4_APLL_CON0L4 EXYNOS_CLKREG(0x15110) +#define EXYNOS4_APLL_CON0L3 EXYNOS_CLKREG(0x15114) +#define EXYNOS4_APLL_CON0L2 EXYNOS_CLKREG(0x15118) +#define EXYNOS4_APLL_CON0L1 EXYNOS_CLKREG(0x1511C) + +#define EXYNOS4_CLKDIV_IEM_L8 EXYNOS_CLKREG(0x15300) +#define EXYNOS4_CLKDIV_IEM_L7 EXYNOS_CLKREG(0x15304) +#define EXYNOS4_CLKDIV_IEM_L6 EXYNOS_CLKREG(0x15308) +#define EXYNOS4_CLKDIV_IEM_L5 EXYNOS_CLKREG(0x1530C) +#define EXYNOS4_CLKDIV_IEM_L4 EXYNOS_CLKREG(0x15310) +#define EXYNOS4_CLKDIV_IEM_L3 EXYNOS_CLKREG(0x15314) +#define EXYNOS4_CLKDIV_IEM_L2 EXYNOS_CLKREG(0x15318) +#define EXYNOS4_CLKDIV_IEM_L1 EXYNOS_CLKREG(0x1531C) + #define EXYNOS4_APLL_LOCKTIME (0x1C20) /* 300us */ #define EXYNOS4_APLLCON0_ENABLE_SHIFT (31) diff --git a/arch/arm/mach-exynos/include/mach/regs-iem.h b/arch/arm/mach-exynos/include/mach/regs-iem.h new file mode 100644 index 00000000000..d9bf1777858 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/regs-iem.h @@ -0,0 +1,27 @@ +/* linux/arch/arm/mach-exynos/include/mach/regs-iem.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * EXYNOS4 - IEM(INTELLIGENT ENERGY MANAGEMENT) register discription + * + * 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. +*/ + +#ifndef __ASM_ARCH_REGS_IEM_H +#define __ASM_ARCH_REGS_IEM_H __FILE__ + +/* Register for IEC */ +#define EXYNOS4_IECDPCCR (0x00000) + +/* Register for APC */ +#define EXYNOS4_APC_CONTROL (0x10010) +#define EXYNOS4_APC_PREDLYSEL (0x10024) +#define EXYNOS4_APC_DBG_DLYCODE (0x100E0) + +#define APC_HPM_EN (1 << 4) +#define IEC_EN (1 << 0) + +#endif /* __ASM_ARCH_REGS_IEM_H */ diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index b243a7ee01f..1ff48a4526b 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -17,6 +17,8 @@ #include <linux/regulator/consumer.h> #include <linux/cpufreq.h> #include <linux/suspend.h> +#include <linux/notifier.h> +#include <linux/reboot.h> #include <mach/cpufreq.h> @@ -46,7 +48,7 @@ static int exynos_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - unsigned int index, old_index; + unsigned int index, old_index = 0, i; unsigned int arm_volt, safe_arm_volt = 0; int ret = 0; struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; @@ -62,10 +64,11 @@ static int exynos_target(struct cpufreq_policy *policy, goto out; } - if (cpufreq_frequency_table_target(policy, freq_table, - freqs.old, relation, &old_index)) { - ret = -EINVAL; - goto out; + for (i = 0; (freq_table[i].frequency != CPUFREQ_TABLE_END); i++) { + if (freqs.old == freq_table[i].frequency) { + old_index = i; + break; + } } if (cpufreq_frequency_table_target(policy, freq_table, @@ -204,8 +207,35 @@ static struct notifier_block exynos_cpufreq_nb = { .notifier_call = exynos_cpufreq_pm_notifier, }; +static int exynos_cpufreq_reboot_notifier(struct notifier_block *this, + unsigned long code, void *_cmd) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ + mutex_lock(&cpufreq_lock); + + if (frequency_locked) + goto out; + frequency_locked = true; + + if (locking_frequency) { + mutex_unlock(&cpufreq_lock); + exynos_target(policy, locking_frequency, CPUFREQ_RELATION_H); + mutex_lock(&cpufreq_lock); + } + +out: + mutex_unlock(&cpufreq_lock); + return NOTIFY_DONE; +} + +static struct notifier_block exynos_cpufreq_reboot_nb = { + .notifier_call = exynos_cpufreq_reboot_notifier, +}; + static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { + int ret; + policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu); cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); @@ -228,16 +258,35 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) cpumask_setall(policy->cpus); } - return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); + ret = cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); + if (ret) + return ret; + + cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); + return 0; + } +static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_put_attr(policy->cpu); + return 0; +} + +static struct freq_attr *exynos4_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + static struct cpufreq_driver exynos_driver = { .flags = CPUFREQ_STICKY, .verify = exynos_verify_speed, .target = exynos_target, .get = exynos_getspeed, .init = exynos_cpufreq_cpu_init, + .exit = exynos4_cpufreq_cpu_exit, .name = "exynos_cpufreq", + .attr = exynos4_cpufreq_attr, #ifdef CONFIG_PM .suspend = exynos_cpufreq_suspend, .resume = exynos_cpufreq_resume, @@ -276,6 +325,7 @@ static int __init exynos_cpufreq_init(void) } register_pm_notifier(&exynos_cpufreq_nb); + register_reboot_notifier(&exynos_cpufreq_reboot_nb); if (cpufreq_register_driver(&exynos_driver)) { pr_err("%s: failed to register cpufreq driver\n", __func__); diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index fb148fa2767..e67453a6489 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -20,6 +20,13 @@ #include <mach/regs-clock.h> #include <mach/cpufreq.h> +#define SUPPORT_1400MHZ (1 << 31) +#define SUPPORT_1200MHZ (1 << 30) +#define SUPPORT_1000MHZ (1 << 29) + +#define SUPPORT_FREQ_SHIFT 29 +#define SUPPORT_FREQ_MASK 7 + #define CPUFREQ_LEVEL_END L5 static int max_support_idx = L0; @@ -35,10 +42,7 @@ struct cpufreq_clkdiv { unsigned int clkdiv; }; -static unsigned int exynos4210_volt_table[CPUFREQ_LEVEL_END] = { - 1250000, 1150000, 1050000, 975000, 950000, -}; - +static unsigned int exynos4210_volt_table[CPUFREQ_LEVEL_END]; static struct cpufreq_clkdiv exynos4210_clkdiv_table[CPUFREQ_LEVEL_END]; @@ -51,6 +55,28 @@ static struct cpufreq_frequency_table exynos4210_freq_table[] = { {0, CPUFREQ_TABLE_END}, }; +/* + * ASV group voltage table + */ +static const unsigned int asv_voltage[CPUFREQ_LEVEL_END][8] = { + /* + * SS, A1, A2, B1, B2, C1, C2, D + * @1200 : + * @1000 : + * @800 : ASV_VOLTAGE_TABLE + * @500 : + * @200 : + */ + { 1350000, 1350000, 1300000, 1275000, 1250000, 1225000, 1200000, + 1175000 }, + { 1300000, 1250000, 1200000, 1175000, 1150000, 1125000, 1100000, + 1075000 }, + { 1200000, 1150000, 1100000, 1075000, 1050000, 1025000, 1000000, + 975000 }, + { 1100000, 1050000, 1000000, 975000, 975000, 950000, 925000, 925000 }, + { 1050000, 1000000, 975000, 950000, 950000, 925000, 925000, 925000 }, +}; + static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = { /* * Clock divider value for following @@ -229,6 +255,31 @@ static void exynos4210_set_frequency(unsigned int old_index, } } +static void __init set_volt_table(void) +{ + unsigned int tmp, i, asv_group = 0; + + tmp = exynos_result_of_asv; + + switch (tmp & (SUPPORT_FREQ_MASK << SUPPORT_FREQ_SHIFT)) { + case SUPPORT_1200MHZ: + asv_group = (tmp & 0xF); + break; + case SUPPORT_1400MHZ: + case SUPPORT_1000MHZ: + default: + /* Not supported and assign typical ASV group */ + asv_group = 2; + break; + } + + printk(KERN_INFO "DVFS: VDD_ARM Voltage table set with %d Group\n", + asv_group); + + for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++) + exynos4210_volt_table[i] = asv_voltage[i][asv_group]; +} + int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) { int i; @@ -275,6 +326,8 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) exynos4210_clkdiv_table[i].clkdiv = tmp; } + set_volt_table(); + info->mpll_freq_khz = rate; info->pm_lock_idx = L2; info->pll_safe_idx = L2; |