ARM: tegra13: dvfs: Add GPU SiMon offsets
Alex Frid [Wed, 18 Jun 2014 22:02:24 +0000 (15:02 -0700)]
Added GPU Vmin -20mV offset for high SiMon grade on Tegra13 platforms.
Constructed the respective GPU DVFS table with offsets applied, and
SiMon notifier to switch between tables w/wo offset. Since no SiMon
grading is available only original DVFS table with no offset is used
for now.

Bug 1511506

Change-Id: I959ed2142e478b9693a5bc425ef2165b43210bab
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/425035
Reviewed-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Tested-by: Sai Gurrappadi <sgurrappadi@nvidia.com>
Reviewed-by: Thomas Cherry <tcherry@nvidia.com>

arch/arm/mach-tegra/tegra13_dvfs.c

index cb2ca33..8fff7b6 100644 (file)
@@ -44,6 +44,7 @@ static bool tegra_dvfs_gpu_disabled;
 #define VDD_SAFE_STEP                  100
 
 static int cpu_vmin_offsets[] = { 0, -20, };
+static int gpu_vmin_offsets[] = { 0, -20, };
 
 static int vdd_core_vmin_trips_table[MAX_THERMAL_LIMITS] = { 20, };
 static int vdd_core_therm_floors_table[MAX_THERMAL_LIMITS] = { 950, };
