162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Re-map IO memory to kernel address space so that we can access it. 462306a36Sopenharmony_ci * This is needed for high PCI addresses that aren't mapped in the 562306a36Sopenharmony_ci * 640k-1MB IO memory area on PC's 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) Copyright 1995 1996 Linus Torvalds 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/vmalloc.h> 1062306a36Sopenharmony_ci#include <linux/mm.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/ioremap.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_civoid __iomem *generic_ioremap_prot(phys_addr_t phys_addr, size_t size, 1662306a36Sopenharmony_ci pgprot_t prot) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci unsigned long offset, vaddr; 1962306a36Sopenharmony_ci phys_addr_t last_addr; 2062306a36Sopenharmony_ci struct vm_struct *area; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci /* An early platform driver might end up here */ 2362306a36Sopenharmony_ci if (WARN_ON_ONCE(!slab_is_available())) 2462306a36Sopenharmony_ci return NULL; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* Disallow wrap-around or zero size */ 2762306a36Sopenharmony_ci last_addr = phys_addr + size - 1; 2862306a36Sopenharmony_ci if (!size || last_addr < phys_addr) 2962306a36Sopenharmony_ci return NULL; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci /* Page-align mappings */ 3262306a36Sopenharmony_ci offset = phys_addr & (~PAGE_MASK); 3362306a36Sopenharmony_ci phys_addr -= offset; 3462306a36Sopenharmony_ci size = PAGE_ALIGN(size + offset); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci area = __get_vm_area_caller(size, VM_IOREMAP, IOREMAP_START, 3762306a36Sopenharmony_ci IOREMAP_END, __builtin_return_address(0)); 3862306a36Sopenharmony_ci if (!area) 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci vaddr = (unsigned long)area->addr; 4162306a36Sopenharmony_ci area->phys_addr = phys_addr; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) { 4462306a36Sopenharmony_ci free_vm_area(area); 4562306a36Sopenharmony_ci return NULL; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return (void __iomem *)(vaddr + offset); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#ifndef ioremap_prot 5262306a36Sopenharmony_civoid __iomem *ioremap_prot(phys_addr_t phys_addr, size_t size, 5362306a36Sopenharmony_ci unsigned long prot) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci return generic_ioremap_prot(phys_addr, size, __pgprot(prot)); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ciEXPORT_SYMBOL(ioremap_prot); 5862306a36Sopenharmony_ci#endif 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_civoid generic_iounmap(volatile void __iomem *addr) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci void *vaddr = (void *)((unsigned long)addr & PAGE_MASK); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (is_ioremap_addr(vaddr)) 6562306a36Sopenharmony_ci vunmap(vaddr); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#ifndef iounmap 6962306a36Sopenharmony_civoid iounmap(volatile void __iomem *addr) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci generic_iounmap(addr); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ciEXPORT_SYMBOL(iounmap); 7462306a36Sopenharmony_ci#endif 75