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