@@ -116,6 +117,7 @@ static struct dvfs_rail tegra13_dvfs_rail_vdd_gpu = {
        .version = "p4_v10",
        .max_millivolts = 1350,
        .min_millivolts = 650,
+       .simon_domain = TEGRA_SIMON_DOMAIN_GPU,
        .step = VDD_SAFE_STEP,
        .step_up = 1350,
        .in_band_pm = true,
@@ -501,12 +503,15 @@ static struct gpu_cvb_dvfs gpu_cvb_dvfs_table[] = {
 static int gpu_vmin[MAX_THERMAL_RANGES];
 static int gpu_peak_millivolts[MAX_DVFS_FREQS];
 static int gpu_millivolts[MAX_THERMAL_RANGES][MAX_DVFS_FREQS];
+static int gpu_millivolts_offs[MAX_THERMAL_RANGES][MAX_DVFS_FREQS];
 static struct dvfs gpu_dvfs = {
        .clk_name       = "gbus",
        .auto_dvfs      = true,
        .dvfs_rail      = &tegra13_dvfs_rail_vdd_gpu,
 };
 
+static struct notifier_block gpu_simon_grade_nb;
+
 int tegra_dvfs_disable_core_set(const char *arg, const struct kernel_param *kp)
 {
        int ret;
@@ -936,7 +941,7 @@ static int __init set_cpu_dvfs_data(unsigned long max_freq,
 static int __init set_gpu_dvfs_data(unsigned long max_freq,
        struct gpu_cvb_dvfs *d, struct dvfs *gpu_dvfs, int *max_freq_index)
 {
-       int i, j, thermal_ranges, mv;
+       int i, j, thermal_ranges, simon_offs, mv;
        struct cvb_dvfs_table *table = NULL;
        int speedo = tegra_gpu_speedo_value();
        struct dvfs_rail *rail = &tegra13_dvfs_rail_vdd_gpu;
@@ -944,6 +949,11 @@ static int __init set_gpu_dvfs_data(unsigned long max_freq,
 
        d->max_mv = round_voltage(d->max_mv, align, false);
 
+       /* Init gpu Vmin SiMon offsets (Tegra13 has exactly 2 offsests) */
+       BUILD_BUG_ON(ARRAY_SIZE(gpu_vmin_offsets) != 2);
+       tegra_dvfs_rail_init_simon_vmin_offsets(gpu_vmin_offsets, 2, rail);
+       simon_offs = rail->simon_vmin_offsets ? rail->simon_vmin_offsets[1] : 0;
+
        /*
         * Init thermal trips, find number of thermal ranges; note that the
         * first trip-point is used for voltage calculations within the lowest
@@ -977,6 +987,16 @@ static int __init set_gpu_dvfs_data(unsigned long max_freq,
                             mvj, rail->min_millivolts);
                        mvj = rail->min_millivolts;
                }
+
+               /* check Vmin SiMon offset: ignore SiMon if it pushes too low */
+               if (mvj + simon_offs < rail->min_millivolts) {
+                       WARN(1, "tegra13_dvfs: gpu simon min %dmV below rail min %dmV\n",
+                            mvj + simon_offs, rail->min_millivolts);
+                       rail->simon_vmin_offsets = NULL;
+                       rail->simon_vmin_offs_num = 0;
+                       simon_offs = 0;
+               }
+
                gpu_vmin[j] = mvj;
        }
 
@@ -994,7 +1014,7 @@ static int __init set_gpu_dvfs_data(unsigned long max_freq,
                        speedo, d->speedo_scale, &table->cvb_pll_param);
 
                for (j = 0; j < thermal_ranges; j++) {
-                       int mvj = mv;
+                       int mvj_offs, mvj = mv;
                        int t = rail->vts_cdev->trip_temperatures[j];
 
                        /* get thermal offset for this trip-point */
@@ -1003,6 +1023,7 @@ static int __init set_gpu_dvfs_data(unsigned long max_freq,
                        mvj = round_cvb_voltage(mvj, d->voltage_scale, align);
 
                        /* clip to minimum, abort if above maximum */
+                       mvj_offs = max(mvj, gpu_vmin[j] + simon_offs);
                        mvj = max(mvj, gpu_vmin[j]);
                        if (mvj > d->max_mv)
                                break;
@@ -1012,6 +1033,10 @@ static int __init set_gpu_dvfs_data(unsigned long max_freq,
                        gpu_millivolts[j][i] = mvj;
                        if (j && (gpu_millivolts[j-1][i] < mvj))
                                gpu_millivolts[j-1][i] = mvj;
+
+                       gpu_millivolts_offs[j][i] = mvj_offs;
+                       if (j && (gpu_millivolts_offs[j-1][i] < mvj_offs))
+                               gpu_millivolts_offs[j-1][i] = mvj_offs;
                }
                /* Make sure all voltages for this frequency are below max */
                if (j < thermal_ranges)
@@ -1052,6 +1077,50 @@ static int __init set_gpu_dvfs_data(unsigned long max_freq,
        return 0;
 }
 
+static int gpu_simon_grade_notify_cb(struct notifier_block *nb,
+                                    unsigned long grade, void *v)
+{
+       struct dvfs_rail *rail = &tegra13_dvfs_rail_vdd_gpu;
+       int curr_domain = (int)((long)v);
+       int ret;
+
+       if (curr_domain != rail->simon_domain)
+               return NOTIFY_DONE;
+
+       /* Only 2 grades are supported; both voltage tables must be valid */
+       ret = tegra_dvfs_replace_voltage_table(&gpu_dvfs,
+               grade ? &gpu_millivolts_offs[0][0] : &gpu_millivolts[0][0]);
+
+       if (!WARN_ON(ret == -EINVAL))
+               pr_info("tegra_dvfs: set %s simon grade %lu\n",
+                       rail->reg_id, grade);
+
+       return NOTIFY_OK;
+};
+
+static int __init tegra13_register_gpu_simon_notifier(void)
+{
+       int ret;
+       struct dvfs_rail *rail = &tegra13_dvfs_rail_vdd_gpu;
+
+       /* Stay at default if no simon offsets or thermal dvfs is broken */
+       if (!gpu_dvfs.therm_dvfs || !rail->simon_vmin_offsets)
+               return 0;
+
+       gpu_simon_grade_nb.notifier_call = gpu_simon_grade_notify_cb;
+
+       ret = tegra_register_simon_notifier(&gpu_simon_grade_nb);
+       if (ret) {
+               pr_err("tegra13_dvfs: failed to register %s simon notifier\n",
+                      rail->reg_id);
+               return ret;
+       }
+
+       pr_info("tegra dvfs: registered %s simon notifier\n", rail->reg_id);
+       return 0;
+}
+late_initcall(tegra13_register_gpu_simon_notifier);
+
 static int __init get_core_nominal_mv_index(int speedo_id)
 {
        int i;