aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/blackfin/kernel/cplb-mpu/cplbmgr.c')
-rw-r--r--arch/blackfin/kernel/cplb-mpu/cplbmgr.c119
1 files changed, 63 insertions, 56 deletions
diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
index baa52e261f0d..87463ce87f5a 100644
--- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
+++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c
@@ -25,15 +25,21 @@
25#include <asm/cplbinit.h> 25#include <asm/cplbinit.h>
26#include <asm/mmu_context.h> 26#include <asm/mmu_context.h>
27 27
28#define FAULT_RW (1 << 16) 28/*
29#define FAULT_USERSUPV (1 << 17) 29 * WARNING
30 *
31 * This file is compiled with certain -ffixed-reg options. We have to
32 * make sure not to call any functions here that could clobber these
33 * registers.
34 */
30 35
31int page_mask_nelts; 36int page_mask_nelts;
32int page_mask_order; 37int page_mask_order;
33unsigned long *current_rwx_mask; 38unsigned long *current_rwx_mask[NR_CPUS];
34 39
35int nr_dcplb_miss, nr_icplb_miss, nr_icplb_supv_miss, nr_dcplb_prot; 40int nr_dcplb_miss[NR_CPUS], nr_icplb_miss[NR_CPUS];
36int nr_cplb_flush; 41int nr_icplb_supv_miss[NR_CPUS], nr_dcplb_prot[NR_CPUS];
42int nr_cplb_flush[NR_CPUS];
37 43
38static inline void disable_dcplb(void) 44static inline void disable_dcplb(void)
39{ 45{
@@ -98,42 +104,42 @@ static inline int write_permitted(int status, unsigned long data)
98} 104}
99 105
100/* Counters to implement round-robin replacement. */ 106/* Counters to implement round-robin replacement. */
101static int icplb_rr_index, dcplb_rr_index; 107static int icplb_rr_index[NR_CPUS], dcplb_rr_index[NR_CPUS];
102 108
103/* 109/*
104 * Find an ICPLB entry to be evicted and return its index. 110 * Find an ICPLB entry to be evicted and return its index.
105 */ 111 */
106static int evict_one_icplb(void) 112static int evict_one_icplb(unsigned int cpu)
107{ 113{
108 int i; 114 int i;
109 for (i = first_switched_icplb; i < MAX_CPLBS; i++) 115 for (i = first_switched_icplb; i < MAX_CPLBS; i++)
110 if ((icplb_tbl[i].data & CPLB_VALID) == 0) 116 if ((icplb_tbl[cpu][i].data & CPLB_VALID) == 0)
111 return i; 117 return i;
112 i = first_switched_icplb + icplb_rr_index; 118 i = first_switched_icplb + icplb_rr_index[cpu];
113 if (i >= MAX_CPLBS) { 119 if (i >= MAX_CPLBS) {
114 i -= MAX_CPLBS - first_switched_icplb; 120 i -= MAX_CPLBS - first_switched_icplb;
115 icplb_rr_index -= MAX_CPLBS - first_switched_icplb; 121 icplb_rr_index[cpu] -= MAX_CPLBS - first_switched_icplb;
116 } 122 }
117 icplb_rr_index++; 123 icplb_rr_index[cpu]++;
118 return i; 124 return i;
119} 125}
120 126
121static int evict_one_dcplb(void) 127static int evict_one_dcplb(unsigned int cpu)
122{ 128{
123 int i; 129 int i;
124 for (i = first_switched_dcplb; i < MAX_CPLBS; i++) 130 for (i = first_switched_dcplb; i < MAX_CPLBS; i++)
125 if ((dcplb_tbl[i].data & CPLB_VALID) == 0) 131 if ((dcplb_tbl[cpu][i].data & CPLB_VALID) == 0)
126 return i; 132 return i;
127 i = first_switched_dcplb + dcplb_rr_index; 133 i = first_switched_dcplb + dcplb_rr_index[cpu];
128 if (i >= MAX_CPLBS) { 134 if (i >= MAX_CPLBS) {
129 i -= MAX_CPLBS - first_switched_dcplb; 135 i -= MAX_CPLBS - first_switched_dcplb;
130 dcplb_rr_index -= MAX_CPLBS - first_switched_dcplb; 136 dcplb_rr_index[cpu] -= MAX_CPLBS - first_switched_dcplb;
131 } 137 }
132 dcplb_rr_index++; 138 dcplb_rr_index[cpu]++;
133 return i; 139 return i;
134} 140}
135 141
136static noinline int dcplb_miss(void) 142static noinline int dcplb_miss(unsigned int cpu)
137{ 143{
138 unsigned long addr = bfin_read_DCPLB_FAULT_ADDR(); 144 unsigned long addr = bfin_read_DCPLB_FAULT_ADDR();
139 int status = bfin_read_DCPLB_STATUS(); 145 int status = bfin_read_DCPLB_STATUS();
@@ -141,7 +147,7 @@ static noinline int dcplb_miss(void)
141 int idx; 147 int idx;
142 unsigned long d_data; 148 unsigned long d_data;
143 149
144 nr_dcplb_miss++; 150 nr_dcplb_miss[cpu]++;
145 151
146 d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; 152 d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
147#ifdef CONFIG_BFIN_DCACHE 153#ifdef CONFIG_BFIN_DCACHE
@@ -168,25 +174,25 @@ static noinline int dcplb_miss(void)
168 } else if (addr >= _ramend) { 174 } else if (addr >= _ramend) {
169 d_data |= CPLB_USER_RD | CPLB_USER_WR; 175 d_data |= CPLB_USER_RD | CPLB_USER_WR;
170 } else { 176 } else {
171 mask = current_rwx_mask; 177 mask = current_rwx_mask[cpu];
172 if (mask) { 178 if (mask) {
173 int page = addr >> PAGE_SHIFT; 179 int page = addr >> PAGE_SHIFT;
174 int offs = page >> 5; 180 int idx = page >> 5;
175 int bit = 1 << (page & 31); 181 int bit = 1 << (page & 31);
176 182
177 if (mask[offs] & bit) 183 if (mask[idx] & bit)
178 d_data |= CPLB_USER_RD; 184 d_data |= CPLB_USER_RD;
179 185
180 mask += page_mask_nelts; 186 mask += page_mask_nelts;
181 if (mask[offs] & bit) 187 if (mask[idx] & bit)
182 d_data |= CPLB_USER_WR; 188 d_data |= CPLB_USER_WR;
183 } 189 }
184 } 190 }
185 idx = evict_one_dcplb(); 191 idx = evict_one_dcplb(cpu);
186 192
187 addr &= PAGE_MASK; 193 addr &= PAGE_MASK;
188 dcplb_tbl[idx].addr = addr; 194 dcplb_tbl[cpu][idx].addr = addr;
189 dcplb_tbl[idx].data = d_data; 195 dcplb_tbl[cpu][idx].data = d_data;
190 196
191 disable_dcplb(); 197 disable_dcplb();
192 bfin_write32(DCPLB_DATA0 + idx * 4, d_data); 198 bfin_write32(DCPLB_DATA0 + idx * 4, d_data);
@@ -196,21 +202,21 @@ static noinline int dcplb_miss(void)
196 return 0; 202 return 0;
197} 203}
198 204
199static noinline int icplb_miss(void) 205static noinline int icplb_miss(unsigned int cpu)
200{ 206{
201 unsigned long addr = bfin_read_ICPLB_FAULT_ADDR(); 207 unsigned long addr = bfin_read_ICPLB_FAULT_ADDR();
202 int status = bfin_read_ICPLB_STATUS(); 208 int status = bfin_read_ICPLB_STATUS();
203 int idx; 209 int idx;
204 unsigned long i_data; 210 unsigned long i_data;
205 211
206 nr_icplb_miss++; 212 nr_icplb_miss[cpu]++;
207 213
208 /* If inside the uncached DMA region, fault. */ 214 /* If inside the uncached DMA region, fault. */
209 if (addr >= _ramend - DMA_UNCACHED_REGION && addr < _ramend) 215 if (addr >= _ramend - DMA_UNCACHED_REGION && addr < _ramend)
210 return CPLB_PROT_VIOL; 216 return CPLB_PROT_VIOL;
211 217
212 if (status & FAULT_USERSUPV) 218 if (status & FAULT_USERSUPV)
213 nr_icplb_supv_miss++; 219 nr_icplb_supv_miss[cpu]++;
214 220
215 /* 221 /*
216 * First, try to find a CPLB that matches this address. If we 222 * First, try to find a CPLB that matches this address. If we
@@ -218,8 +224,8 @@ static noinline int icplb_miss(void)
218 * that the instruction crosses a page boundary. 224 * that the instruction crosses a page boundary.
219 */ 225 */
220 for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) { 226 for (idx = first_switched_icplb; idx < MAX_CPLBS; idx++) {
221 if (icplb_tbl[idx].data & CPLB_VALID) { 227 if (icplb_tbl[cpu][idx].data & CPLB_VALID) {
222 unsigned long this_addr = icplb_tbl[idx].addr; 228 unsigned long this_addr = icplb_tbl[cpu][idx].addr;
223 if (this_addr <= addr && this_addr + PAGE_SIZE > addr) { 229 if (this_addr <= addr && this_addr + PAGE_SIZE > addr) {
224 addr += PAGE_SIZE; 230 addr += PAGE_SIZE;
225 break; 231 break;
@@ -257,23 +263,23 @@ static noinline int icplb_miss(void)
257 * Otherwise, check the x bitmap of the current process. 263 * Otherwise, check the x bitmap of the current process.
258 */ 264 */
259 if (!(status & FAULT_USERSUPV)) { 265 if (!(status & FAULT_USERSUPV)) {
260 unsigned long *mask = current_rwx_mask; 266 unsigned long *mask = current_rwx_mask[cpu];
261 267
262 if (mask) { 268 if (mask) {
263 int page = addr >> PAGE_SHIFT; 269 int page = addr >> PAGE_SHIFT;
264 int offs = page >> 5; 270 int idx = page >> 5;
265 int bit = 1 << (page & 31); 271 int bit = 1 << (page & 31);
266 272
267 mask += 2 * page_mask_nelts; 273 mask += 2 * page_mask_nelts;
268 if (mask[offs] & bit) 274 if (mask[idx] & bit)
269 i_data |= CPLB_USER_RD; 275 i_data |= CPLB_USER_RD;
270 } 276 }
271 } 277 }
272 } 278 }
273 idx = evict_one_icplb(); 279 idx = evict_one_icplb(cpu);
274 addr &= PAGE_MASK; 280 addr &= PAGE_MASK;
275 icplb_tbl[idx].addr = addr; 281 icplb_tbl[cpu][idx].addr = addr;
276 icplb_tbl[idx].data = i_data; 282 icplb_tbl[cpu][idx].data = i_data;
277 283
278 disable_icplb(); 284 disable_icplb();
279 bfin_write32(ICPLB_DATA0 + idx * 4, i_data); 285 bfin_write32(ICPLB_DATA0 + idx * 4, i_data);
@@ -283,19 +289,19 @@ static noinline int icplb_miss(void)
283 return 0; 289 return 0;
284} 290}
285 291
286static noinline int dcplb_protection_fault(void) 292static noinline int dcplb_protection_fault(unsigned int cpu)
287{ 293{
288 int status = bfin_read_DCPLB_STATUS(); 294 int status = bfin_read_DCPLB_STATUS();
289 295
290 nr_dcplb_prot++; 296 nr_dcplb_prot[cpu]++;
291 297
292 if (status & FAULT_RW) { 298 if (status & FAULT_RW) {
293 int idx = faulting_cplb_index(status); 299 int idx = faulting_cplb_index(status);
294 unsigned long data = dcplb_tbl[idx].data; 300 unsigned long data = dcplb_tbl[cpu][idx].data;
295 if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) && 301 if (!(data & CPLB_WT) && !(data & CPLB_DIRTY) &&
296 write_permitted(status, data)) { 302 write_permitted(status, data)) {
297 data |= CPLB_DIRTY; 303 data |= CPLB_DIRTY;
298 dcplb_tbl[idx].data = data; 304 dcplb_tbl[cpu][idx].data = data;
299 bfin_write32(DCPLB_DATA0 + idx * 4, data); 305 bfin_write32(DCPLB_DATA0 + idx * 4, data);
300 return 0; 306 return 0;
301 } 307 }
@@ -306,44 +312,45 @@ static noinline int dcplb_protection_fault(void)
306int cplb_hdr(int seqstat, struct pt_regs *regs) 312int cplb_hdr(int seqstat, struct pt_regs *regs)
307{ 313{
308 int cause = seqstat & 0x3f; 314 int cause = seqstat & 0x3f;
315 unsigned int cpu = smp_processor_id();
309 switch (cause) { 316 switch (cause) {
310 case 0x23: 317 case 0x23:
311 return dcplb_protection_fault(); 318 return dcplb_protection_fault(cpu);
312 case 0x2C: 319 case 0x2C:
313 return icplb_miss(); 320 return icplb_miss(cpu);
314 case 0x26: 321 case 0x26:
315 return dcplb_miss(); 322 return dcplb_miss(cpu);
316 default: 323 default:
317 return 1; 324 return 1;
318 } 325 }
319} 326}
320 327
321void flush_switched_cplbs(void) 328void flush_switched_cplbs(unsigned int cpu)
322{ 329{
323 int i; 330 int i;
324 unsigned long flags; 331 unsigned long flags;
325 332
326 nr_cplb_flush++; 333 nr_cplb_flush[cpu]++;
327 334
328 local_irq_save(flags); 335 local_irq_save_hw(flags);
329 disable_icplb(); 336 disable_icplb();
330 for (i = first_switched_icplb; i < MAX_CPLBS; i++) { 337 for (i = first_switched_icplb; i < MAX_CPLBS; i++) {
331 icplb_tbl[i].data = 0; 338 icplb_tbl[cpu][i].data = 0;
332 bfin_write32(ICPLB_DATA0 + i * 4, 0); 339 bfin_write32(ICPLB_DATA0 + i * 4, 0);
333 } 340 }
334 enable_icplb(); 341 enable_icplb();
335 342
336 disable_dcplb(); 343 disable_dcplb();
337 for (i = first_switched_dcplb; i < MAX_CPLBS; i++) { 344 for (i = first_switched_dcplb; i < MAX_CPLBS; i++) {
338 dcplb_tbl[i].data = 0; 345 dcplb_tbl[cpu][i].data = 0;
339 bfin_write32(DCPLB_DATA0 + i * 4, 0); 346 bfin_write32(DCPLB_DATA0 + i * 4, 0);
340 } 347 }
341 enable_dcplb(); 348 enable_dcplb();
342 local_irq_restore(flags); 349 local_irq_restore_hw(flags);
343 350
344} 351}
345 352
346void set_mask_dcplbs(unsigned long *masks) 353void set_mask_dcplbs(unsigned long *masks, unsigned int cpu)
347{ 354{
348 int i; 355 int i;
349 unsigned long addr = (unsigned long)masks; 356 unsigned long addr = (unsigned long)masks;
@@ -351,12 +358,12 @@ void set_mask_dcplbs(unsigned long *masks)
351 unsigned long flags; 358 unsigned long flags;
352 359
353 if (!masks) { 360 if (!masks) {
354 current_rwx_mask = masks; 361 current_rwx_mask[cpu] = masks;
355 return; 362 return;
356 } 363 }
357 364
358 local_irq_save(flags); 365 local_irq_save_hw(flags);
359 current_rwx_mask = masks; 366 current_rwx_mask[cpu] = masks;
360 367
361 d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; 368 d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB;
362#ifdef CONFIG_BFIN_DCACHE 369#ifdef CONFIG_BFIN_DCACHE
@@ -368,12 +375,12 @@ void set_mask_dcplbs(unsigned long *masks)
368 375
369 disable_dcplb(); 376 disable_dcplb();
370 for (i = first_mask_dcplb; i < first_switched_dcplb; i++) { 377 for (i = first_mask_dcplb; i < first_switched_dcplb; i++) {
371 dcplb_tbl[i].addr = addr; 378 dcplb_tbl[cpu][i].addr = addr;
372 dcplb_tbl[i].data = d_data; 379 dcplb_tbl[cpu][i].data = d_data;
373 bfin_write32(DCPLB_DATA0 + i * 4, d_data); 380 bfin_write32(DCPLB_DATA0 + i * 4, d_data);
374 bfin_write32(DCPLB_ADDR0 + i * 4, addr); 381 bfin_write32(DCPLB_ADDR0 + i * 4, addr);
375 addr += PAGE_SIZE; 382 addr += PAGE_SIZE;
376 } 383 }
377 enable_dcplb(); 384 enable_dcplb();
378 local_irq_restore(flags); 385 local_irq_restore_hw(flags);
379} 386}