1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2009, Wind River Systems Inc
7 * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
8 */
9
10#include <linux/export.h>
11#include <linux/sched.h>
12#include <linux/mm.h>
13#include <linux/fs.h>
14
15#include <asm/cacheflush.h>
16#include <asm/cpuinfo.h>
17
18static void __flush_dcache(unsigned long start, unsigned long end)
19{
20	unsigned long addr;
21
22	start &= ~(cpuinfo.dcache_line_size - 1);
23	end += (cpuinfo.dcache_line_size - 1);
24	end &= ~(cpuinfo.dcache_line_size - 1);
25
26	if (end > start + cpuinfo.dcache_size)
27		end = start + cpuinfo.dcache_size;
28
29	for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
30		__asm__ __volatile__ ("   flushd 0(%0)\n"
31					: /* Outputs */
32					: /* Inputs  */ "r"(addr)
33					/* : No clobber */);
34	}
35}
36
37static void __invalidate_dcache(unsigned long start, unsigned long end)
38{
39	unsigned long addr;
40
41	start &= ~(cpuinfo.dcache_line_size - 1);
42	end += (cpuinfo.dcache_line_size - 1);
43	end &= ~(cpuinfo.dcache_line_size - 1);
44
45	for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
46		__asm__ __volatile__ ("   initda 0(%0)\n"
47					: /* Outputs */
48					: /* Inputs  */ "r"(addr)
49					/* : No clobber */);
50	}
51}
52
53static void __flush_icache(unsigned long start, unsigned long end)
54{
55	unsigned long addr;
56
57	start &= ~(cpuinfo.icache_line_size - 1);
58	end += (cpuinfo.icache_line_size - 1);
59	end &= ~(cpuinfo.icache_line_size - 1);
60
61	if (end > start + cpuinfo.icache_size)
62		end = start + cpuinfo.icache_size;
63
64	for (addr = start; addr < end; addr += cpuinfo.icache_line_size) {
65		__asm__ __volatile__ ("   flushi %0\n"
66					: /* Outputs */
67					: /* Inputs  */ "r"(addr)
68					/* : No clobber */);
69	}
70	__asm__ __volatile(" flushp\n");
71}
72
73static void flush_aliases(struct address_space *mapping, struct page *page)
74{
75	struct mm_struct *mm = current->active_mm;
76	struct vm_area_struct *mpnt;
77	pgoff_t pgoff;
78
79	pgoff = page->index;
80
81	flush_dcache_mmap_lock(mapping);
82	vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) {
83		unsigned long offset;
84
85		if (mpnt->vm_mm != mm)
86			continue;
87		if (!(mpnt->vm_flags & VM_MAYSHARE))
88			continue;
89
90		offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
91		flush_cache_page(mpnt, mpnt->vm_start + offset,
92			page_to_pfn(page));
93	}
94	flush_dcache_mmap_unlock(mapping);
95}
96
97void flush_cache_all(void)
98{
99	__flush_dcache(0, cpuinfo.dcache_size);
100	__flush_icache(0, cpuinfo.icache_size);
101}
102
103void flush_cache_mm(struct mm_struct *mm)
104{
105	flush_cache_all();
106}
107
108void flush_cache_dup_mm(struct mm_struct *mm)
109{
110	flush_cache_all();
111}
112
113void flush_icache_range(unsigned long start, unsigned long end)
114{
115	__flush_dcache(start, end);
116	__flush_icache(start, end);
117}
118
119void flush_dcache_range(unsigned long start, unsigned long end)
120{
121	__flush_dcache(start, end);
122	__flush_icache(start, end);
123}
124EXPORT_SYMBOL(flush_dcache_range);
125
126void invalidate_dcache_range(unsigned long start, unsigned long end)
127{
128	__invalidate_dcache(start, end);
129}
130EXPORT_SYMBOL(invalidate_dcache_range);
131
132void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
133			unsigned long end)
134{
135	__flush_dcache(start, end);
136	if (vma == NULL || (vma->vm_flags & VM_EXEC))
137		__flush_icache(start, end);
138}
139
140void flush_icache_page(struct vm_area_struct *vma, struct page *page)
141{
142	unsigned long start = (unsigned long) page_address(page);
143	unsigned long end = start + PAGE_SIZE;
144
145	__flush_dcache(start, end);
146	__flush_icache(start, end);
147}
148
149void flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
150			unsigned long pfn)
151{
152	unsigned long start = vmaddr;
153	unsigned long end = start + PAGE_SIZE;
154
155	__flush_dcache(start, end);
156	if (vma->vm_flags & VM_EXEC)
157		__flush_icache(start, end);
158}
159
160void __flush_dcache_page(struct address_space *mapping, struct page *page)
161{
162	/*
163	 * Writeback any data associated with the kernel mapping of this
164	 * page.  This ensures that data in the physical page is mutually
165	 * coherent with the kernels mapping.
166	 */
167	unsigned long start = (unsigned long)page_address(page);
168
169	__flush_dcache(start, start + PAGE_SIZE);
170}
171
172void flush_dcache_page(struct page *page)
173{
174	struct address_space *mapping;
175
176	/*
177	 * The zero page is never written to, so never has any dirty
178	 * cache lines, and therefore never needs to be flushed.
179	 */
180	if (page == ZERO_PAGE(0))
181		return;
182
183	mapping = page_mapping(page);
184
185	/* Flush this page if there are aliases. */
186	if (mapping && !mapping_mapped(mapping)) {
187		clear_bit(PG_dcache_clean, &page->flags);
188	} else {
189		__flush_dcache_page(mapping, page);
190		if (mapping) {
191			unsigned long start = (unsigned long)page_address(page);
192			flush_aliases(mapping,  page);
193			flush_icache_range(start, start + PAGE_SIZE);
194		}
195		set_bit(PG_dcache_clean, &page->flags);
196	}
197}
198EXPORT_SYMBOL(flush_dcache_page);
199
200void update_mmu_cache(struct vm_area_struct *vma,
201		      unsigned long address, pte_t *pte)
202{
203	unsigned long pfn = pte_pfn(*pte);
204	struct page *page;
205	struct address_space *mapping;
206
207	if (!pfn_valid(pfn))
208		return;
209
210	/*
211	* The zero page is never written to, so never has any dirty
212	* cache lines, and therefore never needs to be flushed.
213	*/
214	page = pfn_to_page(pfn);
215	if (page == ZERO_PAGE(0))
216		return;
217
218	mapping = page_mapping(page);
219	if (!test_and_set_bit(PG_dcache_clean, &page->flags))
220		__flush_dcache_page(mapping, page);
221
222	if(mapping)
223	{
224		flush_aliases(mapping, page);
225		if (vma->vm_flags & VM_EXEC)
226			flush_icache_page(vma, page);
227	}
228}
229
230void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
231		    struct page *to)
232{
233	__flush_dcache(vaddr, vaddr + PAGE_SIZE);
234	__flush_icache(vaddr, vaddr + PAGE_SIZE);
235	copy_page(vto, vfrom);
236	__flush_dcache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE);
237	__flush_icache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE);
238}
239
240void clear_user_page(void *addr, unsigned long vaddr, struct page *page)
241{
242	__flush_dcache(vaddr, vaddr + PAGE_SIZE);
243	__flush_icache(vaddr, vaddr + PAGE_SIZE);
244	clear_page(addr);
245	__flush_dcache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE);
246	__flush_icache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE);
247}
248
249void copy_from_user_page(struct vm_area_struct *vma, struct page *page,
250			unsigned long user_vaddr,
251			void *dst, void *src, int len)
252{
253	flush_cache_page(vma, user_vaddr, page_to_pfn(page));
254	memcpy(dst, src, len);
255	__flush_dcache((unsigned long)src, (unsigned long)src + len);
256	if (vma->vm_flags & VM_EXEC)
257		__flush_icache((unsigned long)src, (unsigned long)src + len);
258}
259
260void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
261			unsigned long user_vaddr,
262			void *dst, void *src, int len)
263{
264	flush_cache_page(vma, user_vaddr, page_to_pfn(page));
265	memcpy(dst, src, len);
266	__flush_dcache((unsigned long)dst, (unsigned long)dst + len);
267	if (vma->vm_flags & VM_EXEC)
268		__flush_icache((unsigned long)dst, (unsigned long)dst + len);
269}
270