ARM: tegra: dvfs: Add cold zone Tegra3 CPU dvfs limits
Alex Frid [Wed, 14 Dec 2011 23:28:44 +0000 (15:28 -0800)]
Added alternative frequency limits for Tegra3 CPU. These limits are
applied only in the lowest CPU EDP temperature zone, and the offset
from regular Tegra3 dvfs frequencies is set at -50MHz at all scaling
voltage steps. Offset values as well as temperature threshold are to
be updated per characterization.

Bug 913884

Change-Id: Ia420f54b4c9fdc966e44d0269d45d9164d751b5f
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/70189
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Tested-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Reviewed-on: http://git-master/r/75615
Reviewed-by: Varun Wadekar <vwadekar@nvidia.com>
Tested-by: Varun Wadekar <vwadekar@nvidia.com>

arch/arm/mach-tegra/cpu-tegra.c
arch/arm/mach-tegra/dvfs.h
arch/arm/mach-tegra/tegra3_dvfs.c

index 5599c29..24ed5d2 100644 (file)
@@ -41,6 +41,7 @@
 
 #include "clock.h"
 #include "cpu-tegra.h"
+#include "dvfs.h"
 
 /* tegra throttling and edp governors require frequencies in the table
    to be in ascending order */
@@ -226,11 +227,14 @@ int tegra_edp_update_thermal_zone(int temperature)
        mutex_lock(&tegra_cpu_lock);
        edp_thermal_index = index;
 
-       /* Update cpu rate if cpufreq (at least on cpu0) is already started */
+       /* Update cpu rate if cpufreq (at least on cpu0) is already started;
+          alter cpu dvfs table for this thermal zone if necessary */
+       tegra_cpu_dvfs_alter(edp_thermal_index, true);
        if (target_cpu_speed[0]) {
                edp_update_limit();
                tegra_cpu_set_speed_cap(NULL);
        }
+       tegra_cpu_dvfs_alter(edp_thermal_index, false);
        mutex_unlock(&tegra_cpu_lock);
 
        return ret;
index f7e863f..eaecf42 100644 (file)
@@ -125,6 +125,7 @@ int tegra_dvfs_predict_millivolts(struct clk *c, unsigned long rate);
 void tegra_dvfs_core_cap_enable(bool enable);
 void tegra_dvfs_core_cap_level_set(int level);
 int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable);
+void tegra_cpu_dvfs_alter(int edp_thermal_index, bool before_clk_update);
 #else
 static inline void tegra_soc_init_dvfs(void)
 {}
@@ -161,6 +162,9 @@ static inline void tegra_dvfs_core_cap_level_set(int level)
 {}
 static inline int tegra_dvfs_alt_freqs_set(struct dvfs *d, bool enable)
 { return 0; }
+static inline void tegra_cpu_dvfs_alter(int edp_thermal_index,
+                                       bool before_clk_update)
+{}
 #endif
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
index 8bd8bf7..8f560ca 100644 (file)
 
 static bool tegra_dvfs_cpu_disabled;
 static bool tegra_dvfs_core_disabled;
+static struct dvfs *cpu_dvfs;
 
 static const int cpu_millivolts[MAX_DVFS_FREQS] = {
        800, 825, 850, 875, 900, 912, 975, 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, 1212, 1237};
 
+static const unsigned int cpu_cold_offs_mhz[MAX_DVFS_FREQS] = {
+         50,  50,  50,  50,  50,  50,  50,  50,  50,   50,   50,   50,   50,   50,   50,   50,   50,   50};
+
 static const int core_millivolts[MAX_DVFS_FREQS] =
        {1000, 1050, 1100, 1150, 1200, 1250, 1300};
 
@@ -422,6 +426,31 @@ static void __init init_dvfs_one(struct dvfs *d, int nominal_mv_index)
        }
 }
 
+static void __init init_dvfs_cold(struct dvfs *d, int nominal_mv_index)
+{
+       int i;
+       unsigned long offs;
+
+       BUG_ON((nominal_mv_index == 0) || (nominal_mv_index > d->num_freqs));
+
+       for (i = 0; i < d->num_freqs; i++) {
+               offs = cpu_cold_offs_mhz[i] * MHZ;
+               if (i > nominal_mv_index)
+                       d->alt_freqs[i] = d->alt_freqs[i - 1];
+               else if (d->freqs[i] > offs)
+                       d->alt_freqs[i] = d->freqs[i] - offs;
+               else {
+                       d->alt_freqs[i] = d->freqs[i];
+                       pr_warn("tegra3_dvfs: cold offset %lu is too high for"
+                               " regular dvfs limit %lu\n", offs, d->freqs[i]);
+               }
+
+               if (i)
+                       BUG_ON(d->alt_freqs[i] < d->alt_freqs[i - 1]);
+       }
+       d->alt_freqs_state = ALT_FREQS_DISABLED;
+}
+
 static bool __init match_dvfs_one(struct dvfs *d, int speedo_id, int process_id)
 {
        if ((d->process_id != -1 && d->process_id != process_id) ||
@@ -535,7 +564,6 @@ void __init tegra_soc_init_dvfs(void)
        int i;
        int core_nominal_mv_index;
        int cpu_nominal_mv_index;
-       struct dvfs *cpu_dvfs = NULL;
 
 #ifndef CONFIG_TEGRA_CORE_DVFS
        tegra_dvfs_core_disabled = true;
@@ -581,6 +609,7 @@ void __init tegra_soc_init_dvfs(void)
        /* Initialize matching cpu dvfs entry already found when nominal
           voltage was determined */
        init_dvfs_one(cpu_dvfs, cpu_nominal_mv_index);
+       init_dvfs_cold(cpu_dvfs, cpu_nominal_mv_index);
 
        /* Finally disable dvfs on rails if necessary */
        if (tegra_dvfs_core_disabled)
@@ -596,6 +625,17 @@ void __init tegra_soc_init_dvfs(void)
                tegra_dvfs_core_disabled ? "disabled" : "enabled");
 }
 
+void tegra_cpu_dvfs_alter(int edp_thermal_index, bool before_clk_update)
+{
+       bool enable = !edp_thermal_index;
+
+       if (enable != before_clk_update) {
+               int ret = tegra_dvfs_alt_freqs_set(cpu_dvfs, enable);
+               WARN_ONCE(ret, "tegra dvfs: failed to set CPU alternative"
+                              " frequency limits for cold temeperature\n");
+       }
+}
+
 int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
 {
        int ret = 0;