arm: tegra: optimize L2 enable/disable paths for secureos
Varun Wadekar [Tue, 28 Aug 2012 05:59:06 +0000 (10:59 +0530)]
For the CONFIG_TRUSTED_FOUNDATION code paths, differentiate L2
enable vs. reenable, which are different SMCs (won't trigger an
invalidate in the case of a reenable).

On an L2 disable SMC, optionally pass a 0 for the L2 ways arg,
which skips the full clean/invalidate (and simply just disabled
the L2).

In order to safely skip flushing the L2 on the disable, we have
to be careful what we dirty from the type we flush the L1 and
disable the L2.

Reviewed-on: http://git-master/r/119786
Original-author: Chris Johnson <cwj@nvidia.com>

Change-Id: Iebcf1042ce2b58513e40e9d49f87ecec9dfdd301
Signed-off-by: Chris Johnson <cwj@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/130061
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

Rebase-Id: R4dde3b2e285d5917bdba15a318ac18702eb59c90

arch/arm/mach-tegra/common.c
arch/arm/mach-tegra/headsmp.S
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h
arch/arm/mach-tegra/sleep.S

index c65da44..12fd097 100644 (file)
@@ -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>
@@ -247,63 +247,14 @@ static __initdata struct tegra_clk_init_table tegra11x_clk_init_table[] = {
 };
 #endif
 
-#ifdef CONFIG_CACHE_L2X0
-#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #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);
-       }
-}
-
-static void tegra_l2x0_disable(void)
+#define CACHE_LINE_SIZE                32
+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;
                u32 aux_ctrl;
@@ -314,33 +265,65 @@ static void tegra_l2x0_disable(void)
                l2x0_way_mask = (1 << ways) - 1;
        }
 
-       local_irq_save(flags);
-       tegra_cache_smc(false, l2x0_way_mask);
-       local_irq_restore(flags);
+#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");
+               /* 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,
+                       l2x0_way_mask);
+       } 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;
-#ifndef CONFIG_TRUSTED_FOUNDATIONS
-       u32 cache_type;
-       u32 tag_latency, data_latency;
-#endif
 
-#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);
 
-       /* after init, reread aux_ctrl and register handlers */
-       aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
-       l2x0_init(p, aux_ctrl, 0xFFFFFFFF);
+               /* common init called for outer call hookup */
+               aux_ctrl = readl_relaxed(p + L2X0_AUX_CTRL);
+               l2x0_init(p, aux_ctrl, 0xFFFFFFFF);
+
+#ifdef CONFIG_OUTER_CACHE
+               /* use our outer_disable() routine */
+               outer_cache.disable = tegra_l2x0_disable_tz;
+#endif
+       } 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 */
+       tegra_init_cache_tz(init);
 #else
+       void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
+       u32 aux_ctrl;
+       u32 cache_type;
+       u32 tag_latency = 0x770, data_latency = 0x770;
 #if defined(CONFIG_ARCH_TEGRA_2x_SOC)
        tag_latency = 0x331;
        data_latency = 0x441;
@@ -363,9 +346,6 @@ void tegra_init_cache(bool init)
                        data_latency = 0x551;
                }
        }
-#else
-       tag_latency = 0x770;
-       data_latency = 0x770;
 #endif
 #endif
        writel_relaxed(tag_latency, p + L2X0_TAG_LATENCY_CTRL);
@@ -394,14 +374,9 @@ void tegra_init_cache(bool init)
                writel(aux_ctrl, p + L2X0_AUX_CTRL);
        }
        l2x0_enable();
-#endif
+#endif /* CONFIG_TRUSTED_FOUNDATIONS */
 }
-#else
-void tegra_init_cache(bool init)
-{
-}
-#endif
-#endif
+#endif /* CONFIG_CACHE_L2X0 */
 
 static void __init tegra_init_power(void)
 {
index 6827f58..9c97fd2 100644 (file)
@@ -3,7 +3,7 @@
  *
  * CPU initialization routines for Tegra SoCs
  *
- * Copyright (c) 2009-2012, 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>
@@ -87,8 +87,9 @@ ENTRY(tegra_resume)
 #endif
 
 #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       cpu_resume
index f57e69a..40f43e6 100644 (file)
@@ -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
@@ -103,6 +103,7 @@ struct suspend_context {
 };
 
 #ifdef CONFIG_PM_SLEEP
+void *tegra_cpu_context;        /* non-cacheable page for CPU context */
 phys_addr_t tegra_pgd_phys;    /* pgd used by hotplug & LP2 bootup */
 static pgd_t *tegra_pgd;
 static DEFINE_SPINLOCK(tegra_lp2_lock);
@@ -271,6 +272,37 @@ static __init int create_suspend_pgtable(void)
 
        return 0;
 }
