Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6
[pandora-kernel.git] / arch / sh / mm / tlb-flush.c
1 /*
2  * TLB flushing operations for SH with an MMU.
3  *
4  *  Copyright (C) 1999  Niibe Yutaka
5  *  Copyright (C) 2003  Paul Mundt
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License.  See the file "COPYING" in the main directory of this archive
9  * for more details.
10  */
11 #include <linux/mm.h>
12 #include <asm/mmu_context.h>
13 #include <asm/tlbflush.h>
14
15 void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
16 {
17         if (vma->vm_mm && vma->vm_mm->context.id != NO_CONTEXT) {
18                 unsigned long flags;
19                 unsigned long asid;
20                 unsigned long saved_asid = MMU_NO_ASID;
21
22                 asid = vma->vm_mm->context.id & MMU_CONTEXT_ASID_MASK;
23                 page &= PAGE_MASK;
24
25                 local_irq_save(flags);
26                 if (vma->vm_mm != current->mm) {
27                         saved_asid = get_asid();
28                         set_asid(asid);
29                 }
30                 __flush_tlb_page(asid, page);
31                 if (saved_asid != MMU_NO_ASID)
32                         set_asid(saved_asid);
33                 local_irq_restore(flags);
34         }
35 }
36
37 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
38                      unsigned long end)
39 {
40         struct mm_struct *mm = vma->vm_mm;
41
42         if (mm->context.id != NO_CONTEXT) {
43                 unsigned long flags;
44                 int size;
45
46                 local_irq_save(flags);
47                 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
48                 if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
49                         mm->context.id = NO_CONTEXT;
50                         if (mm == current->mm)
51                                 activate_context(mm);
52                 } else {
53                         unsigned long asid;
54                         unsigned long saved_asid = MMU_NO_ASID;
55
56                         asid = mm->context.id & MMU_CONTEXT_ASID_MASK;
57                         start &= PAGE_MASK;
58                         end += (PAGE_SIZE - 1);
59                         end &= PAGE_MASK;
60                         if (mm != current->mm) {
61                                 saved_asid = get_asid();
62                                 set_asid(asid);
63                         }
64                         while (start < end) {
65                                 __flush_tlb_page(asid, start);
66                                 start += PAGE_SIZE;
67                         }
68                         if (saved_asid != MMU_NO_ASID)
69                                 set_asid(saved_asid);
70                 }
71                 local_irq_restore(flags);
72         }
73 }
74
75 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
76 {
77         unsigned long flags;
78         int size;
79
80         local_irq_save(flags);
81         size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
82         if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
83                 flush_tlb_all();
84         } else {
85                 unsigned long asid;
86                 unsigned long saved_asid = get_asid();
87
88                 asid = init_mm.context.id & MMU_CONTEXT_ASID_MASK;
89                 start &= PAGE_MASK;
90                 end += (PAGE_SIZE - 1);
91                 end &= PAGE_MASK;
92                 set_asid(asid);
93                 while (start < end) {
94                         __flush_tlb_page(asid, start);
95                         start += PAGE_SIZE;
96                 }
97                 set_asid(saved_asid);
98         }
99         local_irq_restore(flags);
100 }
101
102 void flush_tlb_mm(struct mm_struct *mm)
103 {
104         /* Invalidate all TLB of this process. */
105         /* Instead of invalidating each TLB, we get new MMU context. */
106         if (mm->context.id != NO_CONTEXT) {
107                 unsigned long flags;
108
109                 local_irq_save(flags);
110                 mm->context.id = NO_CONTEXT;
111                 if (mm == current->mm)
112                         activate_context(mm);
113                 local_irq_restore(flags);
114         }
115 }
116
117 void flush_tlb_all(void)
118 {
119         unsigned long flags, status;
120
121         /*
122          * Flush all the TLB.
123          *
124          * Write to the MMU control register's bit:
125          *      TF-bit for SH-3, TI-bit for SH-4.
126          *      It's same position, bit #2.
127          */
128         local_irq_save(flags);
129         status = ctrl_inl(MMUCR);
130         status |= 0x04;
131         ctrl_outl(status, MMUCR);
132         ctrl_barrier();
133         local_irq_restore(flags);
134 }