/* * linux/arch/m68k/mm/cache.c * * Instruction cache handling * * Copyright (C) 1995 Hamish Macdonald */ #include <linux/module.h> #include <asm/pgalloc.h> #include <asm/traps.h> static unsigned long virt_to_phys_slow(unsigned long vaddr) { if (CPU_IS_060) { unsigned long paddr; /* The PLPAR instruction causes an access error if the translation * is not possible. To catch this we use the same exception mechanism * as for user space accesses in <asm/uaccess.h>. */ asm volatile (".chip 68060\n" "1: plpar (%0)\n" ".chip 68k\n" "2:\n" ".section .fixup,\"ax\"\n" " .even\n" "3: sub.l %0,%0\n" " jra 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" " .align 4\n" " .long 1b,3b\n" ".previous" : "=a" (paddr) : "0" (vaddr)); return paddr; } else if (CPU_IS_040) { unsigned long mmusr; asm volatile (".chip 68040\n\t" "ptestr (%1)\n\t" "movec %%mmusr, %0\n\t" ".chip 68k" : "=r" (mmusr) : "a" (vaddr)); if (mmusr & MMU_R_040) return (mmusr & PAGE_MASK) | (vaddr & ~PAGE_MASK); } else { unsigned short mmusr; unsigned long *descaddr; asm volatile ("ptestr %3,%2@,#7,%0\n\t" "pmove %%psr,%1" : "=a&" (descaddr), "=m" (mmusr) : "a" (vaddr), "d" (get_fs().seg)); if (mmusr & (MMU_I|MMU_B|MMU_L)) return 0; descaddr = phys_to_virt((unsigned long)descaddr); switch (mmusr & MMU_NUM) { case 1: return (*descaddr & 0xfe000000) | (vaddr & 0x01ffffff); case 2: return (*descaddr & 0xfffc0000) | (vaddr & 0x0003ffff); case 3: return (*descaddr & PAGE_MASK) | (vaddr & ~PAGE_MASK); } } return 0; } /* Push n pages at kernel virtual address and clear the icache */ /* RZ: use cpush %bc instead of cpush %dc, cinv %ic */ void flush_icache_range(unsigned long address, unsigned long endaddr) { if (CPU_IS_COLDFIRE) { unsigned long start, end; start = address & ICACHE_SET_MASK; end = endaddr & ICACHE_SET_MASK; if (start > end) { flush_cf_icache(0, end); end = ICACHE_MAX_ADDR; } flush_cf_icache(start, end); } else if (CPU_IS_040_OR_060) { address &= PAGE_MASK; do { asm volatile ("nop\n\t" ".chip 68040\n\t" "cpushp %%bc,(%0)\n\t" ".chip 68k" : : "a" (virt_to_phys_slow(address))); address += PAGE_SIZE; } while (address < endaddr); } else { unsigned long tmp; asm volatile ("movec %%cacr,%0\n\t" "orw %1,%0\n\t" "movec %0,%%cacr" : "=&d" (tmp) : "di" (FLUSH_I)); } } EXPORT_SYMBOL(flush_icache_range); void flush_icache_user_range(struct vm_area_struct *vma, struct page *page, unsigned long addr, int len) { if (CPU_IS_COLDFIRE) { unsigned long start, end; start = addr & ICACHE_SET_MASK; end = (addr + len) & ICACHE_SET_MASK; if (start > end) { flush_cf_icache(0, end); end = ICACHE_MAX_ADDR; } flush_cf_icache(start, end); } else if (CPU_IS_040_OR_060) { asm volatile ("nop\n\t" ".chip 68040\n\t" "cpushp %%bc,(%0)\n\t" ".chip 68k" : : "a" (page_to_phys(page))); } else { unsigned long tmp; asm volatile ("movec %%cacr,%0\n\t" "orw %1,%0\n\t" "movec %0,%%cacr" : "=&d" (tmp) : "di" (FLUSH_I)); } }