ARM: tegra: dvfs: Add dvfs rails thermal profiles
Alex Frid [Wed, 6 Mar 2013 07:34:16 +0000 (23:34 -0800)]
Added thermal profiles for dvfs rails: vdd_cpu and vdd_core. Thermal
profile for each rail specifies a set of temperature trip-points and
matching minimum voltage levels (thermal floors) that limit voltage
down scaling within the respective temperature ranges. For now, only
monotonically descending profiles are supported.

Converted Tegra11 implementation of cold temperature minimum voltage
limit into single-trip-point profile.

Bug 1248374

Change-Id: Ib7f32c2266fdf7c8a28fe4cfaaeefe5a5a6eaacb
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/208151
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>

arch/arm/mach-tegra/dvfs.c
arch/arm/mach-tegra/dvfs.h
arch/arm/mach-tegra/tegra11_dvfs.c
arch/arm/mach-tegra/tegra_cl_dvfs.c

index 2ec44a4..54e5529 100644 (file)
@@ -5,7 +5,7 @@
  * Author:
  *     Colin Cross <ccross@google.com>
  *
- * Copyright (C) 2010-2011 NVIDIA Corporation.
+ * Copyright (C) 2010-2013 NVIDIA CORPORATION. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -73,10 +73,17 @@ void tegra_dvfs_add_relationships(struct dvfs_relationship *rels, int n)
  */
 static void dvfs_validate_cdevs(struct dvfs_rail *rail)
 {
+       if (rail->pll_mode_cdev && !rail->therm_mv_floors) {
+               rail->pll_mode_cdev = NULL;
+               WARN(1, "%s: invalid thermal floors\n", rail->reg_id);
+       }
+
        if (rail->dfll_mode_cdev) {
                if (!rail->pll_mode_cdev ||
                    (rail->dfll_mode_cdev->trip_temperatures !=
-                    rail->pll_mode_cdev->trip_temperatures)) {
+                    rail->pll_mode_cdev->trip_temperatures) ||
+                   (rail->dfll_mode_cdev->trip_temperatures_num !=
+                    rail->pll_mode_cdev->trip_temperatures_num)) {
                        rail->dfll_mode_cdev = NULL;
                        WARN(1, "%s: not matching dfll/pll mode trip-points\n",
                             rail->reg_id);
@@ -304,9 +311,11 @@ static inline int dvfs_rail_apply_limits(struct dvfs_rail *rail, int millivolts)
 {
        int min_mv = rail->min_millivolts;
 
-       if (rail->pll_mode_cdev)
-               min_mv = max(min_mv, rail->thermal_idx ?
-                            0 : rail->min_millivolts_cold);
+       if (rail->pll_mode_cdev) {
+               int i = rail->thermal_idx;
+               if (i < rail->pll_mode_cdev->trip_temperatures_num)
+                       min_mv = rail->therm_mv_floors[i];
+       }
 
        millivolts += rail->offs_millivolts;
        if (millivolts > rail->max_millivolts)
@@ -952,9 +961,11 @@ int tegra_dvfs_rail_dfll_mode_set_cold(struct dvfs_rail *rail)
         * dfll mode.
         */
        mutex_lock(&dvfs_lock);
-       if (rail->dfll_mode && !rail->thermal_idx)
-               ret = dvfs_rail_set_voltage_reg(
-                       rail, rail->min_millivolts_cold);
+       if (rail->dfll_mode &&
+           (rail->thermal_idx < rail->pll_mode_cdev->trip_temperatures_num)) {
+                       int mv = rail->therm_mv_floors[rail->thermal_idx];
+                       ret = dvfs_rail_set_voltage_reg(rail, mv);
+       }
        mutex_unlock(&dvfs_lock);
 #endif
        return ret;
@@ -1071,6 +1082,8 @@ static int dvfs_tree_show(struct seq_file *s, void *data)
        mutex_lock(&dvfs_lock);
 
        list_for_each_entry(rail, &dvfs_rail_list, node) {
+               int thermal_mv_floor = 0;
+
                seq_printf(s, "%s %d mV%s:\n", rail->reg_id, rail->millivolts,
                           rail->dfll_mode ? " dfll mode" :
                                rail->disabled ? " disabled" : "");
@@ -1081,6 +1094,14 @@ static int dvfs_tree_show(struct seq_file *s, void *data)
                }
                seq_printf(s, "   offset     %-7d mV\n", rail->offs_millivolts);
 
+               if ((!rail->dfll_mode && rail->pll_mode_cdev) ||
+                   rail->dfll_mode_cdev) {
+                       int i = rail->thermal_idx;
+                       if (i < rail->pll_mode_cdev->trip_temperatures_num)
+                               thermal_mv_floor = rail->therm_mv_floors[i];
+               }
+               seq_printf(s, "   thermal    %-7d mV\n", thermal_mv_floor);
+
                list_sort(NULL, &rail->dvfs, dvfs_tree_sort_cmp);
 
                list_for_each_entry(d, &rail->dvfs, reg_node) {
index 4b9b085..bfdca6a 100644 (file)
@@ -5,7 +5,7 @@
  * Author:
  *     Colin Cross <ccross@google.com>
  *
- * Copyright (C) 2010-2012 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2010-2013 NVIDIA CORPORATION. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -27,6 +27,7 @@
 #define MAX_DVFS_FREQS 40
 #define MAX_DVFS_TABLES        80
 #define DVFS_RAIL_STATS_TOP_BIN        60
+#define MAX_THERMAL_FLOORS     8
 
 struct clk;
 struct dvfs_rail;
@@ -62,7 +63,7 @@ struct dvfs_rail {
        int max_millivolts;
        int reg_max_millivolts;
        int nominal_millivolts;
-       int min_millivolts_cold;
+       const int *therm_mv_floors;
 
        int step;
        bool jmp_to_zero;
@@ -156,6 +157,8 @@ struct cpu_cvb_dvfs {
        int speedo_scale;
        int voltage_scale;
        struct cpu_cvb_dvfs_table cvb_table[MAX_DVFS_FREQS];
+       int therm_trips_table[MAX_THERMAL_FLOORS];
+       int therm_floors_table[MAX_THERMAL_FLOORS];
 };
 
 extern struct dvfs_rail *tegra_cpu_rail;
index cc6f126..6703550 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/tegra11_dvfs.c
  *
- * Copyright (C) 2012 NVIDIA Corporation.
+ * Copyright (c) 2012-2013 NVIDIA CORPORATION. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -37,24 +37,19 @@ static bool tegra_dvfs_core_disabled;
 /* FIXME: need tegra11 step */
 #define VDD_SAFE_STEP                  100
 
-static int dvfs_temperatures[] = { 20, };
+static int vdd_core_therm_trips_table[MAX_THERMAL_FLOORS] = { 20, };
+static int vdd_core_therm_floors_table[MAX_THERMAL_FLOORS] = { 950, };
 
 static struct tegra_cooling_device cpu_dfll_cdev = {
        .cdev_type = "cpu_dfll_cold",
-       .trip_temperatures = dvfs_temperatures,
-       .trip_temperatures_num = ARRAY_SIZE(dvfs_temperatures),
 };
 
 static struct tegra_cooling_device cpu_pll_cdev = {
        .cdev_type = "cpu_pll_cold",
-       .trip_temperatures = dvfs_temperatures,
-       .trip_temperatures_num = ARRAY_SIZE(dvfs_temperatures),
 };
 
 static struct tegra_cooling_device core_cdev = {
        .cdev_type = "core_cold",
-       .trip_temperatures = dvfs_temperatures,
-       .trip_temperatures_num = ARRAY_SIZE(dvfs_temperatures),
 };
 
 static struct dvfs_rail tegra11_dvfs_rail_vdd_cpu = {
@@ -63,7 +58,6 @@ static struct dvfs_rail tegra11_dvfs_rail_vdd_cpu = {
        .min_millivolts = 800,
        .step = VDD_SAFE_STEP,
        .jmp_to_zero = true,
-       .min_millivolts_cold = 1000,
        .dfll_mode_cdev = &cpu_dfll_cdev,
        .pll_mode_cdev = &cpu_pll_cdev,
 };
@@ -73,7 +67,6 @@ static struct dvfs_rail tegra11_dvfs_rail_vdd_core = {
        .max_millivolts = 1400,
        .min_millivolts = 800,
        .step = VDD_SAFE_STEP,
-       .min_millivolts_cold = 950,
        .pll_mode_cdev = &core_cdev,
 };
 
@@ -126,6 +119,8 @@ static struct cpu_cvb_dvfs cpu_cvb_dvfs_table[] = {
                        {2040000, { 250050,  -6544,   0}, { 140000,    0,    0} },
                        {      0, {      0,      0,   0}, {      0,    0,    0} },
                },
+               .therm_trips_table = { 20, },
+               .therm_floors_table = { 1000, },
        },
        {
                .speedo_id = 1,
@@ -162,6 +157,8 @@ static struct cpu_cvb_dvfs cpu_cvb_dvfs_table[] = {
                        {1810500, { 3304747, -179126, 3576}, { 1400000,    0,    0} },
                        {      0, {       0,       0,    0}, {       0,    0,    0} },
                },
+               .therm_trips_table = { 20, },
+               .therm_floors_table = { 1000, },
        },
        {
                .speedo_id = 1,
@@ -198,6 +195,8 @@ static struct cpu_cvb_dvfs cpu_cvb_dvfs_table[] = {
                        {1810500, { 3304747, -179126, 3576}, { 1400000,    0,    0} },
                        {      0, {       0,       0,    0}, {       0,    0,    0} },
                },
+               .therm_trips_table = { 20, },
+               .therm_floors_table = { 1000, },
        },
        {
                .speedo_id = 2,
@@ -235,6 +234,8 @@ static struct cpu_cvb_dvfs cpu_cvb_dvfs_table[] = {
                        {1912500, { 3395401, -181606, 3576}, { 1400000,    0,    0} },
                        {      0, {       0,       0,    0}, {       0,    0,    0} },
                },
+               .therm_trips_table = { 20, },
+               .therm_floors_table = { 1000, },
        },
 };
 
@@ -413,6 +414,41 @@ module_param_cb(disable_core, &tegra_dvfs_disable_core_ops,
 module_param_cb(disable_cpu, &tegra_dvfs_disable_cpu_ops,
        &tegra_dvfs_cpu_disabled, 0644);
 
+/*
+ * Install rail thermal profile provided:
+ * - voltage floors are descending with temperature increasing
+ * - and the lowest floor is above rail minimum voltage in pll and
+ *   in dfll mode (if applicable)
+ */
+static void __init init_rail_thermal_profile(
+       int *therm_trips_table, int *therm_floors_table,
+       struct dvfs_rail *rail, struct dvfs_dfll_data *d)
+{
+       int i, min_mv;
+
+       for (i = 0; i < MAX_THERMAL_FLOORS - 1; i++) {
+               if (!therm_floors_table[i+1])
+                       break;
+
+               if ((therm_trips_table[i] >= therm_trips_table[i+1]) ||
+                   (therm_floors_table[i] < therm_floors_table[i+1]))
+                       return;
+       }
+
+       min_mv = max(rail->min_millivolts, d ? d->min_millivolts : 0);
+       if (therm_floors_table[i] < min_mv)
+               return;
+
+       if (rail->pll_mode_cdev) {
+               rail->pll_mode_cdev->trip_temperatures_num = i + 1;
+               rail->pll_mode_cdev->trip_temperatures = therm_trips_table;
+       }
+       if (rail->dfll_mode_cdev) {
+               rail->dfll_mode_cdev->trip_temperatures_num = i + 1;
+               rail->dfll_mode_cdev->trip_temperatures = therm_trips_table;
+       }
+       rail->therm_mv_floors = therm_floors_table;
+}
 
 static bool __init can_update_max_rate(struct clk *c, struct dvfs *d)
 {
@@ -624,9 +660,6 @@ static int __init set_cpu_dvfs_data(
        cpu_dvfs->dfll_data.use_dfll_rate_min = fmin_use_dfll * d->freqs_mult;
        cpu_dvfs->dfll_data.min_millivolts = min_dfll_mv;
 
-       /* Invalidate dfll cooling if cold minimum is below dfll minimum */
-       if (cpu_dvfs->dvfs_rail->min_millivolts_cold <= min_dfll_mv)
-               cpu_dvfs->dvfs_rail->dfll_mode_cdev = NULL;
        return 0;
 }
 
@@ -724,6 +757,13 @@ void __init tegra11x_init_dvfs(void)
        }
        BUG_ON((i == ARRAY_SIZE(cpu_cvb_dvfs_table)) || ret);
 
+       /* Init thermal floors */
+       init_rail_thermal_profile(cpu_cvb_dvfs_table[i].therm_trips_table,
+               cpu_cvb_dvfs_table[i].therm_floors_table,
+               &tegra11_dvfs_rail_vdd_cpu, &cpu_dvfs.dfll_data);
+       init_rail_thermal_profile(vdd_core_therm_trips_table,
+               vdd_core_therm_floors_table, &tegra11_dvfs_rail_vdd_core, NULL);
+
        /* Init rail structures and dependencies */
        tegra_dvfs_init_rails(tegra11_dvfs_rails,
                ARRAY_SIZE(tegra11_dvfs_rails));
index 0f5684d..945bf77 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/tegra_cl_dvfs.c
  *
- * Copyright (C) 2012 NVIDIA Corporation.
+ * Copyright (c) 2012-2013 NVIDIA CORPORATION. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -186,7 +186,7 @@ struct tegra_cl_dvfs {
        u8                              safe_output;
        u8                              tune_high_out_start;
        u8                              tune_high_out_min;
-       u8                              cold_out_min;
+       u8                              thermal_out_floors[MAX_THERMAL_FLOORS];
        u8                              minimax_output;
        unsigned long                   dvco_rate_min;
 
@@ -360,7 +360,9 @@ static inline u8 get_output_min(struct tegra_cl_dvfs *cld)
 
        tune_min = cld->tune_state == TEGRA_CL_DVFS_TUNE_LOW ?
                0 : cld->tune_high_out_min;
-       thermal_min = cld->thermal_idx ? 0 : cld->cold_out_min;
+       thermal_min = 0;
+       if (cld->cdev && (cld->thermal_idx < cld->cdev->trip_temperatures_num))
+               thermal_min = cld->thermal_out_floors[cld->thermal_idx];
 
        return max(tune_min, thermal_min);
 }
@@ -760,15 +762,21 @@ static void cl_dvfs_init_tuning_thresholds(struct tegra_cl_dvfs *cld)
 
 static void cl_dvfs_init_cold_output_floor(struct tegra_cl_dvfs *cld)
 {
+       int i;
+       if (!cld->cdev)
+               return;
        /*
-        * Convert minimum voltage at low temperature into output LUT index;
-        * make sure there is room for regulation above cold minimum output
+        * Convert monotonically decreasing thermal floors at low temperature
+        * into output LUT indexes; make sure there is a room for regulation
+        * above maximum thermal floor.
         */
-       cld->cold_out_min = find_mv_out_cap(
-               cld, cld->safe_dvfs->dvfs_rail->min_millivolts_cold);
-       BUG_ON(cld->cold_out_min + 2 >= cld->num_voltages);
-       if (cld->minimax_output <= cld->cold_out_min)
-               cld->minimax_output = cld->cold_out_min + 1;
+       for (i = 0; i < cld->cdev->trip_temperatures_num; i++) {
+               cld->thermal_out_floors[i] = find_mv_out_cap(
+                       cld, cld->safe_dvfs->dvfs_rail->therm_mv_floors[i]);
+       }
+       BUG_ON(cld->thermal_out_floors[0] + 2 >= cld->num_voltages);
+       if (cld->minimax_output <= cld->thermal_out_floors[0])
+               cld->minimax_output = cld->thermal_out_floors[0] + 1;
 }
 
 static void cl_dvfs_init_output_thresholds(struct tegra_cl_dvfs *cld)
@@ -778,7 +786,7 @@ static void cl_dvfs_init_output_thresholds(struct tegra_cl_dvfs *cld)
        cl_dvfs_init_cold_output_floor(cld);
 
        /* make sure safe output is safe at any temperature */
-       cld->safe_output = cld->cold_out_min ? : 1;
+       cld->safe_output = cld->thermal_out_floors[0] ? : 1;
        if (cld->minimax_output <= cld->safe_output)
                cld->minimax_output = cld->safe_output + 1;
 }