ARM: tegra: power: Check Tegra3 auto-hotplug speed balance
Alex Frid [Fri, 22 Apr 2011 04:33:12 +0000 (21:33 -0700)]
When current CPU complex frequency is above target range:
- bring new core on-line only if cpufreq governor requests for
all already on-lined CPUs are above 50% of current CPU frequency
- off-line one core (despite high pick request) if cpufreq
governor requests for at least 2 on-lined CPUs are below 25% of
current CPU frequency
- do nothing if neither of the above conditions is true

Original-Change-Id: I77e1bd543a8fadd51974f7d574f256a6e7e2979a
Reviewed-on: http://git-master/r/29702
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Tested-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>

Rebase-Id: Rc5c717454d1e09ca97ccc79fff60cb33fcf854e9

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

index 8398c3d..04731a1 100644 (file)
@@ -358,6 +358,17 @@ static int tegra_update_cpu_speed(unsigned long rate)
        return 0;
 }
 
+unsigned int tegra_count_slow_cpus(unsigned long speed_limit)
+{
+       unsigned int cnt = 0;
+       int i;
+
+       for_each_online_cpu(i)
+               if (target_cpu_speed[i] <= speed_limit)
+                       cnt++;
+       return cnt;
+}
+
 unsigned int tegra_get_slowest_cpu_n(void) {
        unsigned int cpu = nr_cpu_ids;
        unsigned long rate = ULONG_MAX;
index 9d42f77..d629dc7 100644 (file)
@@ -178,6 +178,26 @@ static struct kernel_param_ops tegra_hp_state_ops = {
 module_param_cb(auto_hotplug, &tegra_hp_state_ops, &hp_state, 0644);
 
 
+enum {
+       TEGRA_CPU_SPEED_BALANCED,
+       TEGRA_CPU_SPEED_BIASED,
+       TEGRA_CPU_SPEED_SKEWED,
+};
+
+static int tegra_cpu_speed_balance(void)
+{
+       unsigned long highest_speed = tegra_cpu_highest_speed();
+
+       /* balanced: freq targets for all CPUs are above 50% of highest speed
+          biased: freq target for at least one CPU is below 50% threshold
+          skewed: freq targets for at least 2 CPUs are below 25% threshold */
+       if (tegra_count_slow_cpus(highest_speed / 4) >= 2)
+               return TEGRA_CPU_SPEED_SKEWED;
+       else if (tegra_count_slow_cpus(highest_speed / 2) >= 1)
+               return TEGRA_CPU_SPEED_BIASED;
+       return TEGRA_CPU_SPEED_BALANCED;
+}
+
 static void tegra_auto_hotplug_work_func(struct work_struct *work)
 {
        bool up = false;
@@ -213,17 +233,32 @@ static void tegra_auto_hotplug_work_func(struct work_struct *work)
                                /* catch-up with governor target speed */
                                tegra_cpu_cap_highest_speed(NULL);
                        }
-                       queue_delayed_work(
-                               hotplug_wq, &hotplug_work, up2gn_delay);
                } else {
-                       cpu = cpumask_next_zero(0, cpu_online_mask);
-                       if (cpu < nr_cpu_ids) {
-                               up = true;
-                               queue_delayed_work(
-                                       hotplug_wq, &hotplug_work, up2gn_delay);
-                               hp_stats_update(cpu, true);
+                       switch (tegra_cpu_speed_balance()) {
+                       /* cpu speed is up and balanced - one more on-line */
+                       case TEGRA_CPU_SPEED_BALANCED:
+                               cpu = cpumask_next_zero(0, cpu_online_mask);
+                               if (cpu < nr_cpu_ids) {
+                                       up = true;
+                                       hp_stats_update(cpu, true);
+                               }
+                               break;
+                       /* cpu speed is up, but skewed - remove one core */
+                       case TEGRA_CPU_SPEED_SKEWED:
+                               cpu = tegra_get_slowest_cpu_n();
+                               if (cpu < nr_cpu_ids) {
+                                       up = false;
+                                       hp_stats_update(cpu, false);
+                               }
+                               break;
+                       /* cpu speed is up, but under-utilized - do nothing */
+                       case TEGRA_CPU_SPEED_BIASED:
+                       default:
+                               break;
                        }
                }
+               queue_delayed_work(
+                       hotplug_wq, &hotplug_work, up2gn_delay);
                break;
        default:
                pr_err("%s: invalid tegra hotplug state %d\n",
index 6714ff6..8e898a5 100644 (file)
@@ -74,6 +74,7 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat);
 
 void tegra_idle_lp2(void);
 
+unsigned int tegra_count_slow_cpus(unsigned long speed_limit);
 unsigned int tegra_get_slowest_cpu_n(void);
 unsigned long tegra_cpu_lowest_speed(void);
 unsigned long tegra_cpu_highest_speed(void);