summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/common.c118
-rw-r--r--arch/arm/mach-tegra/headsmp.S7
-rw-r--r--arch/arm/mach-tegra/pm.c28
-rw-r--r--arch/arm/mach-tegra/pm.h3
-rw-r--r--arch/arm/mach-tegra/sleep.S79
5 files changed, 144 insertions, 91 deletions
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index b914d5d71462..85eb11f64118 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -2,7 +2,7 @@
* arch/arm/mach-tegra/common.c
*
* Copyright (C) 2010 Google, Inc.
- * Copyright (C) 2010-2012 NVIDIA Corporation
+ * Copyright (C) 2010-2012, NVIDIA Corporation. All rights reserved.
*
* Author:
* Colin Cross <ccross@android.com>
@@ -226,61 +226,13 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
{ NULL, NULL, 0, 0},
};
-#ifdef CONFIG_CACHE_L2X0
#ifdef CONFIG_TRUSTED_FOUNDATIONS
-static void tegra_cache_smc(bool enable, u32 arg)
-{
- void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
- bool need_affinity_switch;
- bool can_switch_affinity;
- bool l2x0_enabled;
- cpumask_t local_cpu_mask;
- cpumask_t saved_cpu_mask;
- unsigned long flags;
- long ret;
-
- /*
- * ISSUE : Some registers of PL310 controler must be written
- * from Secure context (and from CPU0)!
- *
- * When called form Normal we obtain an abort or do nothing.
- * Instructions that must be called in Secure:
- * - Write to Control register (L2X0_CTRL==0x100)
- * - Write in Auxiliary controler (L2X0_AUX_CTRL==0x104)
- * - Invalidate all entries (L2X0_INV_WAY==0x77C),
- * mandatory at boot time.
- * - Tag and Data RAM Latency Control Registers
- * (0x108 & 0x10C) must be written in Secure.
- */
- need_affinity_switch = (smp_processor_id() != 0);
- can_switch_affinity = !irqs_disabled();
-
- WARN_ON(need_affinity_switch && !can_switch_affinity);
- if (need_affinity_switch && can_switch_affinity) {
- cpu_set(0, local_cpu_mask);
- sched_getaffinity(0, &saved_cpu_mask);
- ret = sched_setaffinity(0, &local_cpu_mask);
- WARN_ON(ret != 0);
- }
-
- local_irq_save(flags);
- l2x0_enabled = readl_relaxed(p + L2X0_CTRL) & 1;
- if (enable && !l2x0_enabled)
- tegra_generic_smc(0xFFFFF100, 0x00000001, arg);
- else if (!enable && l2x0_enabled)
- tegra_generic_smc(0xFFFFF100, 0x00000002, arg);
- local_irq_restore(flags);
-
- if (need_affinity_switch && can_switch_affinity) {
- ret = sched_setaffinity(0, &saved_cpu_mask);
- WARN_ON(ret != 0);
- }
-}
+#define CACHE_LINE_SIZE 32
-static void tegra_l2x0_disable(void)
+static inline void tegra_l2x0_disable_tz(void)
{
- unsigned long flags;
static u32 l2x0_way_mask;
+ BUG_ON(smp_processor_id() != 0);
if (!l2x0_way_mask) {
void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
@@ -291,30 +243,62 @@ static void tegra_l2x0_disable(void)
ways = (aux_ctrl & (1 << 16)) ? 16 : 8;
l2x0_way_mask = (1 << ways) - 1;
}
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ /* flush all ways on disable */
+ tegra_generic_smc_uncached(0xFFFFF100, 0x00000002, l2x0_way_mask);
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ if (tegra_is_cpu_in_lp2(0)) {
+ register unsigned long sp asm ("sp");
- local_irq_save(flags);
- tegra_cache_smc(false, l2x0_way_mask);
- local_irq_restore(flags);
+ /* flush only the stack, if entering LP2 */
+ __cpuc_flush_dcache_area((void *)sp, (CACHE_LINE_SIZE * 2));
+ outer_flush_range(__pa(sp), __pa(sp) + (CACHE_LINE_SIZE * 2));
+
+ /* pass zero arg, so secureos flushes only its workspace */
+ tegra_generic_smc_uncached(0xFFFFF100, 0x00000002, 0x0);
+ } else {
+ /* flush all ways on disable, if entering LP0/LP1 */
+ tegra_generic_smc_uncached(0xFFFFF100,
+ 0x00000002, l2x0_way_mask);
+ }
+#endif
}
-#endif /* CONFIG_TRUSTED_FOUNDATIONS */
-void tegra_init_cache(bool init)
+static inline void tegra_init_cache_tz(bool init)
{
void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
u32 aux_ctrl;
-#ifdef CONFIG_TRUSTED_FOUNDATIONS
- /* issue the SMC to enable the L2 */
- aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
- tegra_cache_smc(true, aux_ctrl);
+ BUG_ON(smp_processor_id() != 0);
+
+ if (init) {
+ /* init L2 from secureos */
+ tegra_generic_smc(0xFFFFF100, 0x00000001, 0x0);
+
+ /* common init called for outer call hookup */
+ aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
+ l2x0_init(p, aux_ctrl, 0xFFFFFFFF);
- /* after init, reread aux_ctrl and register handlers */
- aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
- l2x0_init(p, aux_ctrl, 0xFFFFFFFF);
+ /* use our outer_disable() routine */
+ outer_cache.disable = tegra_l2x0_disable_tz;
+ } else {
+ /* reenable L2 in secureos */
+ aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
+ tegra_generic_smc_uncached(0xFFFFF100, 0x00000004, aux_ctrl);
+ }
+}
+#endif /* CONFIG_TRUSTED_FOUNDATIONS */
- /* override outer_disable() with our disable */
- outer_cache.disable = tegra_l2x0_disable;
+#ifdef CONFIG_CACHE_L2X0
+void tegra_init_cache(bool init)
+{
+#ifdef CONFIG_TRUSTED_FOUNDATIONS
+ /* enable/re-enable of L2 handled by secureos */
+ return tegra_init_cache_tz(init);
#else
+ void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
+ u32 aux_ctrl;
+
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
writel_relaxed(0x331, p + L2X0_TAG_LATENCY_CTRL);
writel_relaxed(0x441, p + L2X0_DATA_LATENCY_CTRL);
@@ -364,7 +348,7 @@ void tegra_init_cache(bool init)
l2x0_enable();
#endif
}
-#endif
+#endif /* CONFIG_CACHE_L2X0 */
static void __init tegra_init_power(void)
{
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index 4e28a558cc38..63852b99b3d3 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -3,7 +3,7 @@
*
* CPU initialization routines for Tegra SoCs
*
- * Copyright (c) 2009-2011, NVIDIA Corporation.
+ * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
* Copyright (c) 2011 Google, Inc.
* Author: Colin Cross <ccross@android.com>
* Gary King <gking@nvidia.com>
@@ -90,8 +90,9 @@ ENTRY(tegra_resume)
str r1, [r0]
#ifdef CONFIG_TRUSTED_FOUNDATIONS
- /* wake up (should have specified args?) */
- bl tegra_generic_smc
+ /* wake up */
+ mov r0, #0x00000003
+ bl tegra_generic_smc_local
#endif
b tegra_cpu_resume_phys
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 9049e37ca05a..d251e57626aa 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -3,7 +3,7 @@
*
* CPU complex suspend & resume functions for Tegra SoCs
*
- * Copyright (c) 2009-2012, NVIDIA Corporation.
+ * Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -546,17 +546,27 @@ bool tegra_set_cpu_in_lp2(int cpu)
return last_cpu;
}
+bool tegra_is_cpu_in_lp2(int cpu)
+{
+ bool in_lp2;
+
+ spin_lock(&tegra_lp2_lock);
+ in_lp2 = cpumask_test_cpu(cpu, &tegra_in_lp2);
+ spin_unlock(&tegra_lp2_lock);
+ return in_lp2;
+}
+
static void tegra_sleep_core(enum tegra_suspend_mode mode,
unsigned long v2p)
{
#ifdef CONFIG_TRUSTED_FOUNDATIONS
if (mode == TEGRA_SUSPEND_LP0) {
- tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE3,
- virt_to_phys(tegra_resume));
+ tegra_generic_smc_uncached(0xFFFFFFFC, 0xFFFFFFE3,
+ virt_to_phys(tegra_resume));
} else {
- tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE6,
- (TEGRA_RESET_HANDLER_BASE +
- tegra_cpu_reset_handler_offset));
+ tegra_generic_smc_uncached(0xFFFFFFFC, 0xFFFFFFE6,
+ (TEGRA_RESET_HANDLER_BASE +
+ tegra_cpu_reset_handler_offset));
}
#endif
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
@@ -569,9 +579,9 @@ static void tegra_sleep_core(enum tegra_suspend_mode mode,
static inline void tegra_sleep_cpu(unsigned long v2p)
{
#ifdef CONFIG_TRUSTED_FOUNDATIONS
- tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE4,
- (TEGRA_RESET_HANDLER_BASE +
- tegra_cpu_reset_handler_offset));
+ tegra_generic_smc_uncached(0xFFFFFFFC, 0xFFFFFFE4,
+ (TEGRA_RESET_HANDLER_BASE +
+ tegra_cpu_reset_handler_offset));
#endif
tegra_sleep_cpu_save(v2p);
}
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 498170648819..b78e9b1abc00 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -75,6 +75,7 @@ unsigned long tegra_cpu_power_off_time(void);
unsigned long tegra_cpu_lp2_min_residency(void);
void tegra_clear_cpu_in_lp2(int cpu);
bool tegra_set_cpu_in_lp2(int cpu);
+bool tegra_is_cpu_in_lp2(int cpu);
int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags);
@@ -222,6 +223,8 @@ extern bool tegra_all_cpus_booted __read_mostly;
#ifdef CONFIG_TRUSTED_FOUNDATIONS
void tegra_generic_smc(u32 type, u32 subtype, u32 arg);
+void tegra_generic_smc_local(u32 type, u32 subtype, u32 arg);
+void tegra_generic_smc_uncached(u32 type, u32 subtype, u32 arg);
#endif
/* The debug channel uart base physical address */
diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S
index 973c8677bafe..e86795c5c46a 100644
--- a/arch/arm/mach-tegra/sleep.S
+++ b/arch/arm/mach-tegra/sleep.S
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/sleep.S
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
* Copyright (c) 2011, Google, Inc.
*
* Author: Colin Cross <ccross@android.com>
@@ -440,28 +440,83 @@ ENDPROC(tegra_cpu_pllp)
#endif
#ifdef CONFIG_TRUSTED_FOUNDATIONS
+
/*
- * tegra_generic_smc
+ * Confirm we're issuing this SMC from CPU0 (only one
+ * currently supported) and issue the instruction.
*
* r0 = smc type
* r1 = smc subtype
* r2 = argument passed to smc
- *
- * issues SMC (secure monitor call) instruction with
- * the specified parameters.
*/
-ENTRY(tegra_generic_smc)
- adr r3, __tegra_smc_stack
- stmia r3, {r4-r12, lr}
+.macro smc_issue_smc tmp
+ cpu_id \tmp
+ cmp \tmp, #0
+ bne .
mov r3, #0
mov r4, #0
dsb
smc #0
- adr r3, __tegra_smc_stack
- ldmia r3, {r4-r12, pc}
+.endm
+
+/*
+ * Issue SMC with ctx kept on an uncached stack
+ */
+ENTRY(tegra_generic_smc_uncached)
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_CACHE_L2X0)
+ mov32 r3, tegra_cpu_context @ borrow CPU0's non-cached
+ ldr r3, [r3] @ context grows up
+ stmia r3, {r4-r12, sp, lr}
+
+ smc_issue_smc r5
+
+ mov32 r3, tegra_cpu_context @ borrow CPU0's non-cached
+ ldr r3, [r3] @ context grows up
+ ldmia r3, {r4-r12, sp, pc}
+#else
+ mov pc, lr
+#endif
+ENDPROC(tegra_generic_smc_uncached)
+
+/*
+ * Issue SMC with ctx kept on a cacheable stack
+ * (args in R0, R1, R2 and R3 holds save/restore ptr)
+ */
+ENTRY(tegra_generic_smc_cached)
+ stmia r3, {r4-r12, sp, lr}
+ adr r4, __tegra_smc_current_ctx @ save current ptr
+ str r3, [r4]
+
+ smc_issue_smc r5
+
+ adr r4, __tegra_smc_current_ctx @ restore from saved ptr
+ ldr r3, [r4]
+ ldmia r3, {r4-r12, sp, pc}
+ENDPROC(tegra_generic_smc_cached)
+ .type __tegra_smc_current_ctx, %object
+__tegra_smc_current_ctx:
+ .long 0
+ .size __tegra_smc_current_ctx, . - __tegra_smc_current_ctx
+
+#define TEGRA_SMC_SAVED_WORDS 11
+
+/* SMC issued using the current cacheable SP stack */
+ENTRY(tegra_generic_smc)
+ mov r3, sp @ use current stack
+ sub r3, #(TEGRA_SMC_SAVED_WORDS << 2) @ context grows up
+ b tegra_generic_smc_cached
ENDPROC(tegra_generic_smc)
- .type __tegra_smc_stack, %object
+
+/* SMC issued using a local cacheable stack */
+ENTRY(tegra_generic_smc_local)
+ adr r3, __tegra_smc_stack @ use local stack
+ b tegra_generic_smc_cached
+ENDPROC(tegra_generic_smc_local)
+ .align L1_CACHE_SHIFT
+ .type __tegra_smc_stack, %object
__tegra_smc_stack:
- .long 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ .rept TEGRA_SMC_SAVED_WORDS
+ .long 0
+ .endr
.size __tegra_smc_stack, . - __tegra_smc_stack
#endif