Merge branch 'for-linus' of ssh://master.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[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 - 2006  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 <linux/io.h>
13 #include <asm/mmu_context.h>
14 #include <asm/tlbflush.h>
15 #include <asm/cacheflush.h>
16
17 void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
18 {
19         unsigned int cpu = smp_processor_id();
20
21         if (vma->vm_mm && cpu_context(cpu, vma->vm_mm) != NO_CONTEXT) {
22                 unsigned long flags;
23                 unsigned long asid;
24                 unsigned long saved_asid = MMU_NO_ASID;
25
26                 asid = cpu_asid(cpu, vma->vm_mm);
27                 page &= PAGE_MASK;
28
29                 local_irq_save(flags);
30                 if (vma->vm_mm != current->mm) {
31                         saved_asid = get_asid();
32                         set_asid(asid);
33                 }
34                 local_flush_tlb_one(asid, page);
35                 if (saved_asid != MMU_NO_ASID)
36                         set_asid(saved_asid);
37                 local_irq_restore(flags);
38         }
39 }
40
41 void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
42                            unsigned long end)
43 {
44         struct mm_struct *mm = vma->vm_mm;
45         unsigned int cpu = smp_processor_id();
46
47         if (cpu_context(cpu, mm) != NO_CONTEXT) {
48                 unsigned long flags;
49                 int size;
50
51                 local_irq_save(flags);
52                 size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
53                 if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
54                         cpu_context(cpu, mm) = NO_CONTEXT;
55                         if (mm == current->mm)
56                                 activate_context(mm, cpu);
57                 } else {
58                         unsigned long asid;
59                         unsigned long saved_asid = MMU_NO_ASID;
60
61                         asid = cpu_asid(cpu, mm);
62                         start &= PAGE_MASK;
63                         end += (PAGE_SIZE - 1);
64                         end &= PAGE_MASK;
65                         if (mm != current->mm) {
66                                 saved_asid = get_asid();
67                                 set_asid(asid);
68                         }
69                         while (start < end) {
70                                 local_flush_tlb_one(asid, start);
71                                 start += PAGE_SIZE;
72                         }
73                         if (saved_asid != MMU_NO_ASID)
74                                 set_asid(saved_asid);
75                 }
76                 local_irq_restore(flags);
77         }
78 }
79
80 void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
81 {
82         unsigned int cpu = smp_processor_id();
83         unsigned long flags;
84         int size;
85
86         local_irq_save(flags);
87         size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
88         if (size > (MMU_NTLB_ENTRIES/4)) { /* Too many TLB to flush */
89                 local_flush_tlb_all();
90         } else {
91                 unsigned long asid;
92                 unsigned long saved_asid = get_asid();
93
94                 asid = cpu_asid(cpu, &init_mm);
95                 start &= PAGE_MASK;
96                 end += (PAGE_SIZE - 1);
97                 end &= PAGE_MASK;
98                 set_asid(asid);
99                 while (start < end) {
100                         local_flush_tlb_one(asid, start);
101                         start += PAGE_SIZE;
102                 }
103                 set_asid(saved_asid);
104         }
105         local_irq_restore(flags);
106 }
107
108 void local_flush_tlb_mm(struct mm_struct *mm)
109 {
110         unsigned int cpu = smp_processor_id();
111
112         /* Invalidate all TLB of this process. */
113         /* Instead of invalidating each TLB, we get new MMU context. */
114         if (cpu_context(cpu, mm) != NO_CONTEXT) {
115                 unsigned long flags;
116
117                 local_irq_save(flags);
118                 cpu_context(cpu, mm) = NO_CONTEXT;
119                 if (mm == current->mm)
120                         activate_context(mm, cpu);
121                 local_irq_restore(flags);
122         }
123 }
124
125 void local_flush_tlb_all(void)
126 {
127         unsigned long flags, status;
128
129         /*
130          * Flush all the TLB.
131          *
132          * Write to the MMU control register's bit:
133          *      TF-bit for SH-3, TI-bit for SH-4.
134          *      It's same position, bit #2.
135          */
136         local_irq_save(flags);
137         status = ctrl_inl(MMUCR);
138         status |= 0x04;
139         ctrl_outl(status, MMUCR);
140         ctrl_barrier();
141         local_irq_restore(flags);
142 }
143
144 void update_mmu_cache(struct vm_area_struct *vma,
145                       unsigned long address, pte_t pte)
146 {
147         unsigned long flags;
148         unsigned long pteval;
149         unsigned long vpn;
150         struct page *page;
151         unsigned long pfn = pte_pfn(pte);
152         struct address_space *mapping;
153
154         if (!pfn_valid(pfn))
155                 return;
156
157         page = pfn_to_page(pfn);
158         mapping = page_mapping(page);
159         if (mapping) {
160                 unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
161                 int dirty = test_and_clear_bit(PG_dcache_dirty, &page->flags);
162
163                 if (dirty)
164                         __flush_wback_region((void *)P1SEGADDR(phys),
165                                              PAGE_SIZE);
166         }
167
168         local_irq_save(flags);
169
170         /* Set PTEH register */
171         vpn = (address & MMU_VPN_MASK) | get_asid();
172         ctrl_outl(vpn, MMU_PTEH);
173
174         pteval = pte_val(pte);
175
176 #ifdef CONFIG_CPU_HAS_PTEA
177         /* Set PTEA register */
178         /* TODO: make this look less hacky */
179         ctrl_outl(((pteval >> 28) & 0xe) | (pteval & 0x1), MMU_PTEA);
180 #endif
181
182         /* Set PTEL register */
183         pteval &= _PAGE_FLAGS_HARDWARE_MASK; /* drop software flags */
184 #if defined(CONFIG_SH_WRITETHROUGH) && defined(CONFIG_CPU_SH4)
185         pteval |= _PAGE_WT;
186 #endif
187         /* conveniently, we want all the software flags to be 0 anyway */
188         ctrl_outl(pteval, MMU_PTEL);
189
190         /* Load the TLB */
191         asm volatile("ldtlb": /* no output */ : /* no input */ : "memory");
192         local_irq_restore(flags);
193 }