Blackfin arch: SMP supporting patchset: Blackfin CPLB related code
[safe/jmp/linux-2.6] / arch / blackfin / kernel / cplb-nompu / 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 #include <asm/asm-offsets.h>
56
57 #ifdef CONFIG_EXCPT_IRQ_SYSC_L1
58 .section .l1.text
59 #else
60 .text
61 #endif
62
63 .align 2;
64 ENTRY(_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.
77         */
78
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
88         P4.L = LO(ICPLB_FAULT_ADDR);
89         P4.H = HI(ICPLB_FAULT_ADDR);
90
91         P1 = 16;
92         P5.L = _page_size_table;
93         P5.H = _page_size_table;
94
95         P0.L = LO(ICPLB_DATA0);
96         P0.H = HI(ICPLB_DATA0);
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*/
100         R3 = ((16 << 8) | 2);   /* Extract mask, two bits at posn 16 */
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 */
130         P0.L = LO(ICPLB_DATA0);
131         P0.H = HI(ICPLB_DATA0);
132
133         /* The replacement procedure for ICPLBs */
134
135         P4.L = LO(IMEM_CONTROL);
136         P4.H = HI(IMEM_CONTROL);
137
138         /* Turn off CPLBs while we work, necessary according to HRM before
139          * modifying CPLB descriptors
140          */
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];
168         GET_PDA(P2, R2);
169         P3 = [P2 + PDA_IPDT_SWAPCOUNT];
170         P2 = [P2 + PDA_IPDT];
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         GET_PDA(P3, R0);
212         P2 = [P3 + PDA_IPDT];
213 #ifdef  CONFIG_CPLB_INFO
214         P3 = [P3 + PDA_IPDT_SWAPCOUNT];
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);
242         IF !CC JUMP .Linext;
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
263         P1.L = LO(ICPLB_DATA15);                /* ICPLB_DATA15 */
264         P1.H = HI(ICPLB_DATA15);
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:
291         R0 = CPLB_NO_ADDR_MATCH;
292         JUMP .Lfail_ret;
293
294 .Lall_locked:
295         R0 = CPLB_NO_UNLOCKED;
296         JUMP .Lfail_ret;
297
298 .Lprot_violation:
299         R0 = CPLB_PROT_VIOL;
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++];
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
323         P4.L = LO(DCPLB_STATUS);
324         P4.H = HI(DCPLB_STATUS);
325         P3.L = LO(DCPLB_DATA0);
326         P3.H = HI(DCPLB_DATA0);
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
386         P1.L = LO(DCPLB_DATA15);
387         P1.H = HI(DCPLB_DATA15);
388
389         P4.L = LO(DCPLB_FAULT_ADDR);
390         P4.H = HI(DCPLB_FAULT_ADDR);
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.*/
399         P4.L = LO(DMEM_CONTROL);
400         P4.H = HI(DMEM_CONTROL);
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
415         I1.L = LO(DCPLB_DATA0);
416         I1.H = HI(DCPLB_DATA0);
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 */
436 #if ANOMALY_05000209
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];
471         GET_PDA(P2, R2);
472         P3 = [P2 + PDA_DPDT_SWAPCOUNT];
473         P2 = [P2 + PDA_DPDT];
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
505         R1.L = LO(DCPLB_DATA16);                /* DCPLB_DATA15 + 4 */
506         R1.H = HI(DCPLB_DATA16);
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 */
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;
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
540         R0 = I0;                /* Our faulting address */
541
542         GET_PDA(P3, R1);
543         P2 = [P3 + PDA_DPDT];
544 #ifdef  CONFIG_CPLB_INFO
545         P3 = [P3 + PDA_DPDT_SWAPCOUNT];
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
587         P1.L = LO(DCPLB_DATA15);
588         P1.H = HI(DCPLB_DATA15);
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
603 #ifdef CONFIG_BFIN_WT
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;
630 ENDPROC(_cplb_mgr)
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 */