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
16/**********************************************************************/
17
18/*
19 * TLB operations
20 */
21struct tlb_args {
22	struct vm_area_struct *ta_vma;
23	unsigned long ta_start;
24	unsigned long ta_end;
25};
26
27static inline void ipi_flush_tlb_all(void *ignored)
28{
29	local_flush_tlb_all();
30}
31
32static inline void ipi_flush_tlb_mm(void *arg)
33{
34	struct mm_struct *mm = (struct mm_struct *)arg;
35
36	local_flush_tlb_mm(mm);
37}
38
39static inline void ipi_flush_tlb_page(void *arg)
40{
41	struct tlb_args *ta = (struct tlb_args *)arg;
42
43	local_flush_tlb_page(ta->ta_vma, ta->ta_start);
44}
45
46static inline void ipi_flush_tlb_kernel_page(void *arg)
47{
48	struct tlb_args *ta = (struct tlb_args *)arg;
49
50	local_flush_tlb_kernel_page(ta->ta_start);
51}
52
53static inline void ipi_flush_tlb_range(void *arg)
54{
55	struct tlb_args *ta = (struct tlb_args *)arg;
56
57	local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
58}
59
60static inline void ipi_flush_tlb_kernel_range(void *arg)
61{
62	struct tlb_args *ta = (struct tlb_args *)arg;
63
64	local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
65}
66
67void flush_tlb_all(void)
68{
69	if (tlb_ops_need_broadcast())
70		on_each_cpu(ipi_flush_tlb_all, NULL, 1);
71	else
72		local_flush_tlb_all();
73}
74
75void flush_tlb_mm(struct mm_struct *mm)
76{
77	if (tlb_ops_need_broadcast())
78		on_each_cpu_mask(mm_cpumask(mm), ipi_flush_tlb_mm, mm, 1);
79	else
80		local_flush_tlb_mm(mm);
81}
82
83void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
84{
85	if (tlb_ops_need_broadcast()) {
86		struct tlb_args ta;
87		ta.ta_vma = vma;
88		ta.ta_start = uaddr;
89		on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_page,
90					&ta, 1);
91	} else
92		local_flush_tlb_page(vma, uaddr);
93}
94
95void flush_tlb_kernel_page(unsigned long kaddr)
96{
97	if (tlb_ops_need_broadcast()) {
98		struct tlb_args ta;
99		ta.ta_start = kaddr;
100		on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1);
101	} else
102		local_flush_tlb_kernel_page(kaddr);
103}
104
105void flush_tlb_range(struct vm_area_struct *vma,
106                     unsigned long start, unsigned long end)
107{
108	if (tlb_ops_need_broadcast()) {
109		struct tlb_args ta;
110		ta.ta_vma = vma;
111		ta.ta_start = start;
112		ta.ta_end = end;
113		on_each_cpu_mask(mm_cpumask(vma->vm_mm), ipi_flush_tlb_range,
114					&ta, 1);
115	} else
116		local_flush_tlb_range(vma, start, end);
117}
118
119void flush_tlb_kernel_range(unsigned long start, unsigned long end)
120{
121	if (tlb_ops_need_broadcast()) {
122		struct tlb_args ta;
123		ta.ta_start = start;
124		ta.ta_end = end;
125		on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1);
126	} else
127		local_flush_tlb_kernel_range(start, end);
128}
129
130