Branch data Line data Source code
1 : : /*
2 : : * linux/arch/arm/kernel/smp_tlb.c
3 : : *
4 : : * Copyright (C) 2002 ARM Limited, All Rights Reserved.
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU General Public License version 2 as
8 : : * published by the Free Software Foundation.
9 : : */
10 : : #include <linux/preempt.h>
11 : : #include <linux/smp.h>
12 : :
13 : : #include <asm/smp_plat.h>
14 : : #include <asm/tlbflush.h>
15 : : #include <asm/mmu_context.h>
16 : :
17 : : /**********************************************************************/
18 : :
19 : : /*
20 : : * TLB operations
21 : : */
22 : : struct tlb_args {
23 : : struct vm_area_struct *ta_vma;
24 : : unsigned long ta_start;
25 : : unsigned long ta_end;
26 : : };
27 : :
28 : 0 : static inline void ipi_flush_tlb_all(void *ignored)
29 : : {
30 : : local_flush_tlb_all();
31 : 0 : }
32 : :
33 : 0 : static inline void ipi_flush_tlb_mm(void *arg)
34 : : {
35 : : struct mm_struct *mm = (struct mm_struct *)arg;
36 : :
37 : : local_flush_tlb_mm(mm);
38 : 0 : }
39 : :
40 : 0 : static inline void ipi_flush_tlb_page(void *arg)
41 : : {
42 : : struct tlb_args *ta = (struct tlb_args *)arg;
43 : :
44 : 0 : local_flush_tlb_page(ta->ta_vma, ta->ta_start);
45 : 0 : }
46 : :
47 : 0 : static inline void ipi_flush_tlb_kernel_page(void *arg)
48 : : {
49 : : struct tlb_args *ta = (struct tlb_args *)arg;
50 : :
51 : 0 : local_flush_tlb_kernel_page(ta->ta_start);
52 : 0 : }
53 : :
54 : 0 : static inline void ipi_flush_tlb_range(void *arg)
55 : : {
56 : : struct tlb_args *ta = (struct tlb_args *)arg;
57 : :
58 : 0 : local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
59 : 0 : }
60 : :
61 : 0 : static inline void ipi_flush_tlb_kernel_range(void *arg)
62 : : {
63 : : struct tlb_args *ta = (struct tlb_args *)arg;
64 : :
65 : 0 : local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
66 : 0 : }
67 : :
68 : 0 : static inline void ipi_flush_bp_all(void *ignored)
69 : : {
70 : : local_flush_bp_all();
71 : 0 : }
72 : :
73 : : #ifdef CONFIG_ARM_ERRATA_798181
74 : : bool (*erratum_a15_798181_handler)(void);
75 : :
76 : : static bool erratum_a15_798181_partial(void)
77 : : {
78 : : asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (0));
79 : : dsb(ish);
80 : : return false;
81 : : }
82 : :
83 : : static bool erratum_a15_798181_broadcast(void)
84 : : {
85 : : asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (0));
86 : : dsb(ish);
87 : : return true;
88 : : }
89 : :
90 : : void erratum_a15_798181_init(void)
91 : : {
92 : : unsigned int midr = read_cpuid_id();
93 : : unsigned int revidr = read_cpuid(CPUID_REVIDR);
94 : :
95 : : /* Cortex-A15 r0p0..r3p2 w/o ECO fix affected */
96 : : if ((midr & 0xff0ffff0) != 0x410fc0f0 || midr > 0x413fc0f2 ||
97 : : (revidr & 0x210) == 0x210) {
98 : : return;
99 : : }
100 : : if (revidr & 0x10)
101 : : erratum_a15_798181_handler = erratum_a15_798181_partial;
102 : : else
103 : : erratum_a15_798181_handler = erratum_a15_798181_broadcast;
104 : : }
105 : : #endif
106 : :
107 : : static void ipi_flush_tlb_a15_erratum(void *arg)
108 : : {
109 : : dmb();
110 : : }
111 : :
112 : : static void broadcast_tlb_a15_erratum(void)
113 : : {
114 : : if (!erratum_a15_798181())
115 : : return;
116 : :
117 : : smp_call_function(ipi_flush_tlb_a15_erratum, NULL, 1);
118 : : }
119 : :
120 : : static void broadcast_tlb_mm_a15_erratum(struct mm_struct *mm)
121 : : {
122 : : int this_cpu;
123 : : cpumask_t mask = { CPU_BITS_NONE };
124 : :
125 : : if (!erratum_a15_798181())
126 : : return;
127 : :
128 : : this_cpu = get_cpu();
129 : : a15_erratum_get_cpumask(this_cpu, mm, &mask);
130 : : smp_call_function_many(&mask, ipi_flush_tlb_a15_erratum, NULL, 1);
131 : : put_cpu();
132 : : }
133 : :
134 : 0 : void flush_tlb_all(void)
135 : : {
136 [ # # ]: 0 : if (tlb_ops_need_broadcast())
137 : 0 : on_each_cpu(ipi_flush_tlb_all, NULL, 1);
138 : : else
139 : : __flush_tlb_all();
140 : : broadcast_tlb_a15_erratum();
141 : 0 : }
142 : :
143 : 0 : void flush_tlb_mm(struct mm_struct *mm)
144 : : {
145 [ - + ]: 2266002 : if (tlb_ops_need_broadcast())
146 : 0 : on_each_cpu_mask(mm_cpumask(mm), ipi_flush_tlb_mm, mm, 1);
147 : : else
148 : : __flush_tlb_mm(mm);
149 : : broadcast_tlb_mm_a15_erratum(mm);
150 : 2266004 : }
151 : :
152 : 0 : void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
153 : : {
154 [ - + ]: 27856490 : if (tlb_ops_need_broadcast()) {
155 : : struct tlb_args ta;
156 : 0 : ta.ta_vma = vma;
157 : 0 : ta.ta_start = uaddr;
158 : 0 : on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page,
159 : : &ta, 1);
160 : : } else
161 : : __flush_tlb_page(vma, uaddr);
162 : : broadcast_tlb_mm_a15_erratum(vma->vm_mm);
163 : 27857363 : }
164 : :
165 : 0 : void flush_tlb_kernel_page(unsigned long kaddr)
166 : : {
167 [ # # ]: 0 : if (tlb_ops_need_broadcast()) {
168 : : struct tlb_args ta;
169 : 0 : ta.ta_start = kaddr;
170 : 0 : on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1);
171 : : } else
172 : : __flush_tlb_kernel_page(kaddr);
173 : : broadcast_tlb_a15_erratum();
174 : 0 : }
175 : :
176 : 0 : void flush_tlb_range(struct vm_area_struct *vma,
177 : : unsigned long start, unsigned long end)
178 : : {
179 [ - + ]: 449185 : if (tlb_ops_need_broadcast()) {
180 : : struct tlb_args ta;
181 : 0 : ta.ta_vma = vma;
182 : 0 : ta.ta_start = start;
183 : 0 : ta.ta_end = end;
184 : 0 : on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range,
185 : : &ta, 1);
186 : : } else
187 : 449185 : local_flush_tlb_range(vma, start, end);
188 : : broadcast_tlb_mm_a15_erratum(vma->vm_mm);
189 : 449194 : }
190 : :
191 : 0 : void flush_tlb_kernel_range(unsigned long start, unsigned long end)
192 : : {
193 [ - + ]: 2467 : if (tlb_ops_need_broadcast()) {
194 : : struct tlb_args ta;
195 : 0 : ta.ta_start = start;
196 : 0 : ta.ta_end = end;
197 : 0 : on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
198 : : } else
199 : 2467 : local_flush_tlb_kernel_range(start, end);
200 : : broadcast_tlb_a15_erratum();
201 : 2467 : }
202 : :
203 : 0 : void flush_bp_all(void)
204 : : {
205 [ # # ]: 0 : if (tlb_ops_need_broadcast())
206 : 0 : on_each_cpu(ipi_flush_bp_all, NULL, 1);
207 : : else
208 : : __flush_bp_all();
209 : 0 : }
|