aboutsummaryrefslogtreecommitdiffstats
path: root/arch/metag
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2013-03-07 12:20:53 -0500
committerJames Hogan <james.hogan@imgtec.com>2013-03-15 09:21:17 -0400
commit9e7129630329d50b8e8c3403bb71c85a7c3cbe35 (patch)
treef925f533798f07d975abebb2c27df4380ad12afd /arch/metag
parent00e6c92304ce38ff48029471c929d31a25e5cf10 (diff)
metag: smp: copy cache partition and enable GCOn
When starting an SMP hardware thread, copy the cache partition configuration so that the threads share the same cache partitions. Also enable the GCOn bit if running in the local half of the virtual address space to enable coherency of shared local cache partitions. An atomic unlock system event is executed by the new cpu before any memory is read to ensure that any writes made by the boot cpu prior to full coherency taking effect are visible to the new cpu. This is to allow SMP to work even when the bootloader hasn't configured the caches for coherency. A log message is printed to describe the cache partition changes so that the user is aware of potential unintentional cache wastage if they've configured the cache partitions in the wrong way. Signed-off-by: James Hogan <james.hogan@imgtec.com>
Diffstat (limited to 'arch/metag')
-rw-r--r--arch/metag/include/asm/metag_mem.h3
-rw-r--r--arch/metag/kernel/head.S8
-rw-r--r--arch/metag/kernel/smp.c115
3 files changed, 126 insertions, 0 deletions
diff --git a/arch/metag/include/asm/metag_mem.h b/arch/metag/include/asm/metag_mem.h
index 3f7b54d8ccac..aa5a076df439 100644
--- a/arch/metag/include/asm/metag_mem.h
+++ b/arch/metag/include/asm/metag_mem.h
@@ -700,6 +700,9 @@
700#define SYSC_xCPARTG_AND_S 8 700#define SYSC_xCPARTG_AND_S 8
701#define SYSC_xCPARTL_OR_BITS 0x000F0000 /* Ors into top 4 bits */ 701#define SYSC_xCPARTL_OR_BITS 0x000F0000 /* Ors into top 4 bits */
702#define SYSC_xCPARTL_OR_S 16 702#define SYSC_xCPARTL_OR_S 16
703#ifdef METAC_2_1
704#define SYSC_DCPART_GCON_BIT 0x00100000 /* Coherent shared local */
705#endif /* METAC_2_1 */
703#define SYSC_xCPARTG_OR_BITS 0x0F000000 /* Ors into top 4 bits */ 706#define SYSC_xCPARTG_OR_BITS 0x0F000000 /* Ors into top 4 bits */
704#define SYSC_xCPARTG_OR_S 24 707#define SYSC_xCPARTG_OR_S 24
705#define SYSC_CWRMODE_BIT 0x80000000 /* Write cache mode bit */ 708#define SYSC_CWRMODE_BIT 0x80000000 /* Write cache mode bit */
diff --git a/arch/metag/kernel/head.S b/arch/metag/kernel/head.S
index 969dffabc03a..713f71d1bdfe 100644
--- a/arch/metag/kernel/head.S
+++ b/arch/metag/kernel/head.S
@@ -1,6 +1,7 @@
1 ! Copyright 2005,2006,2007,2009 Imagination Technologies 1 ! Copyright 2005,2006,2007,2009 Imagination Technologies
2 2
3#include <linux/init.h> 3#include <linux/init.h>
4#include <asm/metag_mem.h>
4#include <generated/asm-offsets.h> 5#include <generated/asm-offsets.h>
5#undef __exit 6#undef __exit
6 7
@@ -48,6 +49,13 @@ __exit:
48 .global _secondary_startup 49 .global _secondary_startup
49 .type _secondary_startup,function 50 .type _secondary_startup,function
50_secondary_startup: 51_secondary_startup:
52#if CONFIG_PAGE_OFFSET < LINGLOBAL_BASE
53 ! In case GCOn has just been turned on we need to fence any writes that
54 ! the boot thread might have performed prior to coherency taking effect.
55 MOVT D0Re0,#HI(LINSYSEVENT_WR_ATOMIC_UNLOCK)
56 MOV D1Re0,#0
57 SETD [D0Re0], D1Re0
58#endif
51 MOVT A0StP,#HI(_secondary_data_stack) 59 MOVT A0StP,#HI(_secondary_data_stack)
52 ADD A0StP,A0StP,#LO(_secondary_data_stack) 60 ADD A0StP,A0StP,#LO(_secondary_data_stack)
53 GETD A0StP,[A0StP] 61 GETD A0StP,[A0StP]
diff --git a/arch/metag/kernel/smp.c b/arch/metag/kernel/smp.c
index 4b6d1f14df32..4e7751ac75d2 100644
--- a/arch/metag/kernel/smp.c
+++ b/arch/metag/kernel/smp.c
@@ -28,6 +28,8 @@
28#include <asm/cachepart.h> 28#include <asm/cachepart.h>
29#include <asm/core_reg.h> 29#include <asm/core_reg.h>
30#include <asm/cpu.h> 30#include <asm/cpu.h>
31#include <asm/global_lock.h>
32#include <asm/metag_mem.h>
31#include <asm/mmu_context.h> 33#include <asm/mmu_context.h>
32#include <asm/pgtable.h> 34#include <asm/pgtable.h>
33#include <asm/pgalloc.h> 35#include <asm/pgalloc.h>
@@ -37,6 +39,9 @@
37#include <asm/hwthread.h> 39#include <asm/hwthread.h>
38#include <asm/traps.h> 40#include <asm/traps.h>
39 41
42#define SYSC_DCPART(n) (SYSC_DCPART0 + SYSC_xCPARTn_STRIDE * (n))
43#define SYSC_ICPART(n) (SYSC_ICPART0 + SYSC_xCPARTn_STRIDE * (n))
44
40DECLARE_PER_CPU(PTBI, pTBI); 45DECLARE_PER_CPU(PTBI, pTBI);
41 46
42void *secondary_data_stack; 47void *secondary_data_stack;
@@ -99,6 +104,114 @@ int __cpuinit boot_secondary(unsigned int thread, struct task_struct *idle)
99 return 0; 104 return 0;
100} 105}
101 106
107/**
108 * describe_cachepart_change: describe a change to cache partitions.
109 * @thread: Hardware thread number.
110 * @label: Label of cache type, e.g. "dcache" or "icache".
111 * @sz: Total size of the cache.
112 * @old: Old cache partition configuration (*CPART* register).
113 * @new: New cache partition configuration (*CPART* register).
114 *
115 * If the cache partition has changed, prints a message to the log describing
116 * those changes.
117 */
118static __cpuinit void describe_cachepart_change(unsigned int thread,
119 const char *label,
120 unsigned int sz,
121 unsigned int old,
122 unsigned int new)
123{
124 unsigned int lor1, land1, gor1, gand1;
125 unsigned int lor2, land2, gor2, gand2;
126 unsigned int diff = old ^ new;
127
128 if (!diff)
129 return;
130
131 pr_info("Thread %d: %s partition changed:", thread, label);
132 if (diff & (SYSC_xCPARTL_OR_BITS | SYSC_xCPARTL_AND_BITS)) {
133 lor1 = (old & SYSC_xCPARTL_OR_BITS) >> SYSC_xCPARTL_OR_S;
134 lor2 = (new & SYSC_xCPARTL_OR_BITS) >> SYSC_xCPARTL_OR_S;
135 land1 = (old & SYSC_xCPARTL_AND_BITS) >> SYSC_xCPARTL_AND_S;
136 land2 = (new & SYSC_xCPARTL_AND_BITS) >> SYSC_xCPARTL_AND_S;
137 pr_cont(" L:%#x+%#x->%#x+%#x",
138 (lor1 * sz) >> 4,
139 ((land1 + 1) * sz) >> 4,
140 (lor2 * sz) >> 4,
141 ((land2 + 1) * sz) >> 4);
142 }
143 if (diff & (SYSC_xCPARTG_OR_BITS | SYSC_xCPARTG_AND_BITS)) {
144 gor1 = (old & SYSC_xCPARTG_OR_BITS) >> SYSC_xCPARTG_OR_S;
145 gor2 = (new & SYSC_xCPARTG_OR_BITS) >> SYSC_xCPARTG_OR_S;
146 gand1 = (old & SYSC_xCPARTG_AND_BITS) >> SYSC_xCPARTG_AND_S;
147 gand2 = (new & SYSC_xCPARTG_AND_BITS) >> SYSC_xCPARTG_AND_S;
148 pr_cont(" G:%#x+%#x->%#x+%#x",
149 (gor1 * sz) >> 4,
150 ((gand1 + 1) * sz) >> 4,
151 (gor2 * sz) >> 4,
152 ((gand2 + 1) * sz) >> 4);
153 }
154 if (diff & SYSC_CWRMODE_BIT)
155 pr_cont(" %sWR",
156 (new & SYSC_CWRMODE_BIT) ? "+" : "-");
157 if (diff & SYSC_DCPART_GCON_BIT)
158 pr_cont(" %sGCOn",
159 (new & SYSC_DCPART_GCON_BIT) ? "+" : "-");
160 pr_cont("\n");
161}
162
163/**
164 * setup_smp_cache: ensure cache coherency for new SMP thread.
165 * @thread: New hardware thread number.
166 *
167 * Ensures that coherency is enabled and that the threads share the same cache
168 * partitions.
169 */
170static __cpuinit void setup_smp_cache(unsigned int thread)
171{
172 unsigned int this_thread, lflags;
173 unsigned int dcsz, dcpart_this, dcpart_old, dcpart_new;
174 unsigned int icsz, icpart_old, icpart_new;
175
176 /*
177 * Copy over the current thread's cache partition configuration to the
178 * new thread so that they share cache partitions.
179 */
180 __global_lock2(lflags);
181 this_thread = hard_processor_id();
182 /* Share dcache partition */
183 dcpart_this = metag_in32(SYSC_DCPART(this_thread));
184 dcpart_old = metag_in32(SYSC_DCPART(thread));
185 dcpart_new = dcpart_this;
186#if PAGE_OFFSET < LINGLOBAL_BASE
187 /*
188 * For the local data cache to be coherent the threads must also have
189 * GCOn enabled.
190 */
191 dcpart_new |= SYSC_DCPART_GCON_BIT;
192 metag_out32(dcpart_new, SYSC_DCPART(this_thread));
193#endif
194 metag_out32(dcpart_new, SYSC_DCPART(thread));
195 /* Share icache partition too */
196 icpart_new = metag_in32(SYSC_ICPART(this_thread));
197 icpart_old = metag_in32(SYSC_ICPART(thread));
198 metag_out32(icpart_new, SYSC_ICPART(thread));
199 __global_unlock2(lflags);
200
201 /*
202 * Log if the cache partitions were altered so the user is aware of any
203 * potential unintentional cache wastage.
204 */
205 dcsz = get_dcache_size();
206 icsz = get_dcache_size();
207 describe_cachepart_change(this_thread, "dcache", dcsz,
208 dcpart_this, dcpart_new);
209 describe_cachepart_change(thread, "dcache", dcsz,
210 dcpart_old, dcpart_new);
211 describe_cachepart_change(thread, "icache", icsz,
212 icpart_old, icpart_new);
213}
214
102int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle) 215int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
103{ 216{
104 unsigned int thread = cpu_2_hwthread_id[cpu]; 217 unsigned int thread = cpu_2_hwthread_id[cpu];
@@ -108,6 +221,8 @@ int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
108 221
109 flush_tlb_all(); 222 flush_tlb_all();
110 223
224 setup_smp_cache(thread);
225
111 /* 226 /*
112 * Tell the secondary CPU where to find its idle thread's stack. 227 * Tell the secondary CPU where to find its idle thread's stack.
113 */ 228 */