123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- /*
- * 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);
- }
- EXPORT_SYMBOL(flush_dcache_page);
- /* 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 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;
- }
- }
- EXPORT_SYMBOL(flush_icache_range);
|