Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / arch / score / mm / tlb-score.c
1 /*
2  * arch/score/mm/tlb-score.c
3  *
4  * Score Processor version.
5  *
6  * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
7  *  Lennox Wu <lennox.wu@sunplusct.com>
8  *  Chen Liqin <liqin.chen@sunplusct.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see the file COPYING, or write
22  * to the Free Software Foundation, Inc.,
23  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25
26 #include <linux/highmem.h>
27 #include <linux/module.h>
28
29 #include <asm/irq.h>
30 #include <asm/mmu_context.h>
31 #include <asm/tlb.h>
32
33 #define TLBSIZE 32
34
35 unsigned long asid_cache = ASID_FIRST_VERSION;
36 EXPORT_SYMBOL(asid_cache);
37
38 void local_flush_tlb_all(void)
39 {
40         unsigned long flags;
41         unsigned long old_ASID;
42         int entry;
43
44         local_irq_save(flags);
45         old_ASID = pevn_get() & ASID_MASK;
46         pectx_set(0);                   /* invalid */
47         entry = tlblock_get();          /* skip locked entries*/
48
49         for (; entry < TLBSIZE; entry++) {
50                 tlbpt_set(entry);
51                 pevn_set(KSEG1);
52                 barrier();
53                 tlb_write_indexed();
54         }
55         pevn_set(old_ASID);
56         local_irq_restore(flags);
57 }
58
59 /*
60  * If mm is currently active_mm, we can't really drop it. Instead,
61  * we will get a new one for it.
62  */
63 static inline void
64 drop_mmu_context(struct mm_struct *mm)
65 {
66         unsigned long flags;
67
68         local_irq_save(flags);
69         get_new_mmu_context(mm);
70         pevn_set(mm->context & ASID_MASK);
71         local_irq_restore(flags);
72 }
73
74 void local_flush_tlb_mm(struct mm_struct *mm)
75 {
76         if (mm->context != 0)
77                 drop_mmu_context(mm);
78 }
79
80 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
81         unsigned long end)
82 {
83         struct mm_struct *mm = vma->vm_mm;
84         unsigned long vma_mm_context = mm->context;
85         if (mm->context != 0) {
86                 unsigned long flags;
87                 int size;
88
89                 local_irq_save(flags);
90                 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
91                 if (size <= TLBSIZE) {
92                         int oldpid = pevn_get() & ASID_MASK;
93                         int newpid = vma_mm_context & ASID_MASK;
94
95                         start &= PAGE_MASK;
96                         end += (PAGE_SIZE - 1);
97                         end &= PAGE_MASK;
98                         while (start < end) {
99                                 int idx;
100
101                                 pevn_set(start | newpid);
102                                 start += PAGE_SIZE;
103                                 barrier();
104                                 tlb_probe();
105                                 idx = tlbpt_get();
106                                 pectx_set(0);
107                                 pevn_set(KSEG1);
108                                 if (idx < 0)
109                                         continue;
110                                 tlb_write_indexed();
111                         }
112                         pevn_set(oldpid);
113                 } else {
114                         /* Bigger than TLBSIZE, get new ASID directly */
115                         get_new_mmu_context(mm);
116                         if (mm == current->active_mm)
117                                 pevn_set(vma_mm_context & ASID_MASK);
118                 }
119                 local_irq_restore(flags);
120         }
121 }
122
123 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
124 {
125         unsigned long flags;
126         int size;
127
128         local_irq_save(flags);
129         size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
130         if (size <= TLBSIZE) {
131                 int pid = pevn_get();
132
133                 start &= PAGE_MASK;
134                 end += PAGE_SIZE - 1;
135                 end &= PAGE_MASK;
136
137                 while (start < end) {
138                         long idx;
139
140                         pevn_set(start);
141                         start += PAGE_SIZE;
142                         tlb_probe();
143                         idx = tlbpt_get();
144                         if (idx < 0)
145                                 continue;
146                         pectx_set(0);
147                         pevn_set(KSEG1);
148                         barrier();
149                         tlb_write_indexed();
150                 }
151                 pevn_set(pid);
152         } else {
153                 local_flush_tlb_all();
154         }
155
156         local_irq_restore(flags);
157 }
158
159 void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
160 {
161         if (vma && vma->vm_mm->context != 0) {
162                 unsigned long flags;
163                 int oldpid, newpid, idx;
164                 unsigned long vma_ASID = vma->vm_mm->context;
165
166                 newpid = vma_ASID & ASID_MASK;
167                 page &= PAGE_MASK;
168                 local_irq_save(flags);
169                 oldpid = pevn_get() & ASID_MASK;
170                 pevn_set(page | newpid);
171                 barrier();
172                 tlb_probe();
173                 idx = tlbpt_get();
174                 pectx_set(0);
175                 pevn_set(KSEG1);
176                 if (idx < 0)            /* p_bit(31) - 1: miss, 0: hit*/
177                         goto finish;
178                 barrier();
179                 tlb_write_indexed();
180 finish:
181                 pevn_set(oldpid);
182                 local_irq_restore(flags);
183         }
184 }
185
186 /*
187  * This one is only used for pages with the global bit set so we don't care
188  * much about the ASID.
189  */
190 void local_flush_tlb_one(unsigned long page)
191 {
192         unsigned long flags;
193         int oldpid, idx;
194
195         local_irq_save(flags);
196         oldpid = pevn_get();
197         page &= (PAGE_MASK << 1);
198         pevn_set(page);
199         barrier();
200         tlb_probe();
201         idx = tlbpt_get();
202         pectx_set(0);
203         if (idx >= 0) {
204                 /* Make sure all entries differ. */
205                 pevn_set(KSEG1);
206                 barrier();
207                 tlb_write_indexed();
208         }
209         pevn_set(oldpid);
210         local_irq_restore(flags);
211 }
212
213 void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte)
214 {
215         unsigned long flags;
216         int idx, pid;
217
218         /*
219          * Handle debugger faulting in for debugee.
220          */
221         if (current->active_mm != vma->vm_mm)
222                 return;
223
224         pid = pevn_get() & ASID_MASK;
225
226         local_irq_save(flags);
227         address &= PAGE_MASK;
228         pevn_set(address | pid);
229         barrier();
230         tlb_probe();
231         idx = tlbpt_get();
232         pectx_set(pte_val(pte));
233         pevn_set(address | pid);
234         if (idx < 0)
235                 tlb_write_random();
236         else
237                 tlb_write_indexed();
238
239         pevn_set(pid);
240         local_irq_restore(flags);
241 }
242
243 void __cpuinit tlb_init(void)
244 {
245         tlblock_set(0);
246         local_flush_tlb_all();
247         memcpy((void *)(EXCEPTION_VECTOR_BASE_ADDR + 0x100),
248                         &score7_FTLB_refill_Handler, 0xFC);
249         flush_icache_range(EXCEPTION_VECTOR_BASE_ADDR + 0x100,
250                         EXCEPTION_VECTOR_BASE_ADDR + 0x1FC);
251 }