msm: cpufreq: Add API to allow limiting of min and max cpu frequencies
CPUFreq provides scaling sysfs nodes to set min and max frequencies, but
there are times when the operating range has to be controlled
irrespective of the scaling limits, to ensure the thermal stability of
the device.
The API allows kernel thermal safeguards to pitch in and limit the
frequencies.
Change-Id: I3dc7496747792e6223695131f1644dd87054b025
Signed-off-by: Praveen Chidambaram <pchidamb@codeaurora.org>
diff --git a/arch/arm/mach-msm/cpufreq.c b/arch/arm/mach-msm/cpufreq.c
index 63534a4..0fa1e2d 100644
--- a/arch/arm/mach-msm/cpufreq.c
+++ b/arch/arm/mach-msm/cpufreq.c
@@ -19,6 +19,7 @@
#include <linux/earlysuspend.h>
#include <linux/init.h>
+#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
@@ -27,6 +28,7 @@
#include <linux/sched.h>
#include <linux/suspend.h>
#include <mach/socinfo.h>
+#include <mach/cpufreq.h>
#include "acpuclock.h"
@@ -50,10 +52,33 @@
static DEFINE_PER_CPU(struct cpufreq_suspend_t, cpufreq_suspend);
+struct cpu_freq {
+ uint32_t max;
+ uint32_t min;
+ uint32_t allowed_max;
+ uint32_t allowed_min;
+ uint32_t limits_init;
+};
+
+static DEFINE_PER_CPU(struct cpu_freq, cpu_freq_info);
+
static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq)
{
int ret = 0;
struct cpufreq_freqs freqs;
+ struct cpu_freq *limit = &per_cpu(cpu_freq_info, policy->cpu);
+
+ if (limit->limits_init) {
+ if (new_freq > limit->allowed_max) {
+ new_freq = limit->allowed_max;
+ pr_debug("max: limiting freq to %d\n", new_freq);
+ }
+
+ if (new_freq < limit->allowed_min) {
+ new_freq = limit->allowed_min;
+ pr_debug("min: limiting freq to %d\n", new_freq);
+ }
+ }
freqs.old = policy->cur;
freqs.new = new_freq;
@@ -158,6 +183,72 @@
return 0;
}
+static unsigned int msm_cpufreq_get_freq(unsigned int cpu)
+{
+ return acpuclk_get_rate(cpu);
+}
+
+static inline int msm_cpufreq_limits_init(void)
+{
+ int cpu = 0;
+ int i = 0;
+ struct cpufreq_frequency_table *table = NULL;
+ uint32_t min = (uint32_t) -1;
+ uint32_t max = 0;
+ struct cpu_freq *limit = NULL;
+
+ for_each_possible_cpu(cpu) {
+ limit = &per_cpu(cpu_freq_info, cpu);
+ table = cpufreq_frequency_get_table(cpu);
+ if (table == NULL) {
+ pr_err("%s: error reading cpufreq table for cpu %d\n",
+ __func__, cpu);
+ continue;
+ }
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
+ if (table[i].frequency > max)
+ max = table[i].frequency;
+ if (table[i].frequency < min)
+ min = table[i].frequency;
+ }
+ limit->allowed_min = min;
+ limit->allowed_max = max;
+ limit->min = min;
+ limit->max = max;
+ limit->limits_init = 1;
+ }
+
+ return 0;
+}
+
+int msm_cpufreq_set_freq_limits(uint32_t cpu, uint32_t min, uint32_t max)
+{
+ struct cpu_freq *limit = &per_cpu(cpu_freq_info, cpu);
+
+ if (!limit->limits_init)
+ msm_cpufreq_limits_init();
+
+ if ((min != MSM_CPUFREQ_NO_LIMIT) &&
+ min >= limit->min && min <= limit->max)
+ limit->allowed_min = min;
+ else
+ limit->allowed_min = limit->min;
+
+
+ if ((max != MSM_CPUFREQ_NO_LIMIT) &&
+ max <= limit->max && max >= limit->min)
+ limit->allowed_max = max;
+ else
+ limit->allowed_max = limit->max;
+
+ pr_debug("%s: Limiting cpu %d min = %d, max = %d\n",
+ __func__, cpu,
+ limit->allowed_min, limit->allowed_max);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_cpufreq_set_freq_limits);
+
static int __cpuinit msm_cpufreq_init(struct cpufreq_policy *policy)
{
int cur_freq;
@@ -274,6 +365,7 @@
.init = msm_cpufreq_init,
.verify = msm_cpufreq_verify,
.target = msm_cpufreq_target,
+ .get = msm_cpufreq_get_freq,
.name = "msm",
.attr = msm_freq_attr,
};
@@ -300,4 +392,3 @@
}
late_initcall(msm_cpufreq_register);
-