ARM: tegra: power: Update DFLL bypass start/suspend/resume
Alex Frid [Fri, 2 Aug 2013 05:43:18 +0000 (22:43 -0700)]
- Initialized DFLL before legacy dvfs if DFLL bypass is enabled
(reversed common initialization order: first legacy dvfs - then DFLL,
since  DFLL bypass device is used as regulator by legacy dvfs)

- Isolated DFLL output from voltage supply on entry to any state with
CPU cluster powered down (suspend, cluster idle), and resumed normal
operation on exit. This is necessary to avoid unpredictable effect of
DFLL output on CPU voltage during power transitions.

Bug 1310396

Change-Id: Ie42b92633367337ebc08200ab425baaf9043d133
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/257346
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>

arch/arm/mach-tegra/board-ardbeg-power.c
arch/arm/mach-tegra/clock.c
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h

index 5dcbce2..e016549 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/regulator/tps51632-regulator.h>
 #include <linux/regulator/machine.h>
 #include <linux/irq.h>
+#include <linux/gpio.h>
 #include <linux/regulator/tegra-dfll-bypass-regulator.h>
 
 #include <asm/mach-types.h>
@@ -1576,6 +1577,16 @@ static struct tegra_cl_dvfs_platform_data e1735_cl_dvfs_data = {
        .cfg_param = &e1735_cl_dvfs_param,
 };
 
+static void e1735_suspend_dfll_bypass(void)
+{
+       __gpio_set_value(TEGRA_GPIO_PS5, 1); /* tristate external PWM buffer */
+}
+
+static void e1735_resume_dfll_bypass(void)
+{
+       __gpio_set_value(TEGRA_GPIO_PS5, 0); /* enable PWM buffer operations */
+}
+
 static int __init ardbeg_cl_dvfs_init(u16 pmu_board_id)
 {
        struct tegra_cl_dvfs_platform_data *data = NULL;
@@ -1587,6 +1598,10 @@ static int __init ardbeg_cl_dvfs_init(u16 pmu_board_id)
                if (data->u.pmu_pwm.dfll_bypass_dev) {
                        /* this has to be exact to 1uV level from table */
                        e1735_dfll_bypass_init_data.constraints.init_uV = v;
+                       ardbeg_suspend_data.suspend_dfll_bypass =
+                               e1735_suspend_dfll_bypass;
+                       ardbeg_suspend_data.resume_dfll_bypass =
+                               e1735_resume_dfll_bypass;
                } else {
                        (void)e1735_dfll_bypass_dev;
                }
index 0c208b1..4478808 100644 (file)
@@ -1033,9 +1033,23 @@ static int __init tegra_dfll_cpu_start(void)
 
 static int __init tegra_clk_late_init(void)
 {
+#ifdef CONFIG_REGULATOR_TEGRA_DFLL_BYPASS
+       bool init_dfll_first = true;
+#else
+       bool init_dfll_first = false;
+#endif
        tegra_init_disable_boot_clocks(); /* must before dvfs late init */
-       if (!tegra_dvfs_late_init())
-               tegra_dfll_cpu_start(); /* after successful dvfs init only */
+
+       /*
+        * Initialize dfll first if it provides bypass to regulator for legacy
+        * dvfs; otherwise legacy dvfs controls cpu voltage independently, and
+        * initialized before dfll.
+        */
+       if (init_dfll_first)
+               tegra_dfll_cpu_start();
+       if (!tegra_dvfs_late_init() && !init_dfll_first)
+               tegra_dfll_cpu_start();
+
        tegra_sync_cpu_clock();         /* after attempt to get dfll ready */
        tegra_recalculate_cpu_edp_limits();
        return 0;
index ffc9036..7e79ae2 100644 (file)
@@ -343,23 +343,37 @@ unsigned long tegra_min_residency_crail(void)
 }
 #endif
 
-static void suspend_cpu_dfll_mode(void)
+static void suspend_cpu_dfll_mode(unsigned int flags)
 {
 #ifdef CONFIG_ARCH_TEGRA_HAS_CL_DVFS
        /* If DFLL is used as CPU clock source go to open loop mode */
-       if (!is_lp_cluster() && tegra_dfll &&
-           tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail))
-               tegra_clk_cfg_ex(tegra_dfll, TEGRA_CLK_DFLL_LOCK, 0);
+       if (!(flags & TEGRA_POWER_CLUSTER_MASK)) {
+               if (!is_lp_cluster() && tegra_dfll &&
+                   tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail))
+                       tegra_clk_cfg_ex(tegra_dfll, TEGRA_CLK_DFLL_LOCK, 0);
+       }
+
+       /* If defined always suspend dfll bypass (safe cpu rail down) */
+       if (pdata && pdata->suspend_dfll_bypass &&
+           !tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail))
+               pdata->suspend_dfll_bypass();
 #endif
 }
 
-static void resume_cpu_dfll_mode(void)
+static void resume_cpu_dfll_mode(unsigned int flags)
 {
 #ifdef CONFIG_ARCH_TEGRA_HAS_CL_DVFS
+       /* If DFLL is Not used as CPU clock source restore bypass mode */
+       if (pdata && pdata->resume_dfll_bypass && !is_lp_cluster() &&
+           !tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail))
+               pdata->resume_dfll_bypass();
+
        /* If DFLL is used as CPU clock source restore closed loop mode */
-       if (!is_lp_cluster() && tegra_dfll &&
-           tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail))
-               tegra_clk_cfg_ex(tegra_dfll, TEGRA_CLK_DFLL_LOCK, 1);
+       if (!(flags & TEGRA_POWER_CLUSTER_MASK)) {
+               if (!is_lp_cluster() && tegra_dfll &&
+                   tegra_dvfs_rail_is_dfll_mode(tegra_cpu_rail))
+                       tegra_clk_cfg_ex(tegra_dfll, TEGRA_CLK_DFLL_LOCK, 1);
+       }
 #endif
 }
 
@@ -689,6 +703,7 @@ unsigned int tegra_idle_power_down_last(unsigned int sleep_time,
         * We can use clk_get_rate_all_locked() here, because all other cpus
         * are in LP2 state and irqs are disabled
         */
+       suspend_cpu_dfll_mode(flags);
        if (flags & TEGRA_POWER_CLUSTER_MASK) {
                if (is_idle_task(current))
                        trace_nvcpu_cluster_rcuidle(NVPOWER_CPU_CLUSTER_START);
@@ -715,7 +730,6 @@ unsigned int tegra_idle_power_down_last(unsigned int sleep_time,
                }
                tegra_cluster_switch_prolog(flags);
        } else {
-               suspend_cpu_dfll_mode();
                set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
                        clk_get_rate_all_locked(tegra_pclk));
 #if defined(CONFIG_ARCH_TEGRA_HAS_SYMMETRIC_CPU_PWR_GATE)
@@ -826,9 +840,8 @@ unsigned int tegra_idle_power_down_last(unsigned int sleep_time,
                        trace_nvcpu_cluster_rcuidle(NVPOWER_CPU_CLUSTER_DONE);
                else
                        trace_nvcpu_cluster(NVPOWER_CPU_CLUSTER_DONE);
-       } else {
-               resume_cpu_dfll_mode();
        }
+       resume_cpu_dfll_mode(flags);
        tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_epilog);
 
 #if INSTRUMENT_CLUSTER_SWITCH
@@ -1483,7 +1496,7 @@ static struct kobject *suspend_kobj;
 static int tegra_pm_enter_suspend(void)
 {
        pr_info("Entering suspend state %s\n", lp_state[current_suspend_mode]);
-       suspend_cpu_dfll_mode();
+       suspend_cpu_dfll_mode(0);
        if (current_suspend_mode == TEGRA_SUSPEND_LP0)
                tegra_lp0_cpu_mode(true);
        return 0;
@@ -1493,13 +1506,13 @@ static void tegra_pm_enter_resume(void)
 {
        if (current_suspend_mode == TEGRA_SUSPEND_LP0)
                tegra_lp0_cpu_mode(false);
-       resume_cpu_dfll_mode();
+       resume_cpu_dfll_mode(0);
        pr_info("Exited suspend state %s\n", lp_state[current_suspend_mode]);
 }
 
 static void tegra_pm_enter_shutdown(void)
 {
-       suspend_cpu_dfll_mode();
+       suspend_cpu_dfll_mode(0);
        pr_info("Shutting down tegra ...\n");
 }
 
index 7f605f9..bccbde1 100644 (file)
@@ -90,6 +90,9 @@ struct tegra_suspend_platform_data {
        unsigned long min_residency_mc_clk;
        bool usb_vbus_internal_wake; /* support for internal vbus wake */
        bool usb_id_internal_wake; /* support for internal id wake */
+
+       void (*suspend_dfll_bypass)(void);
+       void (*resume_dfll_bypass)(void);
 };
 
 /* clears io dpd settings before kernel code */