 11ab3f3d3c
			
		
	
	
	11ab3f3d3c
	
	
	
		
			
			Signed-off-by: Cui Bixiong <bixiong@sunnorth.com.cn> Signed-off-by: Chen Liqin <liqin.chen@sunplusct.com> modified: arch/score/include/asm/cacheflush.h modified: arch/score/mm/cache.c
		
			
				
	
	
		
			279 lines
		
	
	
	
		
			6.9 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			279 lines
		
	
	
	
		
			6.9 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * arch/score/mm/cache.c
 | |
|  *
 | |
|  * Score Processor version.
 | |
|  *
 | |
|  * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
 | |
|  *  Lennox Wu <lennox.wu@sunplusct.com>
 | |
|  *  Chen Liqin <liqin.chen@sunplusct.com>
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License as published by
 | |
|  * the Free Software Foundation; either version 2 of the License, or
 | |
|  * (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, see the file COPYING, or write
 | |
|  * to the Free Software Foundation, Inc.,
 | |
|  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | |
|  */
 | |
| 
 | |
| #include <linux/init.h>
 | |
| #include <linux/linkage.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/mm.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/fs.h>
 | |
| 
 | |
| #include <asm/mmu_context.h>
 | |
| 
 | |
| /*
 | |
| Just flush entire Dcache!!
 | |
| You must ensure the page doesn't include instructions, because
 | |
| the function will not flush the Icache.
 | |
| The addr must be cache aligned.
 | |
| */
 | |
| static void flush_data_cache_page(unsigned long addr)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	for (i = 0; i < (PAGE_SIZE / L1_CACHE_BYTES); i += L1_CACHE_BYTES) {
 | |
| 		__asm__ __volatile__(
 | |
| 		"cache 0x0e, [%0, 0]\n"
 | |
| 		"cache 0x1a, [%0, 0]\n"
 | |
| 		"nop\n"
 | |
| 		: : "r" (addr));
 | |
| 		addr += L1_CACHE_BYTES;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void flush_dcache_page(struct page *page)
 | |
| {
 | |
| 	struct address_space *mapping = page_mapping(page);
 | |
| 	unsigned long addr;
 | |
| 
 | |
| 	if (PageHighMem(page))
 | |
| 		return;
 | |
| 	if (mapping && !mapping_mapped(mapping)) {
 | |
| 		set_bit(PG_dcache_dirty, &(page)->flags);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * We could delay the flush for the !page_mapping case too.  But that
 | |
| 	 * case is for exec env/arg pages and those are %99 certainly going to
 | |
| 	 * get faulted into the tlb (and thus flushed) anyways.
 | |
| 	 */
 | |
| 	addr = (unsigned long) page_address(page);
 | |
| 	flush_data_cache_page(addr);
 | |
| }
 | |
| 
 | |
| /* called by update_mmu_cache. */
 | |
| void __update_cache(struct vm_area_struct *vma, unsigned long address,
 | |
| 		pte_t pte)
 | |
| {
 | |
| 	struct page *page;
 | |
| 	unsigned long pfn, addr;
 | |
| 	int exec = (vma->vm_flags & VM_EXEC);
 | |
| 
 | |
| 	pfn = pte_pfn(pte);
 | |
| 	if (unlikely(!pfn_valid(pfn)))
 | |
| 		return;
 | |
| 	page = pfn_to_page(pfn);
 | |
| 	if (page_mapping(page) && test_bit(PG_dcache_dirty, &(page)->flags)) {
 | |
| 		addr = (unsigned long) page_address(page);
 | |
| 		if (exec)
 | |
| 			flush_data_cache_page(addr);
 | |
| 		clear_bit(PG_dcache_dirty, &(page)->flags);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static inline void setup_protection_map(void)
 | |
| {
 | |
| 	protection_map[0] = PAGE_NONE;
 | |
| 	protection_map[1] = PAGE_READONLY;
 | |
| 	protection_map[2] = PAGE_COPY;
 | |
| 	protection_map[3] = PAGE_COPY;
 | |
| 	protection_map[4] = PAGE_READONLY;
 | |
| 	protection_map[5] = PAGE_READONLY;
 | |
| 	protection_map[6] = PAGE_COPY;
 | |
| 	protection_map[7] = PAGE_COPY;
 | |
| 	protection_map[8] = PAGE_NONE;
 | |
| 	protection_map[9] = PAGE_READONLY;
 | |
| 	protection_map[10] = PAGE_SHARED;
 | |
| 	protection_map[11] = PAGE_SHARED;
 | |
| 	protection_map[12] = PAGE_READONLY;
 | |
| 	protection_map[13] = PAGE_READONLY;
 | |
| 	protection_map[14] = PAGE_SHARED;
 | |
| 	protection_map[15] = PAGE_SHARED;
 | |
| }
 | |
| 
 | |
| void __devinit cpu_cache_init(void)
 | |
| {
 | |
| 	setup_protection_map();
 | |
| }
 | |
| 
 | |
| void flush_icache_all(void)
 | |
| {
 | |
| 	__asm__ __volatile__(
 | |
| 	"la r8, flush_icache_all\n"
 | |
| 	"cache 0x10, [r8, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\nnop\n"
 | |
| 	: : : "r8");
 | |
| }
 | |
| 
 | |
| void flush_dcache_all(void)
 | |
| {
 | |
| 	__asm__ __volatile__(
 | |
| 	"la r8, flush_dcache_all\n"
 | |
| 	"cache 0x1f, [r8, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\nnop\n"
 | |
| 	"cache 0x1a, [r8, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\nnop\n"
 | |
| 	: : : "r8");
 | |
| }
 | |
| 
 | |
| void flush_cache_all(void)
 | |
| {
 | |
| 	__asm__ __volatile__(
 | |
| 	"la r8, flush_cache_all\n"
 | |
| 	"cache 0x10, [r8, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\nnop\n"
 | |
| 	"cache 0x1f, [r8, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\nnop\n"
 | |
| 	"cache 0x1a, [r8, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\nnop\n"
 | |
| 	: : : "r8");
 | |
| }
 | |
| 
 | |
| void flush_cache_mm(struct mm_struct *mm)
 | |
| {
 | |
| 	if (!(mm->context))
 | |
| 		return;
 | |
| 	flush_cache_all();
 | |
| }
 | |
| 
 | |
| /*if we flush a range precisely , the processing may be very long.
 | |
| We must check each page in the range whether present. If the page is present,
 | |
| we can flush the range in the page. Be careful, the range may be cross two
 | |
| page, a page is present and another is not present.
 | |
| */
 | |
| /*
 | |
| The interface is provided in hopes that the port can find
 | |
| a suitably efficient method for removing multiple page
 | |
| sized regions from the cache.
 | |
| */
 | |
| void flush_cache_range(struct vm_area_struct *vma,
 | |
| 		unsigned long start, unsigned long end)
 | |
| {
 | |
| 	struct mm_struct *mm = vma->vm_mm;
 | |
| 	int exec = vma->vm_flags & VM_EXEC;
 | |
| 	pgd_t *pgdp;
 | |
| 	pud_t *pudp;
 | |
| 	pmd_t *pmdp;
 | |
| 	pte_t *ptep;
 | |
| 
 | |
| 	if (!(mm->context))
 | |
| 		return;
 | |
| 
 | |
| 	pgdp = pgd_offset(mm, start);
 | |
| 	pudp = pud_offset(pgdp, start);
 | |
| 	pmdp = pmd_offset(pudp, start);
 | |
| 	ptep = pte_offset(pmdp, start);
 | |
| 
 | |
| 	while (start <= end) {
 | |
| 		unsigned long tmpend;
 | |
| 		pgdp = pgd_offset(mm, start);
 | |
| 		pudp = pud_offset(pgdp, start);
 | |
| 		pmdp = pmd_offset(pudp, start);
 | |
| 		ptep = pte_offset(pmdp, start);
 | |
| 
 | |
| 		if (!(pte_val(*ptep) & _PAGE_PRESENT)) {
 | |
| 			start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
 | |
| 			continue;
 | |
| 		}
 | |
| 		tmpend = (start | (PAGE_SIZE-1)) > end ?
 | |
| 				 end : (start | (PAGE_SIZE-1));
 | |
| 
 | |
| 		flush_dcache_range(start, tmpend);
 | |
| 		if (exec)
 | |
| 			flush_icache_range(start, tmpend);
 | |
| 		start = (start + PAGE_SIZE) & ~(PAGE_SIZE - 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void flush_cache_page(struct vm_area_struct *vma,
 | |
| 		unsigned long addr, unsigned long pfn)
 | |
| {
 | |
| 	int exec = vma->vm_flags & VM_EXEC;
 | |
| 	unsigned long kaddr = 0xa0000000 | (pfn << PAGE_SHIFT);
 | |
| 
 | |
| 	flush_dcache_range(kaddr, kaddr + PAGE_SIZE);
 | |
| 
 | |
| 	if (exec)
 | |
| 		flush_icache_range(kaddr, kaddr + PAGE_SIZE);
 | |
| }
 | |
| 
 | |
| void flush_cache_sigtramp(unsigned long addr)
 | |
| {
 | |
| 	__asm__ __volatile__(
 | |
| 	"cache 0x02, [%0, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\n"
 | |
| 	"cache 0x02, [%0, 0x4]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\n"
 | |
| 
 | |
| 	"cache 0x0d, [%0, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\n"
 | |
| 	"cache 0x0d, [%0, 0x4]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\n"
 | |
| 
 | |
| 	"cache 0x1a, [%0, 0]\n"
 | |
| 	"nop\nnop\nnop\nnop\nnop\n"
 | |
| 	: : "r" (addr));
 | |
| }
 | |
| 
 | |
| /*
 | |
| 1. WB and invalid a cache line of Dcache
 | |
| 2. Drain Write Buffer
 | |
| the range must be smaller than PAGE_SIZE
 | |
| */
 | |
| void flush_dcache_range(unsigned long start, unsigned long end)
 | |
| {
 | |
| 	int size, i;
 | |
| 
 | |
| 	start = start & ~(L1_CACHE_BYTES - 1);
 | |
| 	end = end & ~(L1_CACHE_BYTES - 1);
 | |
| 	size = end - start;
 | |
| 	/* flush dcache to ram, and invalidate dcache lines. */
 | |
| 	for (i = 0; i < size; i += L1_CACHE_BYTES) {
 | |
| 		__asm__ __volatile__(
 | |
| 		"cache 0x0e, [%0, 0]\n"
 | |
| 		"nop\nnop\nnop\nnop\nnop\n"
 | |
| 		"cache 0x1a, [%0, 0]\n"
 | |
| 		"nop\nnop\nnop\nnop\nnop\n"
 | |
| 		: : "r" (start));
 | |
| 		start += L1_CACHE_BYTES;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void flush_icache_range(unsigned long start, unsigned long end)
 | |
| {
 | |
| 	int size, i;
 | |
| 	start = start & ~(L1_CACHE_BYTES - 1);
 | |
| 	end = end & ~(L1_CACHE_BYTES - 1);
 | |
| 
 | |
| 	size = end - start;
 | |
| 	/* invalidate icache lines. */
 | |
| 	for (i = 0; i < size; i += L1_CACHE_BYTES) {
 | |
| 		__asm__ __volatile__(
 | |
| 		"cache 0x02, [%0, 0]\n"
 | |
| 		"nop\nnop\nnop\nnop\nnop\n"
 | |
| 		: : "r" (start));
 | |
| 		start += L1_CACHE_BYTES;
 | |
| 	}
 | |
| }
 |