ARM: tegra: power: Support CPU rail early startup
Alex Frid [Fri, 16 Aug 2013 02:03:07 +0000 (19:03 -0700)]
Added an option for cluster switch procedure to turn CPU rail ON
via direct access to PMC registers before disabling interrupts,
and then continue scheduler execution while the rail is ramping up.

RAM repair is executed in s/w as well after rail ramp is done. Only
non-CPU partition is power-gated/un-gated by flow controller in the
atomic section. However, rail ramp in this case is serialized with
CPU save context. Hence the trade-off: early startup option reduces
interrupt disabled time during cluster switch, but increases overall
cluster switch time.

Bug 1351735

Change-Id: I5ff9afb2aa6b27b9aa4b2318ee2740dee4908e2f
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/262864
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bo Yan <byan@nvidia.com>

arch/arm/mach-tegra/pm-t3.c
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h

index e71d1d4..6d4e893 100644 (file)
@@ -34,6 +34,7 @@
 #include <mach/io_dpd.h>
 #include <mach/edp.h>
 #include <mach/hardware.h>
+#include <mach/powergate.h>
 
 #include <asm/smp_plat.h>
 #include <asm/cputype.h>
@@ -247,7 +248,8 @@ void tegra_cluster_switch_prolog(unsigned int flags)
            (current_cluster == TEGRA_POWER_CLUSTER_LP))
                reg |= FLOW_CTRL_CSR_ENABLE_EXT_NCPU;
        else if (flags & TEGRA_POWER_CLUSTER_PART_CRAIL)
-               reg |= FLOW_CTRL_CSR_ENABLE_EXT_CRAIL;
+               reg |= tegra_crail_can_start_early() ?
+               FLOW_CTRL_CSR_ENABLE_EXT_NCPU : FLOW_CTRL_CSR_ENABLE_EXT_CRAIL;
 
        if (flags & TEGRA_POWER_CLUSTER_PART_NONCPU)
                reg |= FLOW_CTRL_CSR_ENABLE_EXT_NCPU;
@@ -358,6 +360,11 @@ void tegra_cluster_switch_epilog(unsigned int flags)
        if (!is_lp_cluster()) {
                cluster_switch_epilog_actlr();
                cluster_switch_epilog_gic();
+#if defined(CONFIG_ARCH_TEGRA_HAS_SYMMETRIC_CPU_PWR_GATE)
+       } else  if ((flags & TEGRA_POWER_CLUSTER_PART_CRAIL) &&
+                   tegra_crail_can_start_early()) {
+               tegra_powergate_partition(TEGRA_POWERGATE_CRAIL);
+#endif
        }
 
        /* Disable unused port of PLL_X */
@@ -374,6 +381,52 @@ void tegra_cluster_switch_epilog(unsigned int flags)
        #endif
 }
 
+static int tegra_crail_startup_early(void)
+{
+#if defined(CONFIG_ARCH_TEGRA_HAS_SYMMETRIC_CPU_PWR_GATE)
+       u32 reg;
+       int us = tegra_cpu_power_good_time();
+
+       if (tegra_powergate_is_powered(TEGRA_POWERGATE_CRAIL))
+               return 0;
+
+       /*
+        * Toggle CRAIL, insert s/w  power good delay (load h/w power good
+        * timer with very small settings so it expires for sure within power
+        * gate toggle timeout).
+        */
+       tegra_limit_cpu_power_timers(1, 1);
+       tegra_unpowergate_partition(TEGRA_POWERGATE_CRAIL);
+       if (timekeeping_suspended)
+               udelay(us);                     /* suspend exit */
+       else
+               usleep_range(us, us + 10);      /* regular scheduling */
+
+       if (!tegra_powergate_is_powered(TEGRA_POWERGATE_CRAIL)) {
+               WARN(1, "Failed to turn CRAIL ON in %d us\n", us);
+               return -ETIMEDOUT;
+       }
+
+       /* If needed trigger RAM rapair request in s/w (auto-clear in h/w) */
+       #define RAM_REPAIR_TIMEOUT 500
+
+       reg = readl(FLOW_CTRL_RAM_REPAIR) | FLOW_CTRL_RAM_REPAIR_REQ;
+       if (!(reg & FLOW_CTRL_RAM_REPAIR_BYPASS_EN)) {
+               int ram_repair_time = RAM_REPAIR_TIMEOUT;
+               flowctrl_writel(reg, FLOW_CTRL_RAM_REPAIR);
+               while (readl(FLOW_CTRL_RAM_REPAIR) & FLOW_CTRL_RAM_REPAIR_REQ) {
+                       udelay(1);
+                       if (!(ram_repair_time--)) {
+                               WARN(1, "Failed to repair RAM in %d us\n",
+                                    RAM_REPAIR_TIMEOUT);
+                               return -ETIMEDOUT;
+                       }
+               }
+       }
+#endif
+       return 0;
+}
+
 int tegra_cluster_control(unsigned int us, unsigned int flags)
 {
        static ktime_t last_g2lp;
@@ -409,14 +462,22 @@ int tegra_cluster_control(unsigned int us, unsigned int flags)
                (flags & TEGRA_POWER_CLUSTER_FORCE) ? "force" : "",
                us));
 
