ARM: tegra11x: CPU start up fix
Bo Yan [Tue, 9 Oct 2012 02:52:49 +0000 (19:52 -0700)]
The first time when a CPU powers up in kernel, it has to be
done by directly toggling PMC register.

Subsequent CPU power up sequence is controlled by flow controller.

This is done after LP0 exit as well.

Signed-off-by: Bo Yan <byan@nvidia.com>
Reviewed-on: http://git-master/r/143296

Change-Id: If32712706d827e4d0337d75163449cfa0a3a50f8
Signed-off-by: Bharat Nihalani <bnihalani@nvidia.com>
Reviewed-on: http://git-master/r/146484
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Bo Yan <byan@nvidia.com>
Tested-by: Bo Yan <byan@nvidia.com>
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
GVS: Gerrit_Virtual_Submit

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

index 72a492b..16eeb56 100644 (file)
@@ -49,8 +49,10 @@ int tegra_cpu_kill(unsigned int cpu)
                cpu_relax();
        } while (!(reg & (1<<cpu)));
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
        writel(reg | CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+#endif
 
        return 1;
 }
index f424e09..43d0844 100644 (file)
@@ -52,6 +52,11 @@ static DECLARE_BITMAP(tegra_cpu_init_bits, CONFIG_NR_CPUS) __read_mostly;
 const struct cpumask *const tegra_cpu_init_mask = to_cpumask(tegra_cpu_init_bits);
 #define tegra_cpu_init_map     (*(cpumask_t *)tegra_cpu_init_mask)
 
+static DECLARE_BITMAP(tegra_cpu_power_up_by_fc, CONFIG_NR_CPUS) __read_mostly;
+struct cpumask *tegra_cpu_power_mask =
+                               to_cpumask(tegra_cpu_power_up_by_fc);
+#define tegra_cpu_power_map    (*(cpumask_t *)tegra_cpu_power_mask)
+
 #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
