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