[ARM] pxa: add support for L2 outer cache on XScale3 (attempt 2)
authorEric Miao <eric.miao@marvell.com>
Fri, 6 Jun 2008 08:34:03 +0000 (16:34 +0800)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 28 Jul 2008 22:13:09 +0000 (23:13 +0100)
(20072fd0c93349e19527dd2fa9588b4335960e62 lost most of its changes
somehow, came from a mbox archive applied with git-am.  No idea
what happened.  This puts back the missing bits.  --rmk)

The initial patch from Lothar, and Lennert make it into a cleaner
one, modified and tested on PXA320 by Eric Miao.

This patch moves the L2 cache operations out of proc-xsc3.S into
dedicated outer cache support code.

CACHE_XSC3L2 can be deselected so no L2 cache specific code will be
linked in, and that L2 enable bit will not be set, this applies to
the following cases:

    a. _only_ PXA300/PXA310 support included and no L2 cache wanted
    b. PXA320 support included, but want L2 be disabled

So the enabling of L2 depends on two things:

    - CACHE_XSC3L2 is selected
    - and L2 cache is present

Where the latter is only a safeguard (previous testing shows it works
OK even when this bit is turned on).

IXP series of processors with XScale3 cannot disable L2 cache for the
moment since they depend on the L2 cache for its coherent memory, so
IXP may always select CACHE_XSC3L2.

