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