cpufreq: Add exynos3250 cpufreq support
authorChanwoo Choi <cw00.choi@samsung.com>
Thu, 10 Jul 2014 11:36:17 +0000 (20:36 +0900)
committerChanho Park <chanho61.park@samsung.com>
Thu, 7 Aug 2014 06:17:50 +0000 (15:17 +0900)
This patch add exynos3250 cpufreq driver to change voltage/frequency according
to required cpufreq level.

Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/Makefile
drivers/cpufreq/exynos-cpufreq.c
drivers/cpufreq/exynos-cpufreq.h
drivers/cpufreq/exynos3250-cpufreq.c [new file with mode: 0644]

index 85d3f39..81747ac 100644 (file)
@@ -25,6 +25,13 @@ config ARM_EXYNOS_CPUFREQ
 
          If in doubt, say N.
 
+config ARM_EXYNOS3250_CPUFREQ
+       def_bool SOC_EXYNOS3250
+       depends on ARM_EXYNOS_CPUFREQ
+       help
+         This adds the CPUFreq driver for Samsung EXYNOS3250
+         SoC (EXYNOS3250).
+
 config ARM_EXYNOS4210_CPUFREQ
        def_bool CPU_EXYNOS4210
        depends on ARM_EXYNOS_CPUFREQ
index 9ddfae1..a3b837e 100644 (file)
@@ -54,6 +54,7 @@ obj-$(CONFIG_ARM_DT_BL_CPUFREQ)               += arm_big_little_dt.o
 obj-$(CONFIG_ARCH_DAVINCI)             += davinci-cpufreq.o
 obj-$(CONFIG_UX500_SOC_DB8500)         += dbx500-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS_CPUFREQ)       += exynos-cpufreq.o
+obj-$(CONFIG_ARM_EXYNOS3250_CPUFREQ)   += exynos3250-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ)   += exynos4210-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ)   += exynos4x12-cpufreq.o
 obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ)   += exynos5250-cpufreq.o
index feda2e5..920967d 100644 (file)
@@ -459,7 +459,9 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
 
        exynos_info->dev = &pdev->dev;
 
-       if (soc_is_exynos4210())
+       if (soc_is_exynos3250())
+               ret = exynos3250_cpufreq_init(exynos_info);
+       else if (soc_is_exynos4210())
                ret = exynos4210_cpufreq_init(exynos_info);
        else if (soc_is_exynos4212() || soc_is_exynos4412())
                ret = exynos4x12_cpufreq_init(exynos_info);
index 5fe15fa..8c0235f 100644 (file)
@@ -26,6 +26,15 @@ enum cpufreq_level_index {
                .mps = ((m) << 16 | (p) << 8 | (s)), \
        }
 
+#define APLL_FREQ_EXYNOS3(f, a0, a1, a2, a3, a4, a5, b0, b1, b2, m, p, s) \
+       { \
+               .freq = (f) * 1000, \
+               .clk_div_cpu0 = ((a0) | (a1) << 4 |  (a2) << 16 | (a3) << 20 | \
+                       (a4) << 24 | (a5) << 28), \
+               .clk_div_cpu1 = (b0 << 0 | b1 << 4 | b2 << 8), \
+               .mps = ((m) << 16 | (p) << 8 | (s)), \
+       }
+
 struct apll_freq {
        unsigned int freq;
        u32 clk_div_cpu0;
@@ -45,6 +54,7 @@ struct exynos_dvfs_info {
        bool (*need_apll_change)(unsigned int, unsigned int);
 };
 
+extern int exynos3250_cpufreq_init(struct exynos_dvfs_info *);
 extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *);
 extern int exynos4x12_cpufreq_init(struct exynos_dvfs_info *);
 extern int exynos5250_cpufreq_init(struct exynos_dvfs_info *);
