summaryrefslogtreecommitdiff
path: root/drivers/platform/sprd/cpufreq-scx35.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/platform/sprd/cpufreq-scx35.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/platform/sprd/cpufreq-scx35.c')
-rw-r--r--drivers/platform/sprd/cpufreq-scx35.c1272
1 files changed, 1272 insertions, 0 deletions
diff --git a/drivers/platform/sprd/cpufreq-scx35.c b/drivers/platform/sprd/cpufreq-scx35.c
new file mode 100644
index 00000000..6ef6a5a4
--- /dev/null
+++ b/drivers/platform/sprd/cpufreq-scx35.c
@@ -0,0 +1,1272 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/debugfs.h>
+#include <linux/cpu.h>
+#include <linux/regulator/consumer.h>
+//#include <asm/system.h>
+#include <trace/events/power.h>
+
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/regulator.h>
+#include <soc/sprd/adi.h>
+#include <soc/sprd/sci.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_misc.h>
+#include <linux/of_platform.h>
+
+#if defined(CONFIG_ARCH_SC8825)
+#define MHz (1000000)
+#define GR_MPLL_REFIN_2M (2 * MHz)
+#define GR_MPLL_REFIN_4M (4 * MHz)
+#define GR_MPLL_REFIN_13M (13 * MHz)
+#define GR_MPLL_REFIN_SHIFT 16
+#define GR_MPLL_REFIN_MASK (0x3)
+#define GR_MPLL_N_MASK (0x7ff)
+#define GR_MPLL_MN (REG_GLB_M_PLL_CTL0)
+#define GR_GEN1 (REG_GLB_GEN1)
+#endif
+
+#define FREQ_TABLE_SIZE 10
+#define DVFS_BOOT_TIME (30 * HZ)
+#define SHARK_TDPLL_FREQUENCY (768000)
+#define TRANSITION_LATENCY (50 * 1000) /* ns */
+
+static DEFINE_MUTEX(freq_lock);
+struct cpufreq_freqs global_freqs;
+unsigned int percpu_target[CONFIG_NR_CPUS] = {0};
+static unsigned long boot_done;
+static unsigned int sprd_top_frequency; /* khz */
+
+struct cpufreq_conf {
+ struct clk *clk;
+ struct clk *mpllclk;
+ struct clk *tdpllclk;
+ struct regulator *regulator;
+ struct cpufreq_frequency_table *freq_tbl;
+ unsigned int *vddarm_mv;
+ unsigned int max_axi_freq;
+};
+
+struct cpufreq_table_data {
+ struct cpufreq_frequency_table freq_tbl[FREQ_TABLE_SIZE];
+ unsigned int vddarm_mv[FREQ_TABLE_SIZE];
+};
+
+struct cpufreq_conf *sprd_cpufreq_conf = NULL;
+static struct mutex cpufreq_vddarm_lock;
+
+#if defined(CONFIG_ARCH_SC8825)
+static struct cpufreq_table_data sc8825_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1000000},
+ {1, 500000},
+ {2, CPUFREQ_TABLE_END}
+ },
+ .vddarm_mv = {
+ 0
+ },
+};
+
+struct cpufreq_conf sc8825_cpufreq_conf = {
+ .clk = NULL,
+ .regulator = NULL,
+ .freq_tbl = sc8825_cpufreq_table_data.freq_tbl,
+ .vddarm_mv = sc8825_cpufreq_table_data.vddarm_mv,
+};
+
+static void set_mcu_clk_freq(u32 mcu_freq)
+{
+ u32 val, rate, arm_clk_div, gr_gen1;
+
+ rate = mcu_freq / MHz;
+ switch(1000 / rate)
+ {
+ case 1:
+ arm_clk_div = 0;
+ break;
+ case 2:
+ arm_clk_div = 1;
+ break;
+ default:
+ panic("set_mcu_clk_freq fault\n");
+ break;
+ }
+ pr_debug("%s --- before, AHB_ARM_CLK: %08x, rate = %d, div = %d\n",
+ __func__, __raw_readl(REG_AHB_ARM_CLK), rate, arm_clk_div);
+
+ gr_gen1 = __raw_readl(GR_GEN1);
+ gr_gen1 |= BIT(9);
+ __raw_writel(gr_gen1, GR_GEN1);
+
+ val = __raw_readl(REG_AHB_ARM_CLK);
+ val &= 0xfffffff8;
+ val |= arm_clk_div;
+ __raw_writel(val, REG_AHB_ARM_CLK);
+
+ gr_gen1 &= ~BIT(9);
+ __raw_writel(gr_gen1, GR_GEN1);
+
+ pr_debug("%s --- after, AHB_ARM_CLK: %08x, rate = %d, div = %d\n",
+ __func__, __raw_readl(REG_AHB_ARM_CLK), rate, arm_clk_div);
+
+ return;
+}
+
+static unsigned int get_mcu_clk_freq(void)
+{
+ u32 mpll_refin, mpll_n, mpll_cfg = 0, rate, val;
+
+ mpll_cfg = __raw_readl(GR_MPLL_MN);
+
+ mpll_refin = (mpll_cfg >> GR_MPLL_REFIN_SHIFT) & GR_MPLL_REFIN_MASK;
+ switch(mpll_refin){
+ case 0:
+ mpll_refin = GR_MPLL_REFIN_2M;
+ break;
+ case 1:
+ case 2:
+ mpll_refin = GR_MPLL_REFIN_4M;
+ break;
+ case 3:
+ mpll_refin = GR_MPLL_REFIN_13M;
+ break;
+ default:
+ pr_err("%s mpll_refin: %d\n", __FUNCTION__, mpll_refin);
+ }
+ mpll_n = mpll_cfg & GR_MPLL_N_MASK;
+ rate = mpll_refin * mpll_n;
+
+ /*find div */
+ val = __raw_readl(REG_AHB_ARM_CLK) & 0x7;
+ val += 1;
+ return rate / val;
+}
+#endif
+
+static struct cpufreq_table_data sc8830_cpufreq_table_data_cs = {
+ .freq_tbl = {
+ {0, 1200000},
+ {1, 1000000},
+ {2, SHARK_TDPLL_FREQUENCY},
+ {3, 600000},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1300000,
+ 1200000,
+ 1150000,
+ 1100000,
+ 1000000,
+ },
+};
+
+/*
+for 7715 test
+*/
+static struct cpufreq_table_data sc7715_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1000000},
+ {1, SHARK_TDPLL_FREQUENCY},
+ {2, 600000},
+ {3, SHARK_TDPLL_FREQUENCY/2},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1200000,
+ 1150000,
+ 1100000,
+ 1100000,
+ 1000000,
+ },
+};
+
+
+static struct cpufreq_table_data sc8830_cpufreq_table_data_es = {
+ .freq_tbl = {
+ {0, 1000000},
+ {1, SHARK_TDPLL_FREQUENCY},
+ {2, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1250000,
+ 1200000,
+ 1000000,
+ },
+};
+
+static struct cpufreq_table_data sc8830t_cpufreq_table_data_es = {
+ .freq_tbl = {
+ {0, 1300000},
+ {1, 1200000},
+ {2, 1000000},
+ {3, SHARK_TDPLL_FREQUENCY},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1060000,
+ 1030000,
+ 960000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc8730c_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1400000},
+ {1, 1300000},
+ {2, 1200000},
+ {3, 1000000},
+ {4, SHARK_TDPLL_FREQUENCY},
+ {5, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1060000,
+ 1060000,
+ 1030000,
+ 960000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc8830t_cpufreq_table_data_es_1300 = {
+ .freq_tbl = {
+ {0, 1300000},
+ {1, 1200000},
+ {2, 1000000},
+ {3, SHARK_TDPLL_FREQUENCY},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1060000,
+ 1030000,
+ 960000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc9630_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1500000},
+ {1, 1350000},
+ {2, 900000},
+ {3, 768000},
+ {4, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1100000,
+ 1000000,
+ 900000,
+ 900000,
+ 900000,
+ },
+};
+
+static struct cpufreq_table_data sc7720_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1200000},
+ {1, 813000},
+ {2, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1200000,
+ 1050000,
+ 1050000,
+ },
+};
+
+static struct cpufreq_table_data sc9631l64_cpufreq_table_data_es = {
+ .freq_tbl = {
+ {0, 1300000},
+ {1, 1000000},
+ {2, SHARK_TDPLL_FREQUENCY},
+ {3, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1000000,
+ 900000,
+ 900000,
+ 900000,
+ },
+};
+static struct cpufreq_table_data sc9820_cpufreq_table_data = {
+ .freq_tbl = {
+ {0, 1250000},
+ {1, 800000},
+ {2, CPUFREQ_TABLE_END},
+ },
+ .vddarm_mv = {
+ 1200000,
+ 1050000,
+ 1050000,
+ },
+};
+struct cpufreq_conf sc8830_cpufreq_conf = {
+ .clk = NULL,
+ .mpllclk = NULL,
+ .tdpllclk = NULL,
+ .regulator = NULL,
+ .freq_tbl = NULL,
+ .vddarm_mv = NULL,
+};
+
+int cpufreq_table_thermal_update(unsigned int freq, unsigned int voltage)
+{
+ struct cpufreq_frequency_table *freq_tbl;
+ unsigned int *vddarm;
+ int i;
+
+ if (NULL == sprd_cpufreq_conf)
+ return -1;
+ freq_tbl = sprd_cpufreq_conf->freq_tbl;
+ vddarm = sprd_cpufreq_conf->vddarm_mv;
+ if (NULL == freq_tbl && NULL == vddarm)
+ return -1;
+
+ for (i = 0; freq_tbl[i].frequency != CPUFREQ_TABLE_END; ++i) {
+ if (freq_tbl[i].frequency == freq)
+ goto done;
+ }
+ pr_err(KERN_ERR "%s cpufreq %dMHz isn't find!\n", __func__, freq);
+ return -1;
+done:
+ printk(KERN_ERR "%s: %dMHz voltage is %dmV\n",
+ __func__, freq, voltage);
+ if (vddarm[i] == voltage)
+ return 0;
+
+ mutex_lock(&cpufreq_vddarm_lock);
+ vddarm[i] = voltage;
+ mutex_unlock(&cpufreq_vddarm_lock);
+
+ return 0;
+}
+
+static unsigned int sprd_raw_get_cpufreq(void)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ return clk_get_rate(sprd_cpufreq_conf->clk) / 1000;
+#elif defined(CONFIG_ARCH_SC8825)
+ return get_mcu_clk_freq() / 1000;
+#endif
+}
+
+unsigned int last_freq = 0;
+static void dump_axi_cpu(unsigned int freq)
+{
+ u32 div, axi_freq;
+
+#ifndef CONFIG_ARCH_SCX35L
+ div = sci_glb_read(REG_AP_AHB_CA7_CKG_CFG, -1UL);
+ div &= (0x7<<8);
+ div >>= 8;
+ axi_freq = freq / (div + 1);
+#elif defined CONFIG_ARCH_SCX35LT8
+ div = sci_glb_read(REG_AP_AHB_CA7_CKG_DIV_CFG, -1UL);
+ div &= (0x7<<8);
+ div >>= 8;
+ axi_freq = freq / (div + 1);
+#endif
+ printk("%s(%d): cpu_freq %d, div %d, axi_freq %d\n", __func__, __LINE__,
+ freq, div, axi_freq);
+}
+
+static inline int get_axi_div(unsigned int freq)
+{
+ if (freq % sprd_cpufreq_conf->max_axi_freq)
+ return (freq / sprd_cpufreq_conf->max_axi_freq) + 1;
+ else
+ return freq / sprd_cpufreq_conf->max_axi_freq;
+}
+
+static void cpufreq_adjust_axi_clk(unsigned int freq)
+{
+ struct clk *clk_axi = NULL;
+ int div = 0, div_old = 0;
+ int need_adjust = 0;
+ char clk_name[50] = {0};
+
+ if (last_freq == 0) {
+ last_freq = freq;
+ need_adjust = 1;
+ }
+
+#ifndef CONFIG_ARCH_SCX35L
+ strcpy(clk_name, "clk_ca7_axi");
+#else
+ // for T8, in cpufreq-dt-sprd.c ?
+#endif
+ if (!clk_name[0]) {
+ last_freq = freq;
+ return;
+ }
+
+ div = get_axi_div(freq);
+ div_old = get_axi_div(last_freq);
+ if (!need_adjust && (div != div_old))
+ need_adjust = 1;
+
+ if (!need_adjust) {
+ last_freq = freq;
+ return;
+ }
+ last_freq = freq;
+
+ clk_axi = clk_get_sys(NULL, clk_name);
+ if (IS_ERR_OR_NULL(clk_axi)) {
+ pr_err("%s(%d) err: cannot find clock %s\n", __func__, __LINE__, clk_name);
+ return;
+ }
+
+ if (clk_set_rate(clk_axi, freq * 1000 / div))
+ pr_err("%s(%d) err: clk_set_rate failed\n", __func__, __LINE__);
+
+ clk_put(clk_axi);
+ //dump_axi_cpu(freq);
+}
+
+static void cpufreq_set_clock(unsigned int freq)
+{
+ int ret;
+
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk);
+ if (ret)
+ pr_err("Failed to set cpu parent to tdpll\n");
+ if (freq == SHARK_TDPLL_FREQUENCY/2) {
+ //ca7 clk div
+ #ifndef CONFIG_ARCH_SCX35L
+ sci_glb_set(REG_AP_AHB_CA7_CKG_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #else
+ #ifndef CONFIG_ARCH_SCX35LT8 //TODO
+ sci_glb_set(REG_AP_AHB_CA7_CKG_DIV_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #endif
+ #endif
+ } else if (freq == SHARK_TDPLL_FREQUENCY) {
+ #ifndef CONFIG_ARCH_SCX35L
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #else
+ #ifndef CONFIG_ARCH_SCX35LT8 //TODO
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_DIV_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #endif
+ #endif
+
+ } else {
+ /*
+ if (clk_get_parent(sprd_cpufreq_conf->clk) != sprd_cpufreq_conf->tdpllclk) {
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk);
+ if (ret)
+ pr_err("Failed to set cpu parent to tdpll\n");
+ }
+ */
+ if (!(sci_glb_read(REG_PMU_APB_MPLL_REL_CFG, -1) & BIT_MPLL_AP_SEL)) {
+ sci_glb_set(REG_PMU_APB_MPLL_REL_CFG, BIT_MPLL_AP_SEL);
+ udelay(500);
+ }
+ ret = clk_set_rate(sprd_cpufreq_conf->mpllclk, (freq * 1000));
+ if (ret)
+ pr_err("Failed to set mpll rate\n");
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->mpllclk);
+ if (ret)
+ pr_err("Failed to set cpu parent to mpll\n");
+ #ifndef CONFIG_ARCH_SCX35L
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #else
+ #ifndef CONFIG_ARCH_SCX35LT8 //TODO
+ sci_glb_clr(REG_AP_AHB_CA7_CKG_DIV_CFG, BITS_CA7_MCU_CKG_DIV(1));
+ #endif
+ #endif
+ }
+
+ cpufreq_adjust_axi_clk(freq);
+}
+static void sprd_raw_set_cpufreq(int cpu, struct cpufreq_freqs *freq, int index)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ int ret;
+
+#define CPUFREQ_SET_VOLTAGE() \
+ do { \
+ mutex_lock(&cpufreq_vddarm_lock); \
+ ret = regulator_set_voltage(sprd_cpufreq_conf->regulator, \
+ sprd_cpufreq_conf->vddarm_mv[index], \
+ sprd_cpufreq_conf->vddarm_mv[index]); \
+ mutex_unlock(&cpufreq_vddarm_lock); \
+ if (ret) \
+ pr_err("Failed to set vdd to %d mv\n", \
+ sprd_cpufreq_conf->vddarm_mv[index]); \
+ } while (0)
+#define CPUFREQ_SET_CLOCK() \
+ do { \
+ if (freq->new == SHARK_TDPLL_FREQUENCY) { \
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk); \
+ if (ret) \
+ pr_err("Failed to set cpu parent to tdpll\n"); \
+ } else { \
+ if (clk_get_parent(sprd_cpufreq_conf->clk) != sprd_cpufreq_conf->tdpllclk) { \
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk); \
+ if (ret) \
+ pr_err("Failed to set cpu parent to tdpll\n"); \
+ } \
+ ret = clk_set_rate(sprd_cpufreq_conf->mpllclk, (freq->new * 1000)); \
+ if (ret) \
+ pr_err("Failed to set mpll rate\n"); \
+ ret = clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->mpllclk); \
+ if (ret) \
+ pr_err("Failed to set cpu parent to mpll\n"); \
+ } \
+ } while (0)
+ trace_cpu_frequency(freq->new, cpu);
+
+ if (freq->new >= sprd_raw_get_cpufreq()) {
+ CPUFREQ_SET_VOLTAGE();
+ cpufreq_set_clock(freq->new);
+ } else {
+ cpufreq_set_clock(freq->new);
+ CPUFREQ_SET_VOLTAGE();
+ }
+
+ pr_info("%u --> %u, real=%u, index=%d\n",
+ freq->old, freq->new, sprd_raw_get_cpufreq(), index);
+
+#undef CPUFREQ_SET_VOLTAGE
+#undef CPUFREQ_SET_CLOCK
+
+#elif defined(CONFIG_ARCH_SC8825)
+ set_mcu_clk_freq(freq->new * 1000);
+#endif
+ return;
+}
+
+static void sprd_real_set_cpufreq(struct cpufreq_policy *policy, unsigned int new_speed, int index)
+{
+ mutex_lock(&freq_lock);
+
+ if (global_freqs.old == new_speed) {
+ pr_debug("do nothing for cpu%u, new=old=%u\n",
+ policy->cpu, new_speed);
+ mutex_unlock(&freq_lock);
+ return;
+ }
+ pr_info("--xing-- set %u khz for cpu%u\n",
+ new_speed, policy->cpu);
+ global_freqs.cpu = policy->cpu;
+ global_freqs.new = new_speed;
+
+ cpufreq_notify_transition(policy, &global_freqs, CPUFREQ_PRECHANGE);
+
+ sprd_raw_set_cpufreq(policy->cpu, &global_freqs, index);
+
+ cpufreq_notify_transition(policy, &global_freqs, CPUFREQ_POSTCHANGE);
+
+ global_freqs.old = global_freqs.new;
+
+ mutex_unlock(&freq_lock);
+ return;
+}
+
+static void sprd_find_real_index(unsigned int new_speed, int *index)
+{
+ int i;
+ struct cpufreq_frequency_table *pfreq = sprd_cpufreq_conf->freq_tbl;
+
+ *index = pfreq[0].index;
+ for (i = 0; (pfreq[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if (new_speed == pfreq[i].frequency) {
+ *index = pfreq[i].index;
+ break;
+ }
+ }
+ return;
+}
+
+static int sprd_update_cpu_speed(struct cpufreq_policy *policy,
+ unsigned int target_speed, int index)
+{
+ int i, real_index = 0;
+ unsigned int new_speed = 0;
+
+ /*
+ * CONFIG_NR_CPUS cores are always in the same voltage, at the same
+ * frequency. But, cpu load is calculated individual in each cores,
+ * So we remeber the original target frequency and voltage of core0,
+ * and use the higher one
+ */
+
+ for_each_online_cpu(i) {
+ new_speed = max(new_speed, percpu_target[i]);
+ }
+
+ if (new_speed > sprd_top_frequency)
+ new_speed = sprd_top_frequency;
+
+ if (new_speed != sprd_cpufreq_conf->freq_tbl[index].frequency)
+ sprd_find_real_index(new_speed, &real_index);
+ else
+ real_index = index;
+ sprd_real_set_cpufreq(policy, new_speed, real_index);
+ return 0;
+}
+
+static int sprd_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ if (policy->cpu > CONFIG_NR_CPUS) {
+ pr_err("%s no such cpu id %d\n", __func__, policy->cpu);
+ return -EINVAL;
+ }
+
+ return cpufreq_frequency_table_verify(policy, sprd_cpufreq_conf->freq_tbl);
+}
+
+unsigned int cpufreq_min_limit = ULONG_MAX;
+unsigned int cpufreq_max_limit = 0;
+unsigned int dvfs_score_select = 5;
+unsigned int dvfs_unplug_select = 2;
+unsigned int dvfs_plug_select = 0;
+unsigned int dvfs_score_hi[4] = {0};
+unsigned int dvfs_score_mid[4] = {0};
+unsigned int dvfs_score_critical[4] = {0};
+extern unsigned int percpu_load[4];
+extern unsigned int cur_window_size[4];
+extern unsigned int cur_window_index[4];
+extern unsigned int ga_percpu_total_load[4][8];
+
+static DEFINE_SPINLOCK(cpufreq_state_lock);
+
+static int sprd_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ int ret = -EFAULT;
+ int index;
+ unsigned int new_speed;
+ struct cpufreq_frequency_table *table;
+ int max_freq = cpufreq_max_limit;
+ int min_freq = cpufreq_min_limit;
+ int cur_freq = 0;
+ unsigned long irq_flags;
+
+ /* delay 30s to enable dvfs&dynamic-hotplug,
+ * except requirment from termal-cooling device
+ */
+ if(time_before(jiffies, boot_done)){
+ return 0;
+ }
+
+ if((target_freq < min_freq) || (target_freq > max_freq))
+ {
+ pr_err("invalid target_freq: %d min_freq %d max_freq %d\n", target_freq,min_freq,max_freq);
+ return -EINVAL;
+ }
+ table = cpufreq_frequency_get_table(policy->cpu);
+
+ if (cpufreq_frequency_table_target(policy, table,
+ target_freq, relation, &index)) {
+ pr_err("invalid target_freq: %d\n", target_freq);
+ return -EINVAL;
+ }
+
+ pr_debug("CPU_%d target %d relation %d (%d-%d) selected %d\n",
+ policy->cpu, target_freq, relation,
+ policy->min, policy->max, table[index].frequency);
+
+ new_speed = table[index].frequency;
+
+ percpu_target[policy->cpu] = new_speed;
+ pr_debug("%s cpu:%d new_speed:%u on cpu%d\n",
+ __func__, policy->cpu, new_speed, smp_processor_id());
+
+ ret = sprd_update_cpu_speed(policy, new_speed, index);
+
+ return ret;
+
+}
+
+static unsigned int sprd_cpufreq_getspeed(unsigned int cpu)
+{
+ if (cpu > CONFIG_NR_CPUS) {
+ pr_err("%s no such cpu id %d\n", __func__, cpu);
+ return -EINVAL;
+ }
+
+ return sprd_raw_get_cpufreq();
+}
+
+static void sprd_set_cpureq_limit(void)
+{
+ int i;
+ struct cpufreq_frequency_table *tmp = sprd_cpufreq_conf->freq_tbl;
+ for (i = 0; (tmp[i].frequency != CPUFREQ_TABLE_END); i++) {
+ cpufreq_min_limit = min(tmp[i].frequency, cpufreq_min_limit);
+ cpufreq_max_limit = max(tmp[i].frequency, cpufreq_max_limit);
+ }
+ pr_info("--xing-- %s max=%u min=%u\n", __func__, cpufreq_max_limit, cpufreq_min_limit);
+}
+
+#if defined(CONFIG_ARCH_SCX35LT8)
+#define AON_APB_CHIP_ID REG_AON_APB_CHIP_ID0
+#else
+#define AON_APB_CHIP_ID REG_AON_APB_CHIP_ID
+#endif
+static int sprd_freq_table_init(void)
+{
+ /* we init freq table here depends on which chip being used */
+ if (soc_is_scx35_v0()) {
+ pr_info("%s es_chip\n", __func__);
+ sprd_cpufreq_conf->freq_tbl =
+ sc8830_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8830_cpufreq_table_data_es.vddarm_mv;
+ } else if (soc_is_scx35_v1()) {
+ pr_info("%s cs_chip\n", __func__);
+ sprd_cpufreq_conf->freq_tbl =
+ sc8830_cpufreq_table_data_cs.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8830_cpufreq_table_data_cs.vddarm_mv;
+ } else if (soc_is_sc7715()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc7715_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc7715_cpufreq_table_data.vddarm_mv;
+ } else if (soc_is_scx35g_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc8830t_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8830t_cpufreq_table_data_es.vddarm_mv;
+ sprd_cpufreq_conf->max_axi_freq = 500000;
+ } else if (soc_is_scx30g2_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc8730c_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc8730c_cpufreq_table_data.vddarm_mv;
+ sprd_cpufreq_conf->max_axi_freq = 500000;
+ } else if (soc_is_scx9630_v0() || soc_is_scx9830i_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc9630_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9630_cpufreq_table_data.vddarm_mv;
+ } else if (soc_is_scx9820_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc9820_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9820_cpufreq_table_data.vddarm_mv;
+ } else if (soc_is_scx7720_v0()) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc7720_cpufreq_table_data.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc7720_cpufreq_table_data.vddarm_mv;
+#if defined (CONFIG_ARCH_SCX35LT8) //TODO
+ } else if(__raw_readl(REG_AON_APB_CHIP_ID0) == 0x96310000){
+ sprd_cpufreq_conf->freq_tbl =
+ sc9631l64_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9631l64_cpufreq_table_data_es.vddarm_mv;
+#else
+ } else if (__raw_readl(AON_APB_CHIP_ID) == 0x96310000) {
+ sprd_cpufreq_conf->freq_tbl =
+ sc9631l64_cpufreq_table_data_es.freq_tbl;
+ sprd_cpufreq_conf->vddarm_mv =
+ sc9631l64_cpufreq_table_data_es.vddarm_mv;
+#endif
+ } else {
+#if defined(CONFIG_ARCH_SCX35LT8)
+ pr_info("D-die chip id = 0x%08X\n", __raw_readl(REG_AON_APB_CHIP_ID0));
+#endif
+ pr_err("%s error chip id\n", __func__);
+ return -EINVAL;
+ }
+ pr_info("sprd_freq_table_init \n");
+ sprd_set_cpureq_limit();
+ return 0;
+}
+
+static int sprd_cpufreq_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ cpufreq_frequency_table_cpuinfo(policy, sprd_cpufreq_conf->freq_tbl);
+ policy->cur = sprd_raw_get_cpufreq(); /* current cpu frequency: KHz*/
+ /*
+ * transition_latency 5us is enough now
+ * but sampling too often, unbalance and irregular on each online cpu
+ * so we set 500us here.
+ */
+ policy->cpuinfo.transition_latency = TRANSITION_LATENCY;
+ policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+
+ cpufreq_frequency_table_get_attr(sprd_cpufreq_conf->freq_tbl, policy->cpu);
+
+ percpu_target[policy->cpu] = policy->cur;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, sprd_cpufreq_conf->freq_tbl);
+ if (ret != 0)
+ pr_err("%s Failed to config freq table: %d\n", __func__, ret);
+
+
+ pr_info("%s policy->cpu=%d, policy->cur=%u, ret=%d\n",
+ __func__, policy->cpu, policy->cur, ret);
+
+ cpumask_setall(policy->cpus);
+
+ return ret;
+}
+
+static int sprd_cpufreq_exit(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+
+static struct freq_attr *sprd_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver sprd_cpufreq_driver = {
+ .verify = sprd_cpufreq_verify_speed,
+ .target = sprd_cpufreq_target,
+ .get = sprd_cpufreq_getspeed,
+ .init = sprd_cpufreq_init,
+ .exit = sprd_cpufreq_exit,
+ .name = "sprd",
+ .attr = sprd_cpufreq_attr,
+#if defined(CONFIG_ARCH_SCX35)
+ .flags = CPUFREQ_SHARED
+#endif
+};
+
+static ssize_t cpufreq_min_limit_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ memcpy(buf,&cpufreq_min_limit,sizeof(int));
+ return sizeof(int);
+}
+
+static ssize_t cpufreq_max_limit_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ memcpy(buf,&cpufreq_max_limit,sizeof(int));
+ return sizeof(int);
+}
+
+static ssize_t cpufreq_min_limit_debug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ snprintf(buf,10,"%d\n",cpufreq_min_limit);
+ return strlen(buf) + 1;
+}
+
+static ssize_t cpufreq_max_limit_debug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ snprintf(buf,10,"%d\n",cpufreq_max_limit);
+ return strlen(buf) + 1;
+}
+
+static ssize_t cpufreq_max_axi_freq_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ snprintf(buf, 10, "%d\n", sprd_cpufreq_conf->max_axi_freq);
+ return strlen(buf) + 1;
+}
+
+static ssize_t cpufreq_max_axi_freq_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int value;
+ int temp,max_freq = 0;
+ int i,j;
+
+ strict_strtoul(buf, 16, (long unsigned int *)&value);
+ i = 0;
+ do{
+ temp = value & 0xf;
+ for(j = 0; j < i; j++)
+ temp = temp * 10;
+ max_freq += temp;
+ value = value >> 4;
+ i++;
+ } while(value);
+
+ sprd_cpufreq_conf->max_axi_freq = max_freq;
+ return count;
+}
+
+static ssize_t cpufreq_min_limit_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ spin_lock_irqsave(&cpufreq_state_lock, irq_flags);
+ /*
+ for debug use
+ echo 0xabcde258 > /sys/power/cpufreq_min_limit means set the minimum limit to 600Mhz
+ */
+ if((value & 0xfffff000) == 0xabcde000)
+ {
+ cpufreq_min_limit = value & 0x00000fff;
+ cpufreq_min_limit *= 1000;
+ printk(KERN_ERR"cpufreq_min_limit value %s %d\n",buf,cpufreq_min_limit);
+ }
+ else
+ {
+ cpufreq_min_limit = *(int *)buf;
+ }
+ spin_unlock_irqrestore(&cpufreq_state_lock, irq_flags);
+ return count;
+}
+
+static ssize_t cpufreq_max_limit_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ spin_lock_irqsave(&cpufreq_state_lock, irq_flags);
+
+ /*
+ for debug use
+ echo 0xabcde4b0 > /sys/power/cpufreq_max_limit means set the maximum limit to 1200Mhz
+ */
+ if((value & 0xfffff000) == 0xabcde000)
+ {
+ cpufreq_max_limit = value & 0x00000fff;
+ cpufreq_max_limit *= 1000;
+ printk(KERN_ERR"cpufreq_max_limit value %s %d\n",buf,cpufreq_max_limit);
+ }
+ else
+ {
+ cpufreq_max_limit = *(int *)buf;
+ }
+ spin_unlock_irqrestore(&cpufreq_state_lock, irq_flags);
+
+ return count;
+}
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SPRDEMAND
+static ssize_t dvfs_score_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_score_input %x\n",value);
+
+ dvfs_score_select = (value >> 24) & 0x0f;
+ if(dvfs_score_select < 4)
+ {
+ dvfs_score_critical[dvfs_score_select] = (value >> 16) & 0xff;
+ dvfs_score_hi[dvfs_score_select] = (value >> 8) & 0xff;
+ dvfs_score_mid[dvfs_score_select] = value & 0xff;
+ }
+
+
+ return count;
+}
+
+static ssize_t dvfs_score_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_score_select %d\n",dvfs_score_select);
+ ret += snprintf(buf + ret,200,"dvfs_score_critical[1] = %d dvfs_score_hi[1] = %d dvfs_score_mid[1] = %d\n",dvfs_score_critical[1],dvfs_score_hi[1],dvfs_score_mid[1]);
+ ret += snprintf(buf + ret,200,"dvfs_score_critical[2] = %d dvfs_score_hi[2] = %d dvfs_score_mid[2] = %d\n",dvfs_score_critical[2],dvfs_score_hi[2],dvfs_score_mid[2]);
+ ret += snprintf(buf + ret,200,"dvfs_score_critical[3] = %d dvfs_score_hi[3] = %d dvfs_score_mid[3] = %d\n",dvfs_score_critical[3],dvfs_score_hi[3],dvfs_score_mid[3]);
+
+ ret += snprintf(buf + ret,200,"percpu_total_load[0] = %d,%d->%d\n",
+ percpu_load[0],ga_percpu_total_load[0][(cur_window_index[0] - 1 + 10) % 10],ga_percpu_total_load[0][cur_window_index[0]]);
+ ret += snprintf(buf + ret,200,"percpu_total_load[1] = %d,%d->%d\n",
+ percpu_load[1],ga_percpu_total_load[1][(cur_window_index[1] - 1 + 10) % 10],ga_percpu_total_load[1][cur_window_index[1]]);
+ ret += snprintf(buf + ret,200,"percpu_total_load[2] = %d,%d->%d\n",
+ percpu_load[2],ga_percpu_total_load[2][(cur_window_index[2] - 1 + 10) % 10],ga_percpu_total_load[2][cur_window_index[2]]);
+ ret += snprintf(buf + ret,200,"percpu_total_load[3] = %d,%d->%d\n",
+ percpu_load[3],ga_percpu_total_load[3][(cur_window_index[3] - 1 + 10) % 10],ga_percpu_total_load[3][cur_window_index[3]]);
+
+ return strlen(buf) + 1;
+}
+
+static ssize_t dvfs_unplug_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_score_input %x\n",value);
+
+ dvfs_unplug_select = (value >> 24) & 0x0f;
+ if(dvfs_unplug_select > 7)
+ {
+ cur_window_size[0]= (value >> 8) & 0xff;
+ cur_window_size[1]= (value >> 8) & 0xff;
+ cur_window_size[2]= (value >> 8) & 0xff;
+ cur_window_size[3]= (value >> 8) & 0xff;
+ }
+ return count;
+}
+
+static ssize_t dvfs_unplug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_unplug_select %d\n",dvfs_unplug_select);
+ ret += snprintf(buf + ret,100,"cur_window_size[0] = %d\n",cur_window_size[0]);
+ ret += snprintf(buf + ret,100,"cur_window_size[1] = %d\n",cur_window_size[1]);
+ ret += snprintf(buf + ret,100,"cur_window_size[2] = %d\n",cur_window_size[2]);
+ ret += snprintf(buf + ret,100,"cur_window_size[3] = %d\n",cur_window_size[3]);
+
+ return strlen(buf) + 1;
+}
+
+
+static ssize_t dvfs_plug_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_plug_select %x\n",value);
+
+ dvfs_plug_select = (value ) & 0x0f;
+ return count;
+}
+
+
+static ssize_t dvfs_plug_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_plug_select %d\n",dvfs_plug_select);
+
+ return strlen(buf) + 1;
+}
+#endif
+
+static ssize_t cpufreq_table_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ memcpy(buf,sprd_cpufreq_conf->freq_tbl,sizeof(* sprd_cpufreq_conf->freq_tbl));
+ return sizeof(* sprd_cpufreq_conf->freq_tbl);
+}
+
+static ssize_t dvfs_prop_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ printk(KERN_ERR"dvfs_status %s\n",buf);
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"dvfs_plug_select %x\n",value);
+
+ dvfs_plug_select = (value ) & 0x0f;
+ return count;
+}
+
+static ssize_t dvfs_prop_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"dvfs_plug_select %d\n",dvfs_plug_select);
+
+ return strlen(buf) + 1;
+}
+
+#ifdef CONFIG_SPRD_AVS_DEBUG
+extern unsigned int g_avs_log_flag;
+
+static ssize_t avs_log_store(struct device *dev, struct device_attribute *attr,const char *buf, size_t count)
+{
+ int ret;
+ int value;
+ unsigned long irq_flags;
+
+ printk(KERN_ERR"g_avs_log_flag %s\n",buf);
+ ret = strict_strtoul(buf,16,(long unsigned int *)&value);
+
+ printk(KERN_ERR"g_avs_log_flag %x\n",value);
+
+ g_avs_log_flag = (value ) & 0x0f;
+ return count;
+}
+
+static ssize_t avs_log_show(struct device *dev, struct device_attribute *attr,char *buf)
+{
+ int ret = 0;
+
+ ret = snprintf(buf + ret,50,"g_avs_log_flag %d\n",g_avs_log_flag);
+
+ return strlen(buf) + 1;
+}
+#endif
+static DEVICE_ATTR(cpufreq_min_limit, 0660, cpufreq_min_limit_show, cpufreq_min_limit_store);
+static DEVICE_ATTR(cpufreq_max_limit, 0660, cpufreq_max_limit_show, cpufreq_max_limit_store);
+static DEVICE_ATTR(cpufreq_min_limit_debug, 0440, cpufreq_min_limit_debug_show, NULL);
+static DEVICE_ATTR(cpufreq_max_limit_debug, 0440, cpufreq_max_limit_debug_show, NULL);
+static DEVICE_ATTR(cpufreq_table, 0440, cpufreq_table_show, NULL);
+static DEVICE_ATTR(cpufreq_max_axi_freq, 0660, cpufreq_max_axi_freq_show, cpufreq_max_axi_freq_store);
+
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SPRDEMAND
+static DEVICE_ATTR(dvfs_score, 0660, dvfs_score_show, dvfs_score_store);
+static DEVICE_ATTR(dvfs_unplug, 0660, dvfs_unplug_show, dvfs_unplug_store);
+static DEVICE_ATTR(dvfs_plug, 0660, dvfs_plug_show, dvfs_plug_store);
+#endif
+static DEVICE_ATTR(dvfs_prop, 0660, dvfs_prop_show, dvfs_prop_store);
+
+#ifdef CONFIG_SPRD_AVS_DEBUG
+static DEVICE_ATTR(avs_log, 0660, avs_log_show, avs_log_store);
+#endif
+static struct attribute *g[] = {
+ &dev_attr_cpufreq_min_limit.attr,
+ &dev_attr_cpufreq_max_limit.attr,
+ &dev_attr_cpufreq_min_limit_debug.attr,
+ &dev_attr_cpufreq_max_limit_debug.attr,
+ &dev_attr_cpufreq_table.attr,
+ &dev_attr_cpufreq_max_axi_freq.attr,
+#ifdef CONFIG_CPU_FREQ_DEFAULT_GOV_SPRDEMAND
+ &dev_attr_dvfs_score.attr,
+ &dev_attr_dvfs_unplug.attr,
+ &dev_attr_dvfs_plug.attr,
+#endif
+ &dev_attr_dvfs_prop.attr,
+#ifdef CONFIG_SPRD_AVS_DEBUG
+ &dev_attr_avs_log.attr,
+#endif
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = g,
+};
+
+static int sprd_cpufreq_policy_notifier(
+ struct notifier_block *nb, unsigned long event, void *data)
+{
+ return NOTIFY_OK;
+}
+
+static struct notifier_block sprd_cpufreq_policy_nb = {
+ .notifier_call = sprd_cpufreq_policy_notifier,
+};
+
+static int __init sprd_cpufreq_modinit(void)
+{
+ int ret;
+#if defined(CONFIG_SPRD_CPUFREQ_DT_DRIVER)
+ struct platform_device_info devinfo = { .name = "cpufreq-dt-sprd", };
+
+ platform_device_register_full(&devinfo);
+
+ return;
+#endif
+
+#if defined(CONFIG_ARCH_SCX35)
+ sprd_cpufreq_conf = &sc8830_cpufreq_conf;
+#elif defined(CONFIG_ARCH_SC8825)
+ sprd_cpufreq_conf = &sc8825_cpufreq_conf;
+#endif
+
+#if defined(CONFIG_ARCH_SCX35)
+ ret = sprd_freq_table_init();
+ if (ret)
+ return ret;
+
+ sprd_top_frequency = sprd_cpufreq_conf->freq_tbl[0].frequency;
+ /* TODO:need verify for the initialization of limited max freq */
+
+ sprd_cpufreq_conf->clk = clk_get_sys(NULL, "clk_mcu");
+ if (IS_ERR(sprd_cpufreq_conf->clk))
+ return PTR_ERR(sprd_cpufreq_conf->clk);
+
+ sprd_cpufreq_conf->mpllclk = clk_get_sys(NULL, "clk_mpll");
+ if (IS_ERR(sprd_cpufreq_conf->mpllclk))
+ return PTR_ERR(sprd_cpufreq_conf->mpllclk);
+
+#if !defined(CONFIG_ARCH_SCX35L) && !defined(CONFIG_ARCH_SCX20)
+ sprd_cpufreq_conf->tdpllclk = clk_get_sys(NULL, "clk_tdpll");
+ if (IS_ERR(sprd_cpufreq_conf->tdpllclk))
+ return PTR_ERR(sprd_cpufreq_conf->tdpllclk);
+#else
+// sprd_cpufreq_conf->tdpllclk = clk_get_sys(NULL, "clk_twpll");
+ sprd_cpufreq_conf->tdpllclk = clk_get_sys(NULL, "clk_768m");
+ if (IS_ERR(sprd_cpufreq_conf->tdpllclk))
+ return PTR_ERR(sprd_cpufreq_conf->tdpllclk);
+#endif
+ mutex_init(&cpufreq_vddarm_lock);
+
+ sprd_cpufreq_conf->regulator = regulator_get(NULL, "vddarm");
+
+ if (IS_ERR(sprd_cpufreq_conf->regulator))
+ return PTR_ERR(sprd_cpufreq_conf->regulator);
+
+ /* set max voltage first */
+ /*
+ regulator_set_voltage(sprd_cpufreq_conf->regulator,
+ sprd_cpufreq_conf->vddarm_mv[0],
+ sprd_cpufreq_conf->vddarm_mv[0]);
+ */
+ clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->tdpllclk);
+ /*
+ * clk_set_rate(sprd_cpufreq_conf->mpllclk, (sprd_top_frequency * 1000));
+ */
+ clk_set_parent(sprd_cpufreq_conf->clk, sprd_cpufreq_conf->mpllclk);
+ global_freqs.old = sprd_raw_get_cpufreq();
+
+#endif
+
+ boot_done = jiffies + DVFS_BOOT_TIME;
+ ret = cpufreq_register_notifier(
+ &sprd_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
+ if (ret)
+ return ret;
+
+ ret = cpufreq_register_driver(&sprd_cpufreq_driver);
+
+ ret = sysfs_create_group(power_kobj, &attr_group);
+ return ret;
+}
+
+static void __exit sprd_cpufreq_modexit(void)
+{
+#if defined(CONFIG_ARCH_SCX35)
+ if (!IS_ERR_OR_NULL(sprd_cpufreq_conf->regulator))
+ regulator_put(sprd_cpufreq_conf->regulator);
+#endif
+ cpufreq_unregister_driver(&sprd_cpufreq_driver);
+ cpufreq_unregister_notifier(
+ &sprd_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
+ return;
+}
+
+module_init(sprd_cpufreq_modinit);
+module_exit(sprd_cpufreq_modexit);
+
+MODULE_DESCRIPTION("cpufreq driver for Spreadtrum");
+MODULE_LICENSE("GPL");