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

Rebase-Id: R8909dae486432fd628e8d89735634eee26063f4e

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
arch/arm/mach-tegra/tegra11_clocks.c

index 7517e04..e233ede 100644 (file)
@@ -46,18 +46,32 @@ 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)
+
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
 #define CAR_BOND_OUT_V \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
 #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, pmc + (reg))
+#define pmc_readl(reg)         readl(pmc + (reg))
+
 static void __init setup_core_count(void)
 {
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
@@ -126,6 +140,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)
 {
        cpumask_set_cpu(cpu, to_cpumask(tegra_cpu_init_bits));
@@ -214,8 +237,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());
@@ -223,47 +245,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:
-
-       /* Remove I/O clamps. */
-       ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
-       udelay(10);
-
-enable_cpu_clock:
+       ret = -ETIMEDOUT;
 
-       /* 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);
 
@@ -314,6 +322,7 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
 
        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
@@ -322,7 +331,7 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
         * of the CPU since it should already be in reset.
         */
        tegra_put_cpu_in_reset(cpu);
-
+#endif
 
        switch (tegra_chip_id) {
        case TEGRA20:
@@ -346,9 +355,6 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
                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:
@@ -356,11 +362,13 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *
                break;
        }
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
        if (status)
                goto done;
 
        /* Take the CPU out of reset. */
        tegra_cpu_out_of_reset(cpu);
+#endif
 done:
        return status;
 }
@@ -403,6 +411,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;
 
@@ -416,6 +427,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 smp_operations tegra_smp_ops __initdata = {
        .smp_init_cpus          = tegra_smp_init_cpus,
        .smp_prepare_cpus       = tegra_smp_prepare_cpus,
index 8df17f4..b62701f 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 45d7831..36a132c 100644 (file)
@@ -936,6 +936,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
index 34fa16b..f48515a 100644 (file)
 #define ROUND_DIVIDER_UP       0
 #define ROUND_DIVIDER_DOWN     1
 
+#define CPU_CLOCK(cpu) (0x1 << (8 + cpu))
+#define CPU_RESET(cpu) (0x111001ul << (cpu))
+
 /* PLLP default fixed rate in h/w controlled mode */
 #define PLLP_DEFAULT_FIXED_RATE                216000000
 
@@ -7112,6 +7115,10 @@ static struct syscore_ops tegra_clk_syscore_ops = {
 };
 #endif
 
+static void tegra11_disable_cpu_clock(u32 cpu)
+{
+}
+
 void __init tegra11x_init_clocks(void)
 {
        int i;