18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * arch/parisc/mm/ioremap.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright 1995 1996 Linus Torvalds
68c2ecf20Sopenharmony_ci * (C) Copyright 2001-2019 Helge Deller <deller@gmx.de>
78c2ecf20Sopenharmony_ci * (C) Copyright 2005 Kyle McMartin <kyle@parisc-linux.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
118c2ecf20Sopenharmony_ci#include <linux/errno.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/mm.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * Generic mapping function (not visible outside):
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * Remap an arbitrary physical address space into the kernel virtual
228c2ecf20Sopenharmony_ci * address space.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * NOTE! We need to allow non-page-aligned mappings too: we will obviously
258c2ecf20Sopenharmony_ci * have to convert them into an offset in a page-aligned mapping, but the
268c2ecf20Sopenharmony_ci * caller shouldn't need to know that small detail.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_civoid __iomem *ioremap(unsigned long phys_addr, unsigned long size)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	void __iomem *addr;
318c2ecf20Sopenharmony_ci	struct vm_struct *area;
328c2ecf20Sopenharmony_ci	unsigned long offset, last_addr;
338c2ecf20Sopenharmony_ci	pgprot_t pgprot;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#ifdef CONFIG_EISA
368c2ecf20Sopenharmony_ci	unsigned long end = phys_addr + size - 1;
378c2ecf20Sopenharmony_ci	/* Support EISA addresses */
388c2ecf20Sopenharmony_ci	if ((phys_addr >= 0x00080000 && end < 0x000fffff) ||
398c2ecf20Sopenharmony_ci	    (phys_addr >= 0x00500000 && end < 0x03bfffff))
408c2ecf20Sopenharmony_ci		phys_addr |= F_EXTEND(0xfc000000);
418c2ecf20Sopenharmony_ci#endif
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* Don't allow wraparound or zero size */
448c2ecf20Sopenharmony_ci	last_addr = phys_addr + size - 1;
458c2ecf20Sopenharmony_ci	if (!size || last_addr < phys_addr)
468c2ecf20Sopenharmony_ci		return NULL;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	/*
498c2ecf20Sopenharmony_ci	 * Don't allow anybody to remap normal RAM that we're using..
508c2ecf20Sopenharmony_ci	 */
518c2ecf20Sopenharmony_ci	if (phys_addr < virt_to_phys(high_memory)) {
528c2ecf20Sopenharmony_ci		char *t_addr, *t_end;
538c2ecf20Sopenharmony_ci		struct page *page;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		t_addr = __va(phys_addr);
568c2ecf20Sopenharmony_ci		t_end = t_addr + (size - 1);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		for (page = virt_to_page(t_addr);
598c2ecf20Sopenharmony_ci		     page <= virt_to_page(t_end); page++) {
608c2ecf20Sopenharmony_ci			if(!PageReserved(page))
618c2ecf20Sopenharmony_ci				return NULL;
628c2ecf20Sopenharmony_ci		}
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY |
668c2ecf20Sopenharmony_ci			  _PAGE_ACCESSED | _PAGE_NO_CACHE);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/*
698c2ecf20Sopenharmony_ci	 * Mappings have to be page-aligned
708c2ecf20Sopenharmony_ci	 */
718c2ecf20Sopenharmony_ci	offset = phys_addr & ~PAGE_MASK;
728c2ecf20Sopenharmony_ci	phys_addr &= PAGE_MASK;
738c2ecf20Sopenharmony_ci	size = PAGE_ALIGN(last_addr + 1) - phys_addr;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/*
768c2ecf20Sopenharmony_ci	 * Ok, go for it..
778c2ecf20Sopenharmony_ci	 */
788c2ecf20Sopenharmony_ci	area = get_vm_area(size, VM_IOREMAP);
798c2ecf20Sopenharmony_ci	if (!area)
808c2ecf20Sopenharmony_ci		return NULL;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	addr = (void __iomem *) area->addr;
838c2ecf20Sopenharmony_ci	if (ioremap_page_range((unsigned long)addr, (unsigned long)addr + size,
848c2ecf20Sopenharmony_ci			       phys_addr, pgprot)) {
858c2ecf20Sopenharmony_ci		vunmap(addr);
868c2ecf20Sopenharmony_ci		return NULL;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return (void __iomem *) (offset + (char __iomem *)addr);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ioremap);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_civoid iounmap(const volatile void __iomem *io_addr)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)io_addr & PAGE_MASK;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (is_vmalloc_addr((void *)addr))
988c2ecf20Sopenharmony_ci		vunmap((void *)addr);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iounmap);
101