GNU Linux-libre 5.10.217-gnu1
[releases.git] / arch / csky / mm / tlb.c
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
3
4 #include <linux/init.h>
5 #include <linux/mm.h>
6 #include <linux/module.h>
7 #include <linux/sched.h>
8
9 #include <asm/mmu_context.h>
10 #include <asm/setup.h>
11
12 /*
13  * One C-SKY MMU TLB entry contain two PFN/page entry, ie:
14  * 1VPN -> 2PFN
15  */
16 #define TLB_ENTRY_SIZE          (PAGE_SIZE * 2)
17 #define TLB_ENTRY_SIZE_MASK     (PAGE_MASK << 1)
18
19 void flush_tlb_all(void)
20 {
21         tlb_invalid_all();
22 }
23
24 void flush_tlb_mm(struct mm_struct *mm)
25 {
26 #ifdef CONFIG_CPU_HAS_TLBI
27         asm volatile("tlbi.asids %0"::"r"(cpu_asid(mm)));
28 #else
29         tlb_invalid_all();
30 #endif
31 }
32
33 /*
34  * MMU operation regs only could invalid tlb entry in jtlb and we
35  * need change asid field to invalid I-utlb & D-utlb.
36  */
37 #ifndef CONFIG_CPU_HAS_TLBI
38 #define restore_asid_inv_utlb(oldpid, newpid) \
39 do { \
40         if (oldpid == newpid) \
41                 write_mmu_entryhi(oldpid + 1); \
42         write_mmu_entryhi(oldpid); \
43 } while (0)
44 #endif
45
46 void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
47                         unsigned long end)
48 {
49         unsigned long newpid = cpu_asid(vma->vm_mm);
50
51         start &= TLB_ENTRY_SIZE_MASK;
52         end   += TLB_ENTRY_SIZE - 1;
53         end   &= TLB_ENTRY_SIZE_MASK;
54
55 #ifdef CONFIG_CPU_HAS_TLBI
56         while (start < end) {
57                 asm volatile("tlbi.vas %0"::"r"(start | newpid));
58                 start += 2*PAGE_SIZE;
59         }
60         sync_is();
61 #else
62         {
63         unsigned long flags, oldpid;
64
65         local_irq_save(flags);
66         oldpid = read_mmu_entryhi() & ASID_MASK;
67         while (start < end) {
68                 int idx;
69
70                 write_mmu_entryhi(start | newpid);
71                 start += 2*PAGE_SIZE;
72                 tlb_probe();
73                 idx = read_mmu_index();
74                 if (idx >= 0)
75                         tlb_invalid_indexed();
76         }
77         restore_asid_inv_utlb(oldpid, newpid);
78         local_irq_restore(flags);
79         }
80 #endif
81 }
82
83 void flush_tlb_kernel_range(unsigned long start, unsigned long end)
84 {
85         start &= TLB_ENTRY_SIZE_MASK;
86         end   += TLB_ENTRY_SIZE - 1;
87         end   &= TLB_ENTRY_SIZE_MASK;
88
89 #ifdef CONFIG_CPU_HAS_TLBI
90         while (start < end) {
91                 asm volatile("tlbi.vaas %0"::"r"(start));
92                 start += 2*PAGE_SIZE;
93         }
94         sync_is();
95 #else
96         {
97         unsigned long flags, oldpid;
98
99         local_irq_save(flags);
100         oldpid = read_mmu_entryhi() & ASID_MASK;
101         while (start < end) {
102                 int idx;
103
104                 write_mmu_entryhi(start | oldpid);
105                 start += 2*PAGE_SIZE;
106                 tlb_probe();
107                 idx = read_mmu_index();
108                 if (idx >= 0)
109                         tlb_invalid_indexed();
110         }
111         restore_asid_inv_utlb(oldpid, oldpid);
112         local_irq_restore(flags);
113         }
114 #endif
115 }
116
117 void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
118 {
119         int newpid = cpu_asid(vma->vm_mm);
120
121         addr &= TLB_ENTRY_SIZE_MASK;
122
123 #ifdef CONFIG_CPU_HAS_TLBI
124         asm volatile("tlbi.vas %0"::"r"(addr | newpid));
125         sync_is();
126 #else
127         {
128         int oldpid, idx;
129         unsigned long flags;
130
131         local_irq_save(flags);
132         oldpid = read_mmu_entryhi() & ASID_MASK;
133         write_mmu_entryhi(addr | newpid);
134         tlb_probe();
135         idx = read_mmu_index();
136         if (idx >= 0)
137                 tlb_invalid_indexed();
138
139         restore_asid_inv_utlb(oldpid, newpid);
140         local_irq_restore(flags);
141         }
142 #endif
143 }
144
145 void flush_tlb_one(unsigned long addr)
146 {
147         addr &= TLB_ENTRY_SIZE_MASK;
148
149 #ifdef CONFIG_CPU_HAS_TLBI
150         asm volatile("tlbi.vaas %0"::"r"(addr));
151         sync_is();
152 #else
153         {
154         int oldpid, idx;
155         unsigned long flags;
156
157         local_irq_save(flags);
158         oldpid = read_mmu_entryhi() & ASID_MASK;
159         write_mmu_entryhi(addr | oldpid);
160         tlb_probe();
161         idx = read_mmu_index();
162         if (idx >= 0)
163                 tlb_invalid_indexed();
164
165         restore_asid_inv_utlb(oldpid, oldpid);
166         local_irq_restore(flags);
167         }
168 #endif
169 }
170 EXPORT_SYMBOL(flush_tlb_one);