+/*
+ * alloc_suspend_context
+ *
+ * Allocate a non-cacheable page to hold the CPU contexts.
+ * The standard ARM CPU context save functions don't work if there's
+ * an external L2 cache controller (like a PL310) in system.
+ */
+static __init int alloc_suspend_context(void)
+{
+       pgprot_t prot = __pgprot_modify(pgprot_kernel, L_PTE_MT_MASK,
+                                       L_PTE_MT_BUFFERABLE | L_PTE_XN);
+       struct page *ctx_page;
+
+       ctx_page = alloc_pages(GFP_KERNEL, 0);
+       if (IS_ERR_OR_NULL(ctx_page))
+               goto fail;
+
+       tegra_cpu_context = vm_map_ram(&ctx_page, 1, -1, prot);
+       if (IS_ERR_OR_NULL(tegra_cpu_context))
+               goto fail;
+
+       return 0;
+
+fail:
+       if (!IS_ERR(ctx_page) && ctx_page)
+               __free_page(ctx_page);
+       if (!IS_ERR(tegra_cpu_context) && tegra_cpu_context)
+               vm_unmap_ram((void *)tegra_cpu_context, 1);
+       tegra_cpu_context = NULL;
+       return -ENOMEM;
+}
 
 /* ensures that sufficient time is passed for a register write to
  * serialize into the 32KHz domain */
@@ -516,15 +548,25 @@ 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,
+               tegra_generic_smc_uncached(0xFFFFFFFC, 0xFFFFFFE3,
                                  virt_to_phys(tegra_resume));
        } else {
-               tegra_generic_smc(0xFFFFFFFC, 0xFFFFFFE6,
+               tegra_generic_smc_uncached(0xFFFFFFFC, 0xFFFFFFE6,
                                  (TEGRA_RESET_HANDLER_BASE +
                                   tegra_cpu_reset_handler_offset));
        }
@@ -539,7 +581,7 @@ 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_generic_smc_uncached(0xFFFFFFFC, 0xFFFFFFE4,
                          (TEGRA_RESET_HANDLER_BASE +
                           tegra_cpu_reset_handler_offset));
 #endif
@@ -1106,6 +1148,13 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
                goto fail;
        }
 
+       if (alloc_suspend_context() < 0) {
+               pr_err("%s: alloc_suspend_context failed -- LP0/LP1/LP2 unavailable\n",
+                               __func__);
+               plat->suspend_mode = TEGRA_SUSPEND_NONE;
+               goto fail;
+       }
+
        if ((tegra_get_chipid() == TEGRA_CHIPID_TEGRA3) &&
            (tegra_revision == TEGRA_REVISION_A01) &&
            (plat->suspend_mode == TEGRA_SUSPEND_LP0)) {
index 259c31a..26d8073 100644 (file)
@@ -83,6 +83,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);
 
@@ -233,6 +234,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 */
index e7b8c65..4acd46d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/sleep.S
  *
- * Copyright (c) 2010-2012, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
  * Copyright (c) 2011, Google, Inc.
  *
  * Author: Colin Cross <ccross@android.com>
@@ -257,27 +257,74 @@ ENDPROC(tegra_cpu_pllp)
 
 #ifdef CONFIG_TRUSTED_FOUNDATIONS
 /*
- * tegra_generic_smc
- *
- * r0 = smc type
- * r1 = smc subtype
- * r2 = argument passed to smc
- *
- * issues SMC (secure monitor call) instruction with
- * the specified parameters.
+ * Issue SMC with ctx kept on an uncached stack
  */
-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
+
+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
-       .size   __tegra_smc_stack, . - __tegra_smc_stack
+       .rept   TEGRA_SMC_SAVED_WORDS
+       .long   0
+       .endr
+       .size   __tegra_smc_stack, . - __tegra_smc_stack
 #endif