diff options
author | Jitendra Lanka <jlanka@nvidia.com> | 2010-04-21 10:54:57 +0530 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-04-21 09:10:10 -0700 |
commit | 35367088ddab9684ea29432ce35d2a3f117a9d6b (patch) | |
tree | 08d7a0ab532200619d39b49ef0db48ced7551298 | |
parent | 97981a1f78f0680aec684d9534aa13333fa4ec45 (diff) |
kernel - Backporting explicit cpu_down and L2 cache shutdown from experimental
Bug 673802 - [T20/Harmony/Warm boot] Warm boot stress test failed on Harmony.
Backport the changes related to explicit cpu_down and L2 cache shutdown
from experimental kernel branch to main line
Tested on: Harmony
Change-Id: I8615c7e5010ea7133941857cd400a9fb73c0bcc5
Reviewed-on: http://git-master/r/1151
Reviewed-by: Gary King <gking@nvidia.com>
Tested-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/include/asm/cacheflush.h | 45 | ||||
-rw-r--r-- | arch/arm/include/asm/outercache.h | 84 | ||||
-rw-r--r-- | arch/arm/mach-tegra/init_common.c | 13 | ||||
-rw-r--r-- | arch/arm/mm/Kconfig | 12 | ||||
-rw-r--r-- | arch/arm/mm/cache-l2x0.c | 270 |
5 files changed, 302 insertions, 122 deletions
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h index de24d5190b08..2223ecf5e0db 100644 --- a/arch/arm/include/asm/cacheflush.h +++ b/arch/arm/include/asm/cacheflush.h @@ -15,6 +15,7 @@ #include <asm/glue.h> #include <asm/shmparam.h> #include <asm/cachetype.h> +#include <asm/outercache.h> #define CACHE_COLOUR(vaddr) ((vaddr & (SHMLBA - 1)) >> PAGE_SHIFT) @@ -212,13 +213,6 @@ struct cpu_cache_fns { void (*dma_flush_all)(void); }; -struct outer_cache_fns { - void (*inv_range)(unsigned long, unsigned long); - void (*clean_range)(unsigned long, unsigned long); - void (*flush_range)(unsigned long, unsigned long); - void (*sync)(void); -}; - /* * Select the calling method */ @@ -343,43 +337,6 @@ static inline void smp_dma_flush_all(void) #define smp_dma_flush_all dmac_flush_all #endif -#ifdef CONFIG_OUTER_CACHE - -extern struct outer_cache_fns outer_cache; - -static inline void outer_inv_range(unsigned long start, unsigned long end) -{ - if (outer_cache.inv_range) - outer_cache.inv_range(start, end); -} -static inline void outer_clean_range(unsigned long start, unsigned long end) -{ - if (outer_cache.clean_range) - outer_cache.clean_range(start, end); -} -static inline void outer_flush_range(unsigned long start, unsigned long end) -{ - if (outer_cache.flush_range) - outer_cache.flush_range(start, end); -} -static inline void outer_sync(void) -{ - if (outer_cache.sync) - outer_cache.sync(); -} -#else - -static inline void outer_inv_range(unsigned long start, unsigned long end) -{ } -static inline void outer_clean_range(unsigned long start, unsigned long end) -{ } -static inline void outer_flush_range(unsigned long start, unsigned long end) -{ } -static inline void outer_sync(void) -{ } - -#endif - /* * Copy user data from/to a page which is mapped into a different * processes address space. Really, we want to allow our "user diff --git a/arch/arm/include/asm/outercache.h b/arch/arm/include/asm/outercache.h new file mode 100644 index 000000000000..30b265e12b2b --- /dev/null +++ b/arch/arm/include/asm/outercache.h @@ -0,0 +1,84 @@ +/* + * arch/arm/include/asm/outercache.h + * + * Copyright (C) 2010 ARM Ltd. + * Written by Catalin Marinas <catalin.marinas@arm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ASM_OUTERCACHE_H +#define __ASM_OUTERCACHE_H + +struct outer_cache_fns { + void (*inv_range)(unsigned long, unsigned long); + void (*clean_range)(unsigned long, unsigned long); + void (*flush_range)(unsigned long, unsigned long); + void (*shutdown)(void); +#ifdef CONFIG_OUTER_CACHE_SYNC + void (*sync)(void); +#endif +}; + +#ifdef CONFIG_OUTER_CACHE + +extern struct outer_cache_fns outer_cache; + +static inline void outer_inv_range(unsigned long start, unsigned long end) +{ + if (outer_cache.inv_range) + outer_cache.inv_range(start, end); +} +static inline void outer_clean_range(unsigned long start, unsigned long end) +{ + if (outer_cache.clean_range) + outer_cache.clean_range(start, end); +} +static inline void outer_flush_range(unsigned long start, unsigned long end) +{ + if (outer_cache.flush_range) + outer_cache.flush_range(start, end); +} + +static inline void outer_shutdown(void) +{ + if (outer_cache.shutdown) + outer_cache.shutdown(); +} + +#else + +static inline void outer_inv_range(unsigned long start, unsigned long end) +{ } +static inline void outer_clean_range(unsigned long start, unsigned long end) +{ } +static inline void outer_flush_range(unsigned long start, unsigned long end) +{ } +static inline void outer_shutdown(void) +{ } + +#endif + +#ifdef CONFIG_OUTER_CACHE_SYNC +static inline void outer_sync(void) +{ + if (outer_cache.sync) + outer_cache.sync(); +} +#else +static inline void outer_sync(void) +{ } +#endif + +#endif /* __ASM_OUTERCACHE_H */ diff --git a/arch/arm/mach-tegra/init_common.c b/arch/arm/mach-tegra/init_common.c index d5fae385502c..7d3185b06ae4 100644 --- a/arch/arm/mach-tegra/init_common.c +++ b/arch/arm/mach-tegra/init_common.c @@ -47,6 +47,8 @@ #include "nvrm_interrupt.h" #include "ap20/arusb.h" #include "linux/nvmem_ioctl.h" +#include <asm/cacheflush.h> +#include <asm/outercache.h> const char *tegra_partition_list = NULL; char *tegra_boot_device = NULL; @@ -1003,6 +1005,16 @@ extern int __init tegra_dma_init(void); #define tegra_dma_init() do {} while (0) #endif +extern int disable_nonboot_cpus(void); + +static void tegra_machine_restart(char mode) +{ + disable_nonboot_cpus(); + flush_cache_all(); + outer_shutdown(); + arm_machine_restart(mode); +} + void __init tegra_common_init(void) { if (tegra_get_module_inst_size("iram", 0)) { @@ -1024,6 +1036,7 @@ void __init tegra_common_init(void) tegra_register_usb(); tegra_register_w1(); tegra_wake_init(); + arm_pm_restart = tegra_machine_restart; #ifdef CONFIG_PM #ifdef MACH_TEGRA_GENERIC_DEBUG /* This is needed to get prints on UART diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index d03fd0c90a7e..b99b9ecd217e 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -733,8 +733,14 @@ config NEEDS_SYSCALL_FOR_CMPXCHG It is just not possible. config OUTER_CACHE - bool - default n + bool + +config OUTER_CACHE_SYNC + bool + help + The outer cache has a outer_cache_fns.sync function pointer + that can be used to drain the write buffer of the outer cache. + config CACHE_FEROCEON_L2 bool "Enable the Feroceon L2 cache controller" @@ -765,6 +771,7 @@ config CACHE_L2X0 depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || REALVIEW_EB_A9MP || MACH_REALVIEW_PBX default y select OUTER_CACHE + select OUTER_CACHE_SYNC help This option enables the L2x0 PrimeCell. @@ -773,6 +780,7 @@ config CACHE_PL3X0 depends on ARCH_TEGRA_2x_SOC default y select OUTER_CACHE + select OUTER_CACHE_SYNC help This option enables the PL3x0 PrimeCell. diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 1daa4d09e8f3..6ccf67a57725 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -1,5 +1,5 @@ /* - * arch/arm/mm/cache-l2x0.c - L210/L220/PL310 cache controller support + * arch/arm/mm/cache-l2x0.c - L210/L220 cache controller support * * Copyright (C) 2007 ARM Limited * @@ -26,137 +26,247 @@ #define CACHE_LINE_SIZE 32 static void __iomem *l2x0_base; -static DEFINE_SPINLOCK(l2x0_lock); bool l2x0_disabled; -static inline void sync_writel(unsigned long val, unsigned long reg, - unsigned long complete_mask) +#ifdef CONFIG_CACHE_PL310 +static inline void cache_wait(void __iomem *reg, unsigned long mask) { - unsigned long flags; + /* cache operations are atomic */ +} + +#define l2x0_lock(lock, flags) ((void)(flags)) +#define l2x0_unlock(lock, flags) ((void)(flags)) + +#define block_end(start, end) (end) - spin_lock_irqsave(&l2x0_lock, flags); -#ifdef CONFIG_ARM_ERRATA_484863 - asm volatile("swp %0, %0, [%1]\n" : "+r" (val) : "r" (l2x0_base + reg)); +#define L2CC_TYPE "PL310/L2C-310" #else - writel(val, l2x0_base + reg); +static inline void cache_wait(void __iomem *reg, unsigned long mask) +{ + /* wait for the operation to complete */ + while (readl(reg) & mask) + ; +} + +static DEFINE_SPINLOCK(l2x0_lock); +#define l2x0_lock(lock, flags) spin_lock_irqsave(lock, flags) +#define l2x0_unlock(lock, flags) spin_unlock_irqrestore(lock, flags) + +#define block_end(start, end) ((start) + min((end) - (start), 4096UL)) + +#define L2CC_TYPE "L2x0" #endif + +static inline void cache_wait_always(void __iomem *reg, unsigned long mask) +{ /* wait for the operation to complete */ - while (readl(l2x0_base + reg) & complete_mask) + while (readl(reg) & mask) ; - spin_unlock_irqrestore(&l2x0_lock, flags); } static inline void cache_sync(void) { - sync_writel(0, L2X0_CACHE_SYNC, 1); + void __iomem *base = l2x0_base; + writel(0, base + L2X0_CACHE_SYNC); + cache_wait(base + L2X0_CACHE_SYNC, 1); } +static inline void l2x0_clean_line(unsigned long addr) +{ + void __iomem *base = l2x0_base; + cache_wait(base + L2X0_CLEAN_LINE_PA, 1); + writel(addr, base + L2X0_CLEAN_LINE_PA); +} -#ifdef CONFIG_CACHE_PL3X0 -static inline void maint_sync_writel(unsigned long val, unsigned long reg, - unsigned long complete_mask) +static inline void l2x0_inv_line(unsigned long addr) { - writel(val, l2x0_base + reg); + void __iomem *base = l2x0_base; + cache_wait(base + L2X0_INV_LINE_PA, 1); + writel(addr, base + L2X0_INV_LINE_PA); } -#define maint_cache_sync() +#ifdef CONFIG_PL310_ERRATA_588369 +static void debug_writel(unsigned long val) +{ + extern void omap_smc1(u32 fn, u32 arg); + /* + * Texas Instrument secure monitor api to modify the + * PL310 Debug Control Register. + */ + omap_smc1(0x100, val); +} + +static inline void l2x0_flush_line(unsigned long addr) +{ + void __iomem *base = l2x0_base; + + /* Clean by PA followed by Invalidate by PA */ + cache_wait(base + L2X0_CLEAN_LINE_PA, 1); + writel(addr, base + L2X0_CLEAN_LINE_PA); + cache_wait(base + L2X0_INV_LINE_PA, 1); + writel(addr, base + L2X0_INV_LINE_PA); +} #else -#define maint_sync_writel sync_writel -#define maint_cache_sync() cache_sync() +/* Optimised out for non-errata case */ +static inline void debug_writel(unsigned long val) +{ +} +static inline void l2x0_flush_line(unsigned long addr) +{ + void __iomem *base = l2x0_base; + cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1); + writel(addr, base + L2X0_CLEAN_INV_LINE_PA); +} #endif +static void l2x0_cache_sync(void) +{ + unsigned long flags; + + l2x0_lock(&l2x0_lock, flags); + cache_sync(); + l2x0_unlock(&l2x0_lock, flags); +} + static inline void l2x0_inv_all(void) { + unsigned long flags; + /* invalidate all ways */ - sync_writel(0xff, L2X0_INV_WAY, 0xff); + l2x0_lock(&l2x0_lock, flags); + writel(0xff, l2x0_base + L2X0_INV_WAY); + cache_wait_always(l2x0_base + L2X0_INV_WAY, 0xff); cache_sync(); + l2x0_unlock(&l2x0_lock, flags); +} + +static inline void l2x0_flush_all(void) +{ + unsigned long flags; + + /* flush all ways */ + l2x0_lock(&l2x0_lock, flags); + writel(0xff, l2x0_base + L2X0_CLEAN_INV_WAY); + cache_wait_always(l2x0_base + L2X0_CLEAN_INV_WAY, 0xff); + cache_sync(); + l2x0_unlock(&l2x0_lock, flags); } static void l2x0_inv_range(unsigned long start, unsigned long end) { - unsigned long addr; + void __iomem *base = l2x0_base; + unsigned long flags; + l2x0_lock(&l2x0_lock, flags); if (start & (CACHE_LINE_SIZE - 1)) { start &= ~(CACHE_LINE_SIZE - 1); - maint_sync_writel(start, L2X0_CLEAN_INV_LINE_PA, 1); + debug_writel(0x03); + l2x0_flush_line(start); + debug_writel(0x00); start += CACHE_LINE_SIZE; } if (end & (CACHE_LINE_SIZE - 1)) { end &= ~(CACHE_LINE_SIZE - 1); - maint_sync_writel(end, L2X0_CLEAN_INV_LINE_PA, 1); + debug_writel(0x03); + l2x0_flush_line(end); + debug_writel(0x00); } - for (addr = start; addr < end; addr += CACHE_LINE_SIZE) - maint_sync_writel(addr, L2X0_INV_LINE_PA, 1); - maint_cache_sync(); + while (start < end) { + unsigned long blk_end = block_end(start, end); + + while (start < blk_end) { + l2x0_inv_line(start); + start += CACHE_LINE_SIZE; + } + + if (blk_end < end) { + l2x0_unlock(&l2x0_lock, flags); + l2x0_lock(&l2x0_lock, flags); + } + } + cache_wait(base + L2X0_INV_LINE_PA, 1); + cache_sync(); + l2x0_unlock(&l2x0_lock, flags); } static void l2x0_clean_range(unsigned long start, unsigned long end) { - unsigned long addr; + void __iomem *base = l2x0_base; + unsigned long flags; + l2x0_lock(&l2x0_lock, flags); start &= ~(CACHE_LINE_SIZE - 1); - for (addr = start; addr < end; addr += CACHE_LINE_SIZE) - maint_sync_writel(addr, L2X0_CLEAN_LINE_PA, 1); - maint_cache_sync(); + while (start < end) { + unsigned long blk_end = block_end(start, end); + + while (start < blk_end) { + l2x0_clean_line(start); + start += CACHE_LINE_SIZE; + } + + if (blk_end < end) { + l2x0_unlock(&l2x0_lock, flags); + l2x0_lock(&l2x0_lock, flags); + } + } + cache_wait(base + L2X0_CLEAN_LINE_PA, 1); + cache_sync(); + l2x0_unlock(&l2x0_lock, flags); } static void l2x0_flush_range(unsigned long start, unsigned long end) { - unsigned long addr; + void __iomem *base = l2x0_base; + unsigned long flags; + l2x0_lock(&l2x0_lock, flags); start &= ~(CACHE_LINE_SIZE - 1); - for (addr = start; addr < end; addr += CACHE_LINE_SIZE) - sync_writel(addr, L2X0_CLEAN_INV_LINE_PA, 1); - maint_cache_sync(); -} - -static void l2x0_sync(void) -{ - maint_sync_writel(0, L2X0_CACHE_SYNC, 1); + while (start < end) { + unsigned long blk_end = block_end(start, end); + + debug_writel(0x03); + while (start < blk_end) { + l2x0_flush_line(start); + start += CACHE_LINE_SIZE; + } + debug_writel(0x00); + + if (blk_end < end) { + l2x0_unlock(&l2x0_lock, flags); + l2x0_lock(&l2x0_lock, flags); + } + } + cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1); + cache_sync(); + l2x0_unlock(&l2x0_lock, flags); } -void l2x0_deinit(void) +static void l2x0_shutdown(void) { - /* FIXME: get num_ways from the cache config */ - unsigned int num_ways = 8, i; - unsigned long flags; - - /* this function can leave interrupts disabled for a very long time */ - spin_lock_irqsave(&l2x0_lock, flags); - if (!(readl(l2x0_base + L2X0_CTRL) & 1)) { - spin_unlock_irqrestore(&l2x0_lock, flags); + if (l2x0_disabled) return; - } - /* Lockdown all ways first */ - for (i=0;i<num_ways;i++) { - writel(0xff, l2x0_base + L2X0_LOCKDOWN_WAY_I + i * 4); - writel(0xff, l2x0_base + L2X0_LOCKDOWN_WAY_D + i * 4); + if (readl(l2x0_base + L2X0_CTRL) & 1) { + int m; + /* lockdown all ways, all masters to prevent new line + * allocation during maintenance */ + for (m=0; m<8; m++) { + writel(0xffff, l2x0_base + L2X0_LOCKDOWN_WAY_D + (m*8)); + writel(0xffff, l2x0_base + L2X0_LOCKDOWN_WAY_I + (m*8)); + } + l2x0_flush_all(); + writel(0, l2x0_base + L2X0_CTRL); + /* unlock cache ways */ + for (m=0; m<8; m++) { + writel(0, l2x0_base + L2X0_LOCKDOWN_WAY_D + (m*8)); + writel(0, l2x0_base + L2X0_LOCKDOWN_WAY_I + (m*8)); + } } - - /* flush the entire l2 cache */ - writel(0xff, l2x0_base + L2X0_CLEAN_INV_WAY); - while (readl(l2x0_base + L2X0_CLEAN_INV_WAY) & 0xff) - ; - - writel(0, l2x0_base + L2X0_CACHE_SYNC); - while (readl(l2x0_base + L2X0_CACHE_SYNC) & 0x1) - ; - - /* Unlock all ways */ - for (i=0;i<num_ways;i++) { - writel(0, l2x0_base + L2X0_LOCKDOWN_WAY_I + i * 4); - writel(0, l2x0_base + L2X0_LOCKDOWN_WAY_D + i * 4); - } - - /* disable L2X0 */ - writel(0, l2x0_base + L2X0_CTRL); - spin_unlock_irqrestore(&l2x0_lock, flags); } void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) @@ -164,14 +274,21 @@ void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) __u32 aux; if (l2x0_disabled) { - printk(KERN_INFO "L2X0 cache controller disabled\n"); + pr_info(L2CC_TYPE " cache controller disabled\n"); return; } l2x0_base = base; + /* + * Check if l2x0 controller is already enabled. + * If you are booting from non-secure mode + * accessing the below registers will fault. + */ if (!(readl(l2x0_base + L2X0_CTRL) & 1)) { - /* L2X0 cache controller disabled */ + + /* l2x0 controller is disabled */ + aux = readl(l2x0_base + L2X0_AUX_CTRL); aux &= aux_mask; aux |= aux_val; @@ -186,9 +303,10 @@ void l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask) outer_cache.inv_range = l2x0_inv_range; outer_cache.clean_range = l2x0_clean_range; outer_cache.flush_range = l2x0_flush_range; - outer_cache.sync = l2x0_sync; + outer_cache.sync = l2x0_cache_sync; + outer_cache.shutdown = l2x0_shutdown; - printk(KERN_INFO "L2X0 cache controller enabled\n"); + pr_info(L2CC_TYPE " cache controller enabled\n"); } static int __init l2x0_disable(char *unused) |