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