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 */ 22struct tlb_args { 23 struct vm_area_struct *ta_vma; 24 unsigned long ta_start; 25 unsigned long ta_end; 26}; 27 28static inline void ipi_flush_tlb_all(void *ignored) 29{ 30 local_flush_tlb_all(); 31} 32 33static 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} 39 40static inline void ipi_flush_tlb_page(void *arg) 41{ 42 struct tlb_args *ta = (struct tlb_args *)arg; 43 44 local_flush_tlb_page(ta->ta_vma, ta->ta_start); 45} 46 47static inline void ipi_flush_tlb_kernel_page(void *arg) 48{ 49 struct tlb_args *ta = (struct tlb_args *)arg; 50 51 local_flush_tlb_kernel_page(ta->ta_start); 52} 53 54static inline void ipi_flush_tlb_range(void *arg) 55{ 56 struct tlb_args *ta = (struct tlb_args *)arg; 57 58 local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end); 59} 60 61static inline void ipi_flush_tlb_kernel_range(void *arg) 62{ 63 struct tlb_args *ta = (struct tlb_args *)arg; 64 65 local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); 66} 67 68static inline void ipi_flush_bp_all(void *ignored) 69{ 70 local_flush_bp_all(); 71} 72 73#ifdef CONFIG_ARM_ERRATA_798181 74bool (*erratum_a15_798181_handler)(void); 75 76static 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 83static 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 90void erratum_a15_798181_init(void) 91{ 92 unsigned int midr = read_cpuid_id(); 93 unsigned int revidr = read_cpuid(CPUID_REVIDR); 94 95 /* Brahma-B15 r0p0..r0p2 affected 96 * Cortex-A15 r0p0..r3p2 w/o ECO fix affected */ 97 if ((midr & 0xff0ffff0) == 0x420f00f0 && midr <= 0x420f00f2) 98 erratum_a15_798181_handler = erratum_a15_798181_broadcast; 99 else if ((midr & 0xff0ffff0) == 0x410fc0f0 && midr <= 0x413fc0f2 && 100 (revidr & 0x210) != 0x210) { 101 if (revidr & 0x10) 102 erratum_a15_798181_handler = 103 erratum_a15_798181_partial; 104 else 105 erratum_a15_798181_handler = 106 erratum_a15_798181_broadcast; 107 } 108} 109#endif 110 111static void ipi_flush_tlb_a15_erratum(void *arg) 112{ 113 dmb(); 114} 115 116static void broadcast_tlb_a15_erratum(void) 117{ 118 if (!erratum_a15_798181()) 119 return; 120 121 smp_call_function(ipi_flush_tlb_a15_erratum, NULL, 1); 122} 123 124static void broadcast_tlb_mm_a15_erratum(struct mm_struct *mm) 125{ 126 int this_cpu; 127 cpumask_t mask = { CPU_BITS_NONE }; 128 129 if (!erratum_a15_798181()) 130 return; 131 132 this_cpu = get_cpu(); 133 a15_erratum_get_cpumask(this_cpu, mm, &mask); 134 smp_call_function_many(&mask, ipi_flush_tlb_a15_erratum, NULL, 1); 135 put_cpu(); 136} 137 138void flush_tlb_all(void) 139{ 140 if (tlb_ops_need_broadcast()) 141 on_each_cpu(ipi_flush_tlb_all, NULL, 1); 142 else 143 __flush_tlb_all(); 144 broadcast_tlb_a15_erratum(); 145} 146 147void flush_tlb_mm(struct mm_struct *mm) 148{ 149 if (tlb_ops_need_broadcast()) 150 on_each_cpu_mask(mm_cpumask(mm), ipi_flush_tlb_mm, mm, 1); 151 else 152 __flush_tlb_mm(mm); 153 broadcast_tlb_mm_a15_erratum(mm); 154} 155 156void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr) 157{ 158 if (tlb_ops_need_broadcast()) { 159 struct tlb_args ta; 160 ta.ta_vma = vma; 161 ta.ta_start = uaddr; 162 on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page, 163 &ta, 1); 164 } else 165 __flush_tlb_page(vma, uaddr); 166 broadcast_tlb_mm_a15_erratum(vma->vm_mm); 167} 168 169void flush_tlb_kernel_page(unsigned long kaddr) 170{ 171 if (tlb_ops_need_broadcast()) { 172 struct tlb_args ta; 173 ta.ta_start = kaddr; 174 on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1); 175 } else 176 __flush_tlb_kernel_page(kaddr); 177 broadcast_tlb_a15_erratum(); 178} 179 180void flush_tlb_range(struct vm_area_struct *vma, 181 unsigned long start, unsigned long end) 182{ 183 if (tlb_ops_need_broadcast()) { 184 struct tlb_args ta; 185 ta.ta_vma = vma; 186 ta.ta_start = start; 187 ta.ta_end = end; 188 on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range, 189 &ta, 1); 190 } else 191 local_flush_tlb_range(vma, start, end); 192 broadcast_tlb_mm_a15_erratum(vma->vm_mm); 193} 194 195void flush_tlb_kernel_range(unsigned long start, unsigned long end) 196{ 197 if (tlb_ops_need_broadcast()) { 198 struct tlb_args ta; 199 ta.ta_start = start; 200 ta.ta_end = end; 201 on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1); 202 } else 203 local_flush_tlb_kernel_range(start, end); 204 broadcast_tlb_a15_erratum(); 205} 206 207void flush_bp_all(void) 208{ 209 if (tlb_ops_need_broadcast()) 210 on_each_cpu(ipi_flush_bp_all, NULL, 1); 211 else 212 __flush_bp_all(); 213} 214