932a09d7ef84810bdb4d43c591d63badbff3892c
[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 /*
252  * Ok, this seriously sucks.  We use them to flush a user page but don't
253  * know the virtual address, so we have to blast away the whole icache
254  * which is significantly more expensive than the real thing.  Otoh we at
255  * least know the kernel address of the page so we can flush it
256  * selectivly.
257  */
258 static void tx39_flush_icache_page(struct vm_area_struct *vma, struct page *page)
259 {
260         unsigned long addr;
261         /*
262          * If there's no context yet, or the page isn't executable, no icache
263          * flush is needed.
264          */
265         if (!(vma->vm_flags & VM_EXEC))
266                 return;
267
268         addr = (unsigned long) page_address(page);
269         tx39_blast_dcache_page(addr);
270
271         /*
272          * We're not sure of the virtual address(es) involved here, so
273          * we have to flush the entire I-cache.
274          */
275         tx39_blast_icache();
276 }
277
278 static void tx39_dma_cache_wback_inv(unsigned long addr, unsigned long size)
279 {
280         unsigned long end;
281
282         if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
283                 end = addr + size;
284                 do {
285                         tx39_blast_dcache_page(addr);
286                         addr += PAGE_SIZE;
287                 } while(addr != end);
288         } else if (size > dcache_size) {
289                 tx39_blast_dcache();
290         } else {
291                 blast_dcache_range(addr, addr + size);
292         }
293 }
294
295 static void tx39_dma_cache_inv(unsigned long addr, unsigned long size)
296 {
297         unsigned long end;
298
299         if (((size | addr) & (PAGE_SIZE - 1)) == 0) {
300                 end = addr + size;
301                 do {
302                         tx39_blast_dcache_page(addr);
303                         addr += PAGE_SIZE;
304                 } while(addr != end);
305         } else if (size > dcache_size) {
306                 tx39_blast_dcache();
307         } else {
308                 blast_inv_dcache_range(addr, addr + size);
309         }
310 }
311
312 static void tx39_flush_cache_sigtramp(unsigned long addr)
313 {
314         unsigned long ic_lsize = current_cpu_data.icache.linesz;
315         unsigned long dc_lsize = current_cpu_data.dcache.linesz;
316         unsigned long config;
317         unsigned long flags;
318
319         protected_writeback_dcache_line(addr & ~(dc_lsize - 1));
320
321         /* disable icache (set ICE#) */
322         local_irq_save(flags);
323         config = read_c0_conf();
324         write_c0_conf(config & ~TX39_CONF_ICE);
325         TX39_STOP_STREAMING();
326         protected_flush_icache_line(addr & ~(ic_lsize - 1));
327         write_c0_conf(config);
328         local_irq_restore(flags);
329 }
330
331 static __init void tx39_probe_cache(void)
332 {
333         unsigned long config;
334
335         config = read_c0_conf();
336
337         icache_size = 1 << (10 + ((config & TX39_CONF_ICS_MASK) >>
338                                   TX39_CONF_ICS_SHIFT));
339         dcache_size = 1 << (10 + ((config & TX39_CONF_DCS_MASK) >>
340                                   TX39_CONF_DCS_SHIFT));
341
342         current_cpu_data.icache.linesz = 16;
343         switch (current_cpu_data.cputype) {
344         case CPU_TX3912:
345                 current_cpu_data.icache.ways = 1;
346                 current_cpu_data.dcache.ways = 1;
347                 current_cpu_data.dcache.linesz = 4;
348                 break;
349
350         case CPU_TX3927:
351                 current_cpu_data.icache.ways = 2;
352                 current_cpu_data.dcache.ways = 2;
353                 current_cpu_data.dcache.linesz = 16;
354                 break;
355
356         case CPU_TX3922:
357         default:
358                 current_cpu_data.icache.ways = 1;
359                 current_cpu_data.dcache.ways = 1;
360                 current_cpu_data.dcache.linesz = 16;
361                 break;
362         }
363 }
364
365 void __init tx39_cache_init(void)
366 {
367         extern void build_clear_page(void);
368         extern void build_copy_page(void);
369         unsigned long config;
370
371         config = read_c0_conf();
372         config &= ~TX39_CONF_WBON;
373         write_c0_conf(config);
374
375         tx39_probe_cache();
376
377         switch (current_cpu_data.cputype) {
378         case CPU_TX3912:
379                 /* TX39/H core (writethru direct-map cache) */
380                 flush_cache_all = tx39h_flush_icache_all;
381                 __flush_cache_all       = tx39h_flush_icache_all;
382                 flush_cache_mm          = (void *) tx39h_flush_icache_all;
383                 flush_cache_range       = (void *) tx39h_flush_icache_all;
384                 flush_cache_page        = (void *) tx39h_flush_icache_all;
385                 __flush_icache_page     = (void *) tx39h_flush_icache_all;
386                 flush_icache_range      = (void *) tx39h_flush_icache_all;
387
388                 flush_cache_sigtramp    = (void *) tx39h_flush_icache_all;
389                 local_flush_data_cache_page     = (void *) tx39h_flush_icache_all;
390                 flush_data_cache_page   = (void *) tx39h_flush_icache_all;
391
392                 _dma_cache_wback_inv    = tx39h_dma_cache_wback_inv;
393
394                 shm_align_mask          = PAGE_SIZE - 1;
395
396                 break;
397
398         case CPU_TX3922:
399         case CPU_TX3927:
400         default:
401                 /* TX39/H2,H3 core (writeback 2way-set-associative cache) */
402                 r3k_have_wired_reg = 1;
403                 write_c0_wired(0);      /* set 8 on reset... */
404                 /* board-dependent init code may set WBON */
405
406                 flush_cache_all = tx39_flush_cache_all;
407                 __flush_cache_all = tx39___flush_cache_all;
408                 flush_cache_mm = tx39_flush_cache_mm;
409                 flush_cache_range = tx39_flush_cache_range;
410                 flush_cache_page = tx39_flush_cache_page;
411                 __flush_icache_page = tx39_flush_icache_page;
412                 flush_icache_range = tx39_flush_icache_range;
413
414                 flush_cache_sigtramp = tx39_flush_cache_sigtramp;
415                 local_flush_data_cache_page = local_tx39_flush_data_cache_page;
416                 flush_data_cache_page = tx39_flush_data_cache_page;
417
418                 _dma_cache_wback_inv = tx39_dma_cache_wback_inv;
419                 _dma_cache_wback = tx39_dma_cache_wback_inv;
420                 _dma_cache_inv = tx39_dma_cache_inv;
421
422                 shm_align_mask = max_t(unsigned long,
423                                        (dcache_size / current_cpu_data.dcache.ways) - 1,
424                                        PAGE_SIZE - 1);
425
426                 break;
427         }
428
429         current_cpu_data.icache.waysize = icache_size / current_cpu_data.icache.ways;
430         current_cpu_data.dcache.waysize = dcache_size / current_cpu_data.dcache.ways;
431
432         current_cpu_data.icache.sets =
433                 current_cpu_data.icache.waysize / current_cpu_data.icache.linesz;
434         current_cpu_data.dcache.sets =
435                 current_cpu_data.dcache.waysize / current_cpu_data.dcache.linesz;
436
437         if (current_cpu_data.dcache.waysize > PAGE_SIZE)
438                 current_cpu_data.dcache.flags |= MIPS_CACHE_ALIASES;
439
440         current_cpu_data.icache.waybit = 0;
441         current_cpu_data.dcache.waybit = 0;
442
443         printk("Primary instruction cache %ldkB, linesize %d bytes\n",
444                 icache_size >> 10, current_cpu_data.icache.linesz);
445         printk("Primary data cache %ldkB, linesize %d bytes\n",
446                 dcache_size >> 10, current_cpu_data.dcache.linesz);
447
448         build_clear_page();
449         build_copy_page();
450         tx39h_flush_icache_all();
451 }