ARM: tegra: power: Update Tegra3 CPU auto-hotplug
Alex Frid [Wed, 20 Apr 2011 06:38:46 +0000 (23:38 -0700)]
- taking CPU core off-line: selected CPU with minimum load
- switching from ULP to G CPU mode: set CPU clock to cpufreq
target rate after the mode switch is completed

Original-Change-Id: I9bf4d0f4b48c262cf678c603aac02043dd602674
Reviewed-on: http://git-master/r/28420
Reviewed-by: Varun Colbert <vcolbert@nvidia.com>
Tested-by: Varun Colbert <vcolbert@nvidia.com>
Original-Change-Id: I5a19be79dd8f8fe788637870a22cd34dcfea150e

Rebase-Id: Re264ec676c5c2103f7738c9eab5f4e11a4344975

arch/arm/mach-tegra/cpu-tegra.c
arch/arm/mach-tegra/cpu-tegra3.c
arch/arm/mach-tegra/pm.h

index 7465f33..8398c3d 100644 (file)
@@ -51,7 +51,6 @@ static bool is_suspended;
 
 static unsigned int tegra_getspeed(unsigned int cpu);
 static int tegra_update_cpu_speed(unsigned long rate);
-static unsigned long tegra_cpu_highest_speed(void);
 
 #ifdef CONFIG_TEGRA_THERMAL_THROTTLE
 /* CPU frequency is gradually lowered when throttling is enabled */
@@ -113,7 +112,7 @@ void tegra_throttling_enable(bool enable)
                cancel_delayed_work_sync(&throttle_work);
                is_throttling = false;
                /* restore speed requested by governor */
-               tegra_update_cpu_speed(tegra_cpu_highest_speed());
+               tegra_cpu_cap_highest_speed(NULL);
        }
 
        mutex_unlock(&tegra_cpu_lock);
@@ -237,7 +236,7 @@ static int tegra_cpu_edp_notify(
 
                cpu_speed = tegra_getspeed(0);
                new_speed = edp_governor_speed(cpu_speed);
-               if (cpu_speed != new_speed) {
+               if (new_speed < cpu_speed) {
                        ret = tegra_update_cpu_speed(new_speed);
                        if (ret) {
                                cpu_clear(cpu, edp_cpumask);
@@ -359,8 +358,29 @@ static int tegra_update_cpu_speed(unsigned long rate)
        return 0;
 }
 
-static unsigned long tegra_cpu_highest_speed(void)
-{
+unsigned int tegra_get_slowest_cpu_n(void) {
+       unsigned int cpu = nr_cpu_ids;
+       unsigned long rate = ULONG_MAX;
+       int i;
+
+       for_each_online_cpu(i)
+               if ((i > 0) && (rate > target_cpu_speed[i])) {
+                       cpu = i;
+                       rate = target_cpu_speed[i];
+               }
+       return cpu;
+}
+
+unsigned long tegra_cpu_lowest_speed(void) {
+       unsigned long rate = ULONG_MAX;
+       int i;
+
+       for_each_online_cpu(i)
+               rate = min(rate, target_cpu_speed[i]);
+       return rate;
+}
+
+unsigned long tegra_cpu_highest_speed(void) {
        unsigned long rate = 0;
        int i;
 
@@ -369,6 +389,17 @@ static unsigned long tegra_cpu_highest_speed(void)
        return rate;
 }
 
+int tegra_cpu_cap_highest_speed(unsigned int *speed_cap)
+{
+       unsigned int new_speed = tegra_cpu_highest_speed();
+
+       new_speed = throttle_governor_speed(new_speed);
+       new_speed = edp_governor_speed(new_speed);
+       if (speed_cap)
+               *speed_cap = new_speed;
+       return tegra_update_cpu_speed(new_speed);
+}
+
 static int tegra_target(struct cpufreq_policy *policy,
                       unsigned int target_freq,
                       unsigned int relation)
@@ -391,9 +422,7 @@ static int tegra_target(struct cpufreq_policy *policy,
        freq = freq_table[idx].frequency;
 
        target_cpu_speed[policy->cpu] = freq;
-       new_speed = throttle_governor_speed(tegra_cpu_highest_speed());
-       new_speed = edp_governor_speed(new_speed);
-       ret = tegra_update_cpu_speed(new_speed);
+       ret = tegra_cpu_cap_highest_speed(&new_speed);
        if (ret == 0)
                tegra_auto_hotplug_governor(new_speed);
 out:
index 8e4fe79..9d42f77 100644 (file)
@@ -190,7 +190,7 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
        case TEGRA_HP_IDLE:
                break;
        case TEGRA_HP_DOWN:
-               cpu = cpumask_next(0, cpu_online_mask);
+               cpu = tegra_get_slowest_cpu_n();
                if (cpu < nr_cpu_ids) {
                        up = false;
                        queue_delayed_work(
@@ -210,6 +210,8 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
                        if(!clk_set_parent(cpu_clk, cpu_g_clk)) {
                                hp_stats_update(CONFIG_NR_CPUS, false);
                                hp_stats_update(0, true);
+                               /* catch-up with governor target speed */
+                               tegra_cpu_cap_highest_speed(NULL);
                        }
                        queue_delayed_work(
                                hotplug_wq, &hotplug_work, up2gn_delay);
index 6cf0219..6714ff6 100644 (file)
@@ -74,6 +74,11 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat);
 
 void tegra_idle_lp2(void);
 
+unsigned int tegra_get_slowest_cpu_n(void);
+unsigned long tegra_cpu_lowest_speed(void);
+unsigned long tegra_cpu_highest_speed(void);
+int tegra_cpu_cap_highest_speed(unsigned int *speed_cap);
+
 struct tegra_edp_limits {
        int     temperature;
        unsigned int freq_limits[CONFIG_NR_CPUS];