162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/m68k/mm/kmap.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1997 Roman Hodek 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * 10/01/99 cleaned up the code and changing to the same interface 862306a36Sopenharmony_ci * used by other architectures /Roman Zippel 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/types.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/vmalloc.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/setup.h> 2062306a36Sopenharmony_ci#include <asm/page.h> 2162306a36Sopenharmony_ci#include <asm/io.h> 2262306a36Sopenharmony_ci#include <asm/tlbflush.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#undef DEBUG 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * For 040/060 we can use the virtual memory area like other architectures, 2862306a36Sopenharmony_ci * but for 020/030 we want to use early termination page descriptors and we 2962306a36Sopenharmony_ci * can't mix this with normal page descriptors, so we have to copy that code 3062306a36Sopenharmony_ci * (mm/vmalloc.c) and return appropriately aligned addresses. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#ifdef CPU_M68040_OR_M68060_ONLY 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define IO_SIZE PAGE_SIZE 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline struct vm_struct *get_io_area(unsigned long size) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return get_vm_area(size, VM_IOREMAP); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic inline void free_io_area(void *addr) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci vfree((void *)(PAGE_MASK & (unsigned long)addr)); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#else 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define IO_SIZE PMD_SIZE 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct vm_struct *iolist; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * __free_io_area unmaps nearly everything, so be careful 5662306a36Sopenharmony_ci * Currently it doesn't free pointer/page tables anymore but this 5762306a36Sopenharmony_ci * wasn't used anyway and might be added later. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_cistatic void __free_io_area(void *addr, unsigned long size) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci unsigned long virtaddr = (unsigned long)addr; 6262306a36Sopenharmony_ci pgd_t *pgd_dir; 6362306a36Sopenharmony_ci p4d_t *p4d_dir; 6462306a36Sopenharmony_ci pud_t *pud_dir; 6562306a36Sopenharmony_ci pmd_t *pmd_dir; 6662306a36Sopenharmony_ci pte_t *pte_dir; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci while ((long)size > 0) { 6962306a36Sopenharmony_ci pgd_dir = pgd_offset_k(virtaddr); 7062306a36Sopenharmony_ci p4d_dir = p4d_offset(pgd_dir, virtaddr); 7162306a36Sopenharmony_ci pud_dir = pud_offset(p4d_dir, virtaddr); 7262306a36Sopenharmony_ci if (pud_bad(*pud_dir)) { 7362306a36Sopenharmony_ci printk("iounmap: bad pud(%08lx)\n", pud_val(*pud_dir)); 7462306a36Sopenharmony_ci pud_clear(pud_dir); 7562306a36Sopenharmony_ci return; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci pmd_dir = pmd_offset(pud_dir, virtaddr); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 8062306a36Sopenharmony_ci if (CPU_IS_020_OR_030) { 8162306a36Sopenharmony_ci int pmd_type = pmd_val(*pmd_dir) & _DESCTYPE_MASK; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (pmd_type == _PAGE_PRESENT) { 8462306a36Sopenharmony_ci pmd_clear(pmd_dir); 8562306a36Sopenharmony_ci virtaddr += PMD_SIZE; 8662306a36Sopenharmony_ci size -= PMD_SIZE; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci } else if (pmd_type == 0) 8962306a36Sopenharmony_ci continue; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci#endif 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (pmd_bad(*pmd_dir)) { 9462306a36Sopenharmony_ci printk("iounmap: bad pmd (%08lx)\n", pmd_val(*pmd_dir)); 9562306a36Sopenharmony_ci pmd_clear(pmd_dir); 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci pte_dir = pte_offset_kernel(pmd_dir, virtaddr); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci pte_val(*pte_dir) = 0; 10162306a36Sopenharmony_ci virtaddr += PAGE_SIZE; 10262306a36Sopenharmony_ci size -= PAGE_SIZE; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci flush_tlb_all(); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct vm_struct *get_io_area(unsigned long size) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci unsigned long addr; 11162306a36Sopenharmony_ci struct vm_struct **p, *tmp, *area; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci area = kmalloc(sizeof(*area), GFP_KERNEL); 11462306a36Sopenharmony_ci if (!area) 11562306a36Sopenharmony_ci return NULL; 11662306a36Sopenharmony_ci addr = KMAP_START; 11762306a36Sopenharmony_ci for (p = &iolist; (tmp = *p) ; p = &tmp->next) { 11862306a36Sopenharmony_ci if (size + addr < (unsigned long)tmp->addr) 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci if (addr > KMAP_END-size) { 12162306a36Sopenharmony_ci kfree(area); 12262306a36Sopenharmony_ci return NULL; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci addr = tmp->size + (unsigned long)tmp->addr; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci area->addr = (void *)addr; 12762306a36Sopenharmony_ci area->size = size + IO_SIZE; 12862306a36Sopenharmony_ci area->next = *p; 12962306a36Sopenharmony_ci *p = area; 13062306a36Sopenharmony_ci return area; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic inline void free_io_area(void *addr) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct vm_struct **p, *tmp; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (!addr) 13862306a36Sopenharmony_ci return; 13962306a36Sopenharmony_ci addr = (void *)((unsigned long)addr & -IO_SIZE); 14062306a36Sopenharmony_ci for (p = &iolist ; (tmp = *p) ; p = &tmp->next) { 14162306a36Sopenharmony_ci if (tmp->addr == addr) { 14262306a36Sopenharmony_ci *p = tmp->next; 14362306a36Sopenharmony_ci /* remove gap added in get_io_area() */ 14462306a36Sopenharmony_ci __free_io_area(tmp->addr, tmp->size - IO_SIZE); 14562306a36Sopenharmony_ci kfree(tmp); 14662306a36Sopenharmony_ci return; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#endif 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Map some physical address range into the kernel address space. 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci/* Rewritten by Andreas Schwab to remove all races. */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_civoid __iomem *__ioremap(unsigned long physaddr, unsigned long size, int cacheflag) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct vm_struct *area; 16162306a36Sopenharmony_ci unsigned long virtaddr, retaddr; 16262306a36Sopenharmony_ci long offset; 16362306a36Sopenharmony_ci pgd_t *pgd_dir; 16462306a36Sopenharmony_ci p4d_t *p4d_dir; 16562306a36Sopenharmony_ci pud_t *pud_dir; 16662306a36Sopenharmony_ci pmd_t *pmd_dir; 16762306a36Sopenharmony_ci pte_t *pte_dir; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Don't allow mappings that wrap.. 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci if (!size || physaddr > (unsigned long)(-size)) 17362306a36Sopenharmony_ci return NULL; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#ifdef CONFIG_AMIGA 17662306a36Sopenharmony_ci if (MACH_IS_AMIGA) { 17762306a36Sopenharmony_ci if ((physaddr >= 0x40000000) && (physaddr + size < 0x60000000) 17862306a36Sopenharmony_ci && (cacheflag == IOMAP_NOCACHE_SER)) 17962306a36Sopenharmony_ci return (void __iomem *)physaddr; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci#endif 18262306a36Sopenharmony_ci#ifdef CONFIG_VIRT 18362306a36Sopenharmony_ci if (MACH_IS_VIRT) { 18462306a36Sopenharmony_ci if (physaddr >= 0xff000000 && cacheflag == IOMAP_NOCACHE_SER) 18562306a36Sopenharmony_ci return (void __iomem *)physaddr; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci#endif 18862306a36Sopenharmony_ci#ifdef CONFIG_COLDFIRE 18962306a36Sopenharmony_ci if (__cf_internalio(physaddr)) 19062306a36Sopenharmony_ci return (void __iomem *) physaddr; 19162306a36Sopenharmony_ci#endif 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci#ifdef DEBUG 19462306a36Sopenharmony_ci printk("ioremap: 0x%lx,0x%lx(%d) - ", physaddr, size, cacheflag); 19562306a36Sopenharmony_ci#endif 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * Mappings have to be aligned 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ci offset = physaddr & (IO_SIZE - 1); 20062306a36Sopenharmony_ci physaddr &= -IO_SIZE; 20162306a36Sopenharmony_ci size = (size + offset + IO_SIZE - 1) & -IO_SIZE; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * Ok, go for it.. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci area = get_io_area(size); 20762306a36Sopenharmony_ci if (!area) 20862306a36Sopenharmony_ci return NULL; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci virtaddr = (unsigned long)area->addr; 21162306a36Sopenharmony_ci retaddr = virtaddr + offset; 21262306a36Sopenharmony_ci#ifdef DEBUG 21362306a36Sopenharmony_ci printk("0x%lx,0x%lx,0x%lx", physaddr, virtaddr, retaddr); 21462306a36Sopenharmony_ci#endif 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * add cache and table flags to physical address 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci if (CPU_IS_040_OR_060) { 22062306a36Sopenharmony_ci physaddr |= (_PAGE_PRESENT | _PAGE_GLOBAL040 | 22162306a36Sopenharmony_ci _PAGE_ACCESSED | _PAGE_DIRTY); 22262306a36Sopenharmony_ci switch (cacheflag) { 22362306a36Sopenharmony_ci case IOMAP_FULL_CACHING: 22462306a36Sopenharmony_ci physaddr |= _PAGE_CACHE040; 22562306a36Sopenharmony_ci break; 22662306a36Sopenharmony_ci case IOMAP_NOCACHE_SER: 22762306a36Sopenharmony_ci default: 22862306a36Sopenharmony_ci physaddr |= _PAGE_NOCACHE_S; 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 23162306a36Sopenharmony_ci physaddr |= _PAGE_NOCACHE; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case IOMAP_WRITETHROUGH: 23462306a36Sopenharmony_ci physaddr |= _PAGE_CACHE040W; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci } else { 23862306a36Sopenharmony_ci physaddr |= (_PAGE_PRESENT | _PAGE_ACCESSED | 23962306a36Sopenharmony_ci _PAGE_DIRTY | _PAGE_READWRITE); 24062306a36Sopenharmony_ci switch (cacheflag) { 24162306a36Sopenharmony_ci case IOMAP_NOCACHE_SER: 24262306a36Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci physaddr |= _PAGE_NOCACHE030; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case IOMAP_FULL_CACHING: 24762306a36Sopenharmony_ci case IOMAP_WRITETHROUGH: 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci while ((long)size > 0) { 25362306a36Sopenharmony_ci#ifdef DEBUG 25462306a36Sopenharmony_ci if (!(virtaddr & (PMD_SIZE-1))) 25562306a36Sopenharmony_ci printk ("\npa=%#lx va=%#lx ", physaddr, virtaddr); 25662306a36Sopenharmony_ci#endif 25762306a36Sopenharmony_ci pgd_dir = pgd_offset_k(virtaddr); 25862306a36Sopenharmony_ci p4d_dir = p4d_offset(pgd_dir, virtaddr); 25962306a36Sopenharmony_ci pud_dir = pud_offset(p4d_dir, virtaddr); 26062306a36Sopenharmony_ci pmd_dir = pmd_alloc(&init_mm, pud_dir, virtaddr); 26162306a36Sopenharmony_ci if (!pmd_dir) { 26262306a36Sopenharmony_ci printk("ioremap: no mem for pmd_dir\n"); 26362306a36Sopenharmony_ci return NULL; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 26762306a36Sopenharmony_ci if (CPU_IS_020_OR_030) { 26862306a36Sopenharmony_ci pmd_val(*pmd_dir) = physaddr; 26962306a36Sopenharmony_ci physaddr += PMD_SIZE; 27062306a36Sopenharmony_ci virtaddr += PMD_SIZE; 27162306a36Sopenharmony_ci size -= PMD_SIZE; 27262306a36Sopenharmony_ci } else 27362306a36Sopenharmony_ci#endif 27462306a36Sopenharmony_ci { 27562306a36Sopenharmony_ci pte_dir = pte_alloc_kernel(pmd_dir, virtaddr); 27662306a36Sopenharmony_ci if (!pte_dir) { 27762306a36Sopenharmony_ci printk("ioremap: no mem for pte_dir\n"); 27862306a36Sopenharmony_ci return NULL; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci pte_val(*pte_dir) = physaddr; 28262306a36Sopenharmony_ci virtaddr += PAGE_SIZE; 28362306a36Sopenharmony_ci physaddr += PAGE_SIZE; 28462306a36Sopenharmony_ci size -= PAGE_SIZE; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci#ifdef DEBUG 28862306a36Sopenharmony_ci printk("\n"); 28962306a36Sopenharmony_ci#endif 29062306a36Sopenharmony_ci flush_tlb_all(); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci return (void __iomem *)retaddr; 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ciEXPORT_SYMBOL(__ioremap); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* 29762306a36Sopenharmony_ci * Unmap an ioremap()ed region again 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_civoid iounmap(void __iomem *addr) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci#ifdef CONFIG_AMIGA 30262306a36Sopenharmony_ci if (MACH_IS_AMIGA && 30362306a36Sopenharmony_ci ((unsigned long)addr >= 0x40000000) && 30462306a36Sopenharmony_ci ((unsigned long)addr < 0x60000000)) 30562306a36Sopenharmony_ci return; 30662306a36Sopenharmony_ci#endif 30762306a36Sopenharmony_ci#ifdef CONFIG_VIRT 30862306a36Sopenharmony_ci if (MACH_IS_VIRT && (unsigned long)addr >= 0xff000000) 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci#endif 31162306a36Sopenharmony_ci#ifdef CONFIG_COLDFIRE 31262306a36Sopenharmony_ci if (cf_internalio(addr)) 31362306a36Sopenharmony_ci return; 31462306a36Sopenharmony_ci#endif 31562306a36Sopenharmony_ci free_io_area((__force void *)addr); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ciEXPORT_SYMBOL(iounmap); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * Set new cache mode for some kernel address space. 32162306a36Sopenharmony_ci * The caller must push data for that range itself, if such data may already 32262306a36Sopenharmony_ci * be in the cache. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_civoid kernel_set_cachemode(void *addr, unsigned long size, int cmode) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci unsigned long virtaddr = (unsigned long)addr; 32762306a36Sopenharmony_ci pgd_t *pgd_dir; 32862306a36Sopenharmony_ci p4d_t *p4d_dir; 32962306a36Sopenharmony_ci pud_t *pud_dir; 33062306a36Sopenharmony_ci pmd_t *pmd_dir; 33162306a36Sopenharmony_ci pte_t *pte_dir; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (CPU_IS_040_OR_060) { 33462306a36Sopenharmony_ci switch (cmode) { 33562306a36Sopenharmony_ci case IOMAP_FULL_CACHING: 33662306a36Sopenharmony_ci cmode = _PAGE_CACHE040; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case IOMAP_NOCACHE_SER: 33962306a36Sopenharmony_ci default: 34062306a36Sopenharmony_ci cmode = _PAGE_NOCACHE_S; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 34362306a36Sopenharmony_ci cmode = _PAGE_NOCACHE; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci case IOMAP_WRITETHROUGH: 34662306a36Sopenharmony_ci cmode = _PAGE_CACHE040W; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } else { 35062306a36Sopenharmony_ci switch (cmode) { 35162306a36Sopenharmony_ci case IOMAP_NOCACHE_SER: 35262306a36Sopenharmony_ci case IOMAP_NOCACHE_NONSER: 35362306a36Sopenharmony_ci default: 35462306a36Sopenharmony_ci cmode = _PAGE_NOCACHE030; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci case IOMAP_FULL_CACHING: 35762306a36Sopenharmony_ci case IOMAP_WRITETHROUGH: 35862306a36Sopenharmony_ci cmode = 0; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci while ((long)size > 0) { 36362306a36Sopenharmony_ci pgd_dir = pgd_offset_k(virtaddr); 36462306a36Sopenharmony_ci p4d_dir = p4d_offset(pgd_dir, virtaddr); 36562306a36Sopenharmony_ci pud_dir = pud_offset(p4d_dir, virtaddr); 36662306a36Sopenharmony_ci if (pud_bad(*pud_dir)) { 36762306a36Sopenharmony_ci printk("iocachemode: bad pud(%08lx)\n", pud_val(*pud_dir)); 36862306a36Sopenharmony_ci pud_clear(pud_dir); 36962306a36Sopenharmony_ci return; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci pmd_dir = pmd_offset(pud_dir, virtaddr); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci#if CONFIG_PGTABLE_LEVELS == 3 37462306a36Sopenharmony_ci if (CPU_IS_020_OR_030) { 37562306a36Sopenharmony_ci unsigned long pmd = pmd_val(*pmd_dir); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if ((pmd & _DESCTYPE_MASK) == _PAGE_PRESENT) { 37862306a36Sopenharmony_ci *pmd_dir = __pmd((pmd & _CACHEMASK040) | cmode); 37962306a36Sopenharmony_ci virtaddr += PMD_SIZE; 38062306a36Sopenharmony_ci size -= PMD_SIZE; 38162306a36Sopenharmony_ci continue; 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (pmd_bad(*pmd_dir)) { 38762306a36Sopenharmony_ci printk("iocachemode: bad pmd (%08lx)\n", pmd_val(*pmd_dir)); 38862306a36Sopenharmony_ci pmd_clear(pmd_dir); 38962306a36Sopenharmony_ci return; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci pte_dir = pte_offset_kernel(pmd_dir, virtaddr); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci pte_val(*pte_dir) = (pte_val(*pte_dir) & _CACHEMASK040) | cmode; 39462306a36Sopenharmony_ci virtaddr += PAGE_SIZE; 39562306a36Sopenharmony_ci size -= PAGE_SIZE; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci flush_tlb_all(); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ciEXPORT_SYMBOL(kernel_set_cachemode); 401