ARM: tegra11: dvfs: Tune CL-DVFS target module trimmers
Alex Frid [Wed, 23 Jan 2013 04:34:11 +0000 (20:34 -0800)]
Added mechanism to tune CL-DVFS target module trimmers (along with
CL-DVFS settings) when low/high voltage range boundary is crossed.
Updated CPU clock trimmers on Tegra11 per characterization results.

Bug 1223242

Change-Id: If44a19bd8406e7c109b59b0c9e01182adcd591cb
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/193286
(cherry picked from commit 388dd4977d0680bfa0e4d06cf4c14fbbeded6f7a)
Reviewed-on: http://git-master/r/205790
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>

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

index 1deb20b..a371497 100644 (file)
@@ -104,6 +104,7 @@ struct dvfs_dfll_data {
        int tune_high_min_millivolts;
        int min_millivolts;
        enum dfll_range range;
+       void (*tune_trimmers)(bool trim_high);
 };
 
 struct dvfs {
@@ -278,6 +279,12 @@ static inline void tegra_dvfs_rail_mode_updating(struct dvfs_rail *rail,
                rail->dfll_mode_updating = updating;
 }
 
+static inline void tegra_dvfs_set_dfll_tune_trimmers(
+       struct dvfs *d, void (*tune_trimmers)(bool trim_high))
+{
+       d->dfll_data.tune_trimmers = tune_trimmers;
+}
+
 static inline int tegra_dvfs_rail_get_nominal_millivolts(struct dvfs_rail *rail)
 {
        if (rail)
index c07cd1e..d310b81 100644 (file)
 #define USB_PLLS_USE_LOCKDET                   (1<<6)
 #define USB_PLLS_ENABLE_SWCTL                  ((1<<2) | (1<<0))
 
+/* CPU clock trimmers */
+#define CPU_FINETRIM_BYP                       0x4d0
+#define CPU_FINETRIM_SELECT                    0x4d4
+#define CPU_FINETRIM_DR                                0x4d8
+#define CPU_FINETRIM_DF                                0x4dc
+#define CPU_FINETRIM_F                         0x4e0
+#define CPU_FINETRIM_R                         0x4e4
+
 /* DFLL */
 #define DFLL_BASE                              0x2f4
 #define DFLL_BASE_RESET                                (1<<0)
@@ -3468,11 +3476,33 @@ static struct clk_ops tegra_plle_ops = {
 
 #ifdef CONFIG_ARCH_TEGRA_HAS_CL_DVFS
 /* DFLL operations */
+static void tune_cpu_trimmers(bool trim_high)
+{
+       if (trim_high) {
+               clk_writel(0, CPU_FINETRIM_SELECT);
+               clk_writel(0, CPU_FINETRIM_DR);
+               clk_writel(0, CPU_FINETRIM_R);
+       } else {
+               clk_writel(0x3F, CPU_FINETRIM_SELECT);
+               clk_writel(0x3F, CPU_FINETRIM_DR);
+               clk_writel(0xFFF, CPU_FINETRIM_R);
+       }
+       wmb();
+       clk_readl(CPU_FINETRIM_R);
+}
+
 static void __init tegra11_dfll_cpu_late_init(struct clk *c)
 {
 #ifdef CONFIG_ARCH_TEGRA_HAS_CL_DVFS
        int ret;
-       struct clk *cpu = tegra_get_clock_by_name("cpu");
+       struct clk *cpu = tegra_get_clock_by_name("cpu_g");
+
+       if (!cpu || !cpu->dvfs) {
+               pr_err("%s: CPU dvfs is not present\n", __func__);
+               return;
+       }
+       if (cpu->dvfs->speedo_id > 0)   /* A01P and above parts */
+               tegra_dvfs_set_dfll_tune_trimmers(cpu->dvfs, tune_cpu_trimmers);
 
 #ifdef CONFIG_TEGRA_FPGA_PLATFORM
        u32 netlist, patchid;
@@ -3492,7 +3522,7 @@ static void __init tegra11_dfll_cpu_late_init(struct clk *c)
                c->u.dfll.cl_dvfs = platform_get_drvdata(&tegra_cl_dvfs_device);
 
                use_dfll = CONFIG_TEGRA_USE_DFLL_RANGE;
-               tegra_dvfs_set_dfll_range(cpu->parent->dvfs, use_dfll);
+               tegra_dvfs_set_dfll_range(cpu->dvfs, use_dfll);
                pr_info("Tegra CPU DFLL is initialized\n");
        }
 #endif
index 0694125..97df1f2 100644 (file)
@@ -407,6 +407,23 @@ static void cl_dvfs_load_lut(struct tegra_cl_dvfs *cld)
                pr_debug("%s: set tune state %d\n", __func__, state);   \
        } while (0)
 
+static inline void tune_low(struct tegra_cl_dvfs *cld)
+{
+       if (cld->safe_dvfs->dfll_data.tune_trimmers)
+               cld->safe_dvfs->dfll_data.tune_trimmers(false);
+       cl_dvfs_writel(cld, cld->safe_dvfs->dfll_data.tune0, CL_DVFS_TUNE0);
+       cl_dvfs_wmb(cld);
+}
+
+static inline void tune_high(struct tegra_cl_dvfs *cld)
+{
+       cl_dvfs_writel(cld, cld->safe_dvfs->dfll_data.tune0_high_mv,
+                      CL_DVFS_TUNE0);
+       cl_dvfs_wmb(cld);
+       if (cld->safe_dvfs->dfll_data.tune_trimmers)
+               cld->safe_dvfs->dfll_data.tune_trimmers(true);
+}
+
 static void set_ol_config(struct tegra_cl_dvfs *cld)
 {
        u32 val, out_min;
@@ -414,9 +431,7 @@ static void set_ol_config(struct tegra_cl_dvfs *cld)
        /* always tune low (safe) in open loop */
        if (cld->tune_state != TEGRA_CL_DVFS_TUNE_LOW) {
                set_tune_state(cld, TEGRA_CL_DVFS_TUNE_LOW);
-               cl_dvfs_writel(cld, cld->safe_dvfs->dfll_data.tune0,
-                              CL_DVFS_TUNE0);
-               cl_dvfs_wmb(cld);
+               tune_low(cld);
 
                out_min = get_output_min(cld);
 #if CL_DVFS_DYNAMIC_OUTPUT_CFG
@@ -458,9 +473,7 @@ static void set_cl_config(struct tegra_cl_dvfs *cld, struct dfll_rate_req *req)
        case TEGRA_CL_DVFS_TUNE_HIGH_REQUEST:
                if (req->cap <= cld->tune_high_out_start) {
                        set_tune_state(cld, TEGRA_CL_DVFS_TUNE_LOW);
-                       cl_dvfs_writel(cld, cld->safe_dvfs->dfll_data.tune0,
-                                      CL_DVFS_TUNE0);
-                       cl_dvfs_wmb(cld);
+                       tune_low(cld);
                }
                break;
        default:
@@ -514,8 +527,7 @@ static void tune_timer_cb(unsigned long data)
                    (out_min >= cld->tune_high_out_min)) {
                        udelay(CL_DVFS_OUTPUT_RAMP_DELAY);
                        set_tune_state(cld, TEGRA_CL_DVFS_TUNE_HIGH);
-                       val = cld->safe_dvfs->dfll_data.tune0_high_mv;
-                       cl_dvfs_writel(cld, val, CL_DVFS_TUNE0);
+                       tune_high(cld);
                } else {
                        mod_timer(&cld->tune_timer, jiffies + cld->tune_delay);
                }
@@ -791,6 +803,8 @@ static void cl_dvfs_init_cntrl_logic(struct tegra_cl_dvfs *cld)
                (param->cg_scale ? CL_DVFS_PARAMS_CG_SCALE : 0);
        cl_dvfs_writel(cld, val, CL_DVFS_PARAMS);
 
+       if (cld->safe_dvfs->dfll_data.tune_trimmers)
+               cld->safe_dvfs->dfll_data.tune_trimmers(false);
        cl_dvfs_writel(cld, cld->safe_dvfs->dfll_data.tune0, CL_DVFS_TUNE0);
        cl_dvfs_writel(cld, cld->safe_dvfs->dfll_data.tune1, CL_DVFS_TUNE1);