Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[firefly-linux-kernel-4.4.55.git] / arch / metag / mm / cache.c
index 89da74a80ea728a549ccefd0c2ae1af502d324c7..b5d3b2e7c1606b3fc30500fd6064e11a96343a6e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/io.h>
 #include <asm/cacheflush.h>
 #include <asm/core_reg.h>
+#include <asm/global_lock.h>
 #include <asm/metag_isa.h>
 #include <asm/metag_mem.h>
 #include <asm/metag_regs.h>
@@ -36,6 +37,82 @@ static int icache_set_shift = METAG_TBI_CACHE_SIZE_BASE_LOG2
 static unsigned char dcache_sets_log2 = DEFAULT_CACHE_WAYS_LOG2;
 static unsigned char icache_sets_log2 = DEFAULT_CACHE_WAYS_LOG2;
 
+#ifndef CONFIG_METAG_META12
+/**
+ * metag_lnkget_probe() - Probe whether lnkget/lnkset go around the cache
+ */
+static volatile u32 lnkget_testdata[16] __initdata __aligned(64);
+
+#define LNKGET_CONSTANT 0xdeadbeef
+
+void __init metag_lnkget_probe(void)
+{
+       int temp;
+       long flags;
+
+       /*
+        * It's conceivable the user has configured a globally coherent cache
+        * shared with non-Linux hardware threads, so use LOCK2 to prevent them
+        * from executing and causing cache eviction during the test.
+        */
+       __global_lock2(flags);
+
+       /* read a value to bring it into the cache */
+       (void)lnkget_testdata[0];
+       lnkget_testdata[0] = 0;
+
+       /* lnkget/lnkset it to modify it */
+       asm volatile(
+               "1:     LNKGETD %0, [%1]\n"
+               "       LNKSETD [%1], %2\n"
+               "       DEFR    %0, TXSTAT\n"
+               "       ANDT    %0, %0, #HI(0x3f000000)\n"
+               "       CMPT    %0, #HI(0x02000000)\n"
+               "       BNZ     1b\n"
+               : "=&d" (temp)
+               : "da" (&lnkget_testdata[0]), "bd" (LNKGET_CONSTANT)
+               : "cc");
+
+       /* re-read it to see if the cached value changed */
+       temp = lnkget_testdata[0];
+
+       __global_unlock2(flags);
+
+       /* flush the cache line to fix any incoherency */
+       __builtin_dcache_flush((void *)&lnkget_testdata[0]);
+
+#if defined(CONFIG_METAG_LNKGET_AROUND_CACHE)
+       /* if the cache is right, LNKGET_AROUND_CACHE is unnecessary */
+       if (temp == LNKGET_CONSTANT)
+               pr_info("LNKGET/SET go through cache but CONFIG_METAG_LNKGET_AROUND_CACHE=y\n");
+#elif defined(CONFIG_METAG_ATOMICITY_LNKGET)
+       /*
+        * if the cache is wrong, LNKGET_AROUND_CACHE is really necessary
+        * because the kernel is configured to use LNKGET/SET for atomicity
+        */
+       WARN(temp != LNKGET_CONSTANT,
+            "LNKGET/SET go around cache but CONFIG_METAG_LNKGET_AROUND_CACHE=n\n"
+            "Expect kernel failure as it's used for atomicity primitives\n");
+#elif defined(CONFIG_SMP)
+       /*
+        * if the cache is wrong, LNKGET_AROUND_CACHE should be used or the
+        * gateway page won't flush and userland could break.
+        */
+       WARN(temp != LNKGET_CONSTANT,
+            "LNKGET/SET go around cache but CONFIG_METAG_LNKGET_AROUND_CACHE=n\n"
+            "Expect userland failure as it's used for user gateway page\n");
+#else
+       /*
+        * if the cache is wrong, LNKGET_AROUND_CACHE is set wrong, but it
+        * doesn't actually matter as it doesn't have any effect on !SMP &&
+        * !ATOMICITY_LNKGET.
+        */
+       if (temp != LNKGET_CONSTANT)
+               pr_warn("LNKGET/SET go around cache but CONFIG_METAG_LNKGET_AROUND_CACHE=n\n");
+#endif
+}
+#endif /* !CONFIG_METAG_META12 */
+
 /**
  * metag_cache_probe() - Probe L1 cache configuration.
  *
@@ -68,6 +145,8 @@ void __init metag_cache_probe(void)
        dcache_set_shift += (config & METAC_CORECFG2_DCSZ_BITS)
                                >> METAC_CORECFG2_DCSZ_S;
        dcache_set_shift -= dcache_sets_log2;
+
+       metag_lnkget_probe();
 #else
        /* Extract cache sizes from global heap segment */
        unsigned long val, u;
@@ -381,6 +460,7 @@ void metag_code_cache_flush_all(const void *start)
 
        metag_phys_code_cache_flush(start, 4096);
 }
+EXPORT_SYMBOL(metag_code_cache_flush_all);
 
 void metag_code_cache_flush(const void *start, int bytes)
 {