xref: /openbmc/linux/arch/mips/mm/ioremap.c (revision 4b4193256c8d3bc3a5397b5cd9494c2ad386317d)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * This file is subject to the terms and conditions of the GNU General Public
31da177e4SLinus Torvalds  * License.  See the file "COPYING" in the main directory of this archive
41da177e4SLinus Torvalds  * for more details.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * (C) Copyright 1995 1996 Linus Torvalds
71da177e4SLinus Torvalds  * (C) Copyright 2001, 2002 Ralf Baechle
81da177e4SLinus Torvalds  */
9d9ba5778SPaul Gortmaker #include <linux/export.h>
101da177e4SLinus Torvalds #include <asm/addrspace.h>
111da177e4SLinus Torvalds #include <asm/byteorder.h>
12523402faSPaul Burton #include <linux/ioport.h>
13e8edc6e0SAlexey Dobriyan #include <linux/sched.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/vmalloc.h>
16589ee628SIngo Molnar #include <linux/mm_types.h>
17*c2591eb5SChristoph Hellwig #include <linux/io.h>
185ce704f8SRalf Baechle #include <asm/cacheflush.h>
195ce704f8SRalf Baechle #include <asm/tlbflush.h>
20d257b8feSChristoph Hellwig #include <ioremap.h>
21d257b8feSChristoph Hellwig 
22d257b8feSChristoph Hellwig #define IS_LOW512(addr) (!((phys_addr_t)(addr) & (phys_addr_t) ~0x1fffffffULL))
23d257b8feSChristoph Hellwig #define IS_KSEG1(addr) (((unsigned long)(addr) & ~0x1fffffffUL) == CKSEG1)
245ce704f8SRalf Baechle 
__ioremap_check_ram(unsigned long start_pfn,unsigned long nr_pages,void * arg)25523402faSPaul Burton static int __ioremap_check_ram(unsigned long start_pfn, unsigned long nr_pages,
26523402faSPaul Burton 			       void *arg)
27523402faSPaul Burton {
28523402faSPaul Burton 	unsigned long i;
29523402faSPaul Burton 
30523402faSPaul Burton 	for (i = 0; i < nr_pages; i++) {
31523402faSPaul Burton 		if (pfn_valid(start_pfn + i) &&
32523402faSPaul Burton 		    !PageReserved(pfn_to_page(start_pfn + i)))
33523402faSPaul Burton 			return 1;
34523402faSPaul Burton 	}
35523402faSPaul Burton 
36523402faSPaul Burton 	return 0;
37523402faSPaul Burton }
38523402faSPaul Burton 
391da177e4SLinus Torvalds /*
40d257b8feSChristoph Hellwig  * ioremap_prot     -   map bus memory into CPU space
41d257b8feSChristoph Hellwig  * @phys_addr:    bus address of the memory
42d257b8feSChristoph Hellwig  * @size:      size of the resource to map
431da177e4SLinus Torvalds  *
44d257b8feSChristoph Hellwig  * ioremap_prot gives the caller control over cache coherency attributes (CCA)
451da177e4SLinus Torvalds  */
ioremap_prot(phys_addr_t phys_addr,unsigned long size,unsigned long prot_val)46d257b8feSChristoph Hellwig void __iomem *ioremap_prot(phys_addr_t phys_addr, unsigned long size,
47d257b8feSChristoph Hellwig 		unsigned long prot_val)
481da177e4SLinus Torvalds {
49d257b8feSChristoph Hellwig 	unsigned long flags = prot_val & _CACHE_MASK;
50523402faSPaul Burton 	unsigned long offset, pfn, last_pfn;
511da177e4SLinus Torvalds 	struct vm_struct *area;
5215d45cceSRalf Baechle 	phys_addr_t last_addr;
53*c2591eb5SChristoph Hellwig 	unsigned long vaddr;
54d257b8feSChristoph Hellwig 	void __iomem *cpu_addr;
55d257b8feSChristoph Hellwig 
56d257b8feSChristoph Hellwig 	cpu_addr = plat_ioremap(phys_addr, size, flags);
57d257b8feSChristoph Hellwig 	if (cpu_addr)
58d257b8feSChristoph Hellwig 		return cpu_addr;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	phys_addr = fixup_bigphys_addr(phys_addr, size);
611da177e4SLinus Torvalds 
621da177e4SLinus Torvalds 	/* Don't allow wraparound or zero size */
631da177e4SLinus Torvalds 	last_addr = phys_addr + size - 1;
641da177e4SLinus Torvalds 	if (!size || last_addr < phys_addr)
651da177e4SLinus Torvalds 		return NULL;
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds 	/*
681da177e4SLinus Torvalds 	 * Map uncached objects in the low 512mb of address space using KSEG1,
691da177e4SLinus Torvalds 	 * otherwise map using page tables.
701da177e4SLinus Torvalds 	 */
711da177e4SLinus Torvalds 	if (IS_LOW512(phys_addr) && IS_LOW512(last_addr) &&
721da177e4SLinus Torvalds 	    flags == _CACHE_UNCACHED)
73c3455b0eSMaciej W. Rozycki 		return (void __iomem *) CKSEG1ADDR(phys_addr);
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds 	/*
76523402faSPaul Burton 	 * Don't allow anybody to remap RAM that may be allocated by the page
77523402faSPaul Burton 	 * allocator, since that could lead to races & data clobbering.
781da177e4SLinus Torvalds 	 */
79523402faSPaul Burton 	pfn = PFN_DOWN(phys_addr);
80523402faSPaul Burton 	last_pfn = PFN_DOWN(last_addr);
81523402faSPaul Burton 	if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL,
82523402faSPaul Burton 				  __ioremap_check_ram) == 1) {
83523402faSPaul Burton 		WARN_ONCE(1, "ioremap on RAM at %pa - %pa\n",
84523402faSPaul Burton 			  &phys_addr, &last_addr);
851da177e4SLinus Torvalds 		return NULL;
861da177e4SLinus Torvalds 	}
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds 	/*
891da177e4SLinus Torvalds 	 * Mappings have to be page-aligned
901da177e4SLinus Torvalds 	 */
911da177e4SLinus Torvalds 	offset = phys_addr & ~PAGE_MASK;
921da177e4SLinus Torvalds 	phys_addr &= PAGE_MASK;
931da177e4SLinus Torvalds 	size = PAGE_ALIGN(last_addr + 1) - phys_addr;
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds 	/*
961da177e4SLinus Torvalds 	 * Ok, go for it..
971da177e4SLinus Torvalds 	 */
981da177e4SLinus Torvalds 	area = get_vm_area(size, VM_IOREMAP);
991da177e4SLinus Torvalds 	if (!area)
1001da177e4SLinus Torvalds 		return NULL;
101*c2591eb5SChristoph Hellwig 	vaddr = (unsigned long)area->addr;
102*c2591eb5SChristoph Hellwig 
103*c2591eb5SChristoph Hellwig 	flags |= _PAGE_GLOBAL | _PAGE_PRESENT | __READABLE | __WRITEABLE;
104*c2591eb5SChristoph Hellwig 	if (ioremap_page_range(vaddr, vaddr + size, phys_addr,
105*c2591eb5SChristoph Hellwig 			__pgprot(flags))) {
106*c2591eb5SChristoph Hellwig 		free_vm_area(area);
1071da177e4SLinus Torvalds 		return NULL;
1081da177e4SLinus Torvalds 	}
1091da177e4SLinus Torvalds 
110*c2591eb5SChristoph Hellwig 	return (void __iomem *)(vaddr + offset);
1111da177e4SLinus Torvalds }
112d257b8feSChristoph Hellwig EXPORT_SYMBOL(ioremap_prot);
1131da177e4SLinus Torvalds 
iounmap(const volatile void __iomem * addr)114d257b8feSChristoph Hellwig void iounmap(const volatile void __iomem *addr)
1151da177e4SLinus Torvalds {
116*c2591eb5SChristoph Hellwig 	if (!plat_iounmap(addr) && !IS_KSEG1(addr))
117*c2591eb5SChristoph Hellwig 		vunmap((void *)((unsigned long)addr & PAGE_MASK));
1181da177e4SLinus Torvalds }
119d257b8feSChristoph Hellwig EXPORT_SYMBOL(iounmap);
120