18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/m68k/mm/kmap.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1997 Roman Hodek 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 10/01/99 cleaned up the code and changing to the same interface 88c2ecf20Sopenharmony_ci * used by other architectures /Roman Zippel 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/types.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/setup.h> 208c2ecf20Sopenharmony_ci#include <asm/segment.h> 218c2ecf20Sopenharmony_ci#include <asm/page.h> 228c2ecf20Sopenharmony_ci#include <asm/io.h> 238c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#undef DEBUG 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * For 040/060 we can use the virtual memory area like other architectures, 298c2ecf20Sopenharmony_ci * but for 020/030 we want to use early termination page descriptors and we 308c2ecf20Sopenharmony_ci * can't mix this with normal page descriptors, so we have to copy that code 318c2ecf20Sopenharmony_ci * (mm/vmalloc.c) and return appropriately aligned addresses. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#ifdef CPU_M68040_OR_M68060_ONLY 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define IO_SIZE PAGE_SIZE 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic inline struct vm_struct *get_io_area(unsigned long size) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return get_vm_area(size, VM_IOREMAP); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic inline void free_io_area(void *addr) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci vfree((void *)(PAGE_MASK & (unsigned long)addr)); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#else 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define IO_SIZE PMD_SIZE 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic struct vm_struct *iolist; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* 568c2ecf20Sopenharmony_ci * __free_io_area unmaps nearly everything, so be careful 578c2ecf20Sopenharmony_ci * Currently it doesn't free pointer/page tables anymore but this 588c2ecf20Sopenharmony_ci * wasn't used anyway and might be added later. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_cistatic void __free_io_area(void *addr, unsigned long size) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci unsigned long virtaddr = (unsigned long)addr; 638c2ecf20Sopenharmony_ci pgd_t *pgd_dir; 648c2ecf20Sopenharmony_ci p4d_t *p4d_dir; 658c2ecf20Sopenharmony_ci pud_t *pud_dir; 668c2ecf20Sopenharmony_ci pmd_t *pmd_dir; 678c2ecf20Sopenharmony_ci pte_t *pte_dir; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci while ((long)size > 0) { 708c2ecf20Sopenharmony_ci pgd_dir = pgd_offset_k(virtaddr); 718c2ecf20Sopenharmony_ci p4d_dir = p4d_offset(pgd_dir, virtaddr); 728c2ecf20Sopenharmony_ci pud_dir = pud_offset(p4d_dir, virtaddr); 738c2ecf20Sopenharmony_ci if (pud_bad(*pud_dir)) { 748c2ecf20Sopenharmony_ci printk("iounmap: bad pud(%08lx)\n", pud_val(*pud_dir)); 758c2ecf20Sopenharmony_ci pud_clear(pud_dir); 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci pmd_dir = pmd_offset(pud_dir, virtaddr); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 818c2ecf20Sopenharmony_ci if (CPU_IS_020_OR_030) { 828c2ecf20Sopenharmony_ci int pmd_type = pmd_val(*pmd_dir) & _DESCTYPE_MASK; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (pmd_type == _PAGE_PRESENT) { 858c2ecf20Sopenharmony_ci pmd_clear(pmd_dir); 868c2ecf20Sopenharmony_ci virtaddr += PMD_SIZE; 878c2ecf20Sopenharmony_ci size -= PMD_SIZE; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci } else if (pmd_type == 0) 908c2ecf20Sopenharmony_ci continue; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci#endif 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (pmd_bad(*pmd_dir)) { 958c2ecf20Sopenharmony_ci printk("iounmap: bad pmd (%08lx)\n", pmd_val(*pmd_dir)); 968c2ecf20Sopenharmony_ci pmd_clear(pmd_dir); 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci pte_dir = pte_offset_kernel(pmd_dir, virtaddr); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci pte_val(*pte_dir) = 0; 1028c2ecf20Sopenharmony_ci virtaddr += PAGE_SIZE; 1038c2ecf20Sopenharmony_ci size -= PAGE_SIZE; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci flush_tlb_all(); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic struct vm_struct *get_io_area(unsigned long size) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci unsigned long addr; 1128c2ecf20Sopenharmony_ci struct vm_struct **p, *tmp, *area; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci area = kmalloc(sizeof(*area), GFP_KERNEL); 1158c2ecf20Sopenharmony_ci if (!area) 1168c2ecf20Sopenharmony_ci return NULL; 1178c2ecf20Sopenharmony_ci addr = KMAP_START; 1188c2ecf20Sopenharmony_ci for (p = &iolist; (tmp = *p) ; p = &tmp->next) { 1198c2ecf20Sopenharmony_ci if (size + addr < (unsigned long)tmp->addr) 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci if (addr > KMAP_END-size) { 1228c2ecf20Sopenharmony_ci kfree(area); 1238c2ecf20Sopenharmony_ci return NULL; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci addr = tmp->size + (unsigned long)tmp->addr; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci area->addr = (void *)addr; 1288c2ecf20Sopenharmony_ci area->size = size + IO_SIZE; 1298c2ecf20Sopenharmony_ci area->next = *p; 1308c2ecf20Sopenharmony_ci *p = area; 1318c2ecf20Sopenharmony_ci return area; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic inline void free_io_area(void *addr) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct vm_struct **p, *tmp; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (!addr) 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci addr = (void *)((unsigned long)addr & -IO_SIZE); 1418c2ecf20Sopenharmony_ci for (p = &iolist ; (tmp = *p) ; p = &tmp->next) { 1428c2ecf20Sopenharmony_ci if (tmp->addr == addr) { 1438c2ecf20Sopenharmony_ci *p = tmp->next; 1448c2ecf20Sopenharmony_ci /* remove gap added in get_io_area() */ 1458c2ecf20Sopenharmony_ci __free_io_area(tmp->addr, tmp->size - IO_SIZE); 1468c2ecf20Sopenharmony_ci kfree(tmp); 1478c2ecf20Sopenharmony_ci return; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* 1558c2ecf20Sopenharmony_ci * Map some physical address range into the kernel address space. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci/* Rewritten by Andreas Schwab to remove all races. */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_civoid __iomem *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct vm_struct *area; 1628c2ecf20Sopenharmony_ci unsigned long virtaddr, retaddr; 1638c2ecf20Sopenharmony_ci long offset; 1648c2ecf20Sopenharmony_ci pgd_t *pgd_dir; 1658c2ecf20Sopenharmony_ci p4d_t *p4d_dir; 1668c2ecf20Sopenharmony_ci pud_t *pud_dir; 1678c2ecf20Sopenharmony_ci pmd_t *pmd_dir; 1688c2ecf20Sopenharmony_ci pte_t *pte_dir; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * Don't allow mappings that wrap.. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci if (!size || physaddr > (unsigned long)(-size)) 1748c2ecf20Sopenharmony_ci return NULL; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci#ifdef CONFIG_AMIGA 1778c2ecf20Sopenharmony_ci if (MACH_IS_AMIGA) { 1788c2ecf20Sopenharmony_ci if ((physaddr >= 0x40000000) && (physaddr + size < 0x60000000) 1798c2ecf20Sopenharmony_ci && (cacheflag == IOMAP_NOCACHE_SER)) 1808c2ecf20Sopenharmony_ci return (void __iomem *)physaddr; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci#endif 1838c2ecf20Sopenharmony_ci#ifdef CONFIG_COLDFIRE 1848c2ecf20Sopenharmony_ci if (__cf_internalio(physaddr)) 1858c2ecf20Sopenharmony_ci return (void __iomem *) physaddr; 1868c2ecf20Sopenharmony_ci#endif 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#ifdef DEBUG 1898c2ecf20Sopenharmony_ci printk("ioremap: 0x%lx,0x%lx(%d) - ", physaddr, size, cacheflag); 1908c2ecf20Sopenharmony_ci#endif 1918c2ecf20Sopenharmony_ci /* 1928c2ecf20Sopenharmony_ci * Mappings have to be aligned 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci offset = physaddr & (IO_SIZE - 1); 1958c2ecf20Sopenharmony_ci physaddr &= -IO_SIZE; 1968c2ecf20Sopenharmony_ci size = (size + offset + IO_SIZE - 1) & -IO_SIZE; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Ok, go for it.. 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci area = get_io_area(size); 2028c2ecf20Sopenharmony_ci if (!area) 2038c2ecf20Sopenharmony_ci return NULL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci virtaddr = (unsigned long)area->addr; 2068c2ecf20Sopenharmony_ci retaddr = virtaddr + offset; 2078c2ecf20Sopenharmony_ci#ifdef DEBUG 2088c2ecf20Sopenharmony_ci printk("0x%lx,0x%lx,0x%lx", physaddr, virtaddr, retaddr); 2098c2ecf20Sopenharmony_ci#endif 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * add cache and table flags to physical address 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci if (CPU_IS_040_OR_060) { 2158c2ecf20Sopenharmony_ci physaddr |= (_PAGE_PRESENT | _PAGE_GLOBAL040 | 2168c2ecf20Sopenharmony_ci _PAGE_ACCESSED | _PAGE_DIRTY); 2178c2ecf20Sopenharmony_ci switch (cacheflag) { 2188c2ecf20Sopenharmony_ci case IOMAP_FULL_CACHING: 2198c2ecf20Sopenharmony_ci physaddr |= _PAGE_CACHE040; 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_SER: 2228c2ecf20Sopenharmony_ci default: 2238c2ecf20Sopenharmony_ci physaddr |= _PAGE_NOCACHE_S; 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 2268c2ecf20Sopenharmony_ci physaddr |= _PAGE_NOCACHE; 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci case IOMAP_WRITETHROUGH: 2298c2ecf20Sopenharmony_ci physaddr |= _PAGE_CACHE040W; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci } else { 2338c2ecf20Sopenharmony_ci physaddr |= (_PAGE_PRESENT | _PAGE_ACCESSED | 2348c2ecf20Sopenharmony_ci _PAGE_DIRTY | _PAGE_READWRITE); 2358c2ecf20Sopenharmony_ci switch (cacheflag) { 2368c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_SER: 2378c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 2388c2ecf20Sopenharmony_ci default: 2398c2ecf20Sopenharmony_ci physaddr |= _PAGE_NOCACHE030; 2408c2ecf20Sopenharmony_ci break; 2418c2ecf20Sopenharmony_ci case IOMAP_FULL_CACHING: 2428c2ecf20Sopenharmony_ci case IOMAP_WRITETHROUGH: 2438c2ecf20Sopenharmony_ci break; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci while ((long)size > 0) { 2488c2ecf20Sopenharmony_ci#ifdef DEBUG 2498c2ecf20Sopenharmony_ci if (!(virtaddr & (PMD_SIZE-1))) 2508c2ecf20Sopenharmony_ci printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr); 2518c2ecf20Sopenharmony_ci#endif 2528c2ecf20Sopenharmony_ci pgd_dir = pgd_offset_k(virtaddr); 2538c2ecf20Sopenharmony_ci p4d_dir = p4d_offset(pgd_dir, virtaddr); 2548c2ecf20Sopenharmony_ci pud_dir = pud_offset(p4d_dir, virtaddr); 2558c2ecf20Sopenharmony_ci pmd_dir = pmd_alloc(&init_mm, pud_dir, virtaddr); 2568c2ecf20Sopenharmony_ci if (!pmd_dir) { 2578c2ecf20Sopenharmony_ci printk("ioremap: no mem for pmd_dir\n"); 2588c2ecf20Sopenharmony_ci return NULL; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 2628c2ecf20Sopenharmony_ci if (CPU_IS_020_OR_030) { 2638c2ecf20Sopenharmony_ci pmd_val(*pmd_dir) = physaddr; 2648c2ecf20Sopenharmony_ci physaddr += PMD_SIZE; 2658c2ecf20Sopenharmony_ci virtaddr += PMD_SIZE; 2668c2ecf20Sopenharmony_ci size -= PMD_SIZE; 2678c2ecf20Sopenharmony_ci } else 2688c2ecf20Sopenharmony_ci#endif 2698c2ecf20Sopenharmony_ci { 2708c2ecf20Sopenharmony_ci pte_dir = pte_alloc_kernel(pmd_dir, virtaddr); 2718c2ecf20Sopenharmony_ci if (!pte_dir) { 2728c2ecf20Sopenharmony_ci printk("ioremap: no mem for pte_dir\n"); 2738c2ecf20Sopenharmony_ci return NULL; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pte_val(*pte_dir) = physaddr; 2778c2ecf20Sopenharmony_ci virtaddr += PAGE_SIZE; 2788c2ecf20Sopenharmony_ci physaddr += PAGE_SIZE; 2798c2ecf20Sopenharmony_ci size -= PAGE_SIZE; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci#ifdef DEBUG 2838c2ecf20Sopenharmony_ci printk("\n"); 2848c2ecf20Sopenharmony_ci#endif 2858c2ecf20Sopenharmony_ci flush_tlb_all(); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return (void __iomem *)retaddr; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__ioremap); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/* 2928c2ecf20Sopenharmony_ci * Unmap an ioremap()ed region again 2938c2ecf20Sopenharmony_ci */ 2948c2ecf20Sopenharmony_civoid iounmap(void __iomem *addr) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci#ifdef CONFIG_AMIGA 2978c2ecf20Sopenharmony_ci if ((!MACH_IS_AMIGA) || 2988c2ecf20Sopenharmony_ci (((unsigned long)addr < 0x40000000) || 2998c2ecf20Sopenharmony_ci ((unsigned long)addr > 0x60000000))) 3008c2ecf20Sopenharmony_ci free_io_area((__force void *)addr); 3018c2ecf20Sopenharmony_ci#else 3028c2ecf20Sopenharmony_ci#ifdef CONFIG_COLDFIRE 3038c2ecf20Sopenharmony_ci if (cf_internalio(addr)) 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci#endif 3068c2ecf20Sopenharmony_ci free_io_area((__force void *)addr); 3078c2ecf20Sopenharmony_ci#endif 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iounmap); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* 3128c2ecf20Sopenharmony_ci * Set new cache mode for some kernel address space. 3138c2ecf20Sopenharmony_ci * The caller must push data for that range itself, if such data may already 3148c2ecf20Sopenharmony_ci * be in the cache. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_civoid kernel_set_cachemode(void *addr, unsigned long size, int cmode) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci unsigned long virtaddr = (unsigned long)addr; 3198c2ecf20Sopenharmony_ci pgd_t *pgd_dir; 3208c2ecf20Sopenharmony_ci p4d_t *p4d_dir; 3218c2ecf20Sopenharmony_ci pud_t *pud_dir; 3228c2ecf20Sopenharmony_ci pmd_t *pmd_dir; 3238c2ecf20Sopenharmony_ci pte_t *pte_dir; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (CPU_IS_040_OR_060) { 3268c2ecf20Sopenharmony_ci switch (cmode) { 3278c2ecf20Sopenharmony_ci case IOMAP_FULL_CACHING: 3288c2ecf20Sopenharmony_ci cmode = _PAGE_CACHE040; 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_SER: 3318c2ecf20Sopenharmony_ci default: 3328c2ecf20Sopenharmony_ci cmode = _PAGE_NOCACHE_S; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 3358c2ecf20Sopenharmony_ci cmode = _PAGE_NOCACHE; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case IOMAP_WRITETHROUGH: 3388c2ecf20Sopenharmony_ci cmode = _PAGE_CACHE040W; 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci } else { 3428c2ecf20Sopenharmony_ci switch (cmode) { 3438c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_SER: 3448c2ecf20Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 3458c2ecf20Sopenharmony_ci default: 3468c2ecf20Sopenharmony_ci cmode = _PAGE_NOCACHE030; 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci case IOMAP_FULL_CACHING: 3498c2ecf20Sopenharmony_ci case IOMAP_WRITETHROUGH: 3508c2ecf20Sopenharmony_ci cmode = 0; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci while ((long)size > 0) { 3558c2ecf20Sopenharmony_ci pgd_dir = pgd_offset_k(virtaddr); 3568c2ecf20Sopenharmony_ci p4d_dir = p4d_offset(pgd_dir, virtaddr); 3578c2ecf20Sopenharmony_ci pud_dir = pud_offset(p4d_dir, virtaddr); 3588c2ecf20Sopenharmony_ci if (pud_bad(*pud_dir)) { 3598c2ecf20Sopenharmony_ci printk("iocachemode: bad pud(%08lx)\n", pud_val(*pud_dir)); 3608c2ecf20Sopenharmony_ci pud_clear(pud_dir); 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci pmd_dir = pmd_offset(pud_dir, virtaddr); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 3668c2ecf20Sopenharmony_ci if (CPU_IS_020_OR_030) { 3678c2ecf20Sopenharmony_ci unsigned long pmd = pmd_val(*pmd_dir); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if ((pmd & _DESCTYPE_MASK) == _PAGE_PRESENT) { 3708c2ecf20Sopenharmony_ci *pmd_dir = __pmd((pmd & _CACHEMASK040) | cmode); 3718c2ecf20Sopenharmony_ci virtaddr += PMD_SIZE; 3728c2ecf20Sopenharmony_ci size -= PMD_SIZE; 3738c2ecf20Sopenharmony_ci continue; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci#endif 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (pmd_bad(*pmd_dir)) { 3798c2ecf20Sopenharmony_ci printk("iocachemode: bad pmd (%08lx)\n", pmd_val(*pmd_dir)); 3808c2ecf20Sopenharmony_ci pmd_clear(pmd_dir); 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci pte_dir = pte_offset_kernel(pmd_dir, virtaddr); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci pte_val(*pte_dir) = (pte_val(*pte_dir) & _CACHEMASK040) | cmode; 3868c2ecf20Sopenharmony_ci virtaddr += PAGE_SIZE; 3878c2ecf20Sopenharmony_ci size -= PAGE_SIZE; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci flush_tlb_all(); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(kernel_set_cachemode); 393