summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/reset.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/reset.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/reset.c')
-rw-r--r--arch/arm/mach-tegra/reset.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c
new file mode 100644
index 000000000000..1ae5a1407a37
--- /dev/null
+++ b/arch/arm/mach-tegra/reset.c
@@ -0,0 +1,117 @@
+/*
+ * arch/arm/mach-tegra/reset.c
+ *
+ * Copyright (C) 2011 NVIDIA Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/cpumask.h>
+#include <linux/bitops.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+
+#include "reset.h"
+#include "sleep.h"
+
+static bool is_enabled;
+
+void tegra_cpu_reset_handler_enable(void)
+{
+ void __tegra_cpu_reset_handler(void);
+ void __tegra_cpu_reset_handler_start(void);
+ void __tegra_cpu_reset_handler_end(void);
+ void __iomem *evp_cpu_reset =
+ IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE + 0x100);
+ void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_BASE);
+ void __iomem *sb_ctrl = IO_ADDRESS(TEGRA_SB_BASE);
+ unsigned long cpu_reset_handler_size =
+ __tegra_cpu_reset_handler_end - __tegra_cpu_reset_handler_start;
+ unsigned long cpu_reset_handler_offset =
+ __tegra_cpu_reset_handler - __tegra_cpu_reset_handler_start;
+ unsigned long reg;
+
+ BUG_ON(is_enabled);
+ BUG_ON(cpu_reset_handler_size > TEGRA_RESET_HANDLER_SIZE);
+
+ memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start,
+ cpu_reset_handler_size);
+
+ /* NOTE: This must be the one and only write to the EVP CPU reset
+ vector in the entire system. */
+ writel(TEGRA_RESET_HANDLER_BASE + cpu_reset_handler_offset,
+ evp_cpu_reset);
+ wmb();
+ reg = readl(evp_cpu_reset);
+
+ /* Prevent further modifications to the physical reset vector.
+ NOTE: Has no effect on chips prior to Tegra3. */
+ reg = readl(sb_ctrl);
+ reg |= 2;
+ writel(reg, sb_ctrl);
+ is_enabled = true;
+ wmb();
+}
+
+#ifdef CONFIG_PM_SLEEP
+static unsigned long cpu_reset_handler_save[TEGRA_RESET_DATA_SIZE];
+
+void tegra_cpu_reset_handler_save(void)
+{
+ unsigned int i;
+ BUG_ON(!is_enabled);
+ for (i = 0; i < TEGRA_RESET_DATA_SIZE; i++)
+ cpu_reset_handler_save[i] =
+ tegra_cpu_reset_handler_ptr[i];
+ is_enabled = false;
+}
+
+void tegra_cpu_reset_handler_restore(void)
+{
+ unsigned int i;
+ BUG_ON(is_enabled);
+ for (i = 0; i < TEGRA_RESET_DATA_SIZE; i++)
+ tegra_cpu_reset_handler_ptr[i] =
+ cpu_reset_handler_save[i];
+ is_enabled = true;
+}
+#endif
+
+void __init tegra_cpu_reset_handler_init(void)
+{
+#ifdef CONFIG_SMP
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] =
+ *((u32 *)cpu_present_mask);
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] =
+ virt_to_phys((void *)tegra_secondary_startup);
+#endif
+#ifdef CONFIG_PM_SLEEP
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1] =
+ TEGRA_IRAM_CODE_AREA;
+ __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] =
+ virt_to_phys((void *)tegra_resume);
+#endif
+
+ /* Push all of reset handler data out to the L3 memory system. */
+ __cpuc_coherent_kern_range(
+ (unsigned long)&__tegra_cpu_reset_handler_data[0],
+ (unsigned long)&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]);
+
+ outer_clean_range(__pa(&__tegra_cpu_reset_handler_data[0]),
+ __pa(&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE]));
+
+ tegra_cpu_reset_handler_enable();
+}