11da177e4SLinus Torvalds /* 2f30c2269SUwe Zeisberger * linux/arch/m68k/mm/motorola.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Routines specific to the Motorola MMU, originally from: 51da177e4SLinus Torvalds * linux/arch/m68k/init.c 61da177e4SLinus Torvalds * which are Copyright (C) 1995 Hamish Macdonald 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Moved 8/20/1999 Sam Creasey 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds 111da177e4SLinus Torvalds #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/signal.h> 131da177e4SLinus Torvalds #include <linux/sched.h> 141da177e4SLinus Torvalds #include <linux/mm.h> 151da177e4SLinus Torvalds #include <linux/swap.h> 161da177e4SLinus Torvalds #include <linux/kernel.h> 171da177e4SLinus Torvalds #include <linux/string.h> 181da177e4SLinus Torvalds #include <linux/types.h> 191da177e4SLinus Torvalds #include <linux/init.h> 201da177e4SLinus Torvalds #include <linux/bootmem.h> 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <asm/setup.h> 231da177e4SLinus Torvalds #include <asm/uaccess.h> 241da177e4SLinus Torvalds #include <asm/page.h> 251da177e4SLinus Torvalds #include <asm/pgalloc.h> 261da177e4SLinus Torvalds #include <asm/system.h> 271da177e4SLinus Torvalds #include <asm/machdep.h> 281da177e4SLinus Torvalds #include <asm/io.h> 291da177e4SLinus Torvalds #include <asm/dma.h> 301da177e4SLinus Torvalds #ifdef CONFIG_ATARI 311da177e4SLinus Torvalds #include <asm/atari_stram.h> 321da177e4SLinus Torvalds #endif 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds #undef DEBUG 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds #ifndef mm_cachebits 371da177e4SLinus Torvalds /* 381da177e4SLinus Torvalds * Bits to add to page descriptors for "normal" caching mode. 391da177e4SLinus Torvalds * For 68020/030 this is 0. 401da177e4SLinus Torvalds * For 68040, this is _PAGE_CACHE040 (cachable, copyback) 411da177e4SLinus Torvalds */ 421da177e4SLinus Torvalds unsigned long mm_cachebits; 431da177e4SLinus Torvalds EXPORT_SYMBOL(mm_cachebits); 441da177e4SLinus Torvalds #endif 451da177e4SLinus Torvalds 4612d810c1SRoman Zippel /* size of memory already mapped in head.S */ 4712d810c1SRoman Zippel #define INIT_MAPPED_SIZE (4UL<<20) 4812d810c1SRoman Zippel 4912d810c1SRoman Zippel extern unsigned long availmem; 5012d810c1SRoman Zippel 511da177e4SLinus Torvalds static pte_t * __init kernel_page_table(void) 521da177e4SLinus Torvalds { 531da177e4SLinus Torvalds pte_t *ptablep; 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds ptablep = (pte_t *)alloc_bootmem_low_pages(PAGE_SIZE); 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds clear_page(ptablep); 581da177e4SLinus Torvalds __flush_page_to_ram(ptablep); 591da177e4SLinus Torvalds flush_tlb_kernel_page(ptablep); 601da177e4SLinus Torvalds nocache_page(ptablep); 611da177e4SLinus Torvalds 621da177e4SLinus Torvalds return ptablep; 631da177e4SLinus Torvalds } 641da177e4SLinus Torvalds 651da177e4SLinus Torvalds static pmd_t *last_pgtable __initdata = NULL; 661da177e4SLinus Torvalds pmd_t *zero_pgtable __initdata = NULL; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static pmd_t * __init kernel_ptr_table(void) 691da177e4SLinus Torvalds { 701da177e4SLinus Torvalds if (!last_pgtable) { 711da177e4SLinus Torvalds unsigned long pmd, last; 721da177e4SLinus Torvalds int i; 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /* Find the last ptr table that was used in head.S and 751da177e4SLinus Torvalds * reuse the remaining space in that page for further 761da177e4SLinus Torvalds * ptr tables. 771da177e4SLinus Torvalds */ 781da177e4SLinus Torvalds last = (unsigned long)kernel_pg_dir; 791da177e4SLinus Torvalds for (i = 0; i < PTRS_PER_PGD; i++) { 801da177e4SLinus Torvalds if (!pgd_present(kernel_pg_dir[i])) 811da177e4SLinus Torvalds continue; 821da177e4SLinus Torvalds pmd = __pgd_page(kernel_pg_dir[i]); 831da177e4SLinus Torvalds if (pmd > last) 841da177e4SLinus Torvalds last = pmd; 851da177e4SLinus Torvalds } 861da177e4SLinus Torvalds 871da177e4SLinus Torvalds last_pgtable = (pmd_t *)last; 881da177e4SLinus Torvalds #ifdef DEBUG 891da177e4SLinus Torvalds printk("kernel_ptr_init: %p\n", last_pgtable); 901da177e4SLinus Torvalds #endif 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds last_pgtable += PTRS_PER_PMD; 941da177e4SLinus Torvalds if (((unsigned long)last_pgtable & ~PAGE_MASK) == 0) { 951da177e4SLinus Torvalds last_pgtable = (pmd_t *)alloc_bootmem_low_pages(PAGE_SIZE); 961da177e4SLinus Torvalds 971da177e4SLinus Torvalds clear_page(last_pgtable); 981da177e4SLinus Torvalds __flush_page_to_ram(last_pgtable); 991da177e4SLinus Torvalds flush_tlb_kernel_page(last_pgtable); 1001da177e4SLinus Torvalds nocache_page(last_pgtable); 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds return last_pgtable; 1041da177e4SLinus Torvalds } 1051da177e4SLinus Torvalds 10612d810c1SRoman Zippel static void __init map_node(int node) 1071da177e4SLinus Torvalds { 1081da177e4SLinus Torvalds #define PTRTREESIZE (256*1024) 1091da177e4SLinus Torvalds #define ROOTTREESIZE (32*1024*1024) 11012d810c1SRoman Zippel unsigned long physaddr, virtaddr, size; 1111da177e4SLinus Torvalds pgd_t *pgd_dir; 1121da177e4SLinus Torvalds pmd_t *pmd_dir; 1131da177e4SLinus Torvalds pte_t *pte_dir; 1141da177e4SLinus Torvalds 11512d810c1SRoman Zippel size = m68k_memory[node].size; 11612d810c1SRoman Zippel physaddr = m68k_memory[node].addr; 11712d810c1SRoman Zippel virtaddr = (unsigned long)phys_to_virt(physaddr); 11812d810c1SRoman Zippel physaddr |= m68k_supervisor_cachemode | 11912d810c1SRoman Zippel _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY; 1201da177e4SLinus Torvalds if (CPU_IS_040_OR_060) 1211da177e4SLinus Torvalds physaddr |= _PAGE_GLOBAL040; 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds while (size > 0) { 1241da177e4SLinus Torvalds #ifdef DEBUG 1251da177e4SLinus Torvalds if (!(virtaddr & (PTRTREESIZE-1))) 1261da177e4SLinus Torvalds printk ("\npa=%#lx va=%#lx ", physaddr & PAGE_MASK, 1271da177e4SLinus Torvalds virtaddr); 1281da177e4SLinus Torvalds #endif 1291da177e4SLinus Torvalds pgd_dir = pgd_offset_k(virtaddr); 1301da177e4SLinus Torvalds if (virtaddr && CPU_IS_020_OR_030) { 1311da177e4SLinus Torvalds if (!(virtaddr & (ROOTTREESIZE-1)) && 1321da177e4SLinus Torvalds size >= ROOTTREESIZE) { 1331da177e4SLinus Torvalds #ifdef DEBUG 1341da177e4SLinus Torvalds printk ("[very early term]"); 1351da177e4SLinus Torvalds #endif 1361da177e4SLinus Torvalds pgd_val(*pgd_dir) = physaddr; 1371da177e4SLinus Torvalds size -= ROOTTREESIZE; 1381da177e4SLinus Torvalds virtaddr += ROOTTREESIZE; 1391da177e4SLinus Torvalds physaddr += ROOTTREESIZE; 1401da177e4SLinus Torvalds continue; 1411da177e4SLinus Torvalds } 1421da177e4SLinus Torvalds } 1431da177e4SLinus Torvalds if (!pgd_present(*pgd_dir)) { 1441da177e4SLinus Torvalds pmd_dir = kernel_ptr_table(); 1451da177e4SLinus Torvalds #ifdef DEBUG 1461da177e4SLinus Torvalds printk ("[new pointer %p]", pmd_dir); 1471da177e4SLinus Torvalds #endif 1481da177e4SLinus Torvalds pgd_set(pgd_dir, pmd_dir); 1491da177e4SLinus Torvalds } else 1501da177e4SLinus Torvalds pmd_dir = pmd_offset(pgd_dir, virtaddr); 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds if (CPU_IS_020_OR_030) { 1531da177e4SLinus Torvalds if (virtaddr) { 1541da177e4SLinus Torvalds #ifdef DEBUG 1551da177e4SLinus Torvalds printk ("[early term]"); 1561da177e4SLinus Torvalds #endif 1571da177e4SLinus Torvalds pmd_dir->pmd[(virtaddr/PTRTREESIZE) & 15] = physaddr; 1581da177e4SLinus Torvalds physaddr += PTRTREESIZE; 1591da177e4SLinus Torvalds } else { 1601da177e4SLinus Torvalds int i; 1611da177e4SLinus Torvalds #ifdef DEBUG 1621da177e4SLinus Torvalds printk ("[zero map]"); 1631da177e4SLinus Torvalds #endif 1641da177e4SLinus Torvalds zero_pgtable = kernel_ptr_table(); 1651da177e4SLinus Torvalds pte_dir = (pte_t *)zero_pgtable; 1661da177e4SLinus Torvalds pmd_dir->pmd[0] = virt_to_phys(pte_dir) | 1671da177e4SLinus Torvalds _PAGE_TABLE | _PAGE_ACCESSED; 1681da177e4SLinus Torvalds pte_val(*pte_dir++) = 0; 1691da177e4SLinus Torvalds physaddr += PAGE_SIZE; 1701da177e4SLinus Torvalds for (i = 1; i < 64; physaddr += PAGE_SIZE, i++) 1711da177e4SLinus Torvalds pte_val(*pte_dir++) = physaddr; 1721da177e4SLinus Torvalds } 1731da177e4SLinus Torvalds size -= PTRTREESIZE; 1741da177e4SLinus Torvalds virtaddr += PTRTREESIZE; 1751da177e4SLinus Torvalds } else { 1761da177e4SLinus Torvalds if (!pmd_present(*pmd_dir)) { 1771da177e4SLinus Torvalds #ifdef DEBUG 1781da177e4SLinus Torvalds printk ("[new table]"); 1791da177e4SLinus Torvalds #endif 1801da177e4SLinus Torvalds pte_dir = kernel_page_table(); 1811da177e4SLinus Torvalds pmd_set(pmd_dir, pte_dir); 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds pte_dir = pte_offset_kernel(pmd_dir, virtaddr); 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds if (virtaddr) { 1861da177e4SLinus Torvalds if (!pte_present(*pte_dir)) 1871da177e4SLinus Torvalds pte_val(*pte_dir) = physaddr; 1881da177e4SLinus Torvalds } else 1891da177e4SLinus Torvalds pte_val(*pte_dir) = 0; 1901da177e4SLinus Torvalds size -= PAGE_SIZE; 1911da177e4SLinus Torvalds virtaddr += PAGE_SIZE; 1921da177e4SLinus Torvalds physaddr += PAGE_SIZE; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds } 1961da177e4SLinus Torvalds #ifdef DEBUG 1971da177e4SLinus Torvalds printk("\n"); 1981da177e4SLinus Torvalds #endif 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds /* 2021da177e4SLinus Torvalds * paging_init() continues the virtual memory environment setup which 2031da177e4SLinus Torvalds * was begun by the code in arch/head.S. 2041da177e4SLinus Torvalds */ 2051da177e4SLinus Torvalds void __init paging_init(void) 2061da177e4SLinus Torvalds { 2072dcf15b7SRoman Zippel unsigned long zones_size[MAX_NR_ZONES] = { 0, }; 20812d810c1SRoman Zippel unsigned long min_addr, max_addr; 20912d810c1SRoman Zippel unsigned long addr, size, end; 21012d810c1SRoman Zippel int i; 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds #ifdef DEBUG 213*fb425d0bSGeert Uytterhoeven printk ("start of paging_init (%p, %lx)\n", kernel_pg_dir, availmem); 2141da177e4SLinus Torvalds #endif 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds /* Fix the cache mode in the page descriptors for the 680[46]0. */ 2171da177e4SLinus Torvalds if (CPU_IS_040_OR_060) { 2181da177e4SLinus Torvalds int i; 2191da177e4SLinus Torvalds #ifndef mm_cachebits 2201da177e4SLinus Torvalds mm_cachebits = _PAGE_CACHE040; 2211da177e4SLinus Torvalds #endif 2221da177e4SLinus Torvalds for (i = 0; i < 16; i++) 2231da177e4SLinus Torvalds pgprot_val(protection_map[i]) |= _PAGE_CACHE040; 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 22612d810c1SRoman Zippel min_addr = m68k_memory[0].addr; 22712d810c1SRoman Zippel max_addr = min_addr + m68k_memory[0].size; 22812d810c1SRoman Zippel for (i = 1; i < m68k_num_memory;) { 22912d810c1SRoman Zippel if (m68k_memory[i].addr < min_addr) { 23012d810c1SRoman Zippel printk("Ignoring memory chunk at 0x%lx:0x%lx before the first chunk\n", 23112d810c1SRoman Zippel m68k_memory[i].addr, m68k_memory[i].size); 23212d810c1SRoman Zippel printk("Fix your bootloader or use a memfile to make use of this area!\n"); 23312d810c1SRoman Zippel m68k_num_memory--; 23412d810c1SRoman Zippel memmove(m68k_memory + i, m68k_memory + i + 1, 23512d810c1SRoman Zippel (m68k_num_memory - i) * sizeof(struct mem_info)); 23612d810c1SRoman Zippel continue; 23712d810c1SRoman Zippel } 23812d810c1SRoman Zippel addr = m68k_memory[i].addr + m68k_memory[i].size; 23912d810c1SRoman Zippel if (addr > max_addr) 24012d810c1SRoman Zippel max_addr = addr; 24112d810c1SRoman Zippel i++; 24212d810c1SRoman Zippel } 24312d810c1SRoman Zippel m68k_memoffset = min_addr - PAGE_OFFSET; 24412d810c1SRoman Zippel m68k_virt_to_node_shift = fls(max_addr - min_addr - 1) - 6; 24512d810c1SRoman Zippel 246fbe9c961SRoman Zippel module_fixup(NULL, __start_fixup, __stop_fixup); 247fbe9c961SRoman Zippel flush_icache(); 248fbe9c961SRoman Zippel 24912d810c1SRoman Zippel high_memory = phys_to_virt(max_addr); 2501da177e4SLinus Torvalds 25112d810c1SRoman Zippel min_low_pfn = availmem >> PAGE_SHIFT; 25212d810c1SRoman Zippel max_low_pfn = max_addr >> PAGE_SHIFT; 2531da177e4SLinus Torvalds 25412d810c1SRoman Zippel for (i = 0; i < m68k_num_memory; i++) { 25512d810c1SRoman Zippel addr = m68k_memory[i].addr; 25612d810c1SRoman Zippel end = addr + m68k_memory[i].size; 25712d810c1SRoman Zippel m68k_setup_node(i); 25812d810c1SRoman Zippel availmem = PAGE_ALIGN(availmem); 25912d810c1SRoman Zippel availmem += init_bootmem_node(NODE_DATA(i), 26012d810c1SRoman Zippel availmem >> PAGE_SHIFT, 26112d810c1SRoman Zippel addr >> PAGE_SHIFT, 26212d810c1SRoman Zippel end >> PAGE_SHIFT); 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds 26512d810c1SRoman Zippel /* 26612d810c1SRoman Zippel * Map the physical memory available into the kernel virtual 26712d810c1SRoman Zippel * address space. First initialize the bootmem allocator with 26812d810c1SRoman Zippel * the memory we already mapped, so map_node() has something 26912d810c1SRoman Zippel * to allocate. 27012d810c1SRoman Zippel */ 27112d810c1SRoman Zippel addr = m68k_memory[0].addr; 27212d810c1SRoman Zippel size = m68k_memory[0].size; 27312d810c1SRoman Zippel free_bootmem_node(NODE_DATA(0), availmem, min(INIT_MAPPED_SIZE, size) - (availmem - addr)); 27412d810c1SRoman Zippel map_node(0); 27512d810c1SRoman Zippel if (size > INIT_MAPPED_SIZE) 27612d810c1SRoman Zippel free_bootmem_node(NODE_DATA(0), addr + INIT_MAPPED_SIZE, size - INIT_MAPPED_SIZE); 27712d810c1SRoman Zippel 27812d810c1SRoman Zippel for (i = 1; i < m68k_num_memory; i++) 27912d810c1SRoman Zippel map_node(i); 28012d810c1SRoman Zippel 2811da177e4SLinus Torvalds flush_tlb_all(); 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds /* 2841da177e4SLinus Torvalds * initialize the bad page table and bad page to point 2851da177e4SLinus Torvalds * to a couple of allocated pages 2861da177e4SLinus Torvalds */ 2871da177e4SLinus Torvalds empty_zero_page = alloc_bootmem_pages(PAGE_SIZE); 2881da177e4SLinus Torvalds memset(empty_zero_page, 0, PAGE_SIZE); 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds /* 2911da177e4SLinus Torvalds * Set up SFC/DFC registers 2921da177e4SLinus Torvalds */ 2931da177e4SLinus Torvalds set_fs(KERNEL_DS); 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds #ifdef DEBUG 2961da177e4SLinus Torvalds printk ("before free_area_init\n"); 2971da177e4SLinus Torvalds #endif 29812d810c1SRoman Zippel for (i = 0; i < m68k_num_memory; i++) { 29912d810c1SRoman Zippel zones_size[ZONE_DMA] = m68k_memory[i].size >> PAGE_SHIFT; 30012d810c1SRoman Zippel free_area_init_node(i, pg_data_map + i, zones_size, 30112d810c1SRoman Zippel m68k_memory[i].addr >> PAGE_SHIFT, NULL); 30212d810c1SRoman Zippel } 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds extern char __init_begin, __init_end; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds void free_initmem(void) 3081da177e4SLinus Torvalds { 3091da177e4SLinus Torvalds unsigned long addr; 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds addr = (unsigned long)&__init_begin; 3121da177e4SLinus Torvalds for (; addr < (unsigned long)&__init_end; addr += PAGE_SIZE) { 3131da177e4SLinus Torvalds virt_to_page(addr)->flags &= ~(1 << PG_reserved); 3147835e98bSNick Piggin init_page_count(virt_to_page(addr)); 3151da177e4SLinus Torvalds free_page(addr); 3161da177e4SLinus Torvalds totalram_pages++; 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds 321