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