cache.c revision 6bc9a3966f0395419b09b2ec90f89f7f00341b37
1/*
2 * arch/score/mm/cache.c
3 *
4 * Score Processor version.
5 *
6 * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
7 *  Lennox Wu <lennox.wu@sunplusct.com>
8 *  Chen Liqin <liqin.chen@sunplusct.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see the file COPYING, or write
22 * to the Free Software Foundation, Inc.,
23 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24 */
25
26#include <linux/init.h>
27#include <linux/linkage.h>
28#include <linux/kernel.h>
29#include <linux/mm.h>
30#include <linux/module.h>
31#include <linux/sched.h>
32
33#include <asm/mmu_context.h>
34
35/* Cache operations. */
36void (*flush_cache_all)(void);
37void (*__flush_cache_all)(void);
38void (*flush_cache_mm)(struct mm_struct *mm);
39void (*flush_cache_range)(struct vm_area_struct *vma,
40			unsigned long start, unsigned long end);
41void (*flush_cache_page)(struct vm_area_struct *vma,
42			unsigned long page, unsigned long pfn);
43void (*flush_icache_range)(unsigned long start, unsigned long end);
44void (*__flush_cache_vmap)(void);
45void (*__flush_cache_vunmap)(void);
46void (*flush_cache_sigtramp)(unsigned long addr);
47void (*flush_data_cache_page)(unsigned long addr);
48EXPORT_SYMBOL(flush_data_cache_page);
49void (*flush_icache_all)(void);
50
51/*Score 7 cache operations*/
52static inline void s7___flush_cache_all(void);
53static void s7_flush_cache_mm(struct mm_struct *mm);
54static void s7_flush_cache_range(struct vm_area_struct *vma,
55				unsigned long start, unsigned long end);
56static void s7_flush_cache_page(struct vm_area_struct *vma,
57				unsigned long page, unsigned long pfn);
58static void s7_flush_icache_range(unsigned long start, unsigned long end);
59static void s7_flush_cache_sigtramp(unsigned long addr);
60static void s7_flush_data_cache_page(unsigned long addr);
61static void s7_flush_dcache_range(unsigned long start, unsigned long end);
62
63void __update_cache(struct vm_area_struct *vma, unsigned long address,
64		pte_t pte)
65{
66	struct page *page;
67	unsigned long pfn, addr;
68	int exec = (vma->vm_flags & VM_EXEC);
69
70	pfn = pte_pfn(pte);
71	if (unlikely(!pfn_valid(pfn)))
72		return;
73	page = pfn_to_page(pfn);
74	if (page_mapping(page) && test_bit(PG_arch_1, &page->flags)) {
75		addr = (unsigned long) page_address(page);
76		if (exec)
77			s7_flush_data_cache_page(addr);
78		clear_bit(PG_arch_1, &page->flags);
79	}
80}
81
82static inline void setup_protection_map(void)
83{
84	protection_map[0] = PAGE_NONE;
85	protection_map[1] = PAGE_READONLY;
86	protection_map[2] = PAGE_COPY;
87	protection_map[3] = PAGE_COPY;
88	protection_map[4] = PAGE_READONLY;
89	protection_map[5] = PAGE_READONLY;
90	protection_map[6] = PAGE_COPY;
91	protection_map[7] = PAGE_COPY;
92	protection_map[8] = PAGE_NONE;
93	protection_map[9] = PAGE_READONLY;
94	protection_map[10] = PAGE_SHARED;
95	protection_map[11] = PAGE_SHARED;
96	protection_map[12] = PAGE_READONLY;
97	protection_map[13] = PAGE_READONLY;
98	protection_map[14] = PAGE_SHARED;
99	protection_map[15] = PAGE_SHARED;
100}
101
102void __devinit cpu_cache_init(void)
103{
104	flush_cache_all = s7_flush_cache_all;
105	__flush_cache_all = s7___flush_cache_all;
106	flush_cache_mm = s7_flush_cache_mm;
107	flush_cache_range = s7_flush_cache_range;
108	flush_cache_page = s7_flush_cache_page;
109	flush_icache_range = s7_flush_icache_range;
110	flush_cache_sigtramp = s7_flush_cache_sigtramp;
111	flush_data_cache_page = s7_flush_data_cache_page;
112
113	setup_protection_map();
114}
115
116void s7_flush_icache_all(void)
117{
118	__asm__ __volatile__(
119	"la r8, s7_flush_icache_all\n"
120	"cache 0x10, [r8, 0]\n"
121	"nop\nnop\nnop\nnop\nnop\nnop\n"
122	: : : "r8");
123}
124
125void s7_flush_dcache_all(void)
126{
127	__asm__ __volatile__(
128	"la r8, s7_flush_dcache_all\n"
129	"cache 0x1f, [r8, 0]\n"
130	"nop\nnop\nnop\nnop\nnop\nnop\n"
131	"cache 0x1a, [r8, 0]\n"
132	"nop\nnop\nnop\nnop\nnop\nnop\n"
133	: : : "r8");
134}
135
136void s7_flush_cache_all(void)
137{
138	__asm__ __volatile__(
139	"la r8, s7_flush_cache_all\n"
140	"cache 0x10, [r8, 0]\n"
141	"nop\nnop\nnop\nnop\nnop\nnop\n"
142	"cache 0x1f, [r8, 0]\n"
143	"nop\nnop\nnop\nnop\nnop\nnop\n"
144	"cache 0x1a, [r8, 0]\n"
145	"nop\nnop\nnop\nnop\nnop\nnop\n"
146	: : : "r8");
147}
148
149void s7___flush_cache_all(void)
150{
151	__asm__ __volatile__(
152	"la r8, s7_flush_cache_all\n"
153	"cache 0x10, [r8, 0]\n"
154	"nop\nnop\nnop\nnop\nnop\nnop\n"
155	"cache 0x1f, [r8, 0]\n"
156	"nop\nnop\nnop\nnop\nnop\nnop\n"
157	"cache 0x1a, [r8, 0]\n"
158	"nop\nnop\nnop\nnop\nnop\nnop\n"
159	: : : "r8");
160}
161
162static void s7_flush_cache_mm(struct mm_struct *mm)
163{
164	if (!(mm->context))
165		return;
166	s7_flush_cache_all();
167}
168
169/*if we flush a range precisely , the processing may be very long.
170We must check each page in the range whether present. If the page is present,
171we can flush the range in the page. Be careful, the range may be cross two
172page, a page is present and another is not present.
173*/
174/*
175The interface is provided in hopes that the port can find
176a suitably efficient method for removing multiple page
177sized regions from the cache.
178*/
179static void
180s7_flush_cache_range(struct vm_area_struct *vma,
181		unsigned long start, unsigned long end)
182{
183	struct mm_struct *mm = vma->vm_mm;
184	int exec = vma->vm_flags & VM_EXEC;
185	pgd_t *pgdp;
186	pud_t *pudp;
187	pmd_t *pmdp;
188	pte_t *ptep;
189
190	if (!(mm->context))
191		return;
192
193	pgdp = pgd_offset(mm, start);
194	pudp = pud_offset(pgdp, start);
195	pmdp = pmd_offset(pudp, start);
196	ptep = pte_offset(pmdp, start);
197
198	while (start <= end) {
199		unsigned long tmpend;
200		pgdp = pgd_offset(mm, start);
201		pudp = pud_offset(pgdp, start);
202		pmdp = pmd_offset(pudp, start);
203		ptep = pte_offset(pmdp, start);
204
205		if (!(pte_val(*ptep) & _PAGE_PRESENT)) {
206			start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
207			continue;
208		}
209		tmpend = (start | (PAGE_SIZE-1)) > end ?
210				 end : (start | (PAGE_SIZE-1));
211
212		s7_flush_dcache_range(start, tmpend);
213		if (exec)
214			s7_flush_icache_range(start, tmpend);
215		start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
216	}
217}
218
219static void
220s7_flush_cache_page(struct vm_area_struct *vma,
221		unsigned long addr, unsigned long pfn)
222{
223	int exec = vma->vm_flags & VM_EXEC;
224	unsigned long kaddr = 0xa0000000 | (pfn << PAGE_SHIFT);
225
226	s7_flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
227
228	if (exec)
229		s7_flush_icache_range(kaddr, kaddr + PAGE_SIZE);
230}
231
232static void s7_flush_cache_sigtramp(unsigned long addr)
233{
234	__asm__ __volatile__(
235	"cache 0x02, [%0, 0]\n"
236	"nop\nnop\nnop\nnop\nnop\n"
237	"cache 0x02, [%0, 0x4]\n"
238	"nop\nnop\nnop\nnop\nnop\n"
239
240	"cache 0x0d, [%0, 0]\n"
241	"nop\nnop\nnop\nnop\nnop\n"
242	"cache 0x0d, [%0, 0x4]\n"
243	"nop\nnop\nnop\nnop\nnop\n"
244
245	"cache 0x1a, [%0, 0]\n"
246	"nop\nnop\nnop\nnop\nnop\n"
247	: : "r" (addr));
248}
249
250/*
251Just flush entire Dcache!!
252You must ensure the page doesn't include instructions, because
253the function will not flush the Icache.
254The addr must be cache aligned.
255*/
256static void s7_flush_data_cache_page(unsigned long addr)
257{
258	unsigned int i;
259	for (i = 0; i < (PAGE_SIZE / L1_CACHE_BYTES); i += L1_CACHE_BYTES) {
260		__asm__ __volatile__(
261		"cache 0x0e, [%0, 0]\n"
262		"cache 0x1a, [%0, 0]\n"
263		"nop\n"
264		: : "r" (addr));
265		addr += L1_CACHE_BYTES;
266	}
267}
268
269/*
2701. WB and invalid a cache line of Dcache
2712. Drain Write Buffer
272the range must be smaller than PAGE_SIZE
273*/
274static void s7_flush_dcache_range(unsigned long start, unsigned long end)
275{
276	int size, i;
277
278	start = start & ~(L1_CACHE_BYTES - 1);
279	end = end & ~(L1_CACHE_BYTES - 1);
280	size = end - start;
281	/* flush dcache to ram, and invalidate dcache lines. */
282	for (i = 0; i < size; i += L1_CACHE_BYTES) {
283		__asm__ __volatile__(
284		"cache 0x0e, [%0, 0]\n"
285		"nop\nnop\nnop\nnop\nnop\n"
286		"cache 0x1a, [%0, 0]\n"
287		"nop\nnop\nnop\nnop\nnop\n"
288		: : "r" (start));
289		start += L1_CACHE_BYTES;
290	}
291}
292
293static void s7_flush_icache_range(unsigned long start, unsigned long end)
294{
295	int size, i;
296	start = start & ~(L1_CACHE_BYTES - 1);
297	end = end & ~(L1_CACHE_BYTES - 1);
298
299	size = end - start;
300	/* invalidate icache lines. */
301	for (i = 0; i < size; i += L1_CACHE_BYTES) {
302		__asm__ __volatile__(
303		"cache 0x02, [%0, 0]\n"
304		"nop\nnop\nnop\nnop\nnop\n"
305		: : "r" (start));
306		start += L1_CACHE_BYTES;
307	}
308}
309