18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 38c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 48c2ecf20Sopenharmony_ci * for more details. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (C) Copyright 1995 1996 Linus Torvalds 78c2ecf20Sopenharmony_ci * (C) Copyright 2001, 2002 Ralf Baechle 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <asm/addrspace.h> 118c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 168c2ecf20Sopenharmony_ci#include <linux/mm_types.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 198c2ecf20Sopenharmony_ci#include <asm/tlbflush.h> 208c2ecf20Sopenharmony_ci#include <ioremap.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define IS_LOW512(addr) (!((phys_addr_t)(addr) & (phys_addr_t) ~0x1fffffffULL)) 238c2ecf20Sopenharmony_ci#define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == CKSEG1) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic int __ioremap_check_ram(unsigned long start_pfn, unsigned long nr_pages, 268c2ecf20Sopenharmony_ci void *arg) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci unsigned long i; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 318c2ecf20Sopenharmony_ci if (pfn_valid(start_pfn + i) && 328c2ecf20Sopenharmony_ci !PageReserved(pfn_to_page(start_pfn + i))) 338c2ecf20Sopenharmony_ci return 1; 348c2ecf20Sopenharmony_ci } 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * ioremap_prot - map bus memory into CPU space 418c2ecf20Sopenharmony_ci * @phys_addr: bus address of the memory 428c2ecf20Sopenharmony_ci * @size: size of the resource to map 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * ioremap_prot gives the caller control over cache coherency attributes (CCA) 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_civoid __iomem *ioremap_prot(phys_addr_t phys_addr, unsigned long size, 478c2ecf20Sopenharmony_ci unsigned long prot_val) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci unsigned long flags = prot_val & _CACHE_MASK; 508c2ecf20Sopenharmony_ci unsigned long offset, pfn, last_pfn; 518c2ecf20Sopenharmony_ci struct vm_struct *area; 528c2ecf20Sopenharmony_ci phys_addr_t last_addr; 538c2ecf20Sopenharmony_ci unsigned long vaddr; 548c2ecf20Sopenharmony_ci void __iomem *cpu_addr; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci cpu_addr = plat_ioremap(phys_addr, size, flags); 578c2ecf20Sopenharmony_ci if (cpu_addr) 588c2ecf20Sopenharmony_ci return cpu_addr; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci phys_addr = fixup_bigphys_addr(phys_addr, size); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* Don't allow wraparound or zero size */ 638c2ecf20Sopenharmony_ci last_addr = phys_addr + size - 1; 648c2ecf20Sopenharmony_ci if (!size || last_addr < phys_addr) 658c2ecf20Sopenharmony_ci return NULL; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Map uncached objects in the low 512mb of address space using KSEG1, 698c2ecf20Sopenharmony_ci * otherwise map using page tables. 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci if (IS_LOW512(phys_addr) && IS_LOW512(last_addr) && 728c2ecf20Sopenharmony_ci flags == _CACHE_UNCACHED) 738c2ecf20Sopenharmony_ci return (void __iomem *) CKSEG1ADDR(phys_addr); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * Don't allow anybody to remap RAM that may be allocated by the page 778c2ecf20Sopenharmony_ci * allocator, since that could lead to races & data clobbering. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci pfn = PFN_DOWN(phys_addr); 808c2ecf20Sopenharmony_ci last_pfn = PFN_DOWN(last_addr); 818c2ecf20Sopenharmony_ci if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL, 828c2ecf20Sopenharmony_ci __ioremap_check_ram) == 1) { 838c2ecf20Sopenharmony_ci WARN_ONCE(1, "ioremap on RAM at %pa - %pa\n", 848c2ecf20Sopenharmony_ci &phys_addr, &last_addr); 858c2ecf20Sopenharmony_ci return NULL; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* 898c2ecf20Sopenharmony_ci * Mappings have to be page-aligned 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci offset = phys_addr & ~PAGE_MASK; 928c2ecf20Sopenharmony_ci phys_addr &= PAGE_MASK; 938c2ecf20Sopenharmony_ci size = PAGE_ALIGN(last_addr + 1) - phys_addr; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * Ok, go for it.. 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci area = get_vm_area(size, VM_IOREMAP); 998c2ecf20Sopenharmony_ci if (!area) 1008c2ecf20Sopenharmony_ci return NULL; 1018c2ecf20Sopenharmony_ci vaddr = (unsigned long)area->addr; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci flags |= _PAGE_GLOBAL | _PAGE_PRESENT | __READABLE | __WRITEABLE; 1048c2ecf20Sopenharmony_ci if (ioremap_page_range(vaddr, vaddr + size, phys_addr, 1058c2ecf20Sopenharmony_ci __pgprot(flags))) { 1068c2ecf20Sopenharmony_ci free_vm_area(area); 1078c2ecf20Sopenharmony_ci return NULL; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return (void __iomem *)(vaddr + offset); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap_prot); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_civoid iounmap(const volatile void __iomem *addr) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci if (!plat_iounmap(addr) && !IS_KSEG1(addr)) 1178c2ecf20Sopenharmony_ci vunmap((void *)((unsigned long)addr & PAGE_MASK)); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iounmap); 120