powerpc/mm/radix: Add tlbflush routines
[cascardo/linux.git] / arch / powerpc / mm / tlb-radix.c
1 /*
2  * TLB flush routines for radix kernels.
3  *
4  * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version
9  * 2 of the License, or (at your option) any later version.
10  */
11
12 #include <linux/mm.h>
13 #include <linux/hugetlb.h>
14 #include <linux/memblock.h>
15
16 #include <asm/tlb.h>
17 #include <asm/tlbflush.h>
18
19 static DEFINE_RAW_SPINLOCK(native_tlbie_lock);
20
21 static inline void __tlbiel_pid(unsigned long pid, int set)
22 {
23         unsigned long rb,rs,ric,prs,r;
24
25         rb = PPC_BIT(53); /* IS = 1 */
26         rb |= set << PPC_BITLSHIFT(51);
27         rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
28         prs = 1; /* process scoped */
29         r = 1;   /* raidx format */
30         ric = 2;  /* invalidate all the caches */
31
32         asm volatile("ptesync": : :"memory");
33         asm volatile(".long 0x7c000224 | (%0 << 11) | (%1 << 16) |"
34                      "(%2 << 17) | (%3 << 18) | (%4 << 21)"
35                      : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
36         asm volatile("ptesync": : :"memory");
37 }
38
39 /*
40  * We use 128 set in radix mode and 256 set in hpt mode.
41  */
42 static inline void _tlbiel_pid(unsigned long pid)
43 {
44         int set;
45
46         for (set = 0; set < POWER9_TLB_SETS_RADIX ; set++) {
47                 __tlbiel_pid(pid, set);
48         }
49         return;
50 }
51
52 static inline void _tlbie_pid(unsigned long pid)
53 {
54         unsigned long rb,rs,ric,prs,r;
55
56         rb = PPC_BIT(53); /* IS = 1 */
57         rs = pid << PPC_BITLSHIFT(31);
58         prs = 1; /* process scoped */
59         r = 1;   /* raidx format */
60         ric = 2;  /* invalidate all the caches */
61
62         asm volatile("ptesync": : :"memory");
63         asm volatile(".long 0x7c000264 | (%0 << 11) | (%1 << 16) |"
64                      "(%2 << 17) | (%3 << 18) | (%4 << 21)"
65                      : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
66         asm volatile("eieio; tlbsync; ptesync": : :"memory");
67 }
68
69 static inline void _tlbiel_va(unsigned long va, unsigned long pid,
70                               unsigned long ap)
71 {
72         unsigned long rb,rs,ric,prs,r;
73
74         rb = va & ~(PPC_BITMASK(52, 63));
75         rb |= ap << PPC_BITLSHIFT(58);
76         rs = pid << PPC_BITLSHIFT(31);
77         prs = 1; /* process scoped */
78         r = 1;   /* raidx format */
79         ric = 0;  /* no cluster flush yet */
80
81         asm volatile("ptesync": : :"memory");
82         asm volatile(".long 0x7c000224 | (%0 << 11) | (%1 << 16) |"
83                      "(%2 << 17) | (%3 << 18) | (%4 << 21)"
84                      : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
85         asm volatile("ptesync": : :"memory");
86 }
87
88 static inline void _tlbie_va(unsigned long va, unsigned long pid,
89                              unsigned long ap)
90 {
91         unsigned long rb,rs,ric,prs,r;
92
93         rb = va & ~(PPC_BITMASK(52, 63));
94         rb |= ap << PPC_BITLSHIFT(58);
95         rs = pid << PPC_BITLSHIFT(31);
96         prs = 1; /* process scoped */
97         r = 1;   /* raidx format */
98         ric = 0;  /* no cluster flush yet */
99
100         asm volatile("ptesync": : :"memory");
101         asm volatile(".long 0x7c000264 | (%0 << 11) | (%1 << 16) |"
102                      "(%2 << 17) | (%3 << 18) | (%4 << 21)"
103                      : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
104         asm volatile("eieio; tlbsync; ptesync": : :"memory");
105 }
106
107 /*
108  * Base TLB flushing operations:
109  *
110  *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
111  *  - flush_tlb_page(vma, vmaddr) flushes one page
112  *  - flush_tlb_range(vma, start, end) flushes a range of pages
113  *  - flush_tlb_kernel_range(start, end) flushes kernel pages
114  *
115  *  - local_* variants of page and mm only apply to the current
116  *    processor
117  */
118 void radix__local_flush_tlb_mm(struct mm_struct *mm)
119 {
120         unsigned int pid;
121
122         preempt_disable();
123         pid = mm->context.id;
124         if (pid != MMU_NO_CONTEXT)
125                 _tlbiel_pid(pid);
126         preempt_enable();
127 }
128 EXPORT_SYMBOL(radix__local_flush_tlb_mm);
129
130 void radix___local_flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
131                             unsigned long ap, int nid)
132 {
133         unsigned int pid;
134
135         preempt_disable();
136         pid = mm ? mm->context.id : 0;
137         if (pid != MMU_NO_CONTEXT)
138                 _tlbiel_va(vmaddr, pid, ap);
139         preempt_enable();
140 }
141
142 void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
143 {
144         radix___local_flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr,
145                                mmu_get_ap(mmu_virtual_psize), 0);
146 }
147 EXPORT_SYMBOL(radix__local_flush_tlb_page);
148
149 #ifdef CONFIG_SMP
150 static int mm_is_core_local(struct mm_struct *mm)
151 {
152         return cpumask_subset(mm_cpumask(mm),
153                               topology_sibling_cpumask(smp_processor_id()));
154 }
155
156 void radix__flush_tlb_mm(struct mm_struct *mm)
157 {
158         unsigned int pid;
159
160         preempt_disable();
161         pid = mm->context.id;
162         if (unlikely(pid == MMU_NO_CONTEXT))
163                 goto no_context;
164
165         if (!mm_is_core_local(mm)) {
166                 int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
167
168                 if (lock_tlbie)
169                         raw_spin_lock(&native_tlbie_lock);
170                 _tlbie_pid(pid);
171                 if (lock_tlbie)
172                         raw_spin_unlock(&native_tlbie_lock);
173         } else
174                 _tlbiel_pid(pid);
175 no_context:
176         preempt_enable();
177 }
178 EXPORT_SYMBOL(radix__flush_tlb_mm);
179
180 void radix___flush_tlb_page(struct mm_struct *mm, unsigned long vmaddr,
181                        unsigned long ap, int nid)
182 {
183         unsigned int pid;
184
185         preempt_disable();
186         pid = mm ? mm->context.id : 0;
187         if (unlikely(pid == MMU_NO_CONTEXT))
188                 goto bail;
189         if (!mm_is_core_local(mm)) {
190                 int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
191
192                 if (lock_tlbie)
193                         raw_spin_lock(&native_tlbie_lock);
194                 _tlbie_va(vmaddr, pid, ap);
195                 if (lock_tlbie)
196                         raw_spin_unlock(&native_tlbie_lock);
197         } else
198                 _tlbiel_va(vmaddr, pid, ap);
199 bail:
200         preempt_enable();
201 }
202
203 void radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
204 {
205         radix___flush_tlb_page(vma ? vma->vm_mm : NULL, vmaddr,
206                          mmu_get_ap(mmu_virtual_psize), 0);
207 }
208 EXPORT_SYMBOL(radix__flush_tlb_page);
209
210 #endif /* CONFIG_SMP */
211
212 void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end)
213 {
214         int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
215
216         if (lock_tlbie)
217                 raw_spin_lock(&native_tlbie_lock);
218         _tlbie_pid(0);
219         if (lock_tlbie)
220                 raw_spin_unlock(&native_tlbie_lock);
221 }
222 EXPORT_SYMBOL(radix__flush_tlb_kernel_range);
223
224 /*
225  * Currently, for range flushing, we just do a full mm flush. Because
226  * we use this in code path where we don' track the page size.
227  */
228 void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
229                      unsigned long end)
230
231 {
232         struct mm_struct *mm = vma->vm_mm;
233         radix__flush_tlb_mm(mm);
234 }
235 EXPORT_SYMBOL(radix__flush_tlb_range);
236
237
238 void radix__tlb_flush(struct mmu_gather *tlb)
239 {
240         struct mm_struct *mm = tlb->mm;
241         radix__flush_tlb_mm(mm);
242 }