Other L2 relevant bits are always turned on (i.e. the original code
enclosed by #if L2_CACHE_ENABLED .. #endif), as they showed no side
effects. Specifically, these bits are:

   - OC bits in TTBASE register (table walk outer cache attributes)
   - LLR Outer Cache Attributes (OC) in Auxiliary Control Register

Signed-off-by: Lothar WaÃ<9f>mann <LW@KARO-electronics.de>
Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Signed-off-by: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mm/Kconfig
arch/arm/mm/cache-xsc3l2.c [new file with mode: 0644]
arch/arm/mm/proc-xsc3.S

index 399d1d6..ed15f87 100644 (file)
@@ -742,3 +742,11 @@ config CACHE_L2X0
        select OUTER_CACHE
        help
          This option enables the L2x0 PrimeCell.
+
+config CACHE_XSC3L2
+       bool "Enable the L2 cache on XScale3"
+       depends on CPU_XSC3
+       default y
+       select OUTER_CACHE
+       help
+         This option enables the L2 cache on XScale3.
diff --git a/arch/arm/mm/cache-xsc3l2.c b/arch/arm/mm/cache-xsc3l2.c
new file mode 100644 (file)
index 0000000..158bd96
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * arch/arm/mm/cache-xsc3l2.c - XScale3 L2 cache controller support
+ *
+ * Copyright (C) 2007 ARM Limited
+ *
+ * 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
+ */
+#include <linux/init.h>
+#include <linux/spinlock.h>
+
+#include <asm/system.h>
+#include <asm/cacheflush.h>
+#include <asm/io.h>
+
+#define CR_L2  (1 << 26)
+
+#define CACHE_LINE_SIZE                32
+#define CACHE_LINE_SHIFT       5
+#define CACHE_WAY_PER_SET      8
+
+#define CACHE_WAY_SIZE(l2ctype)        (8192 << (((l2ctype) >> 8) & 0xf))
+#define CACHE_SET_SIZE(l2ctype)        (CACHE_WAY_SIZE(l2ctype) >> CACHE_LINE_SHIFT)
+
+static inline int xsc3_l2_present(void)
+{
+       unsigned long l2ctype;
+
+       __asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+       return !!(l2ctype & 0xf8);
+}
+
+static inline void xsc3_l2_clean_mva(unsigned long addr)
+{
+       __asm__("mcr p15, 1, %0, c7, c11, 1" : : "r" (addr));
+}
+
+static inline void xsc3_l2_clean_pa(unsigned long addr)
+{
+       xsc3_l2_clean_mva(__phys_to_virt(addr));
+}
+
+static inline void xsc3_l2_inv_mva(unsigned long addr)
+{
+       __asm__("mcr p15, 1, %0, c7, c7, 1" : : "r" (addr));
+}
+
+static inline void xsc3_l2_inv_pa(unsigned long addr)
+{
+       xsc3_l2_inv_mva(__phys_to_virt(addr));
+}
+
+static inline void xsc3_l2_inv_all(void)
+{
+       unsigned long l2ctype, set_way;
+       int set, way;
+
+       __asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+       for (set = 0; set < CACHE_SET_SIZE(l2ctype); set++) {
+               for (way = 0; way < CACHE_WAY_PER_SET; way++) {
+                       set_way = (way << 29) | (set << 5);
+                       __asm__("mcr p15, 1, %0, c7, c11, 2" : : "r"(set_way));
+               }
+       }
+
+       dsb();
+}
+
+static void xsc3_l2_inv_range(unsigned long start, unsigned long end)
+{
+       if (start == 0 && end == -1ul) {
+               xsc3_l2_inv_all();
+               return;
+       }
+
+       /*
+        * Clean and invalidate partial first cache line.
+        */
+       if (start & (CACHE_LINE_SIZE - 1)) {
+               xsc3_l2_clean_pa(start & ~(CACHE_LINE_SIZE - 1));
+               xsc3_l2_inv_pa(start & ~(CACHE_LINE_SIZE - 1));
+               start = (start | (CACHE_LINE_SIZE - 1)) + 1;
+       }
+
+       /*
+        * Clean and invalidate partial last cache line.
+        */
+       if (end & (CACHE_LINE_SIZE - 1)) {
+               xsc3_l2_clean_pa(end & ~(CACHE_LINE_SIZE - 1));
+               xsc3_l2_inv_pa(end & ~(CACHE_LINE_SIZE - 1));
+               end &= ~(CACHE_LINE_SIZE - 1);
+       }
+
+       /*
+        * Invalidate all full cache lines between 'start' and 'end'.
+        */
+       while (start != end) {
+               xsc3_l2_inv_pa(start);
+               start += CACHE_LINE_SIZE;
+       }
+
+       dsb();
+}
+
+static void xsc3_l2_clean_range(unsigned long start, unsigned long end)
+{
+       start &= ~(CACHE_LINE_SIZE - 1);
+       while (start < end) {
+               xsc3_l2_clean_pa(start);
+               start += CACHE_LINE_SIZE;
+       }
+
+       dsb();
+}
+
+/*
+ * optimize L2 flush all operation by set/way format
+ */
+static inline void xsc3_l2_flush_all(void)
+{
+       unsigned long l2ctype, set_way;
+       int set, way;
+
+       __asm__("mrc p15, 1, %0, c0, c0, 1" : "=r" (l2ctype));
+
+       for (set = 0; set < CACHE_SET_SIZE(l2ctype); set++) {
+               for (way = 0; way < CACHE_WAY_PER_SET; way++) {
+                       set_way = (way << 29) | (set << 5);
+                       __asm__("mcr p15, 1, %0, c7, c15, 2" : : "r"(set_way));
+               }
+       }
+
+       dsb();
+}
+
+static void xsc3_l2_flush_range(unsigned long start, unsigned long end)
+{
+       if (start == 0 && end == -1ul) {
+               xsc3_l2_flush_all();
+               return;
+       }
+
+       start &= ~(CACHE_LINE_SIZE - 1);
+       while (start < end) {
+               xsc3_l2_clean_pa(start);
+               xsc3_l2_inv_pa(start);
+               start += CACHE_LINE_SIZE;
+       }
+
+       dsb();
+}
+
+static int __init xsc3_l2_init(void)
+{
+       if (!cpu_is_xsc3() || !xsc3_l2_present())
+               return 0;
+
+       if (!(get_cr() & CR_L2)) {
+               pr_info("XScale3 L2 cache enabled.\n");
+               adjust_cr(CR_L2, CR_L2);
+               xsc3_l2_inv_all();
+       }
+
+       outer_cache.inv_range = xsc3_l2_inv_range;
+       outer_cache.clean_range = xsc3_l2_clean_range;
+       outer_cache.flush_range = xsc3_l2_flush_range;
+
+       return 0;
+}
+core_initcall(xsc3_l2_init);
index 3533741..6ff53c2 100644 (file)
  */
 #define CACHESIZE      32768
 
-/*
- * Run with L2 enabled.
- */
-#define L2_CACHE_ENABLE        1
-
 /*
  * This macro is used to wait for a CP15 write and is needed when we
  * have to ensure that the last operation to the coprocessor was
@@ -265,12 +260,9 @@ ENTRY(xsc3_dma_inv_range)
        tst     r0, #CACHELINESIZE - 1
        bic     r0, r0, #CACHELINESIZE - 1
        mcrne   p15, 0, r0, c7, c10, 1          @ clean L1 D line
-       mcrne   p15, 1, r0, c7, c11, 1          @ clean L2 line
        tst     r1, #CACHELINESIZE - 1
        mcrne   p15, 0, r1, c7, c10, 1          @ clean L1 D line
-       mcrne   p15, 1, r1, c7, c11, 1          @ clean L2 line
 1:     mcr     p15, 0, r0, c7, c6, 1           @ invalidate L1 D line
-       mcr     p15, 1, r0, c7, c7, 1           @ invalidate L2 line
        add     r0, r0, #CACHELINESIZE
        cmp     r0, r1
        blo     1b
@@ -288,7 +280,6 @@ ENTRY(xsc3_dma_inv_range)
 ENTRY(xsc3_dma_clean_range)
        bic     r0, r0, #CACHELINESIZE - 1
 1:     mcr     p15, 0, r0, c7, c10, 1          @ clean L1 D line
-       mcr     p15, 1, r0, c7, c11, 1          @ clean L2 line
        add     r0, r0, #CACHELINESIZE
        cmp     r0, r1
        blo     1b
@@ -306,8 +297,6 @@ ENTRY(xsc3_dma_clean_range)
 ENTRY(xsc3_dma_flush_range)
        bic     r0, r0, #CACHELINESIZE - 1
 1:     mcr     p15, 0, r0, c7, c14, 1          @ clean/invalidate L1 D line
-       mcr     p15, 1, r0, c7, c11, 1          @ clean L2 line
-       mcr     p15, 1, r0, c7, c7, 1           @ invalidate L2 line
        add     r0, r0, #CACHELINESIZE
        cmp     r0, r1
        blo     1b
@@ -347,9 +336,7 @@ ENTRY(cpu_xsc3_switch_mm)
        mcr     p15, 0, ip, c7, c5, 0           @ invalidate L1 I cache and BTB
        mcr     p15, 0, ip, c7, c10, 4          @ data write barrier
        mcr     p15, 0, ip, c7, c5, 4           @ prefetch flush
-#ifdef L2_CACHE_ENABLE
        orr     r0, r0, #0x18                   @ cache the page table in L2
-#endif
        mcr     p15, 0, r0, c2, c0, 0           @ load page table pointer
        mcr     p15, 0, ip, c8, c7, 0           @ invalidate I and D TLBs
        cpwait_ret lr, ip
@@ -378,12 +365,10 @@ ENTRY(cpu_xsc3_set_pte_ext)
        orreq   r2, r2, #PTE_EXT_AP_UNO_SRW     @ yes -> user n/a, system r/w
                                                @ combined with user -> user r/w
 
-#if L2_CACHE_ENABLE
        @ If it's cacheable, it needs to be in L2 also.
        eor     ip, r1, #L_PTE_CACHEABLE
        tst     ip, #L_PTE_CACHEABLE
        orreq   r2, r2, #PTE_EXT_TEX(0x5)
-#endif
 
        tst     r3, #L_PTE_PRESENT | L_PTE_YOUNG        @ present and young?
        movne   r2, #0                          @ no -> fault
@@ -408,9 +393,7 @@ __xsc3_setup:
        mcr     p15, 0, ip, c7, c10, 4          @ data write barrier
        mcr     p15, 0, ip, c7, c5, 4           @ prefetch flush
        mcr     p15, 0, ip, c8, c7, 0           @ invalidate I and D TLBs
-#if L2_CACHE_ENABLE
        orr     r4, r4, #0x18                   @ cache the page table in L2
-#endif
        mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
 
        mov     r0, #0                          @ don't allow CP access
@@ -418,9 +401,7 @@ __xsc3_setup:
 
        mrc     p15, 0, r0, c1, c0, 1           @ get auxiliary control reg
        and     r0, r0, #2                      @ preserve bit P bit setting
-#if L2_CACHE_ENABLE
        orr     r0, r0, #(1 << 10)              @ enable L2 for LLR cache
-#endif
        mcr     p15, 0, r0, c1, c0, 1           @ set auxiliary control reg
 
        adr     r5, xsc3_crval
@@ -429,9 +410,6 @@ __xsc3_setup:
        bic     r0, r0, r5                      @ ..V. ..R. .... ..A.
        orr     r0, r0, r6                      @ ..VI Z..S .... .C.M (mmu)
                                                @ ...I Z..S .... .... (uc)
-#if L2_CACHE_ENABLE
-       orr     r0, r0, #0x04000000             @ L2 enable
-#endif
        mov     pc, lr
 
        .size   __xsc3_setup, . - __xsc3_setup