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) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org)
78c2ecf20Sopenharmony_ci * Copyright (C) 2007 MIPS Technologies, Inc.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/fcntl.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/linkage.h>
138c2ecf20Sopenharmony_ci#include <linux/export.h>
148c2ecf20Sopenharmony_ci#include <linux/sched.h>
158c2ecf20Sopenharmony_ci#include <linux/syscalls.h>
168c2ecf20Sopenharmony_ci#include <linux/mm.h>
178c2ecf20Sopenharmony_ci#include <linux/highmem.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
208c2ecf20Sopenharmony_ci#include <asm/processor.h>
218c2ecf20Sopenharmony_ci#include <asm/cpu.h>
228c2ecf20Sopenharmony_ci#include <asm/cpu-features.h>
238c2ecf20Sopenharmony_ci#include <asm/setup.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Cache operations. */
268c2ecf20Sopenharmony_civoid (*flush_cache_all)(void);
278c2ecf20Sopenharmony_civoid (*__flush_cache_all)(void);
288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__flush_cache_all);
298c2ecf20Sopenharmony_civoid (*flush_cache_mm)(struct mm_struct *mm);
308c2ecf20Sopenharmony_civoid (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start,
318c2ecf20Sopenharmony_ci	unsigned long end);
328c2ecf20Sopenharmony_civoid (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page,
338c2ecf20Sopenharmony_ci	unsigned long pfn);
348c2ecf20Sopenharmony_civoid (*flush_icache_range)(unsigned long start, unsigned long end);
358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(flush_icache_range);
368c2ecf20Sopenharmony_civoid (*local_flush_icache_range)(unsigned long start, unsigned long end);
378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(local_flush_icache_range);
388c2ecf20Sopenharmony_civoid (*__flush_icache_user_range)(unsigned long start, unsigned long end);
398c2ecf20Sopenharmony_civoid (*__local_flush_icache_user_range)(unsigned long start, unsigned long end);
408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__local_flush_icache_user_range);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_civoid (*__flush_cache_vmap)(void);
438c2ecf20Sopenharmony_civoid (*__flush_cache_vunmap)(void);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_civoid (*__flush_kernel_vmap_range)(unsigned long vaddr, int size);
468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__flush_kernel_vmap_range);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* MIPS specific cache operations */
498c2ecf20Sopenharmony_civoid (*local_flush_data_cache_page)(void * addr);
508c2ecf20Sopenharmony_civoid (*flush_data_cache_page)(unsigned long addr);
518c2ecf20Sopenharmony_civoid (*flush_icache_all)(void);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(local_flush_data_cache_page);
548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_data_cache_page);
558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(flush_icache_all);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci#ifdef CONFIG_DMA_NONCOHERENT
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/* DMA cache operations. */
608c2ecf20Sopenharmony_civoid (*_dma_cache_wback_inv)(unsigned long start, unsigned long size);
618c2ecf20Sopenharmony_civoid (*_dma_cache_wback)(unsigned long start, unsigned long size);
628c2ecf20Sopenharmony_civoid (*_dma_cache_inv)(unsigned long start, unsigned long size);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#endif /* CONFIG_DMA_NONCOHERENT */
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * We could optimize the case where the cache argument is not BCACHE but
688c2ecf20Sopenharmony_ci * that seems very atypical use ...
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(cacheflush, unsigned long, addr, unsigned long, bytes,
718c2ecf20Sopenharmony_ci	unsigned int, cache)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (bytes == 0)
748c2ecf20Sopenharmony_ci		return 0;
758c2ecf20Sopenharmony_ci	if (!access_ok((void __user *) addr, bytes))
768c2ecf20Sopenharmony_ci		return -EFAULT;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	__flush_icache_user_range(addr, addr + bytes);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return 0;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_civoid __flush_dcache_page(struct page *page)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct address_space *mapping = page_mapping_file(page);
868c2ecf20Sopenharmony_ci	unsigned long addr;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (mapping && !mapping_mapped(mapping)) {
898c2ecf20Sopenharmony_ci		SetPageDcacheDirty(page);
908c2ecf20Sopenharmony_ci		return;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/*
948c2ecf20Sopenharmony_ci	 * We could delay the flush for the !page_mapping case too.  But that
958c2ecf20Sopenharmony_ci	 * case is for exec env/arg pages and those are %99 certainly going to
968c2ecf20Sopenharmony_ci	 * get faulted into the tlb (and thus flushed) anyways.
978c2ecf20Sopenharmony_ci	 */
988c2ecf20Sopenharmony_ci	if (PageHighMem(page))
998c2ecf20Sopenharmony_ci		addr = (unsigned long)kmap_atomic(page);
1008c2ecf20Sopenharmony_ci	else
1018c2ecf20Sopenharmony_ci		addr = (unsigned long)page_address(page);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	flush_data_cache_page(addr);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (PageHighMem(page))
1068c2ecf20Sopenharmony_ci		kunmap_atomic((void *)addr);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__flush_dcache_page);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_civoid __flush_anon_page(struct page *page, unsigned long vmaddr)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long) page_address(page);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (pages_do_alias(addr, vmaddr)) {
1168c2ecf20Sopenharmony_ci		if (page_mapcount(page) && !Page_dcache_dirty(page)) {
1178c2ecf20Sopenharmony_ci			void *kaddr;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci			kaddr = kmap_coherent(page, vmaddr);
1208c2ecf20Sopenharmony_ci			flush_data_cache_page((unsigned long)kaddr);
1218c2ecf20Sopenharmony_ci			kunmap_coherent();
1228c2ecf20Sopenharmony_ci		} else
1238c2ecf20Sopenharmony_ci			flush_data_cache_page(addr);
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__flush_anon_page);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_civoid __update_cache(unsigned long address, pte_t pte)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	struct page *page;
1328c2ecf20Sopenharmony_ci	unsigned long pfn, addr;
1338c2ecf20Sopenharmony_ci	int exec = !pte_no_exec(pte) && !cpu_has_ic_fills_f_dc;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	pfn = pte_pfn(pte);
1368c2ecf20Sopenharmony_ci	if (unlikely(!pfn_valid(pfn)))
1378c2ecf20Sopenharmony_ci		return;
1388c2ecf20Sopenharmony_ci	page = pfn_to_page(pfn);
1398c2ecf20Sopenharmony_ci	if (Page_dcache_dirty(page)) {
1408c2ecf20Sopenharmony_ci		if (PageHighMem(page))
1418c2ecf20Sopenharmony_ci			addr = (unsigned long)kmap_atomic(page);
1428c2ecf20Sopenharmony_ci		else
1438c2ecf20Sopenharmony_ci			addr = (unsigned long)page_address(page);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (exec || pages_do_alias(addr, address & PAGE_MASK))
1468c2ecf20Sopenharmony_ci			flush_data_cache_page(addr);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		if (PageHighMem(page))
1498c2ecf20Sopenharmony_ci			kunmap_atomic((void *)addr);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		ClearPageDcacheDirty(page);
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ciunsigned long _page_cachable_default;
1568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(_page_cachable_default);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic inline void setup_protection_map(void)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	if (cpu_has_rixi) {
1618c2ecf20Sopenharmony_ci		protection_map[0]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
1628c2ecf20Sopenharmony_ci		protection_map[1]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
1638c2ecf20Sopenharmony_ci		protection_map[2]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
1648c2ecf20Sopenharmony_ci		protection_map[3]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
1658c2ecf20Sopenharmony_ci		protection_map[4]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
1668c2ecf20Sopenharmony_ci		protection_map[5]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
1678c2ecf20Sopenharmony_ci		protection_map[6]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
1688c2ecf20Sopenharmony_ci		protection_map[7]  = __pgprot(_page_cachable_default | _PAGE_PRESENT);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		protection_map[8]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
1718c2ecf20Sopenharmony_ci		protection_map[9]  = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC);
1728c2ecf20Sopenharmony_ci		protection_map[10] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE | _PAGE_NO_READ);
1738c2ecf20Sopenharmony_ci		protection_map[11] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE);
1748c2ecf20Sopenharmony_ci		protection_map[12] = __pgprot(_page_cachable_default | _PAGE_PRESENT);
1758c2ecf20Sopenharmony_ci		protection_map[13] = __pgprot(_page_cachable_default | _PAGE_PRESENT);
1768c2ecf20Sopenharmony_ci		protection_map[14] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE);
1778c2ecf20Sopenharmony_ci		protection_map[15] = __pgprot(_page_cachable_default | _PAGE_PRESENT | _PAGE_WRITE);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	} else {
1808c2ecf20Sopenharmony_ci		protection_map[0] = PAGE_NONE;
1818c2ecf20Sopenharmony_ci		protection_map[1] = PAGE_READONLY;
1828c2ecf20Sopenharmony_ci		protection_map[2] = PAGE_COPY;
1838c2ecf20Sopenharmony_ci		protection_map[3] = PAGE_COPY;
1848c2ecf20Sopenharmony_ci		protection_map[4] = PAGE_READONLY;
1858c2ecf20Sopenharmony_ci		protection_map[5] = PAGE_READONLY;
1868c2ecf20Sopenharmony_ci		protection_map[6] = PAGE_COPY;
1878c2ecf20Sopenharmony_ci		protection_map[7] = PAGE_COPY;
1888c2ecf20Sopenharmony_ci		protection_map[8] = PAGE_NONE;
1898c2ecf20Sopenharmony_ci		protection_map[9] = PAGE_READONLY;
1908c2ecf20Sopenharmony_ci		protection_map[10] = PAGE_SHARED;
1918c2ecf20Sopenharmony_ci		protection_map[11] = PAGE_SHARED;
1928c2ecf20Sopenharmony_ci		protection_map[12] = PAGE_READONLY;
1938c2ecf20Sopenharmony_ci		protection_map[13] = PAGE_READONLY;
1948c2ecf20Sopenharmony_ci		protection_map[14] = PAGE_SHARED;
1958c2ecf20Sopenharmony_ci		protection_map[15] = PAGE_SHARED;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_civoid cpu_cache_init(void)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	if (cpu_has_3k_cache) {
2028c2ecf20Sopenharmony_ci		extern void __weak r3k_cache_init(void);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci		r3k_cache_init();
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	if (cpu_has_6k_cache) {
2078c2ecf20Sopenharmony_ci		extern void __weak r6k_cache_init(void);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		r6k_cache_init();
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	if (cpu_has_4k_cache) {
2128c2ecf20Sopenharmony_ci		extern void __weak r4k_cache_init(void);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		r4k_cache_init();
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	if (cpu_has_8k_cache) {
2178c2ecf20Sopenharmony_ci		extern void __weak r8k_cache_init(void);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci		r8k_cache_init();
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci	if (cpu_has_tx39_cache) {
2228c2ecf20Sopenharmony_ci		extern void __weak tx39_cache_init(void);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		tx39_cache_init();
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (cpu_has_octeon_cache) {
2288c2ecf20Sopenharmony_ci		extern void __weak octeon_cache_init(void);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		octeon_cache_init();
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	setup_protection_map();
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ciint __weak __uncached_access(struct file *file, unsigned long addr)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	if (file->f_flags & O_DSYNC)
2398c2ecf20Sopenharmony_ci		return 1;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return addr >= __pa(high_memory);
2428c2ecf20Sopenharmony_ci}
243