patch-1.3.94 linux/arch/m68k/mm/memory.c
Next file: linux/arch/sparc/kernel/devices.c
Previous file: linux/arch/m68k/mm/init.c
Back to the patch index
Back to the overall index
- Lines: 754
- Date:
Thu Apr 18 03:00:00 1996
- Orig file:
v1.3.93/linux/arch/m68k/mm/memory.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.93/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c
@@ -0,0 +1,753 @@
+/*
+ * linux/arch/m68k/mm/memory.c
+ *
+ * Copyright (C) 1995 Hamish Macdonald
+ */
+
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+#include <asm/amigahw.h>
+#include <asm/bootinfo.h>
+
+extern pte_t *kernel_page_table (unsigned long *memavailp);
+
+static struct ptable_desc {
+ struct ptable_desc *prev;
+ struct ptable_desc *next;
+ unsigned long page;
+ unsigned char alloced;
+} ptable_list = { &ptable_list, &ptable_list, 0, 0xff };
+
+#define PD_NONEFREE(dp) ((dp)->alloced == 0xff)
+#define PD_ALLFREE(dp) ((dp)->alloced == 0)
+#define PD_TABLEFREE(dp,i) (!((dp)->alloced & (1<<(i))))
+#define PD_MARKUSED(dp,i) ((dp)->alloced |= (1<<(i)))
+#define PD_MARKFREE(dp,i) ((dp)->alloced &= ~(1<<(i)))
+
+#define PTABLE_SIZE (PTRS_PER_PMD * sizeof(pmd_t))
+
+pmd_t *get_pointer_table (void)
+{
+ pmd_t *pmdp = NULL;
+ unsigned long flags;
+ struct ptable_desc *dp = ptable_list.next;
+ int i;
+
+ /*
+ * For a pointer table for a user process address space, a
+ * table is taken from a page allocated for the purpose. Each
+ * page can hold 8 pointer tables. The page is remapped in
+ * virtual address space to be noncacheable.
+ */
+ if (PD_NONEFREE (dp)) {
+
+ if (!(dp = kmalloc (sizeof(struct ptable_desc),GFP_KERNEL))) {
+ return 0;
+ }
+
+ if (!(dp->page = __get_free_page (GFP_KERNEL))) {
+ kfree (dp);
+ return 0;
+ }
+
+ nocache_page (dp->page);
+
+ dp->alloced = 0;
+ /* put at head of list */
+ save_flags(flags);
+ cli();
+ dp->next = ptable_list.next;
+ dp->prev = ptable_list.next->prev;
+ ptable_list.next->prev = dp;
+ ptable_list.next = dp;
+ restore_flags(flags);
+ }
+
+ for (i = 0; i < 8; i++)
+ if (PD_TABLEFREE (dp, i)) {
+ PD_MARKUSED (dp, i);
+ pmdp = (pmd_t *)(dp->page + PTABLE_SIZE*i);
+ break;
+ }
+
+ if (PD_NONEFREE (dp)) {
+ /* move to end of list */
+ save_flags(flags);
+ cli();
+ dp->prev->next = dp->next;
+ dp->next->prev = dp->prev;
+
+ dp->next = ptable_list.next->prev;
+ dp->prev = ptable_list.prev;
+ ptable_list.prev->next = dp;
+ ptable_list.prev = dp;
+ restore_flags(flags);
+ }
+
+ memset (pmdp, 0, PTABLE_SIZE);
+
+ return pmdp;
+}
+
+void free_pointer_table (pmd_t *ptable)
+{
+ struct ptable_desc *dp;
+ unsigned long page = (unsigned long)ptable & PAGE_MASK;
+ int index = ((unsigned long)ptable - page)/PTABLE_SIZE;
+ unsigned long flags;
+
+ for (dp = ptable_list.next; dp->page && dp->page != page; dp = dp->next)
+ ;
+
+ if (!dp->page)
+ panic ("unable to find desc for ptable %p on list!", ptable);
+
+ if (PD_TABLEFREE (dp, index))
+ panic ("table already free!");
+
+ PD_MARKFREE (dp, index);
+
+ if (PD_ALLFREE (dp)) {
+ /* all tables in page are free, free page */
+ save_flags(flags);
+ cli();
+ dp->prev->next = dp->next;
+ dp->next->prev = dp->prev;
+ restore_flags(flags);
+ cache_page (dp->page);
+ free_page (dp->page);
+ kfree (dp);
+ return;
+ } else {
+ /*
+ * move this descriptor the the front of the list, since
+ * it has one or more free tables.
+ */
+ save_flags(flags);
+ cli();
+ dp->prev->next = dp->next;
+ dp->next->prev = dp->prev;
+
+ dp->next = ptable_list.next;
+ dp->prev = ptable_list.next->prev;
+ ptable_list.next->prev = dp;
+ ptable_list.next = dp;
+ restore_flags(flags);
+ }
+}
+
+static unsigned char alloced = 0;
+extern pmd_t (*kernel_pmd_table)[PTRS_PER_PMD]; /* initialized in head.S */
+
+pmd_t *get_kpointer_table (void)
+{
+ /* For pointer tables for the kernel virtual address space,
+ * use a page that is allocated in head.S that can hold up to
+ * 8 pointer tables. This allows mapping of 8 * 32M = 256M of
+ * physical memory. This should be sufficient for now.
+ */
+ pmd_t *ptable;
+ int i;
+
+ for (i = 0; i < PAGE_SIZE/(PTRS_PER_PMD*sizeof(pmd_t)); i++)
+ if ((alloced & (1 << i)) == 0) {
+ ptable = kernel_pmd_table[i];
+ memset (ptable, 0, PTRS_PER_PMD*sizeof(pmd_t));
+ alloced |= (1 << i);
+ return ptable;
+ }
+ printk ("no space for kernel pointer table\n");
+ return NULL;
+}
+
+void free_kpointer_table (pmd_t *pmdp)
+{
+ int index = (pmd_t (*)[PTRS_PER_PMD])pmdp - kernel_pmd_table;
+
+ if (index < 0 || index > 7 ||
+ /* This works because kernel_pmd_table is page aligned. */
+ ((unsigned long)pmdp & (sizeof(pmd_t) * PTRS_PER_PMD - 1)))
+ panic("attempt to free invalid kernel pointer table");
+ else
+ alloced &= ~(1 << index);
+}
+
+/*
+ * The following two routines map from a physical address to a kernel
+ * virtual address and vice versa.
+ */
+unsigned long mm_vtop (unsigned long vaddr)
+{
+ int i;
+ unsigned long voff = vaddr;
+ unsigned long offset = 0;
+
+ for (i = 0; i < boot_info.num_memory; i++)
+ {
+ if (voff < offset + boot_info.memory[i].size) {
+#ifdef DEBUGPV
+ printk ("VTOP(%lx)=%lx\n", vaddr,
+ boot_info.memory[i].addr + voff - offset);
+#endif
+ return boot_info.memory[i].addr + voff - offset;
+ } else
+ offset += boot_info.memory[i].size;
+ }
+
+ /* not in one of the memory chunks; get the actual
+ * physical address from the MMU.
+ */
+ if (m68k_is040or060 == 6) {
+ unsigned long fs = get_fs();
+ unsigned long paddr;
+
+ set_fs (SUPER_DATA);
+
+ /* The PLPAR instruction causes an access error if the translation
+ * is not possible. We don't catch that here, so a bad kernel trap
+ * will be reported in this case. */
+ asm volatile ("movel %1,%/a0\n\t"
+ ".word 0xf5c8\n\t" /* plpar (a0) */
+ "movel %/a0,%0"
+ : "=g" (paddr)
+ : "g" (vaddr)
+ : "a0" );
+ set_fs (fs);
+
+ return paddr;
+
+ } else if (m68k_is040or060 == 4) {
+ unsigned long mmusr;
+ unsigned long fs = get_fs();
+
+ set_fs (SUPER_DATA);
+
+ asm volatile ("movel %1,%/a0\n\t"
+ ".word 0xf568\n\t" /* ptestr (a0) */
+ ".long 0x4e7a8805\n\t" /* movec mmusr, a0 */
+ "movel %/a0,%0"
+ : "=g" (mmusr)
+ : "g" (vaddr)
+ : "a0", "d0");
+ set_fs (fs);
+
+ if (mmusr & MMU_R_040)
+ return (mmusr & PAGE_MASK) | (vaddr & (PAGE_SIZE-1));
+
+ panic ("VTOP040: bad virtual address %08lx (%lx)", vaddr, mmusr);
+ } else {
+ volatile unsigned short temp;
+ unsigned short mmusr;
+ unsigned long *descaddr;
+
+ asm volatile ("ptestr #5,%2@,#7,%0\n\t"
+ "pmove %/psr,%1@"
+ : "=a&" (descaddr)
+ : "a" (&temp), "a" (vaddr));
+ mmusr = temp;
+
+ if (mmusr & (MMU_I|MMU_B|MMU_L))
+ panic ("VTOP030: bad virtual address %08lx (%x)", vaddr, mmusr);
+
+ descaddr = (unsigned long *)PTOV(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_SIZE-1));
+ default:
+ panic ("VTOP: bad levels (%u) for virtual address %08lx",
+ mmusr & MMU_NUM, vaddr);
+ }
+ }
+
+ panic ("VTOP: bad virtual address %08lx", vaddr);
+}
+
+unsigned long mm_ptov (unsigned long paddr)
+{
+ int i;
+ unsigned long offset = 0;
+
+ for (i = 0; i < boot_info.num_memory; i++)
+ {
+ if (paddr >= boot_info.memory[i].addr &&
+ paddr < (boot_info.memory[i].addr
+ + boot_info.memory[i].size)) {
+#ifdef DEBUGPV
+ printk ("PTOV(%lx)=%lx\n", paddr,
+ (paddr - boot_info.memory[i].addr) + offset);
+#endif
+ return (paddr - boot_info.memory[i].addr) + offset;
+ } else
+ offset += boot_info.memory[i].size;
+ }
+
+ /*
+ * assume that the kernel virtual address is the same as the
+ * physical address.
+ *
+ * This should be reasonable in most situations:
+ * 1) They shouldn't be dereferencing the virtual address
+ * unless they are sure that it is valid from kernel space.
+ * 2) The only usage I see so far is converting a page table
+ * reference to some non-FASTMEM address space when freeing
+ * mmaped "/dev/mem" pages. These addresses are just passed
+ * to "free_page", which ignores addresses that aren't in
+ * the memory list anyway.
+ *
+ */
+
+ /*
+ * if on an amiga and address is in first 16M, move it
+ * to the ZTWO_ADDR range
+ */
+ if (MACH_IS_AMIGA && paddr < 16*1024*1024)
+ return ZTWO_VADDR(paddr);
+ return paddr;
+}
+
+#define clear040(paddr) __asm__ __volatile__ ("movel %0,%/a0\n\t"\
+ ".word 0xf4d0"\
+ /* CINVP I/D (a0) */\
+ : : "g" ((paddr))\
+ : "a0")
+
+#define push040(paddr) __asm__ __volatile__ ("movel %0,%/a0\n\t"\
+ ".word 0xf4f0"\
+ /* CPUSHP I/D (a0) */\
+ : : "g" ((paddr))\
+ : "a0")
+
+#define pushcl040(paddr) do { push040((paddr));\
+ if (m68k_is040or060 == 6) clear040((paddr));\
+ } while(0)
+
+#define pushv040(vaddr) __asm__ __volatile__ ("movel %0,%/a0\n\t"\
+ /* ptestr (a0) */\
+ ".word 0xf568\n\t"\
+ /* movec mmusr,d0 */\
+ ".long 0x4e7a0805\n\t"\
+ "andw #0xf000,%/d0\n\t"\
+ "movel %/d0,%/a0\n\t"\
+ /* CPUSHP I/D (a0) */\
+ ".word 0xf4f0"\
+ : : "g" ((vaddr))\
+ : "a0", "d0")
+
+#define pushv060(vaddr) __asm__ __volatile__ ("movel %0,%/a0\n\t"\
+ /* plpar (a0) */\
+ ".word 0xf5c8\n\t"\
+ /* CPUSHP I/D (a0) */\
+ ".word 0xf4f0"\
+ : : "g" ((vaddr))\
+ : "a0")
+
+
+/*
+ * 040: Hit every page containing an address in the range paddr..paddr+len-1.
+ * (Low order bits of the ea of a CINVP/CPUSHP are "don't care"s).
+ * Hit every page until there is a page or less to go. Hit the next page,
+ * and the one after that if the range hits it.
+ */
+/* ++roman: A little bit more care is required here: The CINVP instruction
+ * invalidates cache entries WITHOUT WRITING DIRTY DATA BACK! So the beginning
+ * and the end of the region must be treated differently if they are not
+ * exactly at the beginning or end of a page boundary. Else, maybe too much
+ * data becomes invalidated and thus lost forever. CPUSHP does what we need:
+ * it invalidates the page after pushing dirty data to memory. (Thanks to Jes
+ * for discovering the problem!)
+ */
+/* ... but on the '060, CPUSH doesn't invalidate (for us, since we have set
+ * the DPI bit in the CACR; would it cause problems with temporarily changing
+ * this?). So we have to push first and then additionally to invalidate.
+ */
+void cache_clear (unsigned long paddr, int len)
+{
+ if (m68k_is040or060) {
+ /* ++roman: There have been too many problems with the CINV, it seems
+ * to break the cache maintainance of DMAing drivers. I don't expect
+ * too much overhead by using CPUSH instead.
+ */
+ while (len > PAGE_SIZE) {
+ pushcl040(paddr);
+ len -= PAGE_SIZE;
+ paddr += PAGE_SIZE;
+ }
+ if (len > 0) {
+ pushcl040(paddr);
+ if (((paddr + len - 1) ^ paddr) & PAGE_MASK) {
+ /* a page boundary gets crossed at the end */
+ pushcl040(paddr + len - 1);
+ }
+ }
+ }
+#if 0
+ /* on 68040, invalidate cache lines for pages in the range */
+ while (len > PAGE_SIZE) {
+ clear040(paddr);
+ len -= PAGE_SIZE;
+ paddr += PAGE_SIZE;
+ }
+ if (len > 0) {
+ /* 0 < len <= PAGE_SIZE */
+ clear040(paddr);
+ if (((paddr + len - 1) / PAGE_SIZE) != (paddr / PAGE_SIZE)) {
+ /* a page boundary gets crossed at the end */
+ clear040(paddr + len - 1);
+ }
+ }
+#endif
+ else /* 68030 or 68020 */
+ asm volatile ("movec %/cacr,%/d0\n\t"
+ "oriw %0,%/d0\n\t"
+ "movec %/d0,%/cacr"
+ : : "i" (FLUSH_I_AND_D)
+ : "d0");
+}
+
+
+
+void cache_push (unsigned long paddr, int len)
+{
+ if (m68k_is040or060) {
+ /*
+ * on 68040 or 68060, push cache lines for pages in the range;
+ * on the '040 this also invalidates the pushed lines, but not on
+ * the '060!
+ */
+ while (len > PAGE_SIZE) {
+ push040(paddr);
+ len -= PAGE_SIZE;
+ paddr += PAGE_SIZE;
+ }
+ if (len > 0) {
+ push040(paddr);
+#if 0
+ if (((paddr + len - 1) / PAGE_SIZE) != (paddr / PAGE_SIZE)) {
+#endif
+ if (((paddr + len - 1) ^ paddr) & PAGE_MASK) {
+ /* a page boundary gets crossed at the end */
+ push040(paddr + len - 1);
+ }
+ }
+ }
+
+
+ /*
+ * 68030/68020 have no writeback cache. On the other hand,
+ * cache_push is actually a superset of cache_clear (the lines
+ * get written back and invalidated), so we should make sure
+ * to perform the corresponding actions. After all, this is getting
+ * called in places where we've just loaded code, or whatever, so
+ * flushing the icache is appropriate; flushing the dcache shouldn't
+ * be required.
+ */
+ else /* 68030 or 68020 */
+ asm volatile ("movec %/cacr,%/d0\n\t"
+ "oriw %0,%/d0\n\t"
+ "movec %/d0,%/cacr"
+ : : "i" (FLUSH_I)
+ : "d0");
+ }
+
+void cache_push_v (unsigned long vaddr, int len)
+{
+ if (m68k_is040or060 == 4) {
+ /* on 68040, push cache lines for pages in the range */
+ while (len > PAGE_SIZE) {
+ pushv040(vaddr);
+ len -= PAGE_SIZE;
+ vaddr += PAGE_SIZE;
+ }
+ if (len > 0) {
+ pushv040(vaddr);
+#if 0
+ if (((vaddr + len - 1) / PAGE_SIZE) != (vaddr / PAGE_SIZE)) {
+#endif
+ if (((vaddr + len - 1) ^ vaddr) & PAGE_MASK) {
+ /* a page boundary gets crossed at the end */
+ pushv040(vaddr + len - 1);
+ }
+ }
+ }
+ else if (m68k_is040or060 == 6) {
+ /* on 68040, push cache lines for pages in the range */
+ while (len > PAGE_SIZE) {
+ pushv060(vaddr);
+ len -= PAGE_SIZE;
+ vaddr += PAGE_SIZE;
+ }
+ if (len > 0) {
+ pushv060(vaddr);
+ if (((vaddr + len - 1) ^ vaddr) & PAGE_MASK) {
+ /* a page boundary gets crossed at the end */
+ pushv060(vaddr + len - 1);
+ }
+ }
+ }
+ /* 68030/68020 have no writeback cache; still need to clear icache. */
+ else /* 68030 or 68020 */
+ asm volatile ("movec %/cacr,%/d0\n\t"
+ "oriw %0,%/d0\n\t"
+ "movec %/d0,%/cacr"
+ : : "i" (FLUSH_I)
+ : "d0");
+}
+
+#undef clear040
+#undef push040
+#undef pushv040
+#undef pushv060
+
+unsigned long mm_phys_to_virt (unsigned long addr)
+{
+ return PTOV (addr);
+}
+
+int mm_end_of_chunk (unsigned long addr, int len)
+{
+ int i;
+
+ for (i = 0; i < boot_info.num_memory; i++)
+ if (boot_info.memory[i].addr + boot_info.memory[i].size
+ == addr + len)
+ return 1;
+ return 0;
+}
+
+/* Map some physical address range into the kernel address space. The
+ * code is copied and adapted from map_chunk().
+ */
+
+unsigned long kernel_map(unsigned long paddr, unsigned long size,
+ int nocacheflag, unsigned long *memavailp )
+{
+#define STEP_SIZE (256*1024)
+
+ static unsigned long vaddr = 0xe0000000; /* safe place */
+ unsigned long physaddr, retaddr;
+ pte_t *ktablep = NULL;
+ pmd_t *kpointerp;
+ pgd_t *page_dir;
+ int pindex; /* index into pointer table */
+ int prot;
+
+ /* Round down 'paddr' to 256 KB and adjust size */
+ physaddr = paddr & ~(STEP_SIZE-1);
+ size += paddr - physaddr;
+ retaddr = vaddr + (paddr - physaddr);
+ paddr = physaddr;
+ /* Round up the size to 256 KB. It doesn't hurt if too much is
+ * mapped... */
+ size = (size + STEP_SIZE - 1) & ~(STEP_SIZE-1);
+
+ if (m68k_is040or060) {
+ prot = _PAGE_PRESENT | _PAGE_GLOBAL040;
+ switch( nocacheflag ) {
+ case KERNELMAP_FULL_CACHING:
+ prot |= _PAGE_CACHE040;
+ break;
+ case KERNELMAP_NOCACHE_SER:
+ default:
+ prot |= _PAGE_NOCACHE_S;
+ break;
+ case KERNELMAP_NOCACHE_NONSER:
+ prot |= _PAGE_NOCACHE;
+ break;
+ case KERNELMAP_NO_COPYBACK:
+ prot |= _PAGE_CACHE040W;
+ /* prot |= 0; */
+ break;
+ }
+ } else
+ prot = _PAGE_PRESENT |
+ ((nocacheflag == KERNELMAP_FULL_CACHING ||
+ nocacheflag == KERNELMAP_NO_COPYBACK) ? 0 : _PAGE_NOCACHE030);
+
+ page_dir = pgd_offset_k(vaddr);
+ if (pgd_present(*page_dir)) {
+ kpointerp = (pmd_t *)pgd_page(*page_dir);
+ pindex = (vaddr >> 18) & 0x7f;
+ if (pindex != 0 && m68k_is040or060) {
+ if (pmd_present(*kpointerp))
+ ktablep = (pte_t *)pmd_page(*kpointerp);
+ else {
+ ktablep = kernel_page_table (memavailp);
+ /* Make entries invalid */
+ memset( ktablep, 0, sizeof(long)*PTRS_PER_PTE);
+ pmd_set(kpointerp,ktablep);
+ }
+ ktablep += (pindex & 15)*64;
+ }
+ }
+ else {
+ /* we need a new pointer table */
+ kpointerp = get_kpointer_table ();
+ pgd_set(page_dir, (pmd_t *)kpointerp);
+ memset( kpointerp, 0, PTRS_PER_PMD*sizeof(pmd_t));
+ pindex = 0;
+ }
+
+ for (physaddr = paddr; physaddr < paddr + size; vaddr += STEP_SIZE) {
+
+ if (pindex > 127) {
+ /* we need a new pointer table */
+ kpointerp = get_kpointer_table ();
+ pgd_set(pgd_offset_k(vaddr), (pmd_t *)kpointerp);
+ memset( kpointerp, 0, PTRS_PER_PMD*sizeof(pmd_t));
+ pindex = 0;
+ }
+
+ if (m68k_is040or060) {
+ int i;
+ unsigned long ktable;
+
+ /*
+ * 68040, use page tables pointed to by the
+ * kernel pointer table.
+ */
+
+ if ((pindex & 15) == 0) {
+ /* Need new page table every 4M on the '040 */
+ ktablep = kernel_page_table (memavailp);
+ /* Make entries invalid */
+ memset( ktablep, 0, sizeof(long)*PTRS_PER_PTE);
+ }
+
+ ktable = VTOP(ktablep);
+
+ /*
+ * initialize section of the page table mapping
+ * this 1M portion.
+ */
+ for (i = 0; i < 64; i++) {
+ pte_val(*ktablep++) = physaddr | prot;
+ physaddr += PAGE_SIZE;
+ }
+
+ /*
+ * make the kernel pointer table point to the
+ * kernel page table.
+ */
+
+ ((unsigned long *)kpointerp)[pindex++] = ktable | _PAGE_TABLE;
+
+ } else {
+ /*
+ * 68030, use early termination page descriptors.
+ * Each one points to 64 pages (256K).
+ */
+ ((unsigned long *)kpointerp)[pindex++] = physaddr | prot;
+ physaddr += 64 * PAGE_SIZE;
+ }
+ }
+
+ return( retaddr );
+}
+
+
+static inline void set_cmode_pte( pmd_t *pmd, unsigned long address,
+ unsigned long size, unsigned cmode )
+{ pte_t *pte;
+ unsigned long end;
+
+ if (pmd_none(*pmd))
+ return;
+
+ pte = pte_offset( pmd, address );
+ address &= ~PMD_MASK;
+ end = address + size;
+ if (end >= PMD_SIZE)
+ end = PMD_SIZE;
+
+ for( ; address < end; pte++ ) {
+ pte_val(*pte) = (pte_val(*pte) & ~_PAGE_NOCACHE) | cmode;
+ address += PAGE_SIZE;
+ }
+}
+
+
+static inline void set_cmode_pmd( pgd_t *dir, unsigned long address,
+ unsigned long size, unsigned cmode )
+{
+ pmd_t *pmd;
+ unsigned long end;
+
+ if (pgd_none(*dir))
+ return;
+
+ pmd = pmd_offset( dir, address );
+ address &= ~PGDIR_MASK;
+ end = address + size;
+ if (end > PGDIR_SIZE)
+ end = PGDIR_SIZE;
+
+ if ((pmd_val(*pmd) & _DESCTYPE_MASK) == _PAGE_PRESENT) {
+ /* 68030 early termination descriptor */
+ pmd_val(*pmd) = (pmd_val(*pmd) & ~_PAGE_NOCACHE) | cmode;
+ return;
+ }
+ else {
+ /* "normal" tables */
+ for( ; address < end; pmd++ ) {
+ set_cmode_pte( pmd, address, end - address, cmode );
+ address = (address + PMD_SIZE) & PMD_MASK;
+ }
+ }
+}
+
+
+/*
+ * Set new cache mode for some kernel address space.
+ * The caller must push data for that range itself, if such data may already
+ * be in the cache.
+ */
+
+void kernel_set_cachemode( unsigned long address, unsigned long size,
+ unsigned cmode )
+{
+ pgd_t *dir = pgd_offset_k( address );
+ unsigned long end = address + size;
+
+ if (m68k_is040or060) {
+ switch( cmode ) {
+ case KERNELMAP_FULL_CACHING:
+ cmode = _PAGE_CACHE040;
+ break;
+ case KERNELMAP_NOCACHE_SER:
+ default:
+ cmode = _PAGE_NOCACHE_S;
+ break;
+ case KERNELMAP_NOCACHE_NONSER:
+ cmode = _PAGE_NOCACHE;
+ break;
+ case KERNELMAP_NO_COPYBACK:
+ cmode = _PAGE_CACHE040W;
+ break;
+ }
+ } else
+ cmode = ((cmode == KERNELMAP_FULL_CACHING ||
+ cmode == KERNELMAP_NO_COPYBACK) ?
+ 0 : _PAGE_NOCACHE030);
+
+ for( ; address < end; dir++ ) {
+ set_cmode_pmd( dir, address, end - address, cmode );
+ address = (address + PGDIR_SIZE) & PGDIR_MASK;
+ }
+ flush_tlb_all();
+}
+
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this