From: Ziggy Date: Fri, 31 Aug 2012 18:25:44 +0000 (-0400) Subject: Implement sysfs interface for vdd X-Git-Url: https://ziggy471.com/git/gitweb.cgi?p=ziggy471-i535-kernel.git;a=commitdiff;h=34c622bbea3afb34b9bfa4d6b00fe66356d0d0f6 Implement sysfs interface for vdd --- --- a/arch/arm/mach-msm/acpuclock-8960.c +++ b/arch/arm/mach-msm/acpuclock-8960.c @@ -40,6 +40,7 @@ #include #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) @@ -1692,3 +1694,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 @@ -219,6 +219,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; } @@ -231,6 +235,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 @@ -679,6 +679,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); @@ -697,6 +750,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, @@ -715,6 +772,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); @@ -2320,6 +2389,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; @@ -2337,6 +2409,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);