gpu: nvgpu: Add GPCPLL DVFS state to debug prints
[linux-3.10.git] / drivers / gpu / nvgpu / gm20b / clk_gm20b.c
index adf5136..759f9f2 100644 (file)
 #include "hw_trim_gm20b.h"
 #include "hw_timer_gm20b.h"
 #include "hw_therm_gm20b.h"
+#include "hw_fuse_gm20b.h"
 #include "clk_gm20b.h"
 
-#define ALLOW_NON_CALIBRATED_NA_MODE   1
-
 #define gk20a_dbg_clk(fmt, arg...) \
        gk20a_dbg(gpu_dbg_clk, fmt, ##arg)
 
@@ -43,6 +42,9 @@
 #define BOOT_GPU_UV    1000000 /* gpu rail boot voltage 1.0V */
 #define ADC_SLOPE_UV   10000   /* default ADC detection slope 10mV */
 
+#define DVFS_SAFE_MARGIN       10      /* 10% */
+static unsigned long dvfs_safe_max_freq;
+
 static struct pll_parms gpc_pll_params = {
        128000,  2600000,       /* freq */
        1300000, 2600000,       /* vco */
@@ -50,7 +52,9 @@ static struct pll_parms gpc_pll_params = {
        1, 255,                 /* M */
        8, 255,                 /* N */
        1, 31,                  /* PL */
-       -58700, 86789,          /* DFS_COEFF */
+       -165230, 214007,        /* DFS_COEFF */
+       0, 0,                   /* ADC char coeff - to be read from fuses */
+       0x7 << 3,               /* vco control in NA mode */
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -222,7 +226,7 @@ found_match:
 
        *target_freq = pll->freq;
 
-       gk20a_dbg_clk("actual target freq %d MHz, M %d, N %d, PL %d(div%d)",
+       gk20a_dbg_clk("actual target freq %d kHz, M %d, N %d, PL %d(div%d)",
                *target_freq, pll->M, pll->N, pll->PL, pl_to_div(pll->PL));
 
        gk20a_dbg_fn("done");
@@ -297,10 +301,11 @@ static void clk_config_dvfs_ndiv(int mv, u32 n_eff, struct na_dvfs *d)
        u32 rem, rem_range;
        struct pll_parms *p = &gpc_pll_params;
 
-       det_delta = (mv * 1000 - d->uv_cal);
-       det_delta = min(det_delta, d->dfs_det_max * p->uvdet_slope);
+       det_delta = DIV_ROUND_CLOSEST(mv * 1000 - p->uvdet_offs,
+                                     p->uvdet_slope);
+       det_delta -= d->dfs_ext_cal;
+       det_delta = min(det_delta, d->dfs_det_max);
        det_delta = det_delta * d->dfs_coeff;
-       det_delta = DIV_ROUND_CLOSEST(det_delta, p->uvdet_slope);
 
        n = (int)(n_eff << DFS_DET_RANGE) - det_delta;
        BUG_ON((n < 0) || (n > (p->max_N << DFS_DET_RANGE)));
@@ -408,13 +413,19 @@ static int clk_enbale_pll_dvfs(struct gk20a *g)
        struct pll_parms *p = &gpc_pll_params;
        bool calibrated = p->uvdet_slope && p->uvdet_offs;
 
-       /* FIXME: Set VCO_CTRL */
-
        /* Enable NA DVFS */
        data = gk20a_readl(g, trim_sys_gpcpll_dvfs1_r());
        data |= trim_sys_gpcpll_dvfs1_en_dfs_m();
        gk20a_writel(g, trim_sys_gpcpll_dvfs1_r(), data);
 
+       /* Set VCO_CTRL */
+       if (p->vco_ctrl) {
+               data = gk20a_readl(g, trim_sys_gpcpll_cfg3_r());
+               data = set_field(data, trim_sys_gpcpll_cfg3_vco_ctrl_m(),
+                                trim_sys_gpcpll_cfg3_vco_ctrl_f(p->vco_ctrl));
+               gk20a_writel(g, trim_sys_gpcpll_cfg3_r(), data);
+       }
+
        /*
         * If calibration parameters are known (either from fuses, or from
         * internal calibration on boot) - use them. Internal calibration is
@@ -743,7 +754,14 @@ pll_locked:
        return 0;
 }
 
-/* GPCPLL programming in legacy (non-DVFS) mode */
+/*
+ *  Change GPCPLL frequency:
+ *  - in legacy (non-DVFS) mode
+ *  - in DVFS mode at constant DVFS detection settings, matching current/lower
+ *    voltage; the same procedure can be used in this case, since maximum DVFS
+ *    detection limit makes sure that PLL output remains under F/V curve when
+ *    voltage increases arbitrary.
+ */
 static int clk_program_gpc_pll(struct gk20a *g, struct pll *gpll_new,
                        int allow_slide)
 {
@@ -868,17 +886,122 @@ set_pldiv:
        return clk_slide_gpc_pll(g, gpll_new);
 }
 
-/* GPCPLL programming in DVFS mode */
+/* Find GPCPLL config safe at DVFS coefficient = 0, matching target frequency */
+static void clk_config_pll_safe_dvfs(struct gk20a *g, struct pll *gpll)
+{
+       u32 nsafe, nmin;
+
+       if (gpll->freq > dvfs_safe_max_freq)
+               gpll->freq = gpll->freq * (100 - DVFS_SAFE_MARGIN) / 100;
+
+       nmin = DIV_ROUND_UP(gpll->M * gpc_pll_params.min_vco, gpll->clk_in);
+       nsafe = gpll->M * gpll->freq / gpll->clk_in;
+
+       /*
+        * If safe frequency is above VCOmin, it can be used in safe PLL config
+        * as is. Since safe frequency is below both old and new frequencies,
+        * in this case all three configurations have same post divider 1:1, and
+        * direct old=>safe=>new n-sliding will be used for transitions.
+        *
+        * Otherwise, if safe frequency is below VCO min, post-divider in safe
+        * configuration (and possibly in old and/or new configurations) is
+        * above 1:1, and each old=>safe and safe=>new transitions includes
+        * sliding to/from VCOmin, as well as divider changes. To avoid extra
+        * dynamic ramps from VCOmin during old=>safe transition and to VCOmin
+        * during safe=>new transition, select nmin as safe NDIV, and set safe
+        * post divider to assure PLL output is below safe frequency
+        */
+       if (nsafe < nmin) {
+               gpll->PL = DIV_ROUND_UP(nmin * gpll->clk_in,
+                                       gpll->M * gpll->freq);
+               nsafe = nmin;
+       }
+       gpll->N = nsafe;
+       clk_config_dvfs_ndiv(gpll->dvfs.mv, gpll->N, &gpll->dvfs);
+
+       gk20a_dbg_clk("safe freq %d kHz, M %d, N %d, PL %d(div%d), mV(cal) %d(%d), DC %d",
+               gpll->freq, gpll->M, gpll->N, gpll->PL, pl_to_div(gpll->PL),
+               gpll->dvfs.mv, gpll->dvfs.uv_cal / 1000, gpll->dvfs.dfs_coeff);
+}
+
+/* Change GPCPLL frequency and DVFS detection settings in DVFS mode */
 static int clk_program_na_gpc_pll(struct gk20a *g, struct pll *gpll_new,
                                  int allow_slide)
 {
+       int ret;
+       struct pll gpll_safe;
+       struct pll *gpll_old = &g->clk.gpc_pll_last;
+
+       BUG_ON(gpll_new->M != 1);       /* the only MDIV in NA mode  */
        clk_config_dvfs(g, gpll_new);
 
-       if (!gpll_new->enabled)
+       /*
+        * In cases below no intermediate steps in PLL DVFS configuration are
+        * necessary because either
+        * - PLL DVFS will be configured under bypass directly to target, or
+        * - voltage is not changing, so DVFS detection settings are the same
+        */
+       if (!allow_slide || !gpll_new->enabled ||
+           (gpll_old->dvfs.mv == gpll_new->dvfs.mv))
                return clk_program_gpc_pll(g, gpll_new, allow_slide);
 
-       /* always under bypass, for now */
-       return clk_program_gpc_pll(g, gpll_new, 0);
+       /*
+        * Interim step for changing DVFS detection settings: low enough
+        * frequency to be safe at at DVFS coeff = 0.
+        *
+        * 1. If voltage is increasing:
+        * - safe frequency target matches the lowest - old - frequency
+        * - DVFS settings are still old
+        * - Voltage already increased to new level by tegra DVFS, but maximum
+        *    detection limit assures PLL output remains under F/V curve
+        *
+        * 2. If voltage is decreasing:
+        * - safe frequency target matches the lowest - new - frequency
+        * - DVFS settings are still old
+        * - Voltage is also old, it will be lowered by tegra DVFS afterwards
+        *
+        * Interim step can be skipped if old frequency is below safe minimum,
+        * i.e., it is low enough to be safe at any voltage in operating range
+        * with zero DVFS coefficient.
+        */
+       if (gpll_old->freq > dvfs_safe_max_freq) {
+               if (gpll_old->dvfs.mv < gpll_new->dvfs.mv) {
+                       gpll_safe = *gpll_old;
+                       gpll_safe.dvfs.mv = gpll_new->dvfs.mv;
+               } else {
+                       gpll_safe = *gpll_new;
+                       gpll_safe.dvfs = gpll_old->dvfs;
+               }
+               clk_config_pll_safe_dvfs(g, &gpll_safe);
+
+               ret = clk_program_gpc_pll(g, &gpll_safe, 1);
+               if (ret) {
+                       gk20a_err(dev_from_gk20a(g), "Safe dvfs program fail\n");
+                       return ret;
+               }
+       }
+
+       /*
+        * DVFS detection settings transition:
+        * - Set DVFS coefficient zero (safe, since already at frequency safe
+        *   at DVFS coeff = 0 for the lowest of the old/new end-points)
+        * - Set calibration level to new voltage (safe, since DVFS coeff = 0)
+        * - Set DVFS coefficient to match new voltage (safe, since already at
+        *   frequency safe at DVFS coeff = 0 for the lowest of the old/new
+        *   end-points.
+        */
+       clk_set_dfs_coeff(g, 0);
+       clk_set_dfs_ext_cal(g, gpll_new->dvfs.dfs_ext_cal);
+       clk_set_dfs_coeff(g, gpll_new->dvfs.dfs_coeff);
+
+       gk20a_dbg_clk("config_pll  %d kHz, M %d, N %d, PL %d(div%d), mV(cal) %d(%d), DC %d",
+               gpll_new->freq, gpll_new->M, gpll_new->N, gpll_new->PL,
+               pl_to_div(gpll_new->PL),
+               max(gpll_new->dvfs.mv, gpll_old->dvfs.mv),
+               gpll_new->dvfs.uv_cal / 1000, gpll_new->dvfs.dfs_coeff);
+
+       /* Finally set target rate (with DVFS detection settings already new) */
+       return clk_program_gpc_pll(g, gpll_new, 1);
 }
 
 static int clk_disable_gpcpll(struct gk20a *g, int allow_slide)
@@ -949,10 +1072,8 @@ struct clk *gm20b_clk_get(struct gk20a *g)
 static int gm20b_init_clk_setup_sw(struct gk20a *g)
 {
        struct clk_gk20a *clk = &g->clk;
-       static int initialized;
+       unsigned long safe_rate;
        struct clk *ref;
-       unsigned long ref_rate;
-       bool calibrated;
 
        gk20a_dbg_fn("");
 
@@ -964,13 +1085,17 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
        if (!gk20a_clk_get(g))
                return -EINVAL;
 
+       /*
+        * On Tegra GPU clock exposed to frequency governor is a shared user on
+        * GPCPLL bus (gbus). The latter can be accessed as GPU clock parent.
+        * Respectively the grandparent is PLL reference clock.
+        */
        ref = clk_get_parent(clk_get_parent(clk->tegra_clk));
        if (IS_ERR(ref)) {
                gk20a_err(dev_from_gk20a(g),
                        "failed to get GPCPLL reference clock");
                return -EINVAL;
        }
-       ref_rate = clk_get_rate(ref);
 
        /*
         * Locking time in both legacy and DVFS mode is 40us. However, in legacy
@@ -982,27 +1107,37 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
        clk->na_pll_delay = 40; /* usec*/
 
        clk->gpc_pll.id = GK20A_GPC_PLL;
-       clk->gpc_pll.clk_in = ref_rate / KHZ;
-
-       /* Initial frequency: 1/3 VCO min (low enough to be safe at Vmin) */
-       if (!initialized) {
-               initialized = 1;
-               clk->gpc_pll.M = 1;
-               clk->gpc_pll.N = DIV_ROUND_UP(gpc_pll_params.min_vco,
-                                       clk->gpc_pll.clk_in);
-               clk->gpc_pll.PL = 3;
-               clk->gpc_pll.freq = clk->gpc_pll.clk_in * clk->gpc_pll.N;
-               clk->gpc_pll.freq /= pl_to_div(clk->gpc_pll.PL);
-       }
-
-       calibrated = !clk_config_calibration_params(g);
+       clk->gpc_pll.clk_in = clk_get_rate(ref) / KHZ;
+
+       safe_rate = tegra_dvfs_get_fmax_at_vmin_safe_t(
+               clk_get_parent(clk->tegra_clk));
+       safe_rate = safe_rate * (100 - DVFS_SAFE_MARGIN) / 100;
+       dvfs_safe_max_freq = rate_gpu_to_gpc2clk(safe_rate);
+       clk->gpc_pll.PL = (dvfs_safe_max_freq == 0) ? 0 :
+               DIV_ROUND_UP(gpc_pll_params.min_vco, dvfs_safe_max_freq);
+
+       /* Initial freq: low enough to be safe at Vmin (default 1/3 VCO min) */
+       clk->gpc_pll.M = 1;
+       clk->gpc_pll.N = DIV_ROUND_UP(gpc_pll_params.min_vco,
+                               clk->gpc_pll.clk_in);
+       clk->gpc_pll.PL = max(clk->gpc_pll.PL, 3U);
+       clk->gpc_pll.freq = clk->gpc_pll.clk_in * clk->gpc_pll.N;
+       clk->gpc_pll.freq /= pl_to_div(clk->gpc_pll.PL);
+
+        /*
+         * All production parts should have ADC fuses burnt. Therefore, check
+         * ADC fuses always, regardless of whether NA mode is selected; and if
+         * NA mode is indeed selected, and part can support it, switch to NA
+         * mode even when ADC calibration is not fused; less accurate s/w
+         * self-calibration will be used for those parts.
+         */
+       clk_config_calibration_params(g);
 #ifdef CONFIG_TEGRA_USE_NA_GPCPLL
-       if (ALLOW_NON_CALIBRATED_NA_MODE || calibrated) {
+       if (tegra_fuse_can_use_na_gpcpll()) {
                /* NA mode is supported only at max update rate 38.4 MHz */
-               if (clk->gpc_pll.clk_in == gpc_pll_params.max_u) {
-                       clk->gpc_pll.mode = GPC_PLL_MODE_DVFS;
-                       gpc_pll_params.min_u = gpc_pll_params.max_u;
-               }
+               BUG_ON(clk->gpc_pll.clk_in != gpc_pll_params.max_u);
+               clk->gpc_pll.mode = GPC_PLL_MODE_DVFS;
+               gpc_pll_params.min_u = gpc_pll_params.max_u;
        }
 #endif
 
@@ -1011,6 +1146,9 @@ static int gm20b_init_clk_setup_sw(struct gk20a *g)
        clk->sw_ready = true;
 
        gk20a_dbg_fn("done");
+       pr_info("gm20b gpu.0 GPCPLL initial settings:%s M=%u, N=%u, P=%u\n",
+               clk->gpc_pll.mode == GPC_PLL_MODE_DVFS ? " NA mode," : "",
+               clk->gpc_pll.M, clk->gpc_pll.N, clk->gpc_pll.PL);
        return 0;
 }
 
@@ -1040,6 +1178,19 @@ static int gm20b_init_clk_setup_hw(struct gk20a *g)
                         trim_sys_bypassctrl_gpcpll_vco_f());
        gk20a_writel(g, trim_sys_bypassctrl_r(), data);
 
+       /* If not fused, set RAM SVOP PDP data 0x2, and enable fuse override */
+       data = gk20a_readl(g, fuse_ctrl_opt_ram_svop_pdp_r());
+       if (!fuse_ctrl_opt_ram_svop_pdp_data_v(data)) {
+               data = set_field(data, fuse_ctrl_opt_ram_svop_pdp_data_m(),
+                        fuse_ctrl_opt_ram_svop_pdp_data_f(0x2));
+               gk20a_writel(g, fuse_ctrl_opt_ram_svop_pdp_r(), data);
+               data = gk20a_readl(g, fuse_ctrl_opt_ram_svop_pdp_override_r());
+               data = set_field(data,
+                       fuse_ctrl_opt_ram_svop_pdp_override_data_m(),
+                       fuse_ctrl_opt_ram_svop_pdp_override_data_yes_f());
+               gk20a_writel(g, fuse_ctrl_opt_ram_svop_pdp_override_r(), data);
+       }
+
        /* Disable idle slow down */
        data = gk20a_readl(g, therm_clk_slowdown_r(0));
        data = set_field(data, therm_clk_slowdown_idle_factor_m(),
@@ -1462,6 +1613,26 @@ static int monitor_get(void *data, u64 *val)
 }
 DEFINE_SIMPLE_ATTRIBUTE(monitor_fops, monitor_get, NULL, "%llu\n");
 
+static int pll_param_show(struct seq_file *s, void *data)
+{
+       seq_printf(s, "ADC offs = %d uV, ADC slope = %d uV, VCO ctrl = 0x%x\n",
+                  gpc_pll_params.uvdet_offs, gpc_pll_params.uvdet_slope,
+                  gpc_pll_params.vco_ctrl);
+       return 0;
+}
+
+static int pll_param_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, pll_param_show, inode->i_private);
+}
+
+static const struct file_operations pll_param_fops = {
+       .open           = pll_param_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static int clk_gm20b_debugfs_init(struct gk20a *g)
 {
        struct dentry *d;
@@ -1487,6 +1658,16 @@ static int clk_gm20b_debugfs_init(struct gk20a *g)
        if (!d)
                goto err_out;
 
+       d = debugfs_create_file(
+               "pll_param", S_IRUGO, platform->debugfs, g, &pll_param_fops);
+       if (!d)
+               goto err_out;
+
+       d = debugfs_create_u32("pll_na_mode", S_IRUGO, platform->debugfs,
+                              (u32 *)&g->clk.gpc_pll.mode);
+       if (!d)
+               goto err_out;
+
        return 0;
 
 err_out: