summaryrefslogtreecommitdiffstats
path: root/arch/metag/kernel/smp.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-04-30 10:09:39 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-04-30 10:09:39 -0700
commit87c1f0f8c9442c86cbb343b9324bef8312029d7d (patch)
tree47f97d468a83d2f13d08fe9603fc91e23cb60c8e /arch/metag/kernel/smp.c
parent165bc513f7ec54d3fa3660f5c851c4bbc533d577 (diff)
parent164c013858a2e89b450cd8021a8be896f9e05697 (diff)
downloadlinux-87c1f0f8c9442c86cbb343b9324bef8312029d7d.tar.bz2
Merge tag 'metag-for-v3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jhogan/metag
Pull arch/metag update from James Hogan: - Various fixes for the interrupting perf counter handling in metag's perf backend. - Add OProfile support based on perf. - Sets up cache partitions for SMP so bootloader doesn't have to. - Patch from Paul Bolle to remove ARCH_POPULATES_NODE_MAP again (touches microblaze too). - Add TLS pointer regset to metag ptrace api. - Add exported metag DSP extended context handling header <asm/ech.h>. - Increase defconfig log buffer size to 128KiB. - Various fixes, typos, missing exports. * tag 'metag-for-v3.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jhogan/metag: metag: defconfigs: increase log buffer 8KiB => 128KiB metag: avoid unnecessary builtin dtb rebuilds metag: add exported <asm/ech.h> for extended context handling metag: export _metag_da_present and cpu_2_hwthread_id metag: ptrace: Implement NT_METAG_TLS memblock: Kill ARCH_POPULATES_NODE_MAP once more metag: cachepart: fix get_global_dcache_size() typo metag: cachepart: take into account small cache bits metag: smp: copy cache partition and enable GCOn metag: OProfile support metag: perf: prepare for use by oprofile metag: perf: don't reset TXTACTCYC metag: perf: use hard_processor_id() to get thread metag: perf: fix frequency sampling (dynamic period) metag: perf: add missing prev_count updates metag: perf: fixes for interrupting perf counters metag: perf: fix wrap handling in delta calculation metag: perf: fix core internal / perf channel mux
Diffstat (limited to 'arch/metag/kernel/smp.c')
-rw-r--r--arch/metag/kernel/smp.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c
index 4de8fc8e31a5..f443ec9a7cbe 100644
--- a/arch/metag/kernel/smp.c
+++ b/arch/metag/kernel/smp.c
@@ -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.
*/