[MIPS] Remove __flush_icache_page
[linux-2.6.git] / arch / mips / mm / c-tx39.c
1 /*
2  * r2300.c: R2000 and R3000 specific mmu/cache code.
3  *
4  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
5  *
6  * with a lot of changes to make this thing work for R3000s
7  * Tx39XX R4k style caches added. HK
8  * Copyright (C) 1998, 1999, 2000 Harald Koerfgen
9  * Copyright (C) 1998 Gleb Raiko & Vladimir Roganov
10  */
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15
16 #include <asm/cacheops.h>
17 #include <asm/page.h>
18 #include <asm/pgtable.h>
19 #include <asm/mmu_context.h>
20 #include <asm/system.h>
21 #include <asm/isadep.h>
22 #include <asm/io.h>
23 #include <asm/bootinfo.h>
24 #include <asm/cpu.h>
25
26 /* For R3000 cores with R4000 style caches */
27 static unsigned long icache_size, dcache_size;          /* Size in bytes */
28
29 #include <asm/r4kcache.h>
30
31 extern int r3k_have_wired_reg;  /* in r3k-tlb.c */
32
33 /* This sequence is required to ensure icache is disabled immediately */
34 #define TX39_STOP_STREAMING() \
35 __asm__ __volatile__( \
36         ".set    push\n\t" \
37         ".set    noreorder\n\t" \
38         "b       1f\n\t" \
39         "nop\n\t" \
40         "1:\n\t" \
41         ".set pop" \
42         )
43
44 /* TX39H-style cache flush routines. */
45 static void tx39h_flush_icache_all(void)
46 {
47         unsigned long flags, config;
48
49         /* disable icache (set ICE#) */
50         local_irq_save(flags);
51         config = read_c0_conf();
52         write_c0_conf(config & ~TX39_CONF_ICE);
53         TX39_STOP_STREAMING();
54         blast_icache16();
55         write_c0_conf(config);
56         local_irq_restore(flags);
57 }
58
59 static void tx39h_dma_cache_wback_inv(unsigned long addr, unsigned long size)
60 {
61         /* Catch bad driver code */
62         BUG_ON(size == 0);
63
64         iob();
65         blast_inv_dcache_range(addr, addr + size);
66 }
67
68
69 /* TX39H2,TX39H3 */
70 static inline void tx39_blast_dcache_page(unsigned long addr)
71 {
72         if (current_cpu_data.cputype != CPU_TX3912)
73                 blast_dcache16_page(addr);
74 }
75
76 static inline void tx39_blast_dcache_page_indexed(unsigned long addr)
77 {
78         blast_dcache16_page_indexed(addr);
79 }
80
81 static inline void tx39_blast_dcache(void)
82 {
83         blast_dcache16();
84 }
85
86 static inline void tx39_blast_icache_page(unsigned long addr)
87 {
88         unsigned long flags, config;
89         /* disable icache (set ICE#) */
90         local_irq_save(flags);
91         config = read_c0_conf();
92         write_c0_conf(config & ~TX39_CONF_ICE);
93         TX39_STOP_STREAMING();
94         blast_icache16_page(addr);
95         write_c0_conf(config);
96         local_irq_restore(flags);
97 }
98
99 static inline void tx39_blast_icache_page_indexed(unsigned long addr)
100 {
101         unsigned long flags, config;
102         /* disable icache (set ICE#) */
103         local_irq_save(flags);
104         config = read_c0_conf();
105         write_c0_conf(config & ~TX39_CONF_ICE);
106         TX39_STOP_STREAMING();
107         blast_icache16_page_indexed(addr);
108         write_c0_conf(config);
109         local_irq_restore(flags);
110 }
111
112 static inline void tx39_blast_icache(void)
113 {
114         unsigned long flags, config;
115         /* disable icache (set ICE#) */
116         local_irq_save(flags);
117         config = read_c0_conf();
118         write_c0_conf(config & ~TX39_CONF_ICE);
119         TX39_STOP_STREAMING();
120         blast_icache16();
121         write_c0_conf(config);
122         local_irq_restore(flags);
123 }
124
125 static inline void tx39_flush_cache_all(void)
126 {
127         if (!cpu_has_dc_aliases)
128                 return;
129
130         tx39_blast_dcache();
131         tx39_blast_icache();
132 }
133
134 static inline void tx39___flush_cache_all(void)
135 {
136         tx39_blast_dcache();
137         tx39_blast_icache();
138 }
139
140 static void tx39_flush_cache_mm(struct mm_struct *mm)
141 {
142         if (!cpu_has_dc_aliases)
143                 return;
144
145         if (cpu_context(smp_processor_id(), mm) != 0) {
146                 tx39_flush_cache_all();
147         }
148 }
149
150 static void tx39_flush_cache_range(struct vm_area_struct *vma,
151         unsigned long start, unsigned long end)
152 {
153         int exec;
154
155         if (!(cpu_context(smp_processor_id(), vma->vm_mm)))
156                 return;
157
158         exec = vma->vm_flags & VM_EXEC;
159         if (cpu_has_dc_aliases || exec)
160                 tx39_blast_dcache();
161         if (exec)
162                 tx39_blast_icache();
163 }
164
165 static void tx39_flush_cache_page(struct vm_area_struct *vma, unsigned long page, unsigned long pfn)
166 {
167         int exec = vma->vm_flags & VM_EXEC;
168         struct mm_struct *mm = vma->vm_mm;
169         pgd_t *pgdp;
170         pud_t *pudp;
171         pmd_t *pmdp;
172         pte_t *ptep;
173
174         /*
175          * If ownes no valid ASID yet, cannot possibly have gotten
176          * this page into the cache.
177          */
178         if (cpu_context(smp_processor_id(), mm) == 0)
179                 return;
180
181         page &= PAGE_MASK;
182         pgdp = pgd_offset(mm, page);
183         pudp = pud_offset(pgdp, page);
184         pmdp = pmd_offset(pudp, page);
185         ptep = pte_offset(pmdp, page);
186
187         /*
188          * If the page isn't marked valid, the page cannot possibly be
189          * in the cache.
190          */
191         if (!(pte_val(*ptep) & _PAGE_PRESENT))
192                 return;
193
194         /*
195          * Doing flushes for another ASID than the current one is
196          * too difficult since stupid R4k caches do a TLB translation
197          * for every cache flush operation.  So we do indexed flushes
198          * in that case, which doesn't overly flush the cache too much.
199          */
200         if ((mm == current->active_mm) && (pte_val(*ptep) & _PAGE_VALID)) {
201                 if (cpu_has_dc_aliases || exec)
202                         tx39_blast_dcache_page(page);
203                 if (exec)
204                         tx39_blast_icache_page(page);
205
206                 return;
207         }
208
209         /*
210          * Do indexed flush, too much work to get the (possible) TLB refills
211          * to work correctly.
212          */
213         if (cpu_has_dc_aliases || exec)
214                 tx39_blast_dcache_page_indexed(page);
215         if (exec)
216                 tx39_blast_icache_page_indexed(page);
217 }
218
219 static void local_tx39_flush_data_cache_page(void * addr)
220 {
221         tx39_blast_dcache_page(addr);
222 }
223
224 static void tx39_flush_data_cache_page(unsigned long addr)
225 {
226         tx39_blast_dcache_page(addr);
227 }
228
229 static void tx39_flush_icache_range(unsigned long start, unsigned long end)
230 {
231         if (end - start > dcache_size)
232                 tx39_blast_dcache();
233         else
234                 protected_blast_dcache_range(start, end);
235
236         if (end - start > icache_size)
237                 tx39_blast_icache();
238         else {
239                 unsigned long flags, config;
240                 /* disable icache (set ICE#) */
241                 local_irq_save(flags);
242                 config = read_c0_conf();
243                 write_c0_conf(config & ~TX39_CONF_ICE);
244                 TX39_STOP_STREAMING();
245                 protected_blast_icache_range(start, end);
246                 write_c0_conf(config);
247                 local_irq_restore(flags);
248         }
249 }
250
251 static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size)
252 {
253         unsigned long end;
254
255         if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
256                 end = addr + size;
257                 do {
258                         tx39_blast_dcache_page(addr);
259                         addr += PAGE_SIZE;
260                 } while(addr != end);
261         } else if (size > dcache_size) {
262                 tx39_blast_dcache();
263         } else {
264                 blast_dcache_range(addr, addr + size);
265         }
266 }
267
268 static void tx39_dma_cache_inv(unsigned long addr, unsigned long size)
269 {
270         unsigned long end;
271
272         if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
273                 end = addr + size;
274                 do {
275                         tx39_blast_dcache_page(addr);
276                         addr += PAGE_SIZE;
277                 } while(addr != end);
278         } else if (size > dcache_size) {
279                 tx39_blast_dcache();
280         } else {
281                 blast_inv_dcache_range(addr, addr + size);
282         }
283 }
284
285 static void tx39_flush_cache_sigtramp(unsigned long addr)
286 {
287         unsigned long ic_lsize = current_cpu_data.icache.linesz;
288         unsigned long dc_lsize = current_cpu_data.dcache.linesz;
289         unsigned long config;
290         unsigned long flags;
291
292         protected_writeback_dcache_line(addr & ~(dc_lsize - 1));
293
294         /* disable icache (set ICE#) */
295         local_irq_save(flags);
296         config = read_c0_conf();
297         write_c0_conf(config & ~TX39_CONF_ICE);
298         TX39_STOP_STREAMING();
299         protected_flush_icache_line(addr & ~(ic_lsize - 1));
300         write_c0_conf(config);
301         local_irq_restore(flags);
302 }
303
304 static __init void tx39_probe_cache(void)
305 {
306         unsigned long config;
307
308         config = read_c0_conf();
309
310         icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >>
311                                   TX39_CONF_ICS_SHIFT));
312         dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >>
313                                   TX39_CONF_DCS_SHIFT));
314
315         current_cpu_data.icache.linesz = 16;
316         switch (current_cpu_data.cputype) {
317         case CPU_TX3912:
318                 current_cpu_data.icache.ways = 1;
319                 current_cpu_data.dcache.ways = 1;
320                 current_cpu_data.dcache.linesz = 4;
321                 break;
322
323         case CPU_TX3927:
324                 current_cpu_data.icache.ways = 2;
325                 current_cpu_data.dcache.ways = 2;
326                 current_cpu_data.dcache.linesz = 16;
327                 break;
328
329         case CPU_TX3922:
330         default:
331                 current_cpu_data.icache.ways = 1;
332                 current_cpu_data.dcache.ways = 1;
333                 current_cpu_data.dcache.linesz = 16;
334                 break;
335         }
336 }
337
338 void __init tx39_cache_init(void)
339 {
340         extern void build_clear_page(void);
341         extern void build_copy_page(void);
342         unsigned long config;
343
344         config = read_c0_conf();
345         config &= ~TX39_CONF_WBON;
346         write_c0_conf(config);
347
348         tx39_probe_cache();
349
350         switch (current_cpu_data.cputype) {
351         case CPU_TX3912:
352                 /* TX39/H core (writethru direct-map cache) */
353                 flush_cache_all = tx39h_flush_icache_all;
354                 __flush_cache_all       = tx39h_flush_icache_all;
355                 flush_cache_mm          = (void *) tx39h_flush_icache_all;
356                 flush_cache_range       = (void *) tx39h_flush_icache_all;
357                 flush_cache_page        = (void *) tx39h_flush_icache_all;
358                 flush_icache_range      = (void *) tx39h_flush_icache_all;
359
360                 flush_cache_sigtramp    = (void *) tx39h_flush_icache_all;
361                 local_flush_data_cache_page     = (void *) tx39h_flush_icache_all;
362                 flush_data_cache_page   = (void *) tx39h_flush_icache_all;
363
364                 _dma_cache_wback_inv    = tx39h_dma_cache_wback_inv;
365
366                 shm_align_mask          = PAGE_SIZE - 1;
367
368                 break;
369
370         case CPU_TX3922:
371         case CPU_TX3927:
372         default:
373                 /* TX39/H2,H3 core (writeback 2way-set-associative cache) */
374                 r3k_have_wired_reg = 1;
375                 write_c0_wired(0);      /* set 8 on reset... */
376                 /* board-dependent init code may set WBON */
377
378                 flush_cache_all = tx39_flush_cache_all;
379                 __flush_cache_all = tx39___flush_cache_all;
380                 flush_cache_mm = tx39_flush_cache_mm;
381                 flush_cache_range = tx39_flush_cache_range;
382                 flush_cache_page = tx39_flush_cache_page;
383                 flush_icache_range = tx39_flush_icache_range;
384
385                 flush_cache_sigtramp = tx39_flush_cache_sigtramp;
386                 local_flush_data_cache_page = local_tx39_flush_data_cache_page;
387                 flush_data_cache_page = tx39_flush_data_cache_page;
388
389                 _dma_cache_wback_inv = tx39_dma_cache_wback_inv;
390                 _dma_cache_wback = tx39_dma_cache_wback_inv;
391                 _dma_cache_inv = tx39_dma_cache_inv;
392
393                 shm_align_mask = max_t(unsigned long,
394                                        (dcache_size / current_cpu_data.dcache.ways) - 1,
395                                        PAGE_SIZE - 1);
396
397                 break;
398         }
399
400         current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways;
401         current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways;
402
403         current_cpu_data.icache.sets =
404                 current_cpu_data.icache.waysize / current_cpu_data.icache.linesz;
405         current_cpu_data.dcache.sets =
406                 current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz;
407
408         if (current_cpu_data.dcache.waysize > PAGE_SIZE)
409                 current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES;
410
411         current_cpu_data.icache.waybit = 0;
412         current_cpu_data.dcache.waybit = 0;
413
414         printk("Primary instruction cache %ldkB, linesize %d bytes\n",
415                 icache_size >> 10, current_cpu_data.icache.linesz);
416         printk("Primary data cache %ldkB, linesize %d bytes\n",
417                 dcache_size >> 10, current_cpu_data.dcache.linesz);
418
419         build_clear_page();
420         build_copy_page();
421         tx39h_flush_icache_all();
422 }