@@ -62,7 +67,12 @@ const struct cpumask *const tegra_cpu_init_mask = to_cpumask(tegra_cpu_init_bits
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
 
 #define CPU_CLOCK(cpu) (0x1<<(8+cpu))
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
 #define CPU_RESET(cpu) (0x1111ul<<(cpu))
+#else
+#define CPU_RESET(cpu) (0x111001ul<<(cpu))
+#endif
 
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
 #define CAR_BOND_OUT_V \
@@ -70,12 +80,21 @@ const struct cpumask *const tegra_cpu_init_mask = to_cpumask(tegra_cpu_init_bits
 #define CAR_BOND_OUT_V_CPU_G   (1<<0)
 #endif
 
+#define CLAMP_STATUS   0x2c
+#define PWRGATE_TOGGLE 0x30
+
+#define PMC_TOGGLE_START       0x100
+
 #ifdef CONFIG_HAVE_ARM_SCU
 static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
 #endif
 
 static unsigned int number_of_cores;
 
+static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+#define pmc_writel(value, reg) writel(value, (u32)pmc + (reg))
+#define pmc_readl(reg)         readl((u32)pmc + (reg))
+
 static void __init setup_core_count(void)
 {
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
@@ -144,6 +163,15 @@ static bool is_cpu_powered(unsigned int cpu)
                return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
 }
 
+static bool is_clamp_removed(unsigned int cpu)
+{
+       u32 reg;
+
+       reg = pmc_readl(CLAMP_STATUS);
+
+       return !((reg >> TEGRA_CPU_POWERGATE_ID(cpu)) & 1);
+}
+
 static void __cpuinit tegra_secondary_init(unsigned int cpu)
 {
        gic_secondary_init(0);
@@ -237,8 +265,7 @@ fail:
 
 static int tegra11x_power_up_cpu(unsigned int cpu)
 {
-       u32 reg;
-       int ret = 0;
+       int ret;
        unsigned long timeout;
 
        BUG_ON(cpu == smp_processor_id());
@@ -246,47 +273,33 @@ static int tegra11x_power_up_cpu(unsigned int cpu)
 
        cpu = cpu_logical_map(cpu);
 
-       timeout = jiffies + 5;
-       do {
-               if (is_cpu_powered(cpu))
-                       goto enable_cpu_clock;
-               udelay(10);
-       } while (time_before(jiffies, timeout));
+       if (cpu_isset(cpu, tegra_cpu_power_map)) {
 
-       /*
-        * flow controller sequence didn't work, so directly toggle PMC
-        * to power it up
-        */
-       if (!is_cpu_powered(cpu)) {
-               ret = tegra_unpowergate_partition(TEGRA_CPU_POWERGATE_ID(cpu));
-               if (ret)
-                       goto fail;
+               /* set SCLK as event trigger for flow conroller */
+               flowctrl_write_cpu_csr(cpu, 0x1);
+               flowctrl_write_cpu_halt(cpu, 0x48000000);
 
-               /* Wait for the power to come up. */
-               timeout = jiffies + 10*HZ;
+       } else {
+               u32 reg;
 
-               do {
-                       if (is_cpu_powered(cpu))
-                               goto remove_clamps;
-                       udelay(10);
-               } while (time_before(jiffies, timeout));
-               ret = -ETIMEDOUT;
-               goto fail;
+               reg = PMC_TOGGLE_START | TEGRA_CPU_POWERGATE_ID(cpu);
+               pmc_writel(reg, PWRGATE_TOGGLE);
        }
 
-remove_clamps:
+       ret = -ETIMEDOUT;
 
-       /* Remove I/O clamps. */
-       ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
-       udelay(10);
-
-enable_cpu_clock:
-
-       /* Enable the CPU clock */
-       writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
-       reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+       /* Wait for the power to come up. */
+       timeout = jiffies + 10;
+       do {
+               if (is_cpu_powered(cpu) && is_clamp_removed(cpu)) {
+                       cpumask_set_cpu(cpu, tegra_cpu_power_mask);
+                       wmb();
+                       ret = 0;
+                       break;
+               }
+               udelay(10);
+       } while (time_before(jiffies, timeout));
 
-fail:
        /* Clear flow controller CSR. */
        flowctrl_write_cpu_csr(cpu, 0);
 
@@ -337,6 +350,7 @@ int tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
 
        smp_wmb();
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        /*
         * Force the CPU into reset. The CPU must remain in reset when the
         * flow controller state is cleared (which will cause the flow
@@ -346,7 +360,7 @@ int tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
         */
        writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
        dmb();
-
+#endif
 
        switch (tegra_chip_id) {
        case TEGRA20:
@@ -370,9 +384,6 @@ int tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
                status = tegra30_power_up_cpu(cpu);
                break;
        case TEGRA11X:
-               /* set SCLK as event trigger for flow conroller */
-               flowctrl_write_cpu_csr(cpu, 0x1);
-               flowctrl_write_cpu_halt(cpu, 0x48000000);
                status = tegra11x_power_up_cpu(cpu);
                break;
        default:
@@ -380,12 +391,14 @@ int tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
                break;
        }
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        if (status)
                goto done;
 
        /* Take the CPU out of reset. */
        writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
        wmb();
+#endif
 done:
        return status;
 }
@@ -430,6 +443,9 @@ static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
        /* Always mark the boot CPU as initialized. */
        cpumask_set_cpu(0, to_cpumask(tegra_cpu_init_bits));
 
+       /* Always mark the boot CPU as initially powered up */
+       cpumask_set_cpu(0, tegra_cpu_power_mask);
+
        if (max_cpus == 1)
                tegra_all_cpus_booted = true;
 
@@ -443,6 +459,14 @@ static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
 #endif
 }
 
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+void tegra_smp_clear_power_mask()
+{
+       cpumask_clear(tegra_cpu_power_mask);
+       cpumask_set_cpu(0, tegra_cpu_power_mask);
+}
+#endif
+
 struct arm_soc_smp_init_ops tegra_soc_smp_init_ops __initdata = {
        .smp_init_cpus          = tegra_smp_init_cpus,
        .smp_prepare_cpus       = tegra_smp_prepare_cpus,
index db0487d..594141d 100644 (file)
@@ -142,6 +142,7 @@ static int cluster_switch_prolog_clock(unsigned int flags)
                        writel(SuperCclkDivier, CAR_SUPER_CCLKG_DIVIDER);
                }
 
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
                /* Hold G CPUs 1-3 in reset after the switch */
                reg = CPU_RESET(1) | CPU_RESET(2) | CPU_RESET(3);
                writel(reg, CAR_RST_CPUG_CMPLX_SET);
@@ -161,6 +162,7 @@ static int cluster_switch_prolog_clock(unsigned int flags)
                /* Enable the G CPU complex clock after the switch */
                reg = CAR_CLK_ENB_V_CPU_G;
                writel(reg, CAR_CLK_ENB_V_SET);
+#endif
        }
        /* Switching to LP? */
        else if (flags & TEGRA_POWER_CLUSTER_LP) {
@@ -180,6 +182,7 @@ static int cluster_switch_prolog_clock(unsigned int flags)
                        writel(SuperCclkDivier, CAR_SUPER_CCLKLP_DIVIDER);
                }
 
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
                /* Take the LP CPU ut of reset after the switch */
                reg = CPU_RESET(0);
                writel(reg, CAR_RST_CPULP_CMPLX_CLR);
@@ -191,6 +194,7 @@ static int cluster_switch_prolog_clock(unsigned int flags)
                /* Enable the LP CPU complex clock after the switch */
                reg = CAR_CLK_ENB_V_CPU_LP;
                writel(reg, CAR_CLK_ENB_V_SET);
+#endif
        }
 
        return 0;
index 22d9095..5c1e96e 100644 (file)
@@ -941,6 +941,7 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags)
                tegra_lp0_suspend_mc();
                tegra_cpu_reset_handler_save();
                tegra_tsc_wait_for_suspend();
+               tegra_smp_clear_power_mask();
        }
        else if (mode == TEGRA_SUSPEND_LP1)
                *iram_cpu_lp1_mask = 1;
index 259c31a..e1e9666 100644 (file)
@@ -231,6 +231,12 @@ extern bool tegra_all_cpus_booted __read_mostly;
 #define tegra_all_cpus_booted (true)
 #endif
 
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC)
+void tegra_smp_clear_power_mask(void);
+#else
+static inline void tegra_smp_clear_power_mask(void){}
+#endif
+
 #ifdef CONFIG_TRUSTED_FOUNDATIONS
 void tegra_generic_smc(u32 type, u32 subtype, u32 arg);
 #endif