Blackfin arch: SMP supporting patchset: Blackfin CPLB related code
[linux-2.6-block.git] / arch / blackfin / kernel / cplb-nompu / cplbmgr.S
CommitLineData
1394f032
BW
1/*
2 * File: arch/blackfin/mach-common/cplbmgtr.S
3 * Based on:
4 * Author: LG Soft India
5 *
6 * Created: ?
7 * Description: CPLB replacement routine for CPLB mismatch
8 *
9 * Modified:
10 * Copyright 2004-2006 Analog Devices Inc.
11 *
12 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see the file COPYING, or write
26 * to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30/* Usage: int _cplb_mgr(is_data_miss,int enable_cache)
31 * is_data_miss==2 => Mark as Dirty, write to the clean data page
32 * is_data_miss==1 => Replace a data CPLB.
33 * is_data_miss==0 => Replace an instruction CPLB.
34 *
35 * Returns:
36 * CPLB_RELOADED => Successfully updated CPLB table.
37 * CPLB_NO_UNLOCKED => All CPLBs are locked, so cannot be evicted.
38 * This indicates that the CPLBs in the configuration
39 * tablei are badly configured, as this should never
40 * occur.
41 * CPLB_NO_ADDR_MATCH => The address being accessed, that triggered the
42 * exception, is not covered by any of the CPLBs in
43 * the configuration table. The application is
44 * presumably misbehaving.
45 * CPLB_PROT_VIOL => The address being accessed, that triggered the
46 * exception, was not a first-write to a clean Write
47 * Back Data page, and so presumably is a genuine
48 * violation of the page's protection attributes.
49 * The application is misbehaving.
50 */
51
52#include <linux/linkage.h>
53#include <asm/blackfin.h>
54#include <asm/cplb.h>
b8a98989 55#include <asm/asm-offsets.h>
1394f032
BW
56
57#ifdef CONFIG_EXCPT_IRQ_SYSC_L1
58.section .l1.text
59#else
60.text
61#endif
62
63.align 2;
64ENTRY(_cplb_mgr)
65
66 [--SP]=( R7:4,P5:3 );
67
68 CC = R0 == 2;
69 IF CC JUMP .Ldcplb_write;
70
71 CC = R0 == 0;
72 IF !CC JUMP .Ldcplb_miss_compare;
73
74 /* ICPLB Miss Exception. We need to choose one of the
75 * currently-installed CPLBs, and replace it with one
76 * from the configuration table.
6a3f0b46 77 */
1394f032 78
f53e8676
RG
79 /* A multi-word instruction can cross a page boundary. This means the
80 * first part of the instruction can be in a valid page, but the
81 * second part is not, and hence generates the instruction miss.
82 * However, the fault address is for the start of the instruction,
83 * not the part that's in the bad page. Therefore, we have to check
84 * whether the fault address applies to a page that is already present
85 * in the table.
86 */
87
e208f83a
MF
88 P4.L = LO(ICPLB_FAULT_ADDR);
89 P4.H = HI(ICPLB_FAULT_ADDR);
1394f032
BW
90
91 P1 = 16;
92 P5.L = _page_size_table;
93 P5.H = _page_size_table;
94
e208f83a
MF
95 P0.L = LO(ICPLB_DATA0);
96 P0.H = HI(ICPLB_DATA0);
1394f032
BW
97 R4 = [P4]; /* Get faulting address*/
98 R6 = 64; /* Advance past the fault address, which*/
99 R6 = R6 + R4; /* we'll use if we find a match*/
f53e8676 100 R3 = ((16 << 8) | 2); /* Extract mask, two bits at posn 16 */
1394f032
BW
101
102 R5 = 0;
103.Lisearch:
104
105 R1 = [P0-0x100]; /* Address for this CPLB */
106
107 R0 = [P0++]; /* Info for this CPLB*/
108 CC = BITTST(R0,0); /* Is the CPLB valid?*/
109 IF !CC JUMP .Lnomatch; /* Skip it, if not.*/
110 CC = R4 < R1(IU); /* If fault address less than page start*/
111 IF CC JUMP .Lnomatch; /* then skip this one.*/
112 R2 = EXTRACT(R0,R3.L) (Z); /* Get page size*/
113 P1 = R2;
114 P1 = P5 + (P1<<2); /* index into page-size table*/
115 R2 = [P1]; /* Get the page size*/
116 R1 = R1 + R2; /* and add to page start, to get page end*/
117 CC = R4 < R1(IU); /* and see whether fault addr is in page.*/
118 IF !CC R4 = R6; /* If so, advance the address and finish loop.*/
119 IF !CC JUMP .Lisearch_done;
120.Lnomatch:
121 /* Go around again*/
122 R5 += 1;
123 CC = BITTST(R5, 4); /* i.e CC = R5 >= 16*/
124 IF !CC JUMP .Lisearch;
125
126.Lisearch_done:
127 I0 = R4; /* Fault address we'll search for*/
128
129 /* set up pointers */
e208f83a
MF
130 P0.L = LO(ICPLB_DATA0);
131 P0.H = HI(ICPLB_DATA0);
1394f032
BW
132
133 /* The replacement procedure for ICPLBs */
134
e208f83a
MF
135 P4.L = LO(IMEM_CONTROL);
136 P4.H = HI(IMEM_CONTROL);
1394f032 137
f53e8676
RG
138 /* Turn off CPLBs while we work, necessary according to HRM before
139 * modifying CPLB descriptors
140 */
1394f032
BW
141 R5 = [P4]; /* Control Register*/
142 BITCLR(R5,ENICPLB_P);
143 CLI R1;
144 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
145 .align 8;
146 [P4] = R5;
147 SSYNC;
148 STI R1;
149
150 R1 = -1; /* end point comparison */
151 R3 = 16; /* counter */
152
153 /* Search through CPLBs for first non-locked entry */
154 /* Overwrite it by moving everyone else up by 1 */
155.Licheck_lock:
156 R0 = [P0++];
157 R3 = R3 + R1;
158 CC = R3 == R1;
159 IF CC JUMP .Lall_locked;
160 CC = BITTST(R0, 0); /* an invalid entry is good */
161 IF !CC JUMP .Lifound_victim;
162 CC = BITTST(R0,1); /* but a locked entry isn't */
163 IF CC JUMP .Licheck_lock;
164
165.Lifound_victim:
166#ifdef CONFIG_CPLB_INFO
167 R7 = [P0 - 0x104];
b8a98989
GY
168 GET_PDA(P2, R2);
169 P3 = [P2 + PDA_IPDT_SWAPCOUNT];
170 P2 = [P2 + PDA_IPDT];
1394f032
BW
171 P3 += -4;
172.Licount:
173 R2 = [P2]; /* address from config table */
174 P2 += 8;
175 P3 += 8;
176 CC = R2==-1;
177 IF CC JUMP .Licount_done;
178 CC = R7==R2;
179 IF !CC JUMP .Licount;
180 R7 = [P3];
181 R7 += 1;
182 [P3] = R7;
183 CSYNC;
184.Licount_done:
185#endif
186 LC0=R3;
187 LSETUP(.Lis_move,.Lie_move) LC0;
188.Lis_move:
189 R0 = [P0];
190 [P0 - 4] = R0;
191 R0 = [P0 - 0x100];
192 [P0-0x104] = R0;
13fe24f3
RG
193.Lie_move:
194 P0+=4;
195
196 /* Clear ICPLB_DATA15, in case we don't find a replacement
197 * otherwise, we would have a duplicate entry, and will crash
198 */
199 R0 = 0;
200 [P0 - 4] = R0;
1394f032
BW
201
202 /* We've made space in the ICPLB table, so that ICPLB15
203 * is now free to be overwritten. Next, we have to determine
204 * which CPLB we need to install, from the configuration
205 * table. This is a matter of getting the start-of-page
206 * addresses and page-lengths from the config table, and
207 * determining whether the fault address falls within that
208 * range.
209 */
210
b8a98989
GY
211 GET_PDA(P3, R0);
212 P2 = [P3 + PDA_IPDT];
1394f032 213#ifdef CONFIG_CPLB_INFO
b8a98989 214 P3 = [P3 + PDA_IPDT_SWAPCOUNT];
1394f032
BW
215 P3 += -8;
216#endif
217 P0.L = _page_size_table;
218 P0.H = _page_size_table;
219
220 /* Retrieve our fault address (which may have been advanced
221 * because the faulting instruction crossed a page boundary).
222 */
223
224 R0 = I0;
225
226 /* An extraction pattern, to get the page-size bits from
227 * the CPLB data entry. Bits 16-17, so two bits at posn 16.
228 */
229
230 R1 = ((16<<8)|2);
231.Linext: R4 = [P2++]; /* address from config table */
232 R2 = [P2++]; /* data from config table */
233#ifdef CONFIG_CPLB_INFO
234 P3 += 8;
235#endif
236
237 CC = R4 == -1; /* End of config table*/
238 IF CC JUMP .Lno_page_in_table;
239
240 /* See if failed address > start address */
241 CC = R4 <= R0(IU);
6a3f0b46 242 IF !CC JUMP .Linext;
1394f032
BW
243
244 /* extract page size (17:16)*/
245 R3 = EXTRACT(R2, R1.L) (Z);
246
247 /* add page size to addr to get range */
248
249 P5 = R3;
250 P5 = P0 + (P5 << 2); /* scaled, for int access*/
251 R3 = [P5];
252 R3 = R3 + R4;
253
254 /* See if failed address < (start address + page size) */
255 CC = R0 < R3(IU);
256 IF !CC JUMP .Linext;
257
258 /* We've found a CPLB in the config table that covers
259 * the faulting address, so install this CPLB into the
260 * last entry of the table.
261 */
262
e208f83a
MF
263 P1.L = LO(ICPLB_DATA15); /* ICPLB_DATA15 */
264 P1.H = HI(ICPLB_DATA15);
1394f032
BW
265 [P1] = R2;
266 [P1-0x100] = R4;
267#ifdef CONFIG_CPLB_INFO
268 R3 = [P3];
269 R3 += 1;
270 [P3] = R3;
271#endif
272
273 /* P4 points to IMEM_CONTROL, and R5 contains its old
274 * value, after we disabled ICPLBS. Re-enable them.
275 */
276
277 BITSET(R5,ENICPLB_P);
278 CLI R2;
279 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
280 .align 8;
281 [P4] = R5;
282 SSYNC;
283 STI R2;
284
285 ( R7:4,P5:3 ) = [SP++];
286 R0 = CPLB_RELOADED;
287 RTS;
288
289/* FAILED CASES*/
290.Lno_page_in_table:
1394f032 291 R0 = CPLB_NO_ADDR_MATCH;
6a3f0b46
RG
292 JUMP .Lfail_ret;
293
1394f032 294.Lall_locked:
1394f032 295 R0 = CPLB_NO_UNLOCKED;
6a3f0b46
RG
296 JUMP .Lfail_ret;
297
1394f032 298.Lprot_violation:
1394f032 299 R0 = CPLB_PROT_VIOL;
6a3f0b46
RG
300
301.Lfail_ret:
302 /* Make sure we turn protection/cache back on, even in the failing case */
303 BITSET(R5,ENICPLB_P);
304 CLI R2;
305 SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */
306 .align 8;
307 [P4] = R5;
308 SSYNC;
309 STI R2;
310
311 ( R7:4,P5:3 ) = [SP++];
1394f032
BW
312 RTS;
313
314.Ldcplb_write:
315
316 /* if a DCPLB is marked as write-back (CPLB_WT==0), and
317 * it is clean (CPLB_DIRTY==0), then a write to the
318 * CPLB's page triggers a protection violation. We have to
319 * mark the CPLB as dirty, to indicate that there are
320 * pending writes associated with the CPLB.
321 */
322
e208f83a
MF
323 P4.L = LO(DCPLB_STATUS);
324 P4.H = HI(DCPLB_STATUS);
325 P3.L = LO(DCPLB_DATA0);
326 P3.H = HI(DCPLB_DATA0);
1394f032
BW
327 R5 = [P4];
328
329 /* A protection violation can be caused by more than just writes
330 * to a clean WB page, so we have to ensure that:
331 * - It's a write
332 * - to a clean WB page
333 * - and is allowed in the mode the access occurred.
334 */
335
336 CC = BITTST(R5, 16); /* ensure it was a write*/
337 IF !CC JUMP .Lprot_violation;
338
339 /* to check the rest, we have to retrieve the DCPLB.*/
340
341 /* The low half of DCPLB_STATUS is a bit mask*/
342
343 R2 = R5.L (Z); /* indicating which CPLB triggered the event.*/
344 R3 = 30; /* so we can use this to determine the offset*/
345 R2.L = SIGNBITS R2;
346 R2 = R2.L (Z); /* into the DCPLB table.*/
347 R3 = R3 - R2;
348 P4 = R3;
349 P3 = P3 + (P4<<2);
350 R3 = [P3]; /* Retrieve the CPLB*/
351
352 /* Now we can check whether it's a clean WB page*/
353
354 CC = BITTST(R3, 14); /* 0==WB, 1==WT*/
355 IF CC JUMP .Lprot_violation;
356 CC = BITTST(R3, 7); /* 0 == clean, 1 == dirty*/
357 IF CC JUMP .Lprot_violation;
358
359 /* Check whether the write is allowed in the mode that was active.*/
360
361 R2 = 1<<3; /* checking write in user mode*/
362 CC = BITTST(R5, 17); /* 0==was user, 1==was super*/
363 R5 = CC;
364 R2 <<= R5; /* if was super, check write in super mode*/
365 R2 = R3 & R2;
366 CC = R2 == 0;
367 IF CC JUMP .Lprot_violation;
368
369 /* It's a genuine write-to-clean-page.*/
370
371 BITSET(R3, 7); /* mark as dirty*/
372 [P3] = R3; /* and write back.*/
373 NOP;
374 CSYNC;
375 ( R7:4,P5:3 ) = [SP++];
376 R0 = CPLB_RELOADED;
377 RTS;
378
379.Ldcplb_miss_compare:
380
381 /* Data CPLB Miss event. We need to choose a CPLB to
382 * evict, and then locate a new CPLB to install from the
383 * config table, that covers the faulting address.
384 */
385
e208f83a
MF
386 P1.L = LO(DCPLB_DATA15);
387 P1.H = HI(DCPLB_DATA15);
1394f032 388
e208f83a
MF
389 P4.L = LO(DCPLB_FAULT_ADDR);
390 P4.H = HI(DCPLB_FAULT_ADDR);
1394f032
BW
391 R4 = [P4];
392 I0 = R4;
393
394 /* The replacement procedure for DCPLBs*/
395
396 R6 = R1; /* Save for later*/
397
398 /* Turn off CPLBs while we work.*/
e208f83a
MF
399 P4.L = LO(DMEM_CONTROL);
400 P4.H = HI(DMEM_CONTROL);
1394f032
BW
401 R5 = [P4];
402 BITCLR(R5,ENDCPLB_P);
403 CLI R0;
404 SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */
405 .align 8;
406 [P4] = R5;
407 SSYNC;
408 STI R0;
409
410 /* Start looking for a CPLB to evict. Our order of preference
411 * is: invalid CPLBs, clean CPLBs, dirty CPLBs. Locked CPLBs
412 * are no good.
413 */
414
e208f83a
MF
415 I1.L = LO(DCPLB_DATA0);
416 I1.H = HI(DCPLB_DATA0);
1394f032
BW
417 P1 = 2;
418 P2 = 16;
419 I2.L = _dcplb_preference;
420 I2.H = _dcplb_preference;
421 LSETUP(.Lsdsearch1, .Ledsearch1) LC0 = P1;
422.Lsdsearch1:
423 R0 = [I2++]; /* Get the bits we're interested in*/
424 P0 = I1; /* Go back to start of table*/
425 LSETUP (.Lsdsearch2, .Ledsearch2) LC1 = P2;
426.Lsdsearch2:
427 R1 = [P0++]; /* Fetch each installed CPLB in turn*/
428 R2 = R1 & R0; /* and test for interesting bits.*/
429 CC = R2 == 0; /* If none are set, it'll do.*/
430 IF !CC JUMP .Lskip_stack_check;
431
432 R2 = [P0 - 0x104]; /* R2 - PageStart */
433 P3.L = _page_size_table; /* retrieve end address */
434 P3.H = _page_size_table; /* retrieve end address */
435 R3 = 0x1002; /* 16th - position, 2 bits -length */
1aafd909 436#if ANOMALY_05000209
1394f032
BW
437 nop; /* Anomaly 05000209 */
438#endif
439 R7 = EXTRACT(R1,R3.l);
440 R7 = R7 << 2; /* Page size index offset */
441 P5 = R7;
442 P3 = P3 + P5;
443 R7 = [P3]; /* page size in bytes */
444
445 R7 = R2 + R7; /* R7 - PageEnd */
446 R4 = SP; /* Test SP is in range */
447
448 CC = R7 < R4; /* if PageEnd < SP */
449 IF CC JUMP .Ldfound_victim;
450 R3 = 0x284; /* stack length from start of trap till
451 * the point.
452 * 20 stack locations for future modifications
453 */
454 R4 = R4 + R3;
455 CC = R4 < R2; /* if SP + stacklen < PageStart */
456 IF CC JUMP .Ldfound_victim;
457.Lskip_stack_check:
458
459.Ledsearch2: NOP;
460.Ledsearch1: NOP;
461
462 /* If we got here, we didn't find a DCPLB we considered
463 * replacable, which means all of them were locked.
464 */
465
466 JUMP .Lall_locked;
467.Ldfound_victim:
468
469#ifdef CONFIG_CPLB_INFO
470 R7 = [P0 - 0x104];
b8a98989
GY
471 GET_PDA(P2, R2);
472 P3 = [P2 + PDA_DPDT_SWAPCOUNT];
473 P2 = [P2 + PDA_DPDT];
1394f032
BW
474 P3 += -4;
475.Ldicount:
476 R2 = [P2];
477 P2 += 8;
478 P3 += 8;
479 CC = R2==-1;
480 IF CC JUMP .Ldicount_done;
481 CC = R7==R2;
482 IF !CC JUMP .Ldicount;
483 R7 = [P3];
484 R7 += 1;
485 [P3] = R7;
486.Ldicount_done:
487#endif
488
489 /* Clean down the hardware loops*/
490 R2 = 0;
491 LC1 = R2;
492 LC0 = R2;
493
494 /* There's a suitable victim in [P0-4] (because we've
495 * advanced already).
496 */
497
498.LDdoverwrite:
499
500 /* [P0-4] is a suitable victim CPLB, so we want to
501 * overwrite it by moving all the following CPLBs
502 * one space closer to the start.
503 */
504
e208f83a
MF
505 R1.L = LO(DCPLB_DATA16); /* DCPLB_DATA15 + 4 */
506 R1.H = HI(DCPLB_DATA16);
1394f032
BW
507 R0 = P0;
508
509 /* If the victim happens to be in DCPLB15,
510 * we don't need to move anything.
511 */
512
513 CC = R1 == R0;
514 IF CC JUMP .Lde_moved;
515 R1 = R1 - R0;
516 R1 >>= 2;
517 P1 = R1;
518 LSETUP(.Lds_move, .Lde_move) LC0=P1;
519.Lds_move:
520 R0 = [P0++]; /* move data */
521 [P0 - 8] = R0;
522 R0 = [P0-0x104] /* move address */
13fe24f3
RG
523.Lde_move:
524 [P0-0x108] = R0;
525
526.Lde_moved:
527 NOP;
528
529 /* Clear DCPLB_DATA15, in case we don't find a replacement
530 * otherwise, we would have a duplicate entry, and will crash
531 */
532 R0 = 0;
533 [P0 - 0x4] = R0;
1394f032
BW
534
535 /* We've now made space in DCPLB15 for the new CPLB to be
536 * installed. The next stage is to locate a CPLB in the
537 * config table that covers the faulting address.
538 */
539
1394f032
BW
540 R0 = I0; /* Our faulting address */
541
b8a98989
GY
542 GET_PDA(P3, R1);
543 P2 = [P3 + PDA_DPDT];
1394f032 544#ifdef CONFIG_CPLB_INFO
b8a98989 545 P3 = [P3 + PDA_DPDT_SWAPCOUNT];
1394f032
BW
546 P3 += -8;
547#endif
548
549 P1.L = _page_size_table;
550 P1.H = _page_size_table;
551
552 /* An extraction pattern, to retrieve bits 17:16.*/
553
554 R1 = (16<<8)|2;
555.Ldnext: R4 = [P2++]; /* address */
556 R2 = [P2++]; /* data */
557#ifdef CONFIG_CPLB_INFO
558 P3 += 8;
559#endif
560
561 CC = R4 == -1;
562 IF CC JUMP .Lno_page_in_table;
563
564 /* See if failed address > start address */
565 CC = R4 <= R0(IU);
566 IF !CC JUMP .Ldnext;
567
568 /* extract page size (17:16)*/
569 R3 = EXTRACT(R2, R1.L) (Z);
570
571 /* add page size to addr to get range */
572
573 P5 = R3;
574 P5 = P1 + (P5 << 2);
575 R3 = [P5];
576 R3 = R3 + R4;
577
578 /* See if failed address < (start address + page size) */
579 CC = R0 < R3(IU);
580 IF !CC JUMP .Ldnext;
581
582 /* We've found the CPLB that should be installed, so
583 * write it into CPLB15, masking off any caching bits
584 * if necessary.
585 */
586
e208f83a
MF
587 P1.L = LO(DCPLB_DATA15);
588 P1.H = HI(DCPLB_DATA15);
1394f032
BW
589
590 /* If the DCPLB has cache bits set, but caching hasn't
591 * been enabled, then we want to mask off the cache-in-L1
592 * bit before installing. Moreover, if caching is off, we
593 * also want to ensure that the DCPLB has WT mode set, rather
594 * than WB, since WB pages still trigger first-write exceptions
595 * even when not caching is off, and the page isn't marked as
596 * cachable. Finally, we could mark the page as clean, not dirty,
597 * but we choose to leave that decision to the user; if the user
598 * chooses to have a CPLB pre-defined as dirty, then they always
599 * pay the cost of flushing during eviction, but don't pay the
600 * cost of first-write exceptions to mark the page as dirty.
601 */
602
3bebca2d 603#ifdef CONFIG_BFIN_WT
1394f032
BW
604 BITSET(R6, 14); /* Set WT*/
605#endif
606
607 [P1] = R2;
608 [P1-0x100] = R4;
609#ifdef CONFIG_CPLB_INFO
610 R3 = [P3];
611 R3 += 1;
612 [P3] = R3;
613#endif
614
615 /* We've installed the CPLB, so re-enable CPLBs. P4
616 * points to DMEM_CONTROL, and R5 is the value we
617 * last wrote to it, when we were disabling CPLBs.
618 */
619
620 BITSET(R5,ENDCPLB_P);
621 CLI R2;
622 .align 8;
623 [P4] = R5;
624 SSYNC;
625 STI R2;
626
627 ( R7:4,P5:3 ) = [SP++];
628 R0 = CPLB_RELOADED;
629 RTS;
51be24c3 630ENDPROC(_cplb_mgr)
1394f032
BW
631
632.data
633.align 4;
634_page_size_table:
635.byte4 0x00000400; /* 1K */
636.byte4 0x00001000; /* 4K */
637.byte4 0x00100000; /* 1M */
638.byte4 0x00400000; /* 4M */
639
640.align 4;
641_dcplb_preference:
642.byte4 0x00000001; /* valid bit */
643.byte4 0x00000002; /* lock bit */