diff options
-rw-r--r-- | arch/arm/include/asm/tlbflush.h | 24 | ||||
-rw-r--r-- | arch/arm/mm/Kconfig | 8 | ||||
-rw-r--r-- | arch/arm/mm/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mm/cache-tegra-cmc.c | 341 | ||||
-rw-r--r-- | arch/arm/mm/proc-v6.S | 24 |
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 | \ |