summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJitendra Lanka <jlanka@nvidia.com>2010-04-21 10:54:57 +0530
committerGary King <gking@nvidia.com>2010-04-21 09:10:10 -0700
commit35367088ddab9684ea29432ce35d2a3f117a9d6b (patch)
tree08d7a0ab532200619d39b49ef0db48ced7551298
parent97981a1f78f0680aec684d9534aa13333fa4ec45 (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.h45
-rw-r--r--arch/arm/include/asm/outercache.h84
-rw-r--r--arch/arm/mach-tegra/init_common.c13
-rw-r--r--arch/arm/mm/Kconfig12
-rw-r--r--arch/arm/mm/cache-l2x0.c270
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)