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