162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (C) Copyright 1995 1996 Linus Torvalds
762306a36Sopenharmony_ci * (C) Copyright 2001, 2002 Ralf Baechle
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <asm/addrspace.h>
1162306a36Sopenharmony_ci#include <asm/byteorder.h>
1262306a36Sopenharmony_ci#include <linux/ioport.h>
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci#include <linux/vmalloc.h>
1662306a36Sopenharmony_ci#include <linux/mm_types.h>
1762306a36Sopenharmony_ci#include <linux/io.h>
1862306a36Sopenharmony_ci#include <asm/cacheflush.h>
1962306a36Sopenharmony_ci#include <asm/tlbflush.h>
2062306a36Sopenharmony_ci#include <ioremap.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define IS_LOW512(addr) (!((phys_addr_t)(addr) & (phys_addr_t) ~0x1fffffffULL))
2362306a36Sopenharmony_ci#define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == CKSEG1)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int __ioremap_check_ram(unsigned long start_pfn, unsigned long nr_pages,
2662306a36Sopenharmony_ci			       void *arg)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	unsigned long i;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	for (i = 0; i < nr_pages; i++) {
3162306a36Sopenharmony_ci		if (pfn_valid(start_pfn + i) &&
3262306a36Sopenharmony_ci		    !PageReserved(pfn_to_page(start_pfn + i)))
3362306a36Sopenharmony_ci			return 1;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	return 0;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * ioremap_prot     -   map bus memory into CPU space
4162306a36Sopenharmony_ci * @phys_addr:    bus address of the memory
4262306a36Sopenharmony_ci * @size:      size of the resource to map
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * ioremap_prot gives the caller control over cache coherency attributes (CCA)
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_civoid __iomem *ioremap_prot(phys_addr_t phys_addr, unsigned long size,
4762306a36Sopenharmony_ci		unsigned long prot_val)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	unsigned long flags = prot_val & _CACHE_MASK;
5062306a36Sopenharmony_ci	unsigned long offset, pfn, last_pfn;
5162306a36Sopenharmony_ci	struct vm_struct *area;
5262306a36Sopenharmony_ci	phys_addr_t last_addr;
5362306a36Sopenharmony_ci	unsigned long vaddr;
5462306a36Sopenharmony_ci	void __iomem *cpu_addr;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	cpu_addr = plat_ioremap(phys_addr, size, flags);
5762306a36Sopenharmony_ci	if (cpu_addr)
5862306a36Sopenharmony_ci		return cpu_addr;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	phys_addr = fixup_bigphys_addr(phys_addr, size);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* Don't allow wraparound or zero size */
6362306a36Sopenharmony_ci	last_addr = phys_addr + size - 1;
6462306a36Sopenharmony_ci	if (!size || last_addr < phys_addr)
6562306a36Sopenharmony_ci		return NULL;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/*
6862306a36Sopenharmony_ci	 * Map uncached objects in the low 512mb of address space using KSEG1,
6962306a36Sopenharmony_ci	 * otherwise map using page tables.
7062306a36Sopenharmony_ci	 */
7162306a36Sopenharmony_ci	if (IS_LOW512(phys_addr) && IS_LOW512(last_addr) &&
7262306a36Sopenharmony_ci	    flags == _CACHE_UNCACHED)
7362306a36Sopenharmony_ci		return (void __iomem *) CKSEG1ADDR(phys_addr);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/*
7662306a36Sopenharmony_ci	 * Don't allow anybody to remap RAM that may be allocated by the page
7762306a36Sopenharmony_ci	 * allocator, since that could lead to races & data clobbering.
7862306a36Sopenharmony_ci	 */
7962306a36Sopenharmony_ci	pfn = PFN_DOWN(phys_addr);
8062306a36Sopenharmony_ci	last_pfn = PFN_DOWN(last_addr);
8162306a36Sopenharmony_ci	if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL,
8262306a36Sopenharmony_ci				  __ioremap_check_ram) == 1) {
8362306a36Sopenharmony_ci		WARN_ONCE(1, "ioremap on RAM at %pa - %pa\n",
8462306a36Sopenharmony_ci			  &phys_addr, &last_addr);
8562306a36Sopenharmony_ci		return NULL;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Mappings have to be page-aligned
9062306a36Sopenharmony_ci	 */
9162306a36Sopenharmony_ci	offset = phys_addr & ~PAGE_MASK;
9262306a36Sopenharmony_ci	phys_addr &= PAGE_MASK;
9362306a36Sopenharmony_ci	size = PAGE_ALIGN(last_addr + 1) - phys_addr;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * Ok, go for it..
9762306a36Sopenharmony_ci	 */
9862306a36Sopenharmony_ci	area = get_vm_area(size, VM_IOREMAP);
9962306a36Sopenharmony_ci	if (!area)
10062306a36Sopenharmony_ci		return NULL;
10162306a36Sopenharmony_ci	vaddr = (unsigned long)area->addr;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	flags |= _PAGE_GLOBAL | _PAGE_PRESENT | __READABLE | __WRITEABLE;
10462306a36Sopenharmony_ci	if (ioremap_page_range(vaddr, vaddr + size, phys_addr,
10562306a36Sopenharmony_ci			__pgprot(flags))) {
10662306a36Sopenharmony_ci		free_vm_area(area);
10762306a36Sopenharmony_ci		return NULL;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	return (void __iomem *)(vaddr + offset);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ciEXPORT_SYMBOL(ioremap_prot);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_civoid iounmap(const volatile void __iomem *addr)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (!plat_iounmap(addr) && !IS_KSEG1(addr))
11762306a36Sopenharmony_ci		vunmap((void *)((unsigned long)addr & PAGE_MASK));
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ciEXPORT_SYMBOL(iounmap);
120