18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (c) Copyright 2006, 2007 Hewlett-Packard Development Company, L.P. 48c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/compiler.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/efi.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 138c2ecf20Sopenharmony_ci#include <asm/io.h> 148c2ecf20Sopenharmony_ci#include <asm/meminit.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic inline void __iomem * 178c2ecf20Sopenharmony_ci__ioremap_uc(unsigned long phys_addr) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci return (void __iomem *) (__IA64_UNCACHED_OFFSET | phys_addr); 208c2ecf20Sopenharmony_ci} 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_civoid __iomem * 238c2ecf20Sopenharmony_ciearly_ioremap (unsigned long phys_addr, unsigned long size) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci u64 attr; 268c2ecf20Sopenharmony_ci attr = kern_mem_attribute(phys_addr, size); 278c2ecf20Sopenharmony_ci if (attr & EFI_MEMORY_WB) 288c2ecf20Sopenharmony_ci return (void __iomem *) phys_to_virt(phys_addr); 298c2ecf20Sopenharmony_ci return __ioremap_uc(phys_addr); 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_civoid __iomem * 338c2ecf20Sopenharmony_ciioremap (unsigned long phys_addr, unsigned long size) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci void __iomem *addr; 368c2ecf20Sopenharmony_ci struct vm_struct *area; 378c2ecf20Sopenharmony_ci unsigned long offset; 388c2ecf20Sopenharmony_ci pgprot_t prot; 398c2ecf20Sopenharmony_ci u64 attr; 408c2ecf20Sopenharmony_ci unsigned long gran_base, gran_size; 418c2ecf20Sopenharmony_ci unsigned long page_base; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* 448c2ecf20Sopenharmony_ci * For things in kern_memmap, we must use the same attribute 458c2ecf20Sopenharmony_ci * as the rest of the kernel. For more details, see 468c2ecf20Sopenharmony_ci * Documentation/ia64/aliasing.rst. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci attr = kern_mem_attribute(phys_addr, size); 498c2ecf20Sopenharmony_ci if (attr & EFI_MEMORY_WB) 508c2ecf20Sopenharmony_ci return (void __iomem *) phys_to_virt(phys_addr); 518c2ecf20Sopenharmony_ci else if (attr & EFI_MEMORY_UC) 528c2ecf20Sopenharmony_ci return __ioremap_uc(phys_addr); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* 558c2ecf20Sopenharmony_ci * Some chipsets don't support UC access to memory. If 568c2ecf20Sopenharmony_ci * WB is supported for the whole granule, we prefer that. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci gran_base = GRANULEROUNDDOWN(phys_addr); 598c2ecf20Sopenharmony_ci gran_size = GRANULEROUNDUP(phys_addr + size) - gran_base; 608c2ecf20Sopenharmony_ci if (efi_mem_attribute(gran_base, gran_size) & EFI_MEMORY_WB) 618c2ecf20Sopenharmony_ci return (void __iomem *) phys_to_virt(phys_addr); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci * WB is not supported for the whole granule, so we can't use 658c2ecf20Sopenharmony_ci * the region 7 identity mapping. If we can safely cover the 668c2ecf20Sopenharmony_ci * area with kernel page table mappings, we can use those 678c2ecf20Sopenharmony_ci * instead. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci page_base = phys_addr & PAGE_MASK; 708c2ecf20Sopenharmony_ci size = PAGE_ALIGN(phys_addr + size) - page_base; 718c2ecf20Sopenharmony_ci if (efi_mem_attribute(page_base, size) & EFI_MEMORY_WB) { 728c2ecf20Sopenharmony_ci prot = PAGE_KERNEL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * Mappings have to be page-aligned 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci offset = phys_addr & ~PAGE_MASK; 788c2ecf20Sopenharmony_ci phys_addr &= PAGE_MASK; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* 818c2ecf20Sopenharmony_ci * Ok, go for it.. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci area = get_vm_area(size, VM_IOREMAP); 848c2ecf20Sopenharmony_ci if (!area) 858c2ecf20Sopenharmony_ci return NULL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci area->phys_addr = phys_addr; 888c2ecf20Sopenharmony_ci addr = (void __iomem *) area->addr; 898c2ecf20Sopenharmony_ci if (ioremap_page_range((unsigned long) addr, 908c2ecf20Sopenharmony_ci (unsigned long) addr + size, phys_addr, prot)) { 918c2ecf20Sopenharmony_ci vunmap((void __force *) addr); 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return (void __iomem *) (offset + (char __iomem *)addr); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return __ioremap_uc(phys_addr); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_civoid __iomem * 1038c2ecf20Sopenharmony_ciioremap_uc(unsigned long phys_addr, unsigned long size) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (kern_mem_attribute(phys_addr, size) & EFI_MEMORY_WB) 1068c2ecf20Sopenharmony_ci return NULL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return __ioremap_uc(phys_addr); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap_uc); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_civoid 1138c2ecf20Sopenharmony_ciearly_iounmap (volatile void __iomem *addr, unsigned long size) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid 1188c2ecf20Sopenharmony_ciiounmap (volatile void __iomem *addr) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci if (REGION_NUMBER(addr) == RGN_GATE) 1218c2ecf20Sopenharmony_ci vunmap((void *) ((unsigned long) addr & PAGE_MASK)); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iounmap); 124