ARM: tegra: power: Enforce cpufreq policy maximum
Alex Frid [Sun, 11 Sep 2011 01:33:28 +0000 (18:33 -0700)]
Tegra cpu complex frequency is set by cpufreq driver to the maximum
of per-cpu target frequencies specified by the respective governors
running on each cpu core. It guarantees that final frequency is above
all per-cpu policy low limits, but policy high limit set on one core,
may be exceeded if the other core has higher target.

This commit implements complementary mode in cpufreq driver that set
final cpu frequency below all per-cpu maximum policy limits. The new
mode is disabled by default, and can be activated via

/sys/module/cpu_tegra/parameters/force_policy_max

(cherry picked from commit d52a93527778b13efd2e4b783ce0707513f53f26)
(cherry picked from commit bc1450eedb97fd2f37544e07dae15946d209866c)

Change-Id: I2b51738a50312e0b3ba747747e6fa68efddc6038
Reviewed-on: http://git-master/r/61020
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>

Rebase-Id: R2fa76e42f800220db708c8720a3fe6b1792e5c59

arch/arm/mach-tegra/cpu-tegra.c

index b87e795..6ec80fc 100644 (file)
@@ -46,11 +46,39 @@ static struct cpufreq_frequency_table *freq_table;
 static struct clk *cpu_clk;
 static struct clk *emc_clk;
 
+static unsigned long policy_max_speed[CONFIG_NR_CPUS];
 static unsigned long target_cpu_speed[CONFIG_NR_CPUS];
 static DEFINE_MUTEX(tegra_cpu_lock);
 static bool is_suspended;
 static int suspend_index;
 
+static bool force_policy_max;
+
+static int force_policy_max_set(const char *arg, const struct kernel_param *kp)
+{
+       int ret;
+       bool old_policy = force_policy_max;
+
+       ret = param_set_bool(arg, kp);
+
+       if ((ret == 0) && (old_policy != force_policy_max))
+               tegra_cpu_set_speed_cap(NULL);
+
+       return ret;
+}
+
+static int force_policy_max_get(char *buffer, const struct kernel_param *kp)
+{
+       return param_get_bool(buffer, kp);
+}
+
+static struct kernel_param_ops policy_ops = {
+       .set = force_policy_max_set,
+       .get = force_policy_max_get,
+};
+module_param_cb(force_policy_max, &policy_ops, &force_policy_max, 0644);
+
+
 #ifdef CONFIG_TEGRA_THERMAL_THROTTLE
 
 static ssize_t show_throttle(struct cpufreq_policy *policy, char *buf)
@@ -384,11 +412,16 @@ unsigned long tegra_cpu_lowest_speed(void) {
 }
 
 unsigned long tegra_cpu_highest_speed(void) {
+       unsigned long policy_max = ULONG_MAX;
        unsigned long rate = 0;
        int i;
 
-       for_each_online_cpu(i)
+       for_each_online_cpu(i) {
+               if (force_policy_max)
+                       policy_max = min(policy_max, policy_max_speed[i]);
                rate = max(rate, target_cpu_speed[i]);
+       }
+       rate = min(rate, policy_max);
        return rate;
 }
 
@@ -508,6 +541,25 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
        return 0;
 }
 
+static int tegra_cpufreq_policy_notifier(
+       struct notifier_block *nb, unsigned long event, void *data)
+{
+       int i, ret;
+       struct cpufreq_policy *policy = data;
+
+       if (event == CPUFREQ_NOTIFY) {
+               ret = cpufreq_frequency_table_target(policy, freq_table,
+                       policy->max, CPUFREQ_RELATION_H, &i);
+               policy_max_speed[policy->cpu] =
+                       ret ? policy->max : freq_table[i].frequency;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block tegra_cpufreq_policy_nb = {
+       .notifier_call = tegra_cpufreq_policy_notifier,
+};
+
 static struct freq_attr *tegra_cpufreq_attr[] = {
        &cpufreq_freq_attr_scaling_available_freqs,
 #ifdef CONFIG_TEGRA_THERMAL_THROTTLE
@@ -547,6 +599,12 @@ static int __init tegra_cpufreq_init(void)
 
        freq_table = table_data->freq_table;
        tegra_cpu_edp_init(false);
+
+       ret = cpufreq_register_notifier(
+               &tegra_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
+       if (ret)
+               return ret;
+
        return cpufreq_register_driver(&tegra_cpufreq_driver);
 }
 
@@ -556,6 +614,8 @@ static void __exit tegra_cpufreq_exit(void)
        tegra_cpu_edp_exit();
        tegra_auto_hotplug_exit();
        cpufreq_unregister_driver(&tegra_cpufreq_driver);
+       cpufreq_unregister_notifier(
+               &tegra_cpufreq_policy_nb, CPUFREQ_POLICY_NOTIFIER);
 }