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
19void flush_tlb_all(void)
20{
21	tlb_invalid_all();
22}
23
24void flush_tlb_mm(struct mm_struct *mm)
25{
26#ifdef CONFIG_CPU_HAS_TLBI
27	sync_is();
28	asm volatile(
29		"tlbi.asids %0	\n"
30		"sync.i		\n"
31		:
32		: "r" (cpu_asid(mm))
33		: "memory");
34#else
35	tlb_invalid_all();
36#endif
37}
38
39/*
40 * MMU operation regs only could invalid tlb entry in jtlb and we
41 * need change asid field to invalid I-utlb & D-utlb.
42 */
43#ifndef CONFIG_CPU_HAS_TLBI
44#define restore_asid_inv_utlb(oldpid, newpid) \
45do { \
46	if (oldpid == newpid) \
47		write_mmu_entryhi(oldpid + 1); \
48	write_mmu_entryhi(oldpid); \
49} while (0)
50#endif
51
52void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
53			unsigned long end)
54{
55	unsigned long newpid = cpu_asid(vma->vm_mm);
56
57	start &= TLB_ENTRY_SIZE_MASK;
58	end   += TLB_ENTRY_SIZE - 1;
59	end   &= TLB_ENTRY_SIZE_MASK;
60
61#ifdef CONFIG_CPU_HAS_TLBI
62	sync_is();
63	while (start < end) {
64		asm volatile(
65			"tlbi.vas %0	\n"
66			:
67			: "r" (start | newpid)
68			: "memory");
69
70		start += 2*PAGE_SIZE;
71	}
72	asm volatile("sync.i\n");
73#else
74	{
75	unsigned long flags, oldpid;
76
77	local_irq_save(flags);
78	oldpid = read_mmu_entryhi() & ASID_MASK;
79	while (start < end) {
80		int idx;
81
82		write_mmu_entryhi(start | newpid);
83		start += 2*PAGE_SIZE;
84		tlb_probe();
85		idx = read_mmu_index();
86		if (idx >= 0)
87			tlb_invalid_indexed();
88	}
89	restore_asid_inv_utlb(oldpid, newpid);
90	local_irq_restore(flags);
91	}
92#endif
93}
94
95void flush_tlb_kernel_range(unsigned long start, unsigned long end)
96{
97	start &= TLB_ENTRY_SIZE_MASK;
98	end   += TLB_ENTRY_SIZE - 1;
99	end   &= TLB_ENTRY_SIZE_MASK;
100
101#ifdef CONFIG_CPU_HAS_TLBI
102	sync_is();
103	while (start < end) {
104		asm volatile(
105			"tlbi.vaas %0	\n"
106			:
107			: "r" (start)
108			: "memory");
109
110		start += 2*PAGE_SIZE;
111	}
112	asm volatile("sync.i\n");
113#else
114	{
115	unsigned long flags, oldpid;
116
117	local_irq_save(flags);
118	oldpid = read_mmu_entryhi() & ASID_MASK;
119	while (start < end) {
120		int idx;
121
122		write_mmu_entryhi(start | oldpid);
123		start += 2*PAGE_SIZE;
124		tlb_probe();
125		idx = read_mmu_index();
126		if (idx >= 0)
127			tlb_invalid_indexed();
128	}
129	restore_asid_inv_utlb(oldpid, oldpid);
130	local_irq_restore(flags);
131	}
132#endif
133}
134
135void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr)
136{
137	int newpid = cpu_asid(vma->vm_mm);
138
139	addr &= TLB_ENTRY_SIZE_MASK;
140
141#ifdef CONFIG_CPU_HAS_TLBI
142	sync_is();
143	asm volatile(
144		"tlbi.vas %0	\n"
145		"sync.i		\n"
146		:
147		: "r" (addr | newpid)
148		: "memory");
149#else
150	{
151	int oldpid, idx;
152	unsigned long flags;
153
154	local_irq_save(flags);
155	oldpid = read_mmu_entryhi() & ASID_MASK;
156	write_mmu_entryhi(addr | newpid);
157	tlb_probe();
158	idx = read_mmu_index();
159	if (idx >= 0)
160		tlb_invalid_indexed();
161
162	restore_asid_inv_utlb(oldpid, newpid);
163	local_irq_restore(flags);
164	}
165#endif
166}
167
168void flush_tlb_one(unsigned long addr)
169{
170	addr &= TLB_ENTRY_SIZE_MASK;
171
172#ifdef CONFIG_CPU_HAS_TLBI
173	sync_is();
174	asm volatile(
175		"tlbi.vaas %0	\n"
176		"sync.i		\n"
177		:
178		: "r" (addr)
179		: "memory");
180#else
181	{
182	int oldpid, idx;
183	unsigned long flags;
184
185	local_irq_save(flags);
186	oldpid = read_mmu_entryhi() & ASID_MASK;
187	write_mmu_entryhi(addr | oldpid);
188	tlb_probe();
189	idx = read_mmu_index();
190	if (idx >= 0)
191		tlb_invalid_indexed();
192
193	restore_asid_inv_utlb(oldpid, oldpid);
194	local_irq_restore(flags);
195	}
196#endif
197}
198EXPORT_SYMBOL(flush_tlb_one);
199