Merge branch 'omap-gpmc-fixes-for-v3.10' of git://github.com/jonhunter/linux into...
[firefly-linux-kernel-4.4.55.git] / arch / metag / kernel / smp.c
index 4de8fc8e31a5b330ee39e5397f4dd57217fce025..f443ec9a7cbef5278108bd28f99c8cd701d84091 100644 (file)
@@ -28,6 +28,8 @@
 #include <asm/cachepart.h>
 #include <asm/core_reg.h>
 #include <asm/cpu.h>
+#include <asm/global_lock.h>
+#include <asm/metag_mem.h>
 #include <asm/mmu_context.h>
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
@@ -37,6 +39,9 @@
 #include <asm/hwthread.h>
 #include <asm/traps.h>
 
+#define SYSC_DCPART(n) (SYSC_DCPART0 + SYSC_xCPARTn_STRIDE * (n))
+#define SYSC_ICPART(n) (SYSC_ICPART0 + SYSC_xCPARTn_STRIDE * (n))
+
 DECLARE_PER_CPU(PTBI, pTBI);
 
 void *secondary_data_stack;
@@ -99,6 +104,114 @@ int __cpuinit boot_secondary(unsigned int thread, struct task_struct *idle)
        return 0;
 }
 
+/**
+ * describe_cachepart_change: describe a change to cache partitions.
+ * @thread:    Hardware thread number.
+ * @label:     Label of cache type, e.g. "dcache" or "icache".
+ * @sz:                Total size of the cache.
+ * @old:       Old cache partition configuration (*CPART* register).
+ * @new:       New cache partition configuration (*CPART* register).
+ *
+ * If the cache partition has changed, prints a message to the log describing
+ * those changes.
+ */
+static __cpuinit void describe_cachepart_change(unsigned int thread,
+                                               const char *label,
+                                               unsigned int sz,
+                                               unsigned int old,
+                                               unsigned int new)
+{
+       unsigned int lor1, land1, gor1, gand1;
+       unsigned int lor2, land2, gor2, gand2;
+       unsigned int diff = old ^ new;
+
+       if (!diff)
+               return;
+
+       pr_info("Thread %d: %s partition changed:", thread, label);
+       if (diff & (SYSC_xCPARTL_OR_BITS | SYSC_xCPARTL_AND_BITS)) {
+               lor1   = (old & SYSC_xCPARTL_OR_BITS)  >> SYSC_xCPARTL_OR_S;
+               lor2   = (new & SYSC_xCPARTL_OR_BITS)  >> SYSC_xCPARTL_OR_S;
+               land1  = (old & SYSC_xCPARTL_AND_BITS) >> SYSC_xCPARTL_AND_S;
+               land2  = (new & SYSC_xCPARTL_AND_BITS) >> SYSC_xCPARTL_AND_S;
+               pr_cont(" L:%#x+%#x->%#x+%#x",
+                       (lor1 * sz) >> 4,
+                       ((land1 + 1) * sz) >> 4,
+                       (lor2 * sz) >> 4,
+                       ((land2 + 1) * sz) >> 4);
+       }
+       if (diff & (SYSC_xCPARTG_OR_BITS | SYSC_xCPARTG_AND_BITS)) {
+               gor1   = (old & SYSC_xCPARTG_OR_BITS)  >> SYSC_xCPARTG_OR_S;
+               gor2   = (new & SYSC_xCPARTG_OR_BITS)  >> SYSC_xCPARTG_OR_S;
+               gand1  = (old & SYSC_xCPARTG_AND_BITS) >> SYSC_xCPARTG_AND_S;
+               gand2  = (new & SYSC_xCPARTG_AND_BITS) >> SYSC_xCPARTG_AND_S;
+               pr_cont(" G:%#x+%#x->%#x+%#x",
+                       (gor1 * sz) >> 4,
+                       ((gand1 + 1) * sz) >> 4,
+                       (gor2 * sz) >> 4,
+                       ((gand2 + 1) * sz) >> 4);
+       }
+       if (diff & SYSC_CWRMODE_BIT)
+               pr_cont(" %sWR",
+                       (new & SYSC_CWRMODE_BIT) ? "+" : "-");
+       if (diff & SYSC_DCPART_GCON_BIT)
+               pr_cont(" %sGCOn",
+                       (new & SYSC_DCPART_GCON_BIT) ? "+" : "-");
+       pr_cont("\n");
+}
+
+/**
+ * setup_smp_cache: ensure cache coherency for new SMP thread.
+ * @thread:    New hardware thread number.
+ *
+ * Ensures that coherency is enabled and that the threads share the same cache
+ * partitions.
+ */
+static __cpuinit void setup_smp_cache(unsigned int thread)
+{
+       unsigned int this_thread, lflags;
+       unsigned int dcsz, dcpart_this, dcpart_old, dcpart_new;
+       unsigned int icsz, icpart_old, icpart_new;
+
+       /*
+        * Copy over the current thread's cache partition configuration to the
+        * new thread so that they share cache partitions.
+        */
+       __global_lock2(lflags);
+       this_thread = hard_processor_id();
+       /* Share dcache partition */
+       dcpart_this = metag_in32(SYSC_DCPART(this_thread));
+       dcpart_old = metag_in32(SYSC_DCPART(thread));
+       dcpart_new = dcpart_this;
+#if PAGE_OFFSET < LINGLOBAL_BASE
+       /*
+        * For the local data cache to be coherent the threads must also have
+        * GCOn enabled.
+        */
+       dcpart_new |= SYSC_DCPART_GCON_BIT;
+       metag_out32(dcpart_new, SYSC_DCPART(this_thread));
+#endif
+       metag_out32(dcpart_new, SYSC_DCPART(thread));
+       /* Share icache partition too */
+       icpart_new = metag_in32(SYSC_ICPART(this_thread));
+       icpart_old = metag_in32(SYSC_ICPART(thread));
+       metag_out32(icpart_new, SYSC_ICPART(thread));
+       __global_unlock2(lflags);
+
+       /*
+        * Log if the cache partitions were altered so the user is aware of any
+        * potential unintentional cache wastage.
+        */
+       dcsz = get_dcache_size();
+       icsz = get_dcache_size();
+       describe_cachepart_change(this_thread, "dcache", dcsz,
+                                 dcpart_this, dcpart_new);
+       describe_cachepart_change(thread, "dcache", dcsz,
+                                 dcpart_old, dcpart_new);
+       describe_cachepart_change(thread, "icache", icsz,
+                                 icpart_old, icpart_new);
+}
+
 int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
 {
        unsigned int thread = cpu_2_hwthread_id[cpu];
@@ -108,6 +221,8 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
 
        flush_tlb_all();
 
+       setup_smp_cache(thread);
+
        /*
         * Tell the secondary CPU where to find its idle thread's stack.
         */