--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -40,6 +40,7 @@
#include <mach/sec_debug.h>
#endif
+
/*
* Source IDs.
* These must be negative to not overlap with the source IDs
@@ -72,6 +73,7 @@
#define HFPLL_NOMINAL_VDD 1050000
#define HFPLL_LOW_VDD 800000
+#define HFPLL_HIGH_VDD 1350000
#define HFPLL_LOW_VDD_PLL_L_MAX 0x28
#define SECCLKAGD BIT(4)
@@ -1811,3 +1813,33 @@ struct acpuclk_soc_data acpuclk_8960_soc
struct acpuclk_soc_data acpuclk_8930_soc_data __initdata = {
.init = acpuclk_8960_init,
};
+
+#ifdef CONFIG_VDD_USERSPACE
+ssize_t acpuclk_get_vdd_levels_str(char *buf)
+{
+ int i, len = 0;
+ if (buf) {
+ mutex_lock(&driver_lock);
+ len += sprintf(buf + len, "Min: %4d\n", HFPLL_LOW_VDD);
+ len += sprintf(buf + len, "Max: %4d\n", HFPLL_HIGH_VDD);
+ for (i = 0; acpu_freq_tbl[i].speed.khz; i++) {
+ len += sprintf(buf + len, "%8u: %4d\n", acpu_freq_tbl[i].speed.khz, acpu_freq_tbl[i].vdd_core);
+ }
+ mutex_unlock(&driver_lock);
+ }
+ return len;
+}
+
+void acpuclk_set_vdd(unsigned int khz, int vdd)
+{
+ int i;
+ mutex_lock(&driver_lock);
+ for (i = 0; acpu_freq_tbl[i].speed.khz; i++) {
+ if (khz == 0)
+ acpu_freq_tbl[i].vdd_core = min(max((unsigned int)(acpu_freq_tbl[i].vdd_core + vdd), (unsigned int)HFPLL_LOW_VDD), (unsigned int)HFPLL_HIGH_VDD);
+ else if (acpu_freq_tbl[i].speed.khz == khz)
+ acpu_freq_tbl[i].vdd_core = min(max((unsigned int)vdd, (unsigned int)HFPLL_LOW_VDD), (unsigned int)HFPLL_HIGH_VDD);
+ }
+ mutex_unlock(&driver_lock);
+}
+#endif
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -221,6 +221,10 @@ static int __cpuinit msm_cpufreq_init(st
init_completion(&cpu_work->complete);
#endif
+#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX
+ policy->min = CONFIG_MSM_CPU_FREQ_MIN;
+ policy->max = CONFIG_MSM_CPU_FREQ_MAX;
+#endif
return 0;
}
@@ -233,6 +237,8 @@ static int msm_cpufreq_suspend(void)
per_cpu(cpufreq_suspend, cpu).device_suspended = 1;
mutex_unlock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex);
}
+ if (num_online_cpus() > 1)
+ cpu_down(1);
return NOTIFY_DONE;
}
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -289,6 +289,14 @@ config SEC_DVFS
default n
depends on CPU_FREQ
+config VDD_USERSPACE
+ bool "VDD sysfs interface"
+ default n
+ depends on CPU_FREQ_STAT
+ help
+ exposes the VDD table to userspace
+ allows users to adjust voltages on the fly
+
menu "x86 CPU frequency scaling drivers"
depends on X86
source "drivers/cpufreq/Kconfig.x86"
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -699,6 +699,59 @@ static ssize_t show_bios_limit(struct cp
return sprintf(buf, "%u\n", policy->cpuinfo.max_freq);
}
+#ifdef CONFIG_VDD_USERSPACE
+extern ssize_t acpuclk_get_vdd_levels_str(char *buf);
+static ssize_t show_vdd_levels(struct kobject *a, struct attribute *b, char *buf)
+{
+ return acpuclk_get_vdd_levels_str(buf);
+}
+
+extern void acpuclk_set_vdd(unsigned acpu_khz, int vdd);
+static ssize_t store_vdd_levels(struct kobject *a, struct attribute *b, const char *buf, size_t count)
+{
+ int i = 0, j;
+ int pair[2] = { 0, 0 };
+ int sign = 0;
+ if (count < 1)
+ return 0;
+ if (buf[0] == '-') {
+ sign = -1;
+ i++;
+ }
+ else if (buf[0] == '+') {
+ sign = 1;
+ i++;
+ }
+ for (j = 0; i < count; i++) {
+ char c = buf[i];
+ if ((c >= '0') && (c <= '9')) {
+ pair[j] *= 10;
+ pair[j] += (c - '0');
+ }
+ else if ((c == ' ') || (c == '\t')) {
+ if (pair[j] != 0) {
+ j++;
+ if ((sign != 0) || (j > 1))
+ break;
+ }
+ }
+ else
+ break;
+ }
+ if (sign != 0) {
+ if (pair[0] > 0)
+ acpuclk_set_vdd(0, sign * pair[0]);
+ }
+ else {
+ if ((pair[0] > 0) && (pair[1] > 0))
+ acpuclk_set_vdd((unsigned)pair[0], pair[1]);
+ else
+ return -EINVAL;
+ }
+ return count;
+}
+#endif /* CONFIG_VDD_USERSPACE */
+
cpufreq_freq_attr_ro_perm(cpuinfo_cur_freq, 0400);
cpufreq_freq_attr_ro(cpuinfo_min_freq);
cpufreq_freq_attr_ro(cpuinfo_max_freq);
@@ -717,6 +770,10 @@ cpufreq_freq_attr_rw(scaling_max_freq);
cpufreq_freq_attr_rw(scaling_governor);
cpufreq_freq_attr_rw(scaling_setspeed);
+#ifdef CONFIG_VDD_USERSPACE
+define_one_global_rw(vdd_levels);
+#endif
+
static struct attribute *default_attrs[] = {
&cpuinfo_min_freq.attr,
&cpuinfo_max_freq.attr,
@@ -735,6 +792,18 @@ static struct attribute *default_attrs[]
NULL
};
+#ifdef CONFIG_VDD_USERSPACE
+static struct attribute *vddtbl_attrs[] = {
+ &vdd_levels.attr,
+ NULL
+};
+
+static struct attribute_group vddtbl_attr_group = {
+ .attrs = vddtbl_attrs,
+ .name = "vdd_table",
+};
+#endif /* CONFIG_VDD_USERSPACE */
+
struct kobject *cpufreq_global_kobject;
EXPORT_SYMBOL(cpufreq_global_kobject);
@@ -2340,6 +2409,9 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_dri
static int __init cpufreq_core_init(void)
{
int cpu;
+#ifdef CONFIG_VDD_USERSPACE
+ int rc;
+#endif /* CONFIG_VDD_USERSPACE */
for_each_possible_cpu(cpu) {
per_cpu(cpufreq_policy_cpu, cpu) = -1;
@@ -2357,6 +2429,10 @@ static int __init cpufreq_core_init(void
#endif
register_syscore_ops(&cpufreq_syscore_ops);
+#ifdef CONFIG_VDD_USERSPACE
+ rc = sysfs_create_group(cpufreq_global_kobject, &vddtbl_attr_group);
+#endif /* CONFIG_VDD_USERSPACE */
+
return 0;
}
core_initcall(cpufreq_core_init);