@@ -23,6 +23,13 @@ config CPU_FREQ_TABLE
config CPU_FREQ_GOV_COMMON
bool
+config CPU_FREQ_OVERCLOCK
+ bool "CPU frequency overclocking support"
+ help
+ Switch to enable support for overclocking support
+
+ If in doubt, say N.
+
config CPU_FREQ_STAT
tristate "CPU frequency translation statistics"
select CPU_FREQ_TABLE
@@ -239,6 +239,99 @@ static struct notifier_block exynos_cpufreq_nb = {
.notifier_call = exynos_cpufreq_pm_notifier,
};
+#ifdef CONFIG_CPU_FREQ_OVERCLOCK
+static int tb_over_clk_update_freq(struct cpufreq_policy *policy, int enable)
+{
+ int ret, index;
+
+ index = exynos_info->get_freq_index(exynos_info->max_over_freq);
+ if (index < 0) {
+ pr_err("%s: Index not found !\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&cpufreq_lock);
+ exynos_info->max_over_idx = index;
+ exynos_info->en_over_clk = enable;
+
+ if (enable)
+ cpufreq_freq_table_set_valid_entry(exynos_info->freq_table,
+ index,
+ exynos_info->max_over_freq);
+ else
+ cpufreq_freq_table_set_invalid_entry(exynos_info->freq_table,
+ index);
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table);
+ mutex_unlock(&cpufreq_lock);
+
+ return ret;
+}
+
+int cpufreq_overclk_en(struct cpufreq_policy *policy)
+{
+ if (!exynos_info->en_over_clk)
+ return tb_over_clk_update_freq(policy, 1);
+
+ return 0;
+}
+
+int cpufreq_overclk_dis(struct cpufreq_policy *policy)
+{
+ if (exynos_info->en_over_clk)
+ return tb_over_clk_update_freq(policy, 0);
+
+ return 0;
+}
+
+int cpufreq_overclk_max(void)
+{
+ return exynos_info->max_over_freq;
+}
+
+static ssize_t show_tb_en_over_clk(struct cpufreq_policy *policy, char *buf)
+{
+ return sprintf(buf, "%d\n", exynos_info->en_over_clk);
+}
+
+static ssize_t store_tb_en_over_clk(struct cpufreq_policy *policy,
+ const char *buf, size_t count)
+{
+ int ret, enable;
+
+ ret = sscanf(buf, "%d", &enable);
+ if (ret != 1 || enable < 0 || enable > 1)
+ return -EINVAL;
+
+ if (tb_over_clk_update_freq(policy, enable)) {
+ pr_err("Cannot enable TurboBoost overclocking!\n");
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+struct freq_attr cpufreq_attr_tb_en_over_clk = {
+ .attr = { .name = "tb_en_over_clk",
+ .mode = 0644,
+ },
+ .show = show_tb_en_over_clk,
+ .store = store_tb_en_over_clk,
+};
+
+static ssize_t show_tb_over_clk_freq(struct cpufreq_policy *policy, char *buf)
+{
+ return sprintf(buf, "%d\n", exynos_info->max_over_freq);
+}
+
+struct freq_attr cpufreq_attr_tb_over_clk_freq = {
+ .attr = { .name = "tb_over_clk_freq",
+ .mode = 0444,
+ },
+ .show = show_tb_over_clk_freq,
+};
+#endif
+
static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu);
@@ -261,6 +354,10 @@ static int exynos_cpufreq_cpu_exit(struct cpufreq_policy *policy)
static struct freq_attr *exynos_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
+#ifdef CONFIG_CPU_FREQ_OVERCLOCK
+ &cpufreq_attr_tb_over_clk_freq,
+ &cpufreq_attr_tb_en_over_clk,
+#endif
NULL,
};
@@ -281,6 +378,7 @@ static struct cpufreq_driver exynos_driver = {
static int __init exynos_cpufreq_init(void)
{
+ struct device_node *node = pdev->dev.of_node;
int ret = -EINVAL;
exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL);
@@ -310,6 +408,16 @@ static int __init exynos_cpufreq_init(void)
goto err_vdd_arm;
}
+#ifdef CONFIG_CPU_FREQ_OVERCLOCK
+ if (of_property_read_bool(node, "overclocking")) {
+ of_property_read_u32(node, "max_overclocking_freq",
+ &exynos_info->max_over_freq);
+ pr_debug("%s: en_overclk: %d max_overclocking_freq: %d\n",
+ __func__, exynos_info->en_over_clk,
+ exynos_info->max_over_freq);
+ }
+#endif
+
locking_frequency = exynos_getspeed(0);
register_pm_notifier(&exynos_cpufreq_nb);
@@ -39,8 +39,15 @@ struct exynos_dvfs_info {
struct clk *cpu_clk;
unsigned int *volt_table;
struct cpufreq_frequency_table *freq_table;
+ unsigned int en_over_clk; /* enable overclocking
+ for this policy */
+ unsigned int max_over_freq; /* maximal overclocked
+ frequency */
+ unsigned int max_over_idx; /* maximal overclocked
+ index at freq_table */
void (*set_freq)(unsigned int, unsigned int);
bool (*need_apll_change)(unsigned int, unsigned int);
+ int (*get_freq_index)(unsigned int);
};
extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *);
@@ -216,6 +216,20 @@ static void exynos4x12_set_frequency(unsigned int old_index,
}
}
+int exynos4x12_get_freq_index(unsigned int freq)
+{
+ /* apll_freq tables are equal size for exynos4412 and exynos 4212 */
+ int i, size = ARRAY_SIZE(apll_freq_4412);
+
+ for (i = 0; i < size; i++)
+ if (apll_freq_4x12[i].freq == freq)
+ return i;
+
+ pr_debug("Entry for freq: %u not found\n", freq);
+
+ return -EINVAL;
+}
+
int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
{
unsigned long rate;
@@ -251,6 +265,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
info->freq_table = exynos4x12_freq_table;
info->set_freq = exynos4x12_set_frequency;
info->need_apll_change = exynos4x12_pms_change;
+ info->get_freq_index = exynos4x12_get_freq_index;
return 0;
@@ -331,6 +331,24 @@ static struct global_attr _name = \
__ATTR(_name, 0644, show_##_name, store_##_name)
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu);
+#ifdef CONFIG_CPU_FREQ_OVERCLOCK
+int cpufreq_overclk_dis(struct cpufreq_policy *policy);
+int cpufreq_overclk_en(struct cpufreq_policy *policy);
+int cpufreq_overclk_max(void);
+#else
+static inline int cpufreq_overclk_dis(struct cpufreq_policy *policy)
+{
+ return 0;
+}
+static inline int cpufreq_overclk_en (struct cpufreq_policy *policy)
+{
+ return 0;
+}
+static inline int cpufreq_overclk_max(void)
+{
+ return 0;
+}
+#endif
void cpufreq_cpu_put(struct cpufreq_policy *data);
const char *cpufreq_get_current_driver(void);
@@ -409,6 +427,20 @@ struct cpufreq_frequency_table {
* order */
};
+static inline void cpufreq_freq_table_set_valid_entry(
+ struct cpufreq_frequency_table *table,
+ unsigned int index, unsigned int freq)
+{
+ table[index].frequency = freq;
+}
+
+static inline void cpufreq_freq_table_set_invalid_entry(
+ struct cpufreq_frequency_table *table,
+ unsigned int index)
+{
+ table[index].frequency = CPUFREQ_ENTRY_INVALID;
+}
+
int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table);