summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/tlbflush.h24
-rw-r--r--arch/arm/mm/Kconfig8
-rw-r--r--arch/arm/mm/Makefile1
-rw-r--r--arch/arm/mm/cache-tegra-cmc.c341
-rw-r--r--arch/arm/mm/proc-v6.S24
5 files changed, 397 insertions, 1 deletions
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index 49cd50de8466..6bcfb84cfa63 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -145,10 +145,17 @@
# define v4wb_always_flags (-1UL)
#endif
+#ifdef CONFIG_ARCH_TEGRA_1x_SOC
+#define v6wbi_tlb_flags (TLB_WB | \
+ TLB_V6_I_FULL | TLB_V6_D_FULL | \
+ TLB_V6_I_PAGE | TLB_V6_D_PAGE | \
+ TLB_V6_I_ASID | TLB_V6_D_ASID)
+#else
#define v6wbi_tlb_flags (TLB_WB | TLB_DCLEAN | \
TLB_V6_I_FULL | TLB_V6_D_FULL | \
TLB_V6_I_PAGE | TLB_V6_D_PAGE | \
TLB_V6_I_ASID | TLB_V6_D_ASID)
+#endif
#ifdef CONFIG_CPU_TLB_V6
# define v6wbi_possible_flags v6wbi_tlb_flags
@@ -188,6 +195,15 @@
#error Unknown TLB model
#endif
+#ifdef CONFIG_ARCH_TEGRA_1x_SOC
+#define TLB_CLEAN_OP tegra_cmc_clean_pmd
+#endif
+
+#ifdef TLB_CLEAN_OP
+extern void TLB_CLEAN_OP(pmd_t *pmd);
+#define tlb_clean_op TLB_CLEAN_OP
+#endif
+
#ifndef __ASSEMBLY__
#include <linux/sched.h>
@@ -470,6 +486,10 @@ static inline void flush_pmd_entry(pmd_t *pmd)
if (tlb_flag(TLB_WB))
dsb();
+
+#if defined(TLB_CLEAN_OP)
+ tlb_clean_op(pmd);
+#endif
}
static inline void clean_pmd_entry(pmd_t *pmd)
@@ -483,6 +503,10 @@ static inline void clean_pmd_entry(pmd_t *pmd)
if (tlb_flag(TLB_L2CLEAN_FR))
asm("mcr p15, 1, %0, c15, c9, 1 @ L2 flush_pmd"
: : "r" (pmd) : "cc");
+
+#if defined(TLB_CLEAN_OP)
+ tlb_clean_op(pmd);
+#endif
}
#undef tlb_flag
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index d4c6c8fe69af..c3626ebef8bc 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -752,6 +752,14 @@ config CACHE_FEROCEON_L2_WRITETHROUGH
Say Y here to use the Feroceon L2 cache in writethrough mode.
Unless you specifically require this, say N for writeback mode.
+config CACHE_TEGRA_CMC
+ bool "Enable the Tegra L2 cache controller"
+ depends on ARCH_TEGRA_1x_SOC
+ help
+ Say Y here to enable the Tegra L2 cache controller and L2 cacheable
+ pagetable walks
+
+
config CACHE_L2X0
bool "Enable the L2x0 outer cache controller"
depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || REALVIEW_EB_A9MP || MACH_REALVIEW_PBX
diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
index b1c548d97842..267ca981702b 100644
--- a/arch/arm/mm/Makefile
+++ b/arch/arm/mm/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_CPU_V7) += proc-v7.o
obj-$(CONFIG_CPU_V7M) += proc-v7m.o
obj-$(CONFIG_CACHE_FEROCEON_L2) += cache-feroceon-l2.o
+obj-$(CONFIG_CACHE_TEGRA_CMC) += cache-tegra-cmc.o
obj-$(CONFIG_CACHE_L2X0) += cache-l2x0.o
obj-$(CONFIG_CACHE_XSC3L2) += cache-xsc3l2.o
diff --git a/arch/arm/mm/cache-tegra-cmc.c b/arch/arm/mm/cache-tegra-cmc.c
new file mode 100644
index 000000000000..8798414a5490
--- /dev/null
+++ b/arch/arm/mm/cache-tegra-cmc.c
@@ -0,0 +1,341 @@
+/*
+ * arch/arm/mm/cache-tegra-cmc.c
+ *
+ * L2 cache controller support and cacheable pagetable walks for Tegra 1x SoCs
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+#include <asm/pgtable-hwdef.h>
+
+#include "mach/nvrm_linux.h"
+
+#include "ap15/arapb_cmc.h"
+#include "ap15/arapb_misc.h"
+#include "ap15/arclk_rst.h"
+#include "ap16/armselect.h"
+
+#include "nvcommon.h"
+#include "nvrm_hardware_access.h"
+#include "nvrm_drf.h"
+#include "nvrm_module.h"
+#include "nvrm_init.h"
+
+#define ENABLE_L2_PTW 1
+#define CACHE_LINE_SIZE 32
+#define CACHE_LINE_MASK (CACHE_LINE_SIZE-1)
+
+static volatile NvU8 * s_pCmc = NULL;
+static int s_HasL2Ptw = 0;
+
+static DEFINE_SPINLOCK(s_CmcLock);
+
+#define L2_POLL(VA) \
+ do { \
+ unsigned int _x; \
+ do { \
+ _x = NV_READ32((VA)+APB_CMC_MAINT_2_0); \
+ } while (NV_DRF_VAL(APB_CMC, MAINT_2, OPCODE, _x)!=0); \
+ } while (0);
+
+
+static inline unsigned long tegra_cmc_mmu_remap(void *va)
+{
+ unsigned long addr = (unsigned long)va;
+ unsigned long ofs = addr & ~(PAGE_MASK);
+ unsigned long phys;
+ unsigned long flags;
+
+ addr &= PAGE_MASK;
+
+ local_irqsave(flags);
+ asm volatile("mcr p15, 0, %0, c7, c8, 0" : : "r"(addr) : "cc");
+ asm volatile("mrc p15, 0, %0, c7, c4, 0" : "=r"(phys) : : "cc");
+ local_irqrestore(flags);
+
+ phys &= PAGE_MASK;
+ phys |= ofs;
+
+ return phys;
+}
+
+static void tegra_cmc_range_op(unsigned long start, unsigned long end,
+ unsigned int operation)
+{
+ unsigned long flags;
+
+ if (!s_pCmc)
+ return;
+
+ start &= ~CACHE_LINE_MASK;
+ spin_lock_irqsave(&s_CmcLock, flags);
+
+ for ( ; start<end ; start += CACHE_LINE_SIZE) {
+ NV_WRITE32(s_pCmc + APB_CMC_MAINT_0_0, pfn);
+ NV_WRITE32(s_pCmc + APB_CMC_MAINT_2_0, operation);
+ L2_POLL(s_pCmc);
+ } while (start<end);
+
+ spin_unlock_irqrestore(&s_CmcLock, flags);
+ dsb();
+}
+
+static void tegra_cmc_inv_range(unsigned long start, unsigned long end)
+{
+ if (start & (CACHE_LINE_SIZE-1)) {
+ start &= ~(CACHE_LINE_SIZE-1);
+ tegra_cmc_range_op(start, start+CACHE_LINE_SIZE,
+ APB_CMC_MAINT_2_0_OPCODE_CLEAN_INVALID_PHY);
+ start += CACHE_LINE_SIZE-1;
+ }
+ if (end & (CACHE_LINE_SIZE-1)) {
+ end &= ~(CACHE_LINE_SIZE-1);
+ tegra_cmc_range_op(end, end+CACHE_LINE_SIZE,
+ APB_CMC_MAINT_2_0_OPCODE_CLEAN_INVALID_PHY);
+ }
+
+ tegra_cmc_range_op(start, end, APB_CMC_MAINT_2_0_OPCODE_INVALID_PHY);
+}
+
+static void tegra_cmc_clean_range(unsigned long start, unsigned long end)
+{
+ tegra_cmc_range_op(start, end, APB_CMC_MAINT_2_0_OPCODE_CLEAN_PHY);
+}
+
+static void tegra_cmc_flush_range(unsigned long start, unsigned long end)
+{
+ tegra_cmc_range_op(start, end,
+ APB_CMC_MAINT_2_0_OPCODE_CLEAN_INVALID_PHY);
+}
+
+void cpu_v6_set_pte_ext(pte_t *ptep, pte_t pte, unsigned int ext)
+{
+#define WBWA (PTE_EXT_TEX(5) | PTE_CACHEABLE | PTE_BUFFERABLE)
+#define SHAREDDEV (PTE_BUFFERABLE)
+#define SO (0)
+ static const NvU32 PteMasks[16] = {
+ SHAREDDEV, // L_PTE_MT_UNCACHED
+ SHAREDDEV, // L_PTE_MT_BUFFERABLE
+ SHAREDDEV, // L_PTE_MT_WRITETROUGH
+ WBWA, // L_PTE_MT_WRITEBACK
+ SHAREDDEV, // L_PTE_MT_DEV_SHARED
+ 0,
+ WBWA, // L_PTE_MT_MINICACHE
+ 0,
+ SHAREDDEV, // L_PTE_MT_DEV_WC
+ 0,
+ SHAREDDEV, // L_PTE_MTD_DEV_CACHED
+ SHAREDDEV, // L_PTE_MTD_DEV_NONSHARED
+ 0,
+ 0,
+ 0
+ };
+
+
+ unsigned long p;
+
+ *ptep = pte;
+ ptep = (pte_t*)(((char*)ptep)-2048);
+ p = pte_val(pte);
+ p &= ~(0x3fc | PTE_TYPE_MASK);
+ p |= (ext | PTE_EXT_AP0 | 2);
+ p |= PteMasks[(pte_val(pte) & L_PTE_MT_MASK)>>2];
+
+ if (!(pte_val(pte) & L_PTE_WRITE) ||
+ !(pte_val(pte) & L_PTE_DIRTY))
+ p |= PTE_EXT_APX;
+
+ if (pte_val(pte) & L_PTE_USER)
+ {
+ p |= PTE_EXT_AP1;
+ if (p & PTE_EXT_APX)
+ p &= ~(PTE_EXT_AP0 | PTE_EXT_APX);
+ }
+
+ if (!(pte_val(pte) & L_PTE_EXEC))
+ p |= PTE_EXT_XN;
+
+
+
+ if (!(pte_val(pte) & L_PTE_YOUNG) ||
+ !(pte_val(pte) & L_PTE_PRESENT))
+ p = 0;
+
+
+ pte_val(pte) = p;
+ *ptep = pte;
+
+
+ asm volatile("mcr p15, 0, %0, c7, c10, 1" : : "r"(ptep) : "cc");
+ dsb();
+
+ if (!s_HasL2Ptw) {
+ unsigned long addr = tegra_cmc_mmu_remap(ptep);
+ tegra_cmc_clean_range(addr, addr+sizeof(*ptep));
+ }
+}
+
+void tegra_cmc_disable(void)
+{
+ if (s_pCmc) {
+ NvU32 Config;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s_CmcLock, flags);
+ NV_WRITE32(s_pCmc + APB_CMC_LOCK_0,
+ NV_DRF_NUM(APB_CMC, LOCK, LOCK_BITMAP, ~0UL));
+ NV_WRITE32(s_pCmc + APB_CMC_MAINT_2_0,
+ NV_DRF_DEF(APB_CMC, MAINT_2, OPCODE, CLEAN_INVALID_WAY)|
+ NV_DRF_NUM(APB_CMC, MAINT_2, WAY_BITMAP, ~0UL));
+ L2_POLL(s_pCmc);
+ Config = NV_READ32(s_pCmc + APB_CMC_CONFIG_0);
+ Config = NV_FLD_SET_DRF_NUM(APB_CMC, CONFIG,
+ ENABLE_CACHE, 0, Config);
+ Config = NV_FLD_SET_DRF_NUM(APB_CMC, CONFIG,
+ ENABLE_STEERING,0, Config);
+ NV_WRITE32(s_pCmc + APB_CMC_CONFIG_0, Config);
+ NV_WRITE32(s_pCmc + APB_CMC_LOCK_0, 0);
+ s_pCmc = NULL;
+ spin_unlock_irqrestore(&s_CmcLock, flags);
+
+ }
+}
+
+void tegra_cmc_clean_pmd(pmd_t *pmd)
+{
+ unsigned long mva = (unsigned long)pmd & ~CACHE_LINE_MASK;
+ unsigned long mpa = tegra_cmc_mmu_remap((void*)mva);
+
+ asm volatile("mcr p15, 0, %0, c7, c10, 1" : : "r"(mva) : "cc");
+ dsb();
+ if (!s_HasL2Ptw) {
+ tegra_cmc_clean_range(mpa, mpa+sizeof(*pmd));
+ }
+}
+
+/* Enables the L2 and L2 PTW, if present */
+void __init tegra_cmc_enable(void)
+{
+ volatile NvU8 *pCar = NULL;
+ volatile NvU8 *pMsl = NULL;
+ volatile NvU8 *pMisc = NULL;
+ NvU32 temp;
+ NvRmPhysAddr pa;
+
+ if (s_pCmc)
+ return;
+
+ NvRmModuleGetBaseAddress(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmPrivModuleID_ClockAndReset, 0), &pa, &temp);
+
+ if (NvRmPhysicalMemMap(pa, temp, NVOS_MEM_READ_WRITE,
+ NvOsMemAttribute_Uncached, (void**)&pCar) != NvSuccess)
+ return;
+
+ NvRmModuleGetBaseAddress(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmModuleID_Misc, 0), &pa, &temp);
+
+ if (NvRmPhysicalMemMap(pa, temp, NVOS_MEM_READ_WRITE,
+ NvOsMemAttribute_Uncached, (void**)&pMisc) != NvSuccess)
+ return;
+
+ /* FIXME: Relocation table for MSelect is wrong, so just
+ * hack the physical address in here for now */
+ if (NvRmPhysicalMemMap(0x50042000UL, 4096, NVOS_MEM_READ_WRITE,
+ NvOsMemAttribute_Uncached, (void**)&pMsl) != NvSuccess)
+ return;
+
+ NvRmModuleGetBaseAddress(s_hRmGlobal,
+ NVRM_MODULE_ID(NvRmModuleID_CacheMemCtrl, 0), &pa, &temp);
+
+ if (NvRmPhysicalMemMap(pa, temp, NVOS_MEM_READ_WRITE,
+ NvOsMemAttribute_Uncached, (void**)&s_pCmc) != NvSuccess)
+ return;
+
+
+ // Take L2 out of reset.
+ temp = NV_READ32(pCar + CLK_RST_CONTROLLER_RST_DEVICES_L_0);
+ temp = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, RST_DEVICES_L,
+ SWR_CACHE1_RST, 0, temp);
+ NV_WRITE32(pCar + CLK_RST_CONTROLLER_RST_DEVICES_L_0, temp);
+
+ // Enable clock to CMC and cache RAMs
+ temp = NV_READ32(pCar + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0);
+ temp = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, CLK_OUT_ENB_L,
+ CLK_ENB_CACHE1, 1, temp);
+ NV_WRITE32(pCar + CLK_RST_CONTROLLER_CLK_OUT_ENB_L_0, temp);
+
+ temp = NV_READ32(pCar + CLK_RST_CONTROLLER_MISC_CLK_ENB_0);
+ temp = NV_FLD_SET_DRF_NUM(CLK_RST_CONTROLLER, MISC_CLK_ENB,
+ CLK_ENB_CRAM1, 1, temp);
+ NV_WRITE32(pCar + CLK_RST_CONTROLLER_MISC_CLK_ENB_0, temp);
+
+ temp = 0;
+ asm volatile("mcr p15, 0, %0, c7, c14, 0" : : "r" (temp) : "memory");
+ dsb();
+
+ // invalidate all ways in L2
+ temp = NV_DRF_NUM(APB_CMC, LOCK, LOCK_BITMAP, ~0UL);
+ NV_WRITE32(s_pCmc + APB_CMC_LOCK_0, temp);
+
+ temp = NV_DRF_DEF(APB_CMC, MAINT_2, OPCODE, INVALID_WAY) |
+ NV_DRF_NUM(APB_CMC, MAINT_2, WAY_BITMAP, ~0UL);
+ NV_WRITE32(s_pCmc + APB_CMC_MAINT_2_0, temp);
+ L2_POLL(s_pCmc);
+
+ temp = NV_READ32(s_pCmc + APB_CMC_CONFIG_0);
+ temp = NV_FLD_SET_DRF_NUM(APB_CMC, CONFIG, ENABLE_STEERING, 1, temp);
+ temp = NV_FLD_SET_DRF_NUM(APB_CMC, CONFIG, ENABLE_CACHE, 1, temp);
+ NV_WRITE32(s_pCmc + APB_CMC_CONFIG_0, temp);
+
+ NV_WRITE32(s_pCmc + APB_CMC_LOCK_0, 0);
+
+ temp = NV_READ32(pMisc + APB_MISC_GP_HIDREV_0);
+ if (NV_DRF_VAL(APB_MISC_GP, HIDREV, CHIPID, temp)>=0x16 &&
+ NV_DRF_VAL(APB_MISC_GP, HIDREV, MAJORREV, temp)!=0 &&
+ (NV_DRF_VAL(APB_MISC_GP, HIDREV, MAJORREV, temp)>0x1 ||
+ NV_DRF_VAL(APB_MISC_GP, HIDREV, MINORREV, temp)>=0x3)) {
+#if ENABLE_L2_PTW
+ temp = NV_READ32(pMsl + MSELECT_CONFIG_0);
+ temp = NV_FLD_SET_DRF_NUM(MSELECT, CONFIG,
+ ENABLE_PTW_L2, 1, temp);
+ NV_WRITE32(pMsl + MSELECT_CONFIG_0, temp);
+ // update TTBR flags for TTB1 to reflect outer WBWA
+ asm volatile("mrc p15, 0, %0, c2, c0, 1" : "=r"(temp) : : "cc");
+ temp |= 0x8; // WBWA
+ asm volatile("mcr p15, 0, %0, c2, c0, 1" : : "r"(temp) : "cc");
+ // update TTBR flags for TTB0 to reflect outer WBWA
+ asm volatile("mrc p15, 0, %0, c2, c0, 0" : "=r"(temp) : : "cc");
+ temp |= 0x8;
+ asm volatile("mcr p15, 0, %0, c2, c0, 0" : : "r"(temp) : "cc");
+ s_HasL2Ptw = 1;
+#else
+ printk("L2 Page table walk supported, not enabled\n");
+#endif
+ }
+
+ outer_cache.inv_range = tegra_cmc_inv_range;
+ outer_cache.clean_range = tegra_cmc_clean_range;
+ outer_cache.flush_range = tegra_cmc_flush_range;
+}
+
diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S
index 7959567970c7..69381989644d 100644
--- a/arch/arm/mm/proc-v6.S
+++ b/arch/arm/mm/proc-v6.S
@@ -29,11 +29,15 @@
#define TTB_RGN_WT (2 << 3)
#define TTB_RGN_WB (3 << 3)
+#ifdef CONFIG_ARCH_TEGRA_1x_SOC
+#define TTB_FLAGS TTB_RGN_NC
+#else
#ifndef CONFIG_SMP
#define TTB_FLAGS TTB_RGN_WBWA
#else
#define TTB_FLAGS TTB_RGN_WBWA|TTB_S
#endif
+#endif
ENTRY(cpu_v6_proc_init)
mov pc, lr
@@ -77,12 +81,16 @@ ENTRY(cpu_v6_do_idle)
mov pc, lr
ENTRY(cpu_v6_dcache_clean_area)
+#ifdef CONFIG_ARCH_TEGRA_1x_SOC
+ bl cpu_ap15_dcache_clean_area
+#else
#ifndef TLB_CAN_READ_FROM_L1_CACHE
1: mcr p15, 0, r0, c7, c10, 1 @ clean D entry
add r0, r0, #D_CACHE_LINE_SIZE
subs r1, r1, #D_CACHE_LINE_SIZE
bhi 1b
#endif
+#endif
mov pc, lr
/*
@@ -119,12 +127,13 @@ ENTRY(cpu_v6_switch_mm)
*/
armv6_mt_table cpu_v6
+#if !(defined(CONFIG_ARCH_TEGRA_1x_SOC) && defined(CONFIG_CACHE_TEGRA_CMC))
ENTRY(cpu_v6_set_pte_ext)
#ifdef CONFIG_MMU
armv6_set_pte_ext cpu_v6
#endif
mov pc, lr
-
+#endif
@@ -201,7 +210,11 @@ __v6_setup:
*/
.type v6_crval, #object
v6_crval:
+#ifdef CONFIG_ARCH_TEGRA_1x_SOC
+ crval clear=0x01ffffff, mmuset=0x00c0787f, ucset=0x00c0187c
+#else
crval clear=0x01e0fb7f, mmuset=0x00c0387d, ucset=0x00c0187c
+#endif
.type v6_processor_functions, #object
ENTRY(v6_processor_functions)
@@ -234,6 +247,14 @@ cpu_elf_name:
*/
.type __v6_proc_info, #object
__v6_proc_info:
+#ifdef ARCH_TEGRA_1x_SOC
+ .long 0x410fb020
+ .long 0xfffffff0
+ .long PMD_TYPE_SECT | \
+ PMD_SECT_WBWA | \
+ PMD_SECT_AP_WRITE | \
+ PMD_SECT_AP_READ
+#else
.long 0x0007b000
.long 0x0007f000
.long PMD_TYPE_SECT | \
@@ -241,6 +262,7 @@ __v6_proc_info:
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
+#endif
.long PMD_TYPE_SECT | \
PMD_SECT_XN | \
PMD_SECT_AP_WRITE | \