| /* |
| * 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) |
| : "a" (&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)); |
| } |
| } |
| |