18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2009, Wind River Systems Inc
78c2ecf20Sopenharmony_ci * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/export.h>
118c2ecf20Sopenharmony_ci#include <linux/sched.h>
128c2ecf20Sopenharmony_ci#include <linux/mm.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
168c2ecf20Sopenharmony_ci#include <asm/cpuinfo.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic void __flush_dcache(unsigned long start, unsigned long end)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	unsigned long addr;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	start &= ~(cpuinfo.dcache_line_size - 1);
238c2ecf20Sopenharmony_ci	end += (cpuinfo.dcache_line_size - 1);
248c2ecf20Sopenharmony_ci	end &= ~(cpuinfo.dcache_line_size - 1);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	if (end > start + cpuinfo.dcache_size)
278c2ecf20Sopenharmony_ci		end = start + cpuinfo.dcache_size;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
308c2ecf20Sopenharmony_ci		__asm__ __volatile__ ("   flushd 0(%0)\n"
318c2ecf20Sopenharmony_ci					: /* Outputs */
328c2ecf20Sopenharmony_ci					: /* Inputs  */ "r"(addr)
338c2ecf20Sopenharmony_ci					/* : No clobber */);
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void __invalidate_dcache(unsigned long start, unsigned long end)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	unsigned long addr;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	start &= ~(cpuinfo.dcache_line_size - 1);
428c2ecf20Sopenharmony_ci	end += (cpuinfo.dcache_line_size - 1);
438c2ecf20Sopenharmony_ci	end &= ~(cpuinfo.dcache_line_size - 1);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	for (addr = start; addr < end; addr += cpuinfo.dcache_line_size) {
468c2ecf20Sopenharmony_ci		__asm__ __volatile__ ("   initda 0(%0)\n"
478c2ecf20Sopenharmony_ci					: /* Outputs */
488c2ecf20Sopenharmony_ci					: /* Inputs  */ "r"(addr)
498c2ecf20Sopenharmony_ci					/* : No clobber */);
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void __flush_icache(unsigned long start, unsigned long end)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned long addr;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	start &= ~(cpuinfo.icache_line_size - 1);
588c2ecf20Sopenharmony_ci	end += (cpuinfo.icache_line_size - 1);
598c2ecf20Sopenharmony_ci	end &= ~(cpuinfo.icache_line_size - 1);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (end > start + cpuinfo.icache_size)
628c2ecf20Sopenharmony_ci		end = start + cpuinfo.icache_size;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	for (addr = start; addr < end; addr += cpuinfo.icache_line_size) {
658c2ecf20Sopenharmony_ci		__asm__ __volatile__ ("   flushi %0\n"
668c2ecf20Sopenharmony_ci					: /* Outputs */
678c2ecf20Sopenharmony_ci					: /* Inputs  */ "r"(addr)
688c2ecf20Sopenharmony_ci					/* : No clobber */);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	__asm__ __volatile(" flushp\n");
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void flush_aliases(struct address_space *mapping, struct page *page)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	struct mm_struct *mm = current->active_mm;
768c2ecf20Sopenharmony_ci	struct vm_area_struct *mpnt;
778c2ecf20Sopenharmony_ci	pgoff_t pgoff;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	pgoff = page->index;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	flush_dcache_mmap_lock(mapping);
828c2ecf20Sopenharmony_ci	vma_interval_tree_foreach(mpnt, &mapping->i_mmap, pgoff, pgoff) {
838c2ecf20Sopenharmony_ci		unsigned long offset;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		if (mpnt->vm_mm != mm)
868c2ecf20Sopenharmony_ci			continue;
878c2ecf20Sopenharmony_ci		if (!(mpnt->vm_flags & VM_MAYSHARE))
888c2ecf20Sopenharmony_ci			continue;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		offset = (pgoff - mpnt->vm_pgoff) << PAGE_SHIFT;
918c2ecf20Sopenharmony_ci		flush_cache_page(mpnt, mpnt->vm_start + offset,
928c2ecf20Sopenharmony_ci			page_to_pfn(page));
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci	flush_dcache_mmap_unlock(mapping);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_civoid flush_cache_all(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	__flush_dcache(0, cpuinfo.dcache_size);
1008c2ecf20Sopenharmony_ci	__flush_icache(0, cpuinfo.icache_size);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_civoid flush_cache_mm(struct mm_struct *mm)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	flush_cache_all();
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_civoid flush_cache_dup_mm(struct mm_struct *mm)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	flush_cache_all();
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_civoid flush_icache_range(unsigned long start, unsigned long end)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	__flush_dcache(start, end);
1168c2ecf20Sopenharmony_ci	__flush_icache(start, end);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_civoid flush_dcache_range(unsigned long start, unsigned long end)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	__flush_dcache(start, end);
1228c2ecf20Sopenharmony_ci	__flush_icache(start, end);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_range);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_civoid invalidate_dcache_range(unsigned long start, unsigned long end)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	__invalidate_dcache(start, end);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(invalidate_dcache_range);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_civoid flush_cache_range(struct vm_area_struct *vma, unsigned long start,
1338c2ecf20Sopenharmony_ci			unsigned long end)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	__flush_dcache(start, end);
1368c2ecf20Sopenharmony_ci	if (vma == NULL || (vma->vm_flags & VM_EXEC))
1378c2ecf20Sopenharmony_ci		__flush_icache(start, end);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_civoid flush_icache_page(struct vm_area_struct *vma, struct page *page)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	unsigned long start = (unsigned long) page_address(page);
1438c2ecf20Sopenharmony_ci	unsigned long end = start + PAGE_SIZE;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	__flush_dcache(start, end);
1468c2ecf20Sopenharmony_ci	__flush_icache(start, end);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_civoid flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
1508c2ecf20Sopenharmony_ci			unsigned long pfn)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	unsigned long start = vmaddr;
1538c2ecf20Sopenharmony_ci	unsigned long end = start + PAGE_SIZE;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	__flush_dcache(start, end);
1568c2ecf20Sopenharmony_ci	if (vma->vm_flags & VM_EXEC)
1578c2ecf20Sopenharmony_ci		__flush_icache(start, end);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_civoid __flush_dcache_page(struct address_space *mapping, struct page *page)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	/*
1638c2ecf20Sopenharmony_ci	 * Writeback any data associated with the kernel mapping of this
1648c2ecf20Sopenharmony_ci	 * page.  This ensures that data in the physical page is mutually
1658c2ecf20Sopenharmony_ci	 * coherent with the kernels mapping.
1668c2ecf20Sopenharmony_ci	 */
1678c2ecf20Sopenharmony_ci	unsigned long start = (unsigned long)page_address(page);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	__flush_dcache(start, start + PAGE_SIZE);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_civoid flush_dcache_page(struct page *page)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct address_space *mapping;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/*
1778c2ecf20Sopenharmony_ci	 * The zero page is never written to, so never has any dirty
1788c2ecf20Sopenharmony_ci	 * cache lines, and therefore never needs to be flushed.
1798c2ecf20Sopenharmony_ci	 */
1808c2ecf20Sopenharmony_ci	if (page == ZERO_PAGE(0))
1818c2ecf20Sopenharmony_ci		return;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	mapping = page_mapping_file(page);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* Flush this page if there are aliases. */
1868c2ecf20Sopenharmony_ci	if (mapping && !mapping_mapped(mapping)) {
1878c2ecf20Sopenharmony_ci		clear_bit(PG_dcache_clean, &page->flags);
1888c2ecf20Sopenharmony_ci	} else {
1898c2ecf20Sopenharmony_ci		__flush_dcache_page(mapping, page);
1908c2ecf20Sopenharmony_ci		if (mapping) {
1918c2ecf20Sopenharmony_ci			unsigned long start = (unsigned long)page_address(page);
1928c2ecf20Sopenharmony_ci			flush_aliases(mapping,  page);
1938c2ecf20Sopenharmony_ci			flush_icache_range(start, start + PAGE_SIZE);
1948c2ecf20Sopenharmony_ci		}
1958c2ecf20Sopenharmony_ci		set_bit(PG_dcache_clean, &page->flags);
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_dcache_page);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_civoid update_mmu_cache(struct vm_area_struct *vma,
2018c2ecf20Sopenharmony_ci		      unsigned long address, pte_t *ptep)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	pte_t pte = *ptep;
2048c2ecf20Sopenharmony_ci	unsigned long pfn = pte_pfn(pte);
2058c2ecf20Sopenharmony_ci	struct page *page;
2068c2ecf20Sopenharmony_ci	struct address_space *mapping;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	reload_tlb_page(vma, address, pte);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (!pfn_valid(pfn))
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/*
2148c2ecf20Sopenharmony_ci	* The zero page is never written to, so never has any dirty
2158c2ecf20Sopenharmony_ci	* cache lines, and therefore never needs to be flushed.
2168c2ecf20Sopenharmony_ci	*/
2178c2ecf20Sopenharmony_ci	page = pfn_to_page(pfn);
2188c2ecf20Sopenharmony_ci	if (page == ZERO_PAGE(0))
2198c2ecf20Sopenharmony_ci		return;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mapping = page_mapping_file(page);
2228c2ecf20Sopenharmony_ci	if (!test_and_set_bit(PG_dcache_clean, &page->flags))
2238c2ecf20Sopenharmony_ci		__flush_dcache_page(mapping, page);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	if(mapping)
2268c2ecf20Sopenharmony_ci	{
2278c2ecf20Sopenharmony_ci		flush_aliases(mapping, page);
2288c2ecf20Sopenharmony_ci		if (vma->vm_flags & VM_EXEC)
2298c2ecf20Sopenharmony_ci			flush_icache_page(vma, page);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_civoid copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
2348c2ecf20Sopenharmony_ci		    struct page *to)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	__flush_dcache(vaddr, vaddr + PAGE_SIZE);
2378c2ecf20Sopenharmony_ci	__flush_icache(vaddr, vaddr + PAGE_SIZE);
2388c2ecf20Sopenharmony_ci	copy_page(vto, vfrom);
2398c2ecf20Sopenharmony_ci	__flush_dcache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE);
2408c2ecf20Sopenharmony_ci	__flush_icache((unsigned long)vto, (unsigned long)vto + PAGE_SIZE);
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_civoid clear_user_page(void *addr, unsigned long vaddr, struct page *page)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	__flush_dcache(vaddr, vaddr + PAGE_SIZE);
2468c2ecf20Sopenharmony_ci	__flush_icache(vaddr, vaddr + PAGE_SIZE);
2478c2ecf20Sopenharmony_ci	clear_page(addr);
2488c2ecf20Sopenharmony_ci	__flush_dcache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE);
2498c2ecf20Sopenharmony_ci	__flush_icache((unsigned long)addr, (unsigned long)addr + PAGE_SIZE);
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_civoid copy_from_user_page(struct vm_area_struct *vma, struct page *page,
2538c2ecf20Sopenharmony_ci			unsigned long user_vaddr,
2548c2ecf20Sopenharmony_ci			void *dst, void *src, int len)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	flush_cache_page(vma, user_vaddr, page_to_pfn(page));
2578c2ecf20Sopenharmony_ci	memcpy(dst, src, len);
2588c2ecf20Sopenharmony_ci	__flush_dcache((unsigned long)src, (unsigned long)src + len);
2598c2ecf20Sopenharmony_ci	if (vma->vm_flags & VM_EXEC)
2608c2ecf20Sopenharmony_ci		__flush_icache((unsigned long)src, (unsigned long)src + len);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_civoid copy_to_user_page(struct vm_area_struct *vma, struct page *page,
2648c2ecf20Sopenharmony_ci			unsigned long user_vaddr,
2658c2ecf20Sopenharmony_ci			void *dst, void *src, int len)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	flush_cache_page(vma, user_vaddr, page_to_pfn(page));
2688c2ecf20Sopenharmony_ci	memcpy(dst, src, len);
2698c2ecf20Sopenharmony_ci	__flush_dcache((unsigned long)dst, (unsigned long)dst + len);
2708c2ecf20Sopenharmony_ci	if (vma->vm_flags & VM_EXEC)
2718c2ecf20Sopenharmony_ci		__flush_icache((unsigned long)dst, (unsigned long)dst + len);
2728c2ecf20Sopenharmony_ci}
273