diff --git a/drivers/cpufreq/exynos3250-cpufreq.c b/drivers/cpufreq/exynos3250-cpufreq.c
new file mode 100644 (file)
index 0000000..2206312
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * EXYNOS3250 - 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/regs-clock.h>
+
+#include "exynos-cpufreq.h"
+
+#define ASV_GROUP_NR   15
+
+static struct clk *cpu_clk;
+static struct clk *mout_core;
+static struct clk *mout_mpll;
+static struct clk *mout_apll;
+
+static unsigned int exynos3250_volt_table[] = {
+       1050000, 1050000, 1000000, 950000, 900000, 900000,
+       900000,  900000,  900000, 900000, 900000,
+};
+
+/* ASV setting value */
+static const unsigned int arm_asv_voltage_3250[][ASV_GROUP_NR] = {
+       /*   ASV0     ASV1     ASV2     ASV3     ASV4     ASV5     ASV6     ASV7     ASV8    ASV9    ASV10    ASV11    ASV12    ASV13    ASV14 */
+       { 1150000, 1150000, 1125000, 1100000, 1075000, 1050000, 1025000, 1000000, 1000000, 975000,  975000,  950000,  950000,  925000,  925000},        /* L0 - Unused  */
+       { 1150000, 1150000, 1125000, 1100000, 1075000, 1050000, 1025000, 1000000, 1000000, 975000,  975000,  950000,  950000,  925000,  925000},        /* L1 - 1000MHz */
+       { 1112500, 1112500, 1087500, 1062500, 1037500, 1012500, 987500,  962500,  962500,  937500,  937500,  912500,  912500,  887500,  887500},        /* L2 - 900MHz  */
+       { 1075000, 1075000, 1050000, 1025000, 1000500, 975000,  950000,  925000,  925000,  900000,  900000,  875000,  875000,  850000,  850000},        /* L3 - 800MHz  */
+       { 1037500, 1037500, 1012500, 987500,  962500,  937500,  912500,  887500,  887500,  862500,  862500,  850000,  850000,  850000,  850000},        /* L4 - 700MHz  */
+       { 1000000, 1000000, 975000,  950000,  925000,  900000,  875000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000},        /* L5 - 600MHz  */
+       { 962500,  962500,  937500,  912500,  887500,  862500,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000},        /* L6 - 500MHz  */
+       { 925000,  925000,  900000,  875000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000},        /* L7 - 400MHz  */
+       { 887500,  887500,  862500,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000},        /* L8 - 300MHz  */
+       { 850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000},        /* L9 - 200MHz  */
+       { 850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000,  850000},        /* L10- 100MHz  */
+};
+
+static struct cpufreq_frequency_table exynos3250_freq_table[] = {
+       {L0, CPUFREQ_ENTRY_INVALID},
+       {L1, 1000 * 1000},
+       {L2,  900 * 1000},
+       {L3,  800 * 1000},
+       {L4,  700 * 1000},
+       {L5,  600 * 1000},
+       {L6,  500 * 1000},
+       {L7,  400 * 1000},
+       {L8,  300 * 1000},
+       {L9,  200 * 1000},
+       {L10,  100 * 1000},
+       {0, CPUFREQ_TABLE_END},
+};
+
+static struct apll_freq apll_freq_3250[] = {
+       /*
+        * values:
+        * freq
+        * clock divider for CORE, COREM, ATB, PCLK_DBG, APLL, CORE2
+        * clock divider for COPY, HPM, RESERVED
+        * PLL M, P, S
+        */
+       APLL_FREQ_EXYNOS3(1000, 0, 1, 4, 7, 1, 0, 7, 7, 0, 250, 3, 1),
+       APLL_FREQ_EXYNOS3(1000, 0, 1, 4, 7, 1, 0, 7, 7, 0, 250, 3, 1),
+       APLL_FREQ_EXYNOS3(900,  0, 1, 3, 7, 1, 0, 7, 7, 0, 300, 4, 1),
+       APLL_FREQ_EXYNOS3(800,  0, 1, 3, 7, 1, 0, 7, 7, 0, 200, 3, 1),
+       APLL_FREQ_EXYNOS3(700,  0, 1, 3, 7, 1, 0, 7, 7, 0, 175, 3, 1),
+       APLL_FREQ_EXYNOS3(600,  0, 1, 3, 7, 1, 0, 7, 7, 0, 400, 4, 2),
+       APLL_FREQ_EXYNOS3(500,  0, 1, 3, 7, 1, 0, 7, 7, 0, 250, 3, 2),
+       APLL_FREQ_EXYNOS3(400,  0, 1, 3, 7, 1, 0, 7, 7, 0, 200, 3, 2),
+       APLL_FREQ_EXYNOS3(300,  0, 1, 3, 5, 1, 0, 7, 7, 0, 400, 4, 3),
+       APLL_FREQ_EXYNOS3(200,  0, 1, 3, 3, 1, 0, 7, 7, 0, 200, 3, 3),
+       APLL_FREQ_EXYNOS3(100,  0, 1, 1, 1, 1, 0, 7, 7, 0, 200, 3, 4),
+};
+
+static void exynos3250_set_clkdiv(unsigned int div_index)
+{
+       unsigned int tmp;
+
+       /* Change Divider - CPU0 */
+       tmp = apll_freq_3250[div_index].clk_div_cpu0;
+
+       __raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
+
+       while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111)
+               cpu_relax();
+
+       /* Change Divider - CPU1 */
+       tmp = apll_freq_3250[div_index].clk_div_cpu1;
+
+       __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
+
+       while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & 0x111)
+               cpu_relax();
+}
+
+static void exynos3250_set_apll(unsigned int index)
+{
+       unsigned int tmp, freq = apll_freq_3250[index].freq * 1000;
+       struct clk *clk;
+
+       clk = clk_get_parent(mout_apll);
+
+       /* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
+       clk_set_parent(mout_core, mout_mpll);
+       do {
+               cpu_relax();
+               tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
+                       >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
+               tmp &= 0x7;
+       } while (tmp != 0x2);
+
+       clk_set_rate(clk, freq);
+
+       /* MUX_CORE_SEL = APLL */
+       clk_set_parent(mout_core, mout_apll);
+       do {
+               cpu_relax();
+               tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
+               tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
+       } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
+}
+
+static void exynos3250_set_frequency(unsigned int old_index,
+                                 unsigned int new_index)
+{
+       if (old_index > new_index) {
+               exynos3250_set_clkdiv(new_index);
+               exynos3250_set_apll(new_index);
+       } else if (old_index < new_index) {
+               exynos3250_set_apll(new_index);
+               exynos3250_set_clkdiv(new_index);
+       }
+}
+
+static void __init exynos3250_set_volt_table(void)
+{
+       unsigned int i;
+       int exynos_result_of_asv = 0;
+
+       pr_info("DVFS : VDD_ARM Voltage table set with %d Group\n", exynos_result_of_asv);
+
+       for (i = 0; i < ARRAY_SIZE(exynos3250_volt_table); i++)
+               exynos3250_volt_table[i]
+                       = arm_asv_voltage_3250[i][exynos_result_of_asv];
+}
+
+int exynos3250_cpufreq_init(struct exynos_dvfs_info *info)
+{
+       unsigned long rate;
+
+       exynos3250_set_volt_table();
+
+       cpu_clk = clk_get(info->dev, "div_core2");
+       if (IS_ERR(cpu_clk))
+               return PTR_ERR(cpu_clk);
+
+       mout_core = clk_get(info->dev, "mout_core");
+       if (IS_ERR(mout_core))
+               goto err_moutcore;
+
+       mout_mpll = clk_get(info->dev, "mout_mpll_user_c");
+       if (IS_ERR(mout_mpll))
+               goto err_mout_mpll;
+       rate = clk_get_rate(mout_mpll) / 1000;
+
+       mout_apll = clk_get(info->dev, "mout_apll");
+       if (IS_ERR(mout_apll))
+               goto err_mout_apll;
+
+       info->mpll_freq_khz = rate;
+       info->pll_safe_idx = L5;
+       info->cpu_clk = cpu_clk;
+
+       info->volt_table = exynos3250_volt_table;
+       info->freq_table = exynos_of_parse_freq_table(info, "freq_table");
+       if (!info->freq_table)
+               info->freq_table = exynos3250_freq_table;
+       info->set_freq = exynos3250_set_frequency;
+
+       return 0;
+
+err_mout_apll:
+       clk_put(mout_mpll);
+err_mout_mpll:
+       clk_put(mout_core);
+err_moutcore:
+       clk_put(cpu_clk);
+
+       pr_debug("%s: failed initialization\n", __func__);
+       return -EINVAL;
+}
+EXPORT_SYMBOL(exynos3250_cpufreq_init);