summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/platsmp.c
diff options
context:
space:
mode:
authorScott Williams <scwilliams@nvidia.com>2011-07-05 18:05:26 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:46:54 -0800
commit27a435eb62ef122692e904cbd2959ac191bc43cc (patch)
treec9bb6481c999823028e5fde632c184aeefb96f15 /arch/arm/mach-tegra/platsmp.c
parent935cf35887d167f8cac58b72d8998a5d6712b27e (diff)
ARM: tegra: Redesign Tegra CPU reset handling
- Add a single unified handler for all CPU resets that is copied to IRAM. - Add state information to direct the flow of execution through the reset handler based on the reason a CPU was reset. - Write the EVP CPU reset vector only once per cold/warm boot session. - Prevent modification of the EVP CPU reset vector in Tegra3. Bug 786290 Bug 790458 Change-Id: Ica6707f3514986ee914e73a2d9766a4e06ce2d29 Signed-off-by: Scott Williams <scwilliams@nvidia.com> DW: Split into logical changes Signed-off-by: Dan Willemsen <dwillemsen@nvidia.com> Rebase-Id: R7b9859a83717e76c3c083bdde724bd5fef9ce089
Diffstat (limited to 'arch/arm/mach-tegra/platsmp.c')
-rw-r--r--arch/arm/mach-tegra/platsmp.c277
1 files changed, 139 insertions, 138 deletions
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index 97bfb4db28a5..a7b81919ba90 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -20,6 +20,7 @@
#include <linux/smp.h>
#include <linux/delay.h>
#include <linux/clk.h>
+#include <linux/cpumask.h>
#include <asm/hardware/gic.h>
#include <asm/smp_scu.h>
@@ -29,10 +30,15 @@
#include "pm.h"
#include "clock.h"
+#include "reset.h"
#include "sleep.h"
-#define EVP_CPU_RESET_VECTOR \
- (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
+bool tegra_all_cpus_booted;
+
+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)
+
#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
@@ -43,38 +49,144 @@
#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
#define CPU_RESET(cpu) (0x1111ul<<(cpu))
-static unsigned int available_cpus(void);
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
-static inline int is_g_cluster_available(unsigned int cpu)
-{ return -EPERM; }
-static inline bool is_cpu_powered(unsigned int cpu)
-{ return true; }
-static inline int power_up_cpu(unsigned int cpu)
-{ return 0; }
-
-/* For Tegra2 use the software-written value of the reset regsiter for status.*/
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+/* For Tegra2 use the software-written value of the reset register for status.*/
#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET
-
#else
-static int is_g_cluster_available(unsigned int cpu);
-static bool is_cpu_powered(unsigned int cpu);
-static int power_up_cpu(unsigned int cpu);
-
+#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
+ (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
#define CAR_BOND_OUT_V \
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
#define CAR_BOND_OUT_V_CPU_G (1<<0)
#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
+#endif
+static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
+
+static unsigned int available_cpus(void)
+{
+ static unsigned int ncores;
+
+ if (ncores == 0) {
+ ncores = scu_get_core_count(scu_base);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if (ncores > 1) {
+ u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+ ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
+ BUG_ON((int)ncores <= 0);
+ }
#endif
+ }
+ return ncores;
+}
+
+static int is_g_cluster_available(unsigned int cpu)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ return -EPERM;
+#else
+ u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+ u32 bond_out = readl(CAR_BOND_OUT_V);
-extern void tegra_secondary_startup(void);
+ /* Does the G CPU complex exist at all? */
+ if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
+ (bond_out & CAR_BOND_OUT_V_CPU_G))
+ return -EPERM;
-static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
+ if (cpu >= available_cpus())
+ return -EPERM;
+
+ /* FIXME: The G CPU can be unavailable for a number of reasons
+ * (e.g., low battery, over temperature, etc.). Add checks for
+ * these conditions. */
+ return 0;
+#endif
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static bool is_cpu_powered(unsigned int cpu)
+{
+ if (is_lp_cluster())
+ return true;
+ else
+ return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
+}
+#endif
+
+static int power_up_cpu(unsigned int cpu)
+{
+ u32 reg;
+ int ret = 0;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long timeout;
+
+ BUG_ON(cpu == smp_processor_id());
+ BUG_ON(is_lp_cluster());
+
+ /* If this cpu has booted this function is entered after
+ * CPU has been already un-gated by flow controller. Wait
+ * for confirmation that cpu is powered and remove clamps.
+ * On first boot entry do not wait - go to direct ungate.
+ */
+ if (cpu_isset(cpu, tegra_cpu_init_map)) {
+ timeout = jiffies + HZ;
+ do {
+ if (is_cpu_powered(cpu))
+ goto remove_clamps;
+ udelay(10);
+ } while (time_before(jiffies, timeout));
+ }
+
+ /* First boot or Flow controller did not work as expected. Try to
+ directly toggle power gates. Error if direct power on also fails. */
+ if (!is_cpu_powered(cpu)) {
+ ret = tegra_powergate_power_on(TEGRA_CPU_POWERGATE_ID(cpu));
+ if (ret)
+ goto fail;
+
+ /* Wait for the power to come up. */
+ timeout = jiffies + 10*HZ;
+
+ do {
+ if (is_cpu_powered(cpu))
+ goto remove_clamps;
+ udelay(10);
+ } while (time_before(jiffies, timeout));
+ ret = -ETIMEDOUT;
+ goto fail;
+ }
+
+remove_clamps:
+ /* CPU partition is powered. Enable the CPU clock. */
+ writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
+ udelay(10);
+
+ /* Remove I/O clamps. */
+ ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
+ udelay(10);
+fail:
+#else
+ /* Enable the CPU clock. */
+ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+ barrier();
+ reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+#endif
+ /* Clear flow controller CSR. */
+ flowctrl_writel(0, FLOW_CTRL_CPU_CSR(cpu));
+ return ret;
+}
void __cpuinit platform_secondary_init(unsigned int cpu)
{
gic_secondary_init(0);
+
+ cpumask_set_cpu(cpu, to_cpumask(tegra_cpu_init_bits));
+ if (!tegra_all_cpus_booted)
+ if (cpumask_equal(tegra_cpu_init_mask, cpu_present_mask))
+ tegra_all_cpus_booted = true;
}
int boot_secondary(unsigned int cpu, struct task_struct *idle)
@@ -106,9 +218,6 @@ int boot_secondary(unsigned int cpu, struct task_struct *idle)
smp_wmb();
- /* set the reset vector to point to the secondary_startup routine */
- writel(virt_to_phys(tegra_secondary_startup), EVP_CPU_RESET_VECTOR);
-
/* Force the CPU into reset. The CPU must remain in reset when the
flow controller state is cleared (which will cause the flow
controller to stop driving reset if the CPU has been power-gated
@@ -123,22 +232,13 @@ int boot_secondary(unsigned int cpu, struct task_struct *idle)
is now driving reset. */
flowctrl_writel(0, FLOW_CTRL_HALT_CPU(cpu));
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
- {
- /* enable cpu clock on cpu */
- u32 reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
- writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
- dmb();
- }
-#endif
status = power_up_cpu(cpu);
if (status)
goto done;
- dmb();
- udelay(10); /* power up delay */
+ /* Take the CPU out of reset. */
writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
-
+ wmb();
done:
return status;
}
@@ -167,111 +267,12 @@ void __init smp_init_cpus(void)
void __init platform_smp_prepare_cpus(unsigned int max_cpus)
{
- scu_enable(scu_base);
-}
-
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
-
-static bool is_cpu_powered(unsigned int cpu)
-{
- if (is_lp_cluster())
- return true;
- else
- return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
-}
-
-static int power_up_cpu(unsigned int cpu)
-{
- int ret;
- u32 reg;
- unsigned long timeout;
-
- BUG_ON(cpu == smp_processor_id());
- BUG_ON(is_lp_cluster());
-
- /* If this cpu has booted this function is entered after
- * CPU has been already un-gated by flow controller. Wait
- * for confirmation that cpu is powered and remove clamps.
- * On first boot entry do not wait - go to direct ungate.
- */
-#if 0 /* FIXME! */
- if (cpu_isset(cpu,*(cpumask_t*)&tegra_cpu_init_map))
- {
- timeout = jiffies + HZ;
- do {
- if (is_cpu_powered(cpu))
- goto remove_clamps;
- udelay(10);
- } while (time_before(jiffies, timeout));
- }
-#endif
- /* 1'st boot or Flow controller did not work as expected - try directly toggle
- power gates. Bail out if direct power on also failed */
- if (!is_cpu_powered(cpu))
- {
- ret = tegra_powergate_power_on(TEGRA_CPU_POWERGATE_ID(cpu));
- if (ret)
- goto fail;
+ /* Always mark the boot CPU as initialized. */
+ cpumask_set_cpu(0, to_cpumask(tegra_cpu_init_bits));
- /* Wait for the power to come up. */
- timeout = jiffies + 10*HZ;
+ if (max_cpus == 1)
+ tegra_all_cpus_booted = true;
- do {
- if (is_cpu_powered(cpu))
- goto remove_clamps;
- udelay(10);
- } while (time_before(jiffies, timeout));
- ret = -ETIMEDOUT;
- goto fail;
- }
-
-remove_clamps:
- /* now CPU is up: enable clock, propagate reset, and remove clamps */
- reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
- writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
- barrier();
- reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-
- udelay(10);
- ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
-fail:
- return ret;
-}
-
-static int is_g_cluster_available(unsigned int cpu)
-{
- u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
- u32 bond_out = readl(CAR_BOND_OUT_V);
-
- /* Does the G CPU complex exist at all? */
- if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
- (bond_out & CAR_BOND_OUT_V_CPU_G))
- return -EPERM;
-
- if (cpu >= available_cpus())
- return -EPERM;
-
- /* FIXME: The G CPU can be unavailable for a number of reasons
- * (e.g., low battery, over temperature, etc.). Add checks for
- * these conditions. */
-
- return 0;
-}
-#endif
-
-static unsigned int available_cpus(void)
-{
- static unsigned int ncores = 0;
-
- if (ncores == 0) {
- ncores = scu_get_core_count(scu_base);
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
- if (ncores > 1) {
- u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
- ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
- BUG_ON((int)ncores <= 0);
- }
-#endif
- }
- return ncores;
+ tegra_cpu_reset_handler_init();
+ scu_enable(scu_base);
}