-       if (current_cluster != target_cluster && !timekeeping_suspended) {
-               if (target_cluster == TEGRA_POWER_CLUSTER_G) {
+       if ((current_cluster == TEGRA_POWER_CLUSTER_LP) &&
+           (target_cluster == TEGRA_POWER_CLUSTER_G)) {
+               if (!timekeeping_suspended) {
                        ktime_t now = ktime_get();
                        s64 t = ktime_to_us(ktime_sub(now, last_g2lp));
                        s64 t_off = tegra_cpu_power_off_time();
                        if (t_off > t)
                                udelay((unsigned int)(t_off - t));
                }
+
+               /* Start CPU rail transition up early - before disabling irq */
+               if (tegra_crail_can_start_early()) {
+                       int ret = tegra_crail_startup_early();
+                       if (ret)
+                               return ret;
+               }
        }
 
        local_irq_save(irq_flags);
index 4a4df67..74e6567 100644 (file)
@@ -364,6 +364,11 @@ unsigned long tegra_min_residency_crail(void)
                        ? pdata->min_residency_crail
                        : TEGRA_MIN_RESIDENCY_CRAIL;
 }
+
+bool tegra_crail_can_start_early(void)
+{
+       return pdata && pdata->crail_up_early;
+}
 #endif
 
 static void suspend_cpu_dfll_mode(unsigned int flags)
@@ -462,6 +467,12 @@ static void set_power_timers(unsigned long us_on, unsigned long us_off,
        last_us_off = us_off;
 }
 
+void tegra_limit_cpu_power_timers(unsigned long us_on, unsigned long us_off)
+{
+       /* make sure power timers would not exceed specified limits */
+       set_power_timers(us_on, us_off, clk_get_min_rate(tegra_pclk));
+}
+
 /*
  * restore_cpu_complex
  *
@@ -738,7 +749,7 @@ unsigned int tegra_idle_power_down_last(unsigned int sleep_time,
                         * transition. Before the transition, enable
                         * the vdd_cpu rail.
                         */
-                       if (is_lp_cluster()) {
+                       if (!tegra_crail_can_start_early() && is_lp_cluster()) {
 #if defined(CONFIG_ARCH_TEGRA_HAS_SYMMETRIC_CPU_PWR_GATE)
                                reg = readl(FLOW_CTRL_CPU_PWR_CSR);
                                reg |= FLOW_CTRL_CPU_PWR_CSR_RAIL_ENABLE;
index a04be56..88332a0 100644 (file)
@@ -86,6 +86,7 @@ struct tegra_suspend_platform_data {
        unsigned long min_residency_ncpu_slow;
        unsigned long min_residency_ncpu_fast;
        unsigned long min_residency_crail;
+       bool crail_up_early;
 #endif
        unsigned long min_residency_mc_clk;
        bool usb_vbus_internal_wake; /* support for internal vbus wake */
@@ -106,7 +107,12 @@ unsigned long tegra_mc_clk_stop_min_residency(void);
 unsigned long tegra_min_residency_vmin_fmin(void);
 unsigned long tegra_min_residency_ncpu(void);
 unsigned long tegra_min_residency_crail(void);
+bool tegra_crail_can_start_early(void);
+#else
+static inline bool tegra_crail_can_start_early(void)
+{ return false; }
 #endif
+void tegra_limit_cpu_power_timers(unsigned long us_on, unsigned long us_off);
 void tegra_clear_cpu_in_pd(int cpu);
 bool tegra_set_cpu_in_pd(int cpu);
 
@@ -135,6 +141,8 @@ unsigned long tegra_lp1bb_emc_min_rate_get(void);
 #define FLOW_CTRL_RAM_REPAIR \
        (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x40)
 #define FLOW_CTRL_RAM_REPAIR_BYPASS_EN (1<<2)
+#define FLOW_CTRL_RAM_REPAIR_STS       (1<<1)
+#define FLOW_CTRL_RAM_REPAIR_REQ       (1<<0)
 
 #define FUSE_SKU_DIRECT_CONFIG \
        (IO_ADDRESS(TEGRA_FUSE_BASE) + 0x1F4)