dvfs: tegra: Add relationship between CPU and GPU
Anshul Jain [Fri, 6 Nov 2015 22:56:39 +0000 (14:56 -0800)]
Darcy board has same GPU and CPU rail, that means CL DVFS should adjust
Vmin based on the requirement from GPU DVFS.

This change adds relationship between CPU and GPU, GPU DVFS can now call
a set function in CL_DVFS that can force the new voltage requirement on
CL_DVFS.

Bug 1669968

Change-Id: I9b4727fa2cc43874ea21574567463017730cd605
Signed-off-by: Anshul Jain <anshulj@nvidia.com>
Reviewed-on: http://git-master/r/828694
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Vinayak Pane <vpane@nvidia.com>

drivers/platform/tegra/tegra21_dvfs.c
drivers/platform/tegra/tegra_cl_dvfs.c
include/linux/platform/tegra/dvfs.h

index 585b5d5..ea630da 100644 (file)
@@ -132,6 +132,8 @@ static struct dvfs_rail *tegra21_dvfs_rails[] = {
        &tegra21_dvfs_rail_vdd_gpu,
 };
 
+static struct clk *dfll_clk;
+
 /* FIXME: Remove after bringup */
 #define BRINGUP_CVB_V_MARGIN   25
 #define BRINGUP_CVB_V_MARGIN_EX        5
@@ -192,6 +194,32 @@ static struct dvfs_rail *tegra21_dvfs_rails[] = {
                { 0,       { }, { }, }, \
        }
 
+/*
+ * This function solves the relationship between CPU and GPU Vmin
+ * and sets the new Vmin in cl_dvfs.
+ */
+static int tegra21_dvfs_rel_vdd_gpu_vdd_cpu(struct dvfs_rail *vdd_gpu,
+                               struct dvfs_rail *vdd_cpu)
+{
+       int new_cl_dvfs_mv;
+
+       new_cl_dvfs_mv = max(vdd_gpu->millivolts, vdd_gpu->new_millivolts);
+
+       tegra_dvfs_set_rail_relations_dfll_vmin(dfll_clk, new_cl_dvfs_mv);
+
+       return max(vdd_cpu->new_millivolts, new_cl_dvfs_mv);
+}
+
+
+static struct dvfs_relationship tegra21_dvfs_relationships[] = {
+       {
+               .from = &tegra21_dvfs_rail_vdd_gpu,
+               .to = &tegra21_dvfs_rail_vdd_cpu,
+               .solve = tegra21_dvfs_rel_vdd_gpu_vdd_cpu,
+               .solved_at_nominal = true,
+       },
+};
+
 static struct cpu_cvb_dvfs cpu_cvb_dvfs_table[] = {
        {
                .speedo_id = 6,
@@ -2016,7 +2044,11 @@ void __init tegra21x_init_dvfs(void)
        int gpu_max_freq_index = 0;
        int cpu_max_freq_index = 0;
        int cpu_lp_max_freq_index = 0;
+       bool darcy_sku = false;
 
+#ifdef CONFIG_OF
+       darcy_sku = of_machine_is_compatible("nvidia,darcy");
+#endif
 #ifndef CONFIG_TEGRA_CORE_DVFS
        tegra_dvfs_core_disabled = true;
 #endif
@@ -2073,6 +2105,20 @@ void __init tegra21x_init_dvfs(void)
        tegra_dvfs_init_rails(tegra21_dvfs_rails,
                ARRAY_SIZE(tegra21_dvfs_rails));
 
+       if (darcy_sku) {
+               pr_info("tegra_dvfs: CPU-GPU realtionship for Darcy SKU\n");
+               /* Add DVFS relationships */
+               tegra_dvfs_add_relationships(tegra21_dvfs_relationships,
+                               ARRAY_SIZE(tegra21_dvfs_relationships));
+
+               /* Get handle for dfll clock that will be used for
+                * solving the relationship and setting new Vmin
+                * of CL DVFS */
+               dfll_clk = tegra_get_clock_by_name("dfll_cpu");
+               if (!dfll_clk)
+                       pr_err("DVFS:%s: dfll cpu clock is NULL!", __func__);
+       }
+
        /* Search core dvfs table for speedo/process matching entries and
           initialize dvfs-ed clocks */
        if (!tegra_platform_is_linsim()) {
index d4d1ee7..c6606f9 100644 (file)
@@ -235,6 +235,7 @@ struct tegra_cl_dvfs {
        struct voltage_reg_map          *out_map[MAX_CL_DVFS_VOLTAGES];
        u8                              num_voltages;
        u8                              safe_output;
+       u8                              rail_relations_out_min;
 
        u32                             tune0_low;
        u32                             tune0_high;
@@ -710,7 +711,9 @@ static inline u8 get_output_min(struct tegra_cl_dvfs *cld)
        if (cld->therm_floor_idx < cld->therm_floors_num)
                thermal_min = cld->thermal_out_floors[cld->therm_floor_idx];
 
-       return max(tune_min, thermal_min);
+       /* return max of all the possible output min settings */
+       return max_t(u8, max(tune_min, thermal_min),
+                                       cld->rail_relations_out_min);
 }
 
 static inline void _load_lut(struct tegra_cl_dvfs *cld)
@@ -3278,6 +3281,43 @@ int tegra_dvfs_clamp_dfll_at_vmin(struct clk *dfll_clk, bool clamp)
 }
 EXPORT_SYMBOL(tegra_dvfs_clamp_dfll_at_vmin);
 
+/*
+ * Get the new Vmin setting from external rail that is connected to same CPU
+ * regulator.
+ */
+int tegra_dvfs_set_rail_relations_dfll_vmin(struct clk *dfll_clk,
+                                               int rail_relations_vmin)
+{
+       struct tegra_cl_dvfs *cld;
+       unsigned long flags;
+       u8 rail_relations_out_min;
+
+       if (!dfll_clk)
+               return -EINVAL;
+
+       /* get handle to cl_dvfs from dfll_clk */
+       cld = tegra_dfll_get_cl_dvfs_data(dfll_clk);
+       if (IS_ERR(cld))
+               return PTR_ERR(cld);
+
+       clk_lock_save(cld->dfll_clk, &flags);
+
+       /* convert mv to output value of cl_dvfs */
+       rail_relations_out_min = find_mv_out_cap(cld, rail_relations_vmin);
+
+       if (cld->rail_relations_out_min != rail_relations_out_min) {
+               cld->rail_relations_out_min = rail_relations_out_min;
+               if (cld->mode == TEGRA_CL_DVFS_CLOSED_LOOP) {
+                       tegra_cl_dvfs_request_rate(cld,
+                               tegra_cl_dvfs_request_get(cld));
+                       /* Delay to make sure new Vmin delivery started */
+                       udelay(2 * GET_SAMPLE_PERIOD(cld));
+               }
+       }
+       clk_unlock_restore(cld->dfll_clk, &flags);
+       return 0;
+}
+
 #ifdef CONFIG_DEBUG_FS
 
 static int lock_get(void *data, u64 *val)
index 8183bd2..1edfdb7 100644 (file)
@@ -431,6 +431,8 @@ static inline int tegra_dvfs_rail_get_override_floor(struct dvfs_rail *rail)
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
 int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail);
 int tegra_dvfs_rail_post_enable(struct dvfs_rail *rail);
+int tegra_dvfs_set_rail_relations_dfll_vmin(struct clk *dfll_clk,
+                                                       int external_vmin);
 #else
 static inline int tegra_dvfs_rail_disable_prepare(struct dvfs_rail *rail)
 { return 0; }