summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorCatalin Marinas <catalin.marinas@arm.com>2013-02-19 16:34:29 +0000
committerRiham Haidar <rhaidar@nvidia.com>2013-06-04 14:33:48 -0700
commit9cb99a62091d141d59dfb5649009a9f37299d367 (patch)
treecb356180e21bb7cac252f3b92e6207bfb3f1fd26 /arch
parenta0d526a8196b466dc072d3c69cd53a81cda44388 (diff)
arm: errata: Workaround for Cortex-A15 erratum 798181 (TLBI/DSB operations)
On Cortex-A15 (r0p0..r3p2) the TLBI/DSB are not adequately shooting down all use of the old entries. This patch implements the erratum workaround which consists of: 1. Dummy TLBIMVAIS and DSB on the CPU doing the TLBI operation. 2. Send IPI to the CPUs that are running the same mm (and ASID) as the one being invalidated (or all the online CPUs for global pages). 3. CPU receiving the IPI executes a DMB and CLREX (part of the exception return code already). The switch_mm() code includes a DMB operation since the IPI is only sent to CPUs running the same ASID. Change-Id: Ideb7f479910f7d4bf25182c84eb5e71691c42a93 Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Bo Yan <byan@nvidia.com> Reviewed-on: http://git-master/r/209830 (cherry picked from commit e11ccb30b44fc55ba0576f5082e5e17e9a1d1854) Reviewed-on: http://git-master/r/221353 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Tested-by: Sang-Hun Lee <sanlee@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/Kconfig10
-rw-r--r--arch/arm/include/asm/highmem.h7
-rw-r--r--arch/arm/include/asm/mmu_context.h7
-rw-r--r--arch/arm/include/asm/tlbflush.h15
-rw-r--r--arch/arm/kernel/smp_tlb.c65
-rw-r--r--arch/arm/mm/context.c1
6 files changed, 105 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 8ec3ec25820d..344c3cf78dd1 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1450,6 +1450,16 @@ config PL310_ERRATA_769419
on systems with an outer cache, the store buffer is drained
explicitly.
+config ARM_ERRATA_798181
+ bool "ARM errata: TLBI/DSB failure on Cortex-A15"
+ depends on CPU_V7 && SMP
+ help
+ On Cortex-A15 (r0p0..r3p2) the TLBI*IS/DSB operations are not
+ adequately shooting down all use of the old entries. This
+ option enables the Linux kernel workaround for this erratum
+ which sends an IPI to the CPUs that are running the same mm
+ (and ASID) as the one being invalidated.
+
config ARM_ERRATA_799270
bool "ARM errata: Writing ACTLR.SMP when the L2 cache has been idle for an extended period may not work correctly"
depends on CPU_V7 && SMP
diff --git a/arch/arm/include/asm/highmem.h b/arch/arm/include/asm/highmem.h
index 8c5e828f484d..91b99abe7a95 100644
--- a/arch/arm/include/asm/highmem.h
+++ b/arch/arm/include/asm/highmem.h
@@ -41,6 +41,13 @@ extern void kunmap_high(struct page *page);
#endif
#endif
+/*
+ * Needed to be able to broadcast the TLB invalidation for kmap.
+ */
+#ifdef CONFIG_ARM_ERRATA_798181
+#undef ARCH_NEEDS_KMAP_HIGH_GET
+#endif
+
#ifdef ARCH_NEEDS_KMAP_HIGH_GET
extern void *kmap_high_get(struct page *page);
#else
diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h
index a0b3cac0547c..20d1dfeccfe3 100644
--- a/arch/arm/include/asm/mmu_context.h
+++ b/arch/arm/include/asm/mmu_context.h
@@ -125,6 +125,13 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
#endif
check_context(next);
cpu_switch_mm(next->pgd, next);
+#ifdef CONFIG_ARM_ERRATA_798181
+ /*
+ * DMB required since we only send IPI to the other CPUs if
+ * they run the same mm as the one being invalidated.
+ */
+ dmb();
+#endif
if (cache_is_vivt())
cpumask_clear_cpu(cpu, mm_cpumask(prev));
}
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index 85fe61e73202..9b4541e784a2 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -447,6 +447,21 @@ static inline void local_flush_tlb_kernel_page(unsigned long kaddr)
}
}
+#ifdef CONFIG_ARM_ERRATA_798181
+static inline void dummy_flush_tlb_a15_erratum(void)
+{
+ /*
+ * Dummy TLBIMVAIS. Using the unmapped address 0 and ASID 0.
+ */
+ asm("mcr p15, 0, %0, c8, c3, 1" : : "r" (0));
+ dsb();
+}
+#else
+static inline void dummy_flush_tlb_a15_erratum(void)
+{
+}
+#endif
+
/*
* flush_pmd_entry
*
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 02c5d2ce23bf..c92997ef7266 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -12,6 +12,7 @@
#include <asm/smp_plat.h>
#include <asm/tlbflush.h>
+#include <asm/mmu_context.h>
/**********************************************************************/
@@ -64,12 +65,71 @@ static inline void ipi_flush_tlb_kernel_range(void *arg)
local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
}
+#ifdef CONFIG_ARM_ERRATA_798181
+static int erratum_a15_798181(void)
+{
+ unsigned int midr = read_cpuid_id();
+
+ /* Cortex-A15 r0p0..r3p2 affected */
+ if ((midr & 0xff0ffff0) != 0x410fc0f0 || midr > 0x413fc0f2)
+ return 0;
+ return 1;
+}
+#else
+static int erratum_a15_798181(void)
+{
+ return 0;
+}
+#endif
+
+static void ipi_flush_tlb_a15_erratum(void *arg)
+{
+ dmb();
+}
+
+static void broadcast_tlb_a15_erratum(void)
+{
+ if (!erratum_a15_798181())
+ return;
+
+ dummy_flush_tlb_a15_erratum();
+ smp_call_function_many(cpu_online_mask, ipi_flush_tlb_a15_erratum,
+ NULL, 1);
+}
+
+static void broadcast_tlb_mm_a15_erratum(struct mm_struct *mm)
+{
+ int cpu;
+ cpumask_t mask = { CPU_BITS_NONE };
+
+ if (!erratum_a15_798181())
+ return;
+
+ dummy_flush_tlb_a15_erratum();
+ for_each_online_cpu(cpu) {
+ if (cpu == smp_processor_id())
+ continue;
+ /*
+ * We only need to send an IPI if the other CPUs are running
+ * the same mm (and ASID) as the one being invalidated. There
+ * is no need for locking around the current_mm check since
+ * the switch_mm() function has a dmb() for this erratum in
+ * case a context switch happens on another CPU after the
+ * condition below.
+ */
+ if (mm == per_cpu(current_mm, cpu))
+ cpumask_set_cpu(cpu, &mask);
+ }
+ smp_call_function_many(&mask, ipi_flush_tlb_a15_erratum, NULL, 1);
+}
+
void flush_tlb_all(void)
{
if (tlb_ops_need_broadcast())
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
else
local_flush_tlb_all();
+ broadcast_tlb_a15_erratum();
}
void flush_tlb_mm(struct mm_struct *mm)
@@ -78,6 +138,7 @@ void flush_tlb_mm(struct mm_struct *mm)
on_each_cpu_mask(mm_cpumask(mm), ipi_flush_tlb_mm, mm, 1);
else
local_flush_tlb_mm(mm);
+ broadcast_tlb_mm_a15_erratum(mm);
}
void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
@@ -90,6 +151,7 @@ void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
&ta, 1);
} else
local_flush_tlb_page(vma, uaddr);
+ broadcast_tlb_mm_a15_erratum(vma->vm_mm);
}
void flush_tlb_kernel_page(unsigned long kaddr)
@@ -100,6 +162,7 @@ void flush_tlb_kernel_page(unsigned long kaddr)
on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1);
} else
local_flush_tlb_kernel_page(kaddr);
+ broadcast_tlb_a15_erratum();
}
void flush_tlb_range(struct vm_area_struct *vma,
@@ -114,6 +177,7 @@ void flush_tlb_range(struct vm_area_struct *vma,
&ta, 1);
} else
local_flush_tlb_range(vma, start, end);
+ broadcast_tlb_mm_a15_erratum(vma->vm_mm);
}
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
@@ -125,5 +189,6 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
} else
local_flush_tlb_kernel_range(start, end);
+ broadcast_tlb_a15_erratum();
}
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c
index aaa291fc072e..11f3448ee22a 100644
--- a/arch/arm/mm/context.c
+++ b/arch/arm/mm/context.c
@@ -65,6 +65,7 @@ static void flush_context(void)
{
cpu_set_reserved_ttbr0();
local_flush_tlb_all();
+ dummy_flush_tlb_a15_erratum();
if (icache_is_vivt_asid_tagged()) {
__flush_icache_all();
dsb();