xref: /linux/arch/x86/mm/ioremap.c (revision 8a0a5da6d9cbf1b142115ff6e6b253a82633c3d9)
1e64c8aa0SThomas Gleixner /*
2e64c8aa0SThomas Gleixner  * Re-map IO memory to kernel address space so that we can access it.
3e64c8aa0SThomas Gleixner  * This is needed for high PCI addresses that aren't mapped in the
4e64c8aa0SThomas Gleixner  * 640k-1MB IO memory area on PC's
5e64c8aa0SThomas Gleixner  *
6e64c8aa0SThomas Gleixner  * (C) Copyright 1995 1996 Linus Torvalds
7e64c8aa0SThomas Gleixner  */
8e64c8aa0SThomas Gleixner 
9e64c8aa0SThomas Gleixner #include <linux/bootmem.h>
10e64c8aa0SThomas Gleixner #include <linux/init.h>
11e64c8aa0SThomas Gleixner #include <linux/io.h>
12e64c8aa0SThomas Gleixner #include <linux/module.h>
13e64c8aa0SThomas Gleixner #include <linux/slab.h>
14e64c8aa0SThomas Gleixner #include <linux/vmalloc.h>
15d61fc448SPekka Paalanen #include <linux/mmiotrace.h>
16e64c8aa0SThomas Gleixner 
17e64c8aa0SThomas Gleixner #include <asm/cacheflush.h>
18e64c8aa0SThomas Gleixner #include <asm/e820.h>
19e64c8aa0SThomas Gleixner #include <asm/fixmap.h>
20e64c8aa0SThomas Gleixner #include <asm/pgtable.h>
21e64c8aa0SThomas Gleixner #include <asm/tlbflush.h>
22f6df72e7SJeremy Fitzhardinge #include <asm/pgalloc.h>
23d7677d40Svenkatesh.pallipadi@intel.com #include <asm/pat.h>
24e64c8aa0SThomas Gleixner 
2578c86e5eSJeremy Fitzhardinge #include "physaddr.h"
26e64c8aa0SThomas Gleixner 
27e64c8aa0SThomas Gleixner /*
28e64c8aa0SThomas Gleixner  * Fix up the linear direct mapping of the kernel to avoid cache attribute
29e64c8aa0SThomas Gleixner  * conflicts.
30e64c8aa0SThomas Gleixner  */
313a96ce8cSvenkatesh.pallipadi@intel.com int ioremap_change_attr(unsigned long vaddr, unsigned long size,
32b14097bdSJuergen Gross 			enum page_cache_mode pcm)
33e64c8aa0SThomas Gleixner {
34d806e5eeSThomas Gleixner 	unsigned long nrpages = size >> PAGE_SHIFT;
3593809be8SHarvey Harrison 	int err;
36e64c8aa0SThomas Gleixner 
37b14097bdSJuergen Gross 	switch (pcm) {
38b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_UC:
39d806e5eeSThomas Gleixner 	default:
401219333dSvenkatesh.pallipadi@intel.com 		err = _set_memory_uc(vaddr, nrpages);
41d806e5eeSThomas Gleixner 		break;
42b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_WC:
43b310f381Svenkatesh.pallipadi@intel.com 		err = _set_memory_wc(vaddr, nrpages);
44b310f381Svenkatesh.pallipadi@intel.com 		break;
45623dffb2SToshi Kani 	case _PAGE_CACHE_MODE_WT:
46623dffb2SToshi Kani 		err = _set_memory_wt(vaddr, nrpages);
47623dffb2SToshi Kani 		break;
48b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_WB:
491219333dSvenkatesh.pallipadi@intel.com 		err = _set_memory_wb(vaddr, nrpages);
50d806e5eeSThomas Gleixner 		break;
51d806e5eeSThomas Gleixner 	}
52e64c8aa0SThomas Gleixner 
53e64c8aa0SThomas Gleixner 	return err;
54e64c8aa0SThomas Gleixner }
55e64c8aa0SThomas Gleixner 
56c81c8a1eSRoland Dreier static int __ioremap_check_ram(unsigned long start_pfn, unsigned long nr_pages,
57c81c8a1eSRoland Dreier 			       void *arg)
58c81c8a1eSRoland Dreier {
59c81c8a1eSRoland Dreier 	unsigned long i;
60c81c8a1eSRoland Dreier 
61c81c8a1eSRoland Dreier 	for (i = 0; i < nr_pages; ++i)
62c81c8a1eSRoland Dreier 		if (pfn_valid(start_pfn + i) &&
63c81c8a1eSRoland Dreier 		    !PageReserved(pfn_to_page(start_pfn + i)))
64c81c8a1eSRoland Dreier 			return 1;
65c81c8a1eSRoland Dreier 
66c81c8a1eSRoland Dreier 	return 0;
67c81c8a1eSRoland Dreier }
68c81c8a1eSRoland Dreier 
69e64c8aa0SThomas Gleixner /*
70e64c8aa0SThomas Gleixner  * Remap an arbitrary physical address space into the kernel virtual
715d72b4fbSToshi Kani  * address space. It transparently creates kernel huge I/O mapping when
725d72b4fbSToshi Kani  * the physical address is aligned by a huge page size (1GB or 2MB) and
735d72b4fbSToshi Kani  * the requested size is at least the huge page size.
745d72b4fbSToshi Kani  *
755d72b4fbSToshi Kani  * NOTE: MTRRs can override PAT memory types with a 4KB granularity.
765d72b4fbSToshi Kani  * Therefore, the mapping code falls back to use a smaller page toward 4KB
775d72b4fbSToshi Kani  * when a mapping range is covered by non-WB type of MTRRs.
78e64c8aa0SThomas Gleixner  *
79e64c8aa0SThomas Gleixner  * NOTE! We need to allow non-page-aligned mappings too: we will obviously
80e64c8aa0SThomas Gleixner  * have to convert them into an offset in a page-aligned mapping, but the
81e64c8aa0SThomas Gleixner  * caller shouldn't need to know that small detail.
82e64c8aa0SThomas Gleixner  */
8323016969SChristoph Lameter static void __iomem *__ioremap_caller(resource_size_t phys_addr,
84b14097bdSJuergen Gross 		unsigned long size, enum page_cache_mode pcm, void *caller)
85e64c8aa0SThomas Gleixner {
86ffa71f33SKenji Kaneshige 	unsigned long offset, vaddr;
87ffa71f33SKenji Kaneshige 	resource_size_t pfn, last_pfn, last_addr;
8887e547feSPekka Paalanen 	const resource_size_t unaligned_phys_addr = phys_addr;
8987e547feSPekka Paalanen 	const unsigned long unaligned_size = size;
90e64c8aa0SThomas Gleixner 	struct vm_struct *area;
91b14097bdSJuergen Gross 	enum page_cache_mode new_pcm;
92d806e5eeSThomas Gleixner 	pgprot_t prot;
93dee7cbb2SVenki Pallipadi 	int retval;
94d61fc448SPekka Paalanen 	void __iomem *ret_addr;
95e64c8aa0SThomas Gleixner 
96e64c8aa0SThomas Gleixner 	/* Don't allow wraparound or zero size */
97e64c8aa0SThomas Gleixner 	last_addr = phys_addr + size - 1;
98e64c8aa0SThomas Gleixner 	if (!size || last_addr < phys_addr)
99e64c8aa0SThomas Gleixner 		return NULL;
100e64c8aa0SThomas Gleixner 
101e3100c82SThomas Gleixner 	if (!phys_addr_valid(phys_addr)) {
1026997ab49Svenkatesh.pallipadi@intel.com 		printk(KERN_WARNING "ioremap: invalid physical address %llx\n",
1034c8337acSRandy Dunlap 		       (unsigned long long)phys_addr);
104e3100c82SThomas Gleixner 		WARN_ON_ONCE(1);
105e3100c82SThomas Gleixner 		return NULL;
106e3100c82SThomas Gleixner 	}
107e3100c82SThomas Gleixner 
108e64c8aa0SThomas Gleixner 	/*
109e64c8aa0SThomas Gleixner 	 * Don't remap the low PCI/ISA area, it's always mapped..
110e64c8aa0SThomas Gleixner 	 */
111bcc643dcSAndreas Herrmann 	if (is_ISA_range(phys_addr, last_addr))
112e64c8aa0SThomas Gleixner 		return (__force void __iomem *)phys_to_virt(phys_addr);
113e64c8aa0SThomas Gleixner 
114e64c8aa0SThomas Gleixner 	/*
115e64c8aa0SThomas Gleixner 	 * Don't allow anybody to remap normal RAM that we're using..
116e64c8aa0SThomas Gleixner 	 */
117c81c8a1eSRoland Dreier 	pfn      = phys_addr >> PAGE_SHIFT;
118ffa71f33SKenji Kaneshige 	last_pfn = last_addr >> PAGE_SHIFT;
119c81c8a1eSRoland Dreier 	if (walk_system_ram_range(pfn, last_pfn - pfn + 1, NULL,
1201c9cf9b2SToshi Kani 					  __ioremap_check_ram) == 1) {
121*8a0a5da6SThomas Gleixner 		WARN_ONCE(1, "ioremap on RAM at %pa - %pa\n",
122*8a0a5da6SThomas Gleixner 			  &phys_addr, &last_addr);
123e64c8aa0SThomas Gleixner 		return NULL;
124906e36c5SMike Travis 	}
1259a58eebeSToshi Kani 
126d7677d40Svenkatesh.pallipadi@intel.com 	/*
127d7677d40Svenkatesh.pallipadi@intel.com 	 * Mappings have to be page-aligned
128d7677d40Svenkatesh.pallipadi@intel.com 	 */
129d7677d40Svenkatesh.pallipadi@intel.com 	offset = phys_addr & ~PAGE_MASK;
130ffa71f33SKenji Kaneshige 	phys_addr &= PHYSICAL_PAGE_MASK;
131d7677d40Svenkatesh.pallipadi@intel.com 	size = PAGE_ALIGN(last_addr+1) - phys_addr;
132d7677d40Svenkatesh.pallipadi@intel.com 
133e213e877SAndi Kleen 	retval = reserve_memtype(phys_addr, (u64)phys_addr + size,
134e00c8cc9SJuergen Gross 						pcm, &new_pcm);
135dee7cbb2SVenki Pallipadi 	if (retval) {
136279e669bSVenkatesh Pallipadi 		printk(KERN_ERR "ioremap reserve_memtype failed %d\n", retval);
137dee7cbb2SVenki Pallipadi 		return NULL;
138dee7cbb2SVenki Pallipadi 	}
139dee7cbb2SVenki Pallipadi 
140b14097bdSJuergen Gross 	if (pcm != new_pcm) {
141b14097bdSJuergen Gross 		if (!is_new_memtype_allowed(phys_addr, size, pcm, new_pcm)) {
142279e669bSVenkatesh Pallipadi 			printk(KERN_ERR
143b14097bdSJuergen Gross 		"ioremap error for 0x%llx-0x%llx, requested 0x%x, got 0x%x\n",
1444c8337acSRandy Dunlap 				(unsigned long long)phys_addr,
1454c8337acSRandy Dunlap 				(unsigned long long)(phys_addr + size),
146b14097bdSJuergen Gross 				pcm, new_pcm);
147de2a47cfSXiaotian Feng 			goto err_free_memtype;
148d7677d40Svenkatesh.pallipadi@intel.com 		}
149b14097bdSJuergen Gross 		pcm = new_pcm;
150d7677d40Svenkatesh.pallipadi@intel.com 	}
151d7677d40Svenkatesh.pallipadi@intel.com 
152be43d728SJeremy Fitzhardinge 	prot = PAGE_KERNEL_IO;
153b14097bdSJuergen Gross 	switch (pcm) {
154b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_UC:
155b14097bdSJuergen Gross 	default:
156b14097bdSJuergen Gross 		prot = __pgprot(pgprot_val(prot) |
157b14097bdSJuergen Gross 				cachemode2protval(_PAGE_CACHE_MODE_UC));
158b14097bdSJuergen Gross 		break;
159b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_UC_MINUS:
160b14097bdSJuergen Gross 		prot = __pgprot(pgprot_val(prot) |
161b14097bdSJuergen Gross 				cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS));
162b14097bdSJuergen Gross 		break;
163b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_WC:
164b14097bdSJuergen Gross 		prot = __pgprot(pgprot_val(prot) |
165b14097bdSJuergen Gross 				cachemode2protval(_PAGE_CACHE_MODE_WC));
166b14097bdSJuergen Gross 		break;
167d838270eSToshi Kani 	case _PAGE_CACHE_MODE_WT:
168d838270eSToshi Kani 		prot = __pgprot(pgprot_val(prot) |
169d838270eSToshi Kani 				cachemode2protval(_PAGE_CACHE_MODE_WT));
170d838270eSToshi Kani 		break;
171b14097bdSJuergen Gross 	case _PAGE_CACHE_MODE_WB:
172d806e5eeSThomas Gleixner 		break;
173d806e5eeSThomas Gleixner 	}
174e64c8aa0SThomas Gleixner 
175e64c8aa0SThomas Gleixner 	/*
176e64c8aa0SThomas Gleixner 	 * Ok, go for it..
177e64c8aa0SThomas Gleixner 	 */
17823016969SChristoph Lameter 	area = get_vm_area_caller(size, VM_IOREMAP, caller);
179e64c8aa0SThomas Gleixner 	if (!area)
180de2a47cfSXiaotian Feng 		goto err_free_memtype;
181e64c8aa0SThomas Gleixner 	area->phys_addr = phys_addr;
182e66aadbeSThomas Gleixner 	vaddr = (unsigned long) area->addr;
18343a432b1SSuresh Siddha 
184b14097bdSJuergen Gross 	if (kernel_map_sync_memtype(phys_addr, size, pcm))
185de2a47cfSXiaotian Feng 		goto err_free_area;
186e64c8aa0SThomas Gleixner 
187de2a47cfSXiaotian Feng 	if (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot))
188de2a47cfSXiaotian Feng 		goto err_free_area;
189e64c8aa0SThomas Gleixner 
190d61fc448SPekka Paalanen 	ret_addr = (void __iomem *) (vaddr + offset);
19187e547feSPekka Paalanen 	mmiotrace_ioremap(unaligned_phys_addr, unaligned_size, ret_addr);
192d61fc448SPekka Paalanen 
193c7a7b814STim Gardner 	/*
194c7a7b814STim Gardner 	 * Check if the request spans more than any BAR in the iomem resource
195c7a7b814STim Gardner 	 * tree.
196c7a7b814STim Gardner 	 */
197c7a7b814STim Gardner 	WARN_ONCE(iomem_map_sanity_check(unaligned_phys_addr, unaligned_size),
198c7a7b814STim Gardner 		  KERN_INFO "Info: mapping multiple BARs. Your kernel is fine.");
199c7a7b814STim Gardner 
200d61fc448SPekka Paalanen 	return ret_addr;
201de2a47cfSXiaotian Feng err_free_area:
202de2a47cfSXiaotian Feng 	free_vm_area(area);
203de2a47cfSXiaotian Feng err_free_memtype:
204de2a47cfSXiaotian Feng 	free_memtype(phys_addr, phys_addr + size);
205de2a47cfSXiaotian Feng 	return NULL;
206e64c8aa0SThomas Gleixner }
207e64c8aa0SThomas Gleixner 
208e64c8aa0SThomas Gleixner /**
209e64c8aa0SThomas Gleixner  * ioremap_nocache     -   map bus memory into CPU space
2109efc31b8SWanpeng Li  * @phys_addr:    bus address of the memory
211e64c8aa0SThomas Gleixner  * @size:      size of the resource to map
212e64c8aa0SThomas Gleixner  *
213e64c8aa0SThomas Gleixner  * ioremap_nocache performs a platform specific sequence of operations to
214e64c8aa0SThomas Gleixner  * make bus memory CPU accessible via the readb/readw/readl/writeb/
215e64c8aa0SThomas Gleixner  * writew/writel functions and the other mmio helpers. The returned
216e64c8aa0SThomas Gleixner  * address is not guaranteed to be usable directly as a virtual
217e64c8aa0SThomas Gleixner  * address.
218e64c8aa0SThomas Gleixner  *
219e64c8aa0SThomas Gleixner  * This version of ioremap ensures that the memory is marked uncachable
220e64c8aa0SThomas Gleixner  * on the CPU as well as honouring existing caching rules from things like
221e64c8aa0SThomas Gleixner  * the PCI bus. Note that there are other caches and buffers on many
222e64c8aa0SThomas Gleixner  * busses. In particular driver authors should read up on PCI writes
223e64c8aa0SThomas Gleixner  *
224e64c8aa0SThomas Gleixner  * It's useful if some control registers are in such an area and
225e64c8aa0SThomas Gleixner  * write combining or read caching is not desirable:
226e64c8aa0SThomas Gleixner  *
227e64c8aa0SThomas Gleixner  * Must be freed with iounmap.
228e64c8aa0SThomas Gleixner  */
229b9e76a00SLinus Torvalds void __iomem *ioremap_nocache(resource_size_t phys_addr, unsigned long size)
230e64c8aa0SThomas Gleixner {
231de33c442SSuresh Siddha 	/*
232de33c442SSuresh Siddha 	 * Ideally, this should be:
233cb32edf6SLuis R. Rodriguez 	 *	pat_enabled() ? _PAGE_CACHE_MODE_UC : _PAGE_CACHE_MODE_UC_MINUS;
234de33c442SSuresh Siddha 	 *
235de33c442SSuresh Siddha 	 * Till we fix all X drivers to use ioremap_wc(), we will use
236e4b6be33SLuis R. Rodriguez 	 * UC MINUS. Drivers that are certain they need or can already
237e4b6be33SLuis R. Rodriguez 	 * be converted over to strong UC can use ioremap_uc().
238de33c442SSuresh Siddha 	 */
239b14097bdSJuergen Gross 	enum page_cache_mode pcm = _PAGE_CACHE_MODE_UC_MINUS;
240de33c442SSuresh Siddha 
241b14097bdSJuergen Gross 	return __ioremap_caller(phys_addr, size, pcm,
24223016969SChristoph Lameter 				__builtin_return_address(0));
243e64c8aa0SThomas Gleixner }
244e64c8aa0SThomas Gleixner EXPORT_SYMBOL(ioremap_nocache);
245e64c8aa0SThomas Gleixner 
246b310f381Svenkatesh.pallipadi@intel.com /**
247e4b6be33SLuis R. Rodriguez  * ioremap_uc     -   map bus memory into CPU space as strongly uncachable
248e4b6be33SLuis R. Rodriguez  * @phys_addr:    bus address of the memory
249e4b6be33SLuis R. Rodriguez  * @size:      size of the resource to map
250e4b6be33SLuis R. Rodriguez  *
251e4b6be33SLuis R. Rodriguez  * ioremap_uc performs a platform specific sequence of operations to
252e4b6be33SLuis R. Rodriguez  * make bus memory CPU accessible via the readb/readw/readl/writeb/
253e4b6be33SLuis R. Rodriguez  * writew/writel functions and the other mmio helpers. The returned
254e4b6be33SLuis R. Rodriguez  * address is not guaranteed to be usable directly as a virtual
255e4b6be33SLuis R. Rodriguez  * address.
256e4b6be33SLuis R. Rodriguez  *
257e4b6be33SLuis R. Rodriguez  * This version of ioremap ensures that the memory is marked with a strong
258e4b6be33SLuis R. Rodriguez  * preference as completely uncachable on the CPU when possible. For non-PAT
259e4b6be33SLuis R. Rodriguez  * systems this ends up setting page-attribute flags PCD=1, PWT=1. For PAT
260e4b6be33SLuis R. Rodriguez  * systems this will set the PAT entry for the pages as strong UC.  This call
261e4b6be33SLuis R. Rodriguez  * will honor existing caching rules from things like the PCI bus. Note that
262e4b6be33SLuis R. Rodriguez  * there are other caches and buffers on many busses. In particular driver
263e4b6be33SLuis R. Rodriguez  * authors should read up on PCI writes.
264e4b6be33SLuis R. Rodriguez  *
265e4b6be33SLuis R. Rodriguez  * It's useful if some control registers are in such an area and
266e4b6be33SLuis R. Rodriguez  * write combining or read caching is not desirable:
267e4b6be33SLuis R. Rodriguez  *
268e4b6be33SLuis R. Rodriguez  * Must be freed with iounmap.
269e4b6be33SLuis R. Rodriguez  */
270e4b6be33SLuis R. Rodriguez void __iomem *ioremap_uc(resource_size_t phys_addr, unsigned long size)
271e4b6be33SLuis R. Rodriguez {
272e4b6be33SLuis R. Rodriguez 	enum page_cache_mode pcm = _PAGE_CACHE_MODE_UC;
273e4b6be33SLuis R. Rodriguez 
274e4b6be33SLuis R. Rodriguez 	return __ioremap_caller(phys_addr, size, pcm,
275e4b6be33SLuis R. Rodriguez 				__builtin_return_address(0));
276e4b6be33SLuis R. Rodriguez }
277e4b6be33SLuis R. Rodriguez EXPORT_SYMBOL_GPL(ioremap_uc);
278e4b6be33SLuis R. Rodriguez 
279e4b6be33SLuis R. Rodriguez /**
280b310f381Svenkatesh.pallipadi@intel.com  * ioremap_wc	-	map memory into CPU space write combined
2819efc31b8SWanpeng Li  * @phys_addr:	bus address of the memory
282b310f381Svenkatesh.pallipadi@intel.com  * @size:	size of the resource to map
283b310f381Svenkatesh.pallipadi@intel.com  *
284b310f381Svenkatesh.pallipadi@intel.com  * This version of ioremap ensures that the memory is marked write combining.
285b310f381Svenkatesh.pallipadi@intel.com  * Write combining allows faster writes to some hardware devices.
286b310f381Svenkatesh.pallipadi@intel.com  *
287b310f381Svenkatesh.pallipadi@intel.com  * Must be freed with iounmap.
288b310f381Svenkatesh.pallipadi@intel.com  */
289d639bab8Svenkatesh.pallipadi@intel.com void __iomem *ioremap_wc(resource_size_t phys_addr, unsigned long size)
290b310f381Svenkatesh.pallipadi@intel.com {
291b14097bdSJuergen Gross 	return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WC,
29223016969SChristoph Lameter 					__builtin_return_address(0));
293b310f381Svenkatesh.pallipadi@intel.com }
294b310f381Svenkatesh.pallipadi@intel.com EXPORT_SYMBOL(ioremap_wc);
295b310f381Svenkatesh.pallipadi@intel.com 
296d838270eSToshi Kani /**
297d838270eSToshi Kani  * ioremap_wt	-	map memory into CPU space write through
298d838270eSToshi Kani  * @phys_addr:	bus address of the memory
299d838270eSToshi Kani  * @size:	size of the resource to map
300d838270eSToshi Kani  *
301d838270eSToshi Kani  * This version of ioremap ensures that the memory is marked write through.
302d838270eSToshi Kani  * Write through stores data into memory while keeping the cache up-to-date.
303d838270eSToshi Kani  *
304d838270eSToshi Kani  * Must be freed with iounmap.
305d838270eSToshi Kani  */
306d838270eSToshi Kani void __iomem *ioremap_wt(resource_size_t phys_addr, unsigned long size)
307d838270eSToshi Kani {
308d838270eSToshi Kani 	return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WT,
309d838270eSToshi Kani 					__builtin_return_address(0));
310d838270eSToshi Kani }
311d838270eSToshi Kani EXPORT_SYMBOL(ioremap_wt);
312d838270eSToshi Kani 
313b9e76a00SLinus Torvalds void __iomem *ioremap_cache(resource_size_t phys_addr, unsigned long size)
3145f868152SThomas Gleixner {
315b14097bdSJuergen Gross 	return __ioremap_caller(phys_addr, size, _PAGE_CACHE_MODE_WB,
31623016969SChristoph Lameter 				__builtin_return_address(0));
3175f868152SThomas Gleixner }
3185f868152SThomas Gleixner EXPORT_SYMBOL(ioremap_cache);
3195f868152SThomas Gleixner 
32028b2ee20SRik van Riel void __iomem *ioremap_prot(resource_size_t phys_addr, unsigned long size,
32128b2ee20SRik van Riel 				unsigned long prot_val)
32228b2ee20SRik van Riel {
323b14097bdSJuergen Gross 	return __ioremap_caller(phys_addr, size,
324b14097bdSJuergen Gross 				pgprot2cachemode(__pgprot(prot_val)),
32528b2ee20SRik van Riel 				__builtin_return_address(0));
32628b2ee20SRik van Riel }
32728b2ee20SRik van Riel EXPORT_SYMBOL(ioremap_prot);
32828b2ee20SRik van Riel 
329e64c8aa0SThomas Gleixner /**
330e64c8aa0SThomas Gleixner  * iounmap - Free a IO remapping
331e64c8aa0SThomas Gleixner  * @addr: virtual address from ioremap_*
332e64c8aa0SThomas Gleixner  *
333e64c8aa0SThomas Gleixner  * Caller must ensure there is only one unmapping for the same pointer.
334e64c8aa0SThomas Gleixner  */
335e64c8aa0SThomas Gleixner void iounmap(volatile void __iomem *addr)
336e64c8aa0SThomas Gleixner {
337e64c8aa0SThomas Gleixner 	struct vm_struct *p, *o;
338e64c8aa0SThomas Gleixner 
339e64c8aa0SThomas Gleixner 	if ((void __force *)addr <= high_memory)
340e64c8aa0SThomas Gleixner 		return;
341e64c8aa0SThomas Gleixner 
342e64c8aa0SThomas Gleixner 	/*
343e64c8aa0SThomas Gleixner 	 * __ioremap special-cases the PCI/ISA range by not instantiating a
344e64c8aa0SThomas Gleixner 	 * vm_area and by simply returning an address into the kernel mapping
345e64c8aa0SThomas Gleixner 	 * of ISA space.   So handle that here.
346e64c8aa0SThomas Gleixner 	 */
3476e92a5a6SThomas Gleixner 	if ((void __force *)addr >= phys_to_virt(ISA_START_ADDRESS) &&
3486e92a5a6SThomas Gleixner 	    (void __force *)addr < phys_to_virt(ISA_END_ADDRESS))
349e64c8aa0SThomas Gleixner 		return;
350e64c8aa0SThomas Gleixner 
351e64c8aa0SThomas Gleixner 	addr = (volatile void __iomem *)
352e64c8aa0SThomas Gleixner 		(PAGE_MASK & (unsigned long __force)addr);
353e64c8aa0SThomas Gleixner 
354d61fc448SPekka Paalanen 	mmiotrace_iounmap(addr);
355d61fc448SPekka Paalanen 
356e64c8aa0SThomas Gleixner 	/* Use the vm area unlocked, assuming the caller
357e64c8aa0SThomas Gleixner 	   ensures there isn't another iounmap for the same address
358e64c8aa0SThomas Gleixner 	   in parallel. Reuse of the virtual address is prevented by
359e64c8aa0SThomas Gleixner 	   leaving it in the global lists until we're done with it.
360e64c8aa0SThomas Gleixner 	   cpa takes care of the direct mappings. */
361ef932473SJoonsoo Kim 	p = find_vm_area((void __force *)addr);
362e64c8aa0SThomas Gleixner 
363e64c8aa0SThomas Gleixner 	if (!p) {
364e64c8aa0SThomas Gleixner 		printk(KERN_ERR "iounmap: bad address %p\n", addr);
365e64c8aa0SThomas Gleixner 		dump_stack();
366e64c8aa0SThomas Gleixner 		return;
367e64c8aa0SThomas Gleixner 	}
368e64c8aa0SThomas Gleixner 
369d7677d40Svenkatesh.pallipadi@intel.com 	free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p));
370d7677d40Svenkatesh.pallipadi@intel.com 
371e64c8aa0SThomas Gleixner 	/* Finally remove it */
3726e92a5a6SThomas Gleixner 	o = remove_vm_area((void __force *)addr);
373e64c8aa0SThomas Gleixner 	BUG_ON(p != o || o == NULL);
374e64c8aa0SThomas Gleixner 	kfree(p);
375e64c8aa0SThomas Gleixner }
376e64c8aa0SThomas Gleixner EXPORT_SYMBOL(iounmap);
377e64c8aa0SThomas Gleixner 
3781e6277deSJan Beulich int __init arch_ioremap_pud_supported(void)
3795d72b4fbSToshi Kani {
3805d72b4fbSToshi Kani #ifdef CONFIG_X86_64
3815d72b4fbSToshi Kani 	return cpu_has_gbpages;
3825d72b4fbSToshi Kani #else
3835d72b4fbSToshi Kani 	return 0;
3845d72b4fbSToshi Kani #endif
3855d72b4fbSToshi Kani }
3865d72b4fbSToshi Kani 
3871e6277deSJan Beulich int __init arch_ioremap_pmd_supported(void)
3885d72b4fbSToshi Kani {
3895d72b4fbSToshi Kani 	return cpu_has_pse;
3905d72b4fbSToshi Kani }
3915d72b4fbSToshi Kani 
392e045fb2aSvenkatesh.pallipadi@intel.com /*
393e045fb2aSvenkatesh.pallipadi@intel.com  * Convert a physical pointer to a virtual kernel pointer for /dev/mem
394e045fb2aSvenkatesh.pallipadi@intel.com  * access
395e045fb2aSvenkatesh.pallipadi@intel.com  */
3964707a341SThierry Reding void *xlate_dev_mem_ptr(phys_addr_t phys)
397e045fb2aSvenkatesh.pallipadi@intel.com {
398e045fb2aSvenkatesh.pallipadi@intel.com 	unsigned long start  = phys &  PAGE_MASK;
39994d4b476SIngo Molnar 	unsigned long offset = phys & ~PAGE_MASK;
400562bfca4SIngo Molnar 	void *vaddr;
401e045fb2aSvenkatesh.pallipadi@intel.com 
402e045fb2aSvenkatesh.pallipadi@intel.com 	/* If page is RAM, we can use __va. Otherwise ioremap and unmap. */
403e045fb2aSvenkatesh.pallipadi@intel.com 	if (page_is_ram(start >> PAGE_SHIFT))
404e045fb2aSvenkatesh.pallipadi@intel.com 		return __va(phys);
405e045fb2aSvenkatesh.pallipadi@intel.com 
406562bfca4SIngo Molnar 	vaddr = ioremap_cache(start, PAGE_SIZE);
40794d4b476SIngo Molnar 	/* Only add the offset on success and return NULL if the ioremap() failed: */
40894d4b476SIngo Molnar 	if (vaddr)
40994d4b476SIngo Molnar 		vaddr += offset;
410e045fb2aSvenkatesh.pallipadi@intel.com 
411562bfca4SIngo Molnar 	return vaddr;
412e045fb2aSvenkatesh.pallipadi@intel.com }
413e045fb2aSvenkatesh.pallipadi@intel.com 
4144707a341SThierry Reding void unxlate_dev_mem_ptr(phys_addr_t phys, void *addr)
415e045fb2aSvenkatesh.pallipadi@intel.com {
416e045fb2aSvenkatesh.pallipadi@intel.com 	if (page_is_ram(phys >> PAGE_SHIFT))
417e045fb2aSvenkatesh.pallipadi@intel.com 		return;
418e045fb2aSvenkatesh.pallipadi@intel.com 
419e045fb2aSvenkatesh.pallipadi@intel.com 	iounmap((void __iomem *)((unsigned long)addr & PAGE_MASK));
420e045fb2aSvenkatesh.pallipadi@intel.com }
421e045fb2aSvenkatesh.pallipadi@intel.com 
42245c7b28fSJeremy Fitzhardinge static pte_t bm_pte[PAGE_SIZE/sizeof(pte_t)] __page_aligned_bss;
423e64c8aa0SThomas Gleixner 
424551889a6SIan Campbell static inline pmd_t * __init early_ioremap_pmd(unsigned long addr)
425e64c8aa0SThomas Gleixner {
42637cc8d7fSJeremy Fitzhardinge 	/* Don't assume we're using swapper_pg_dir at this point */
42737cc8d7fSJeremy Fitzhardinge 	pgd_t *base = __va(read_cr3());
42837cc8d7fSJeremy Fitzhardinge 	pgd_t *pgd = &base[pgd_index(addr)];
429551889a6SIan Campbell 	pud_t *pud = pud_offset(pgd, addr);
430551889a6SIan Campbell 	pmd_t *pmd = pmd_offset(pud, addr);
431551889a6SIan Campbell 
432551889a6SIan Campbell 	return pmd;
433e64c8aa0SThomas Gleixner }
434e64c8aa0SThomas Gleixner 
435551889a6SIan Campbell static inline pte_t * __init early_ioremap_pte(unsigned long addr)
436e64c8aa0SThomas Gleixner {
437551889a6SIan Campbell 	return &bm_pte[pte_index(addr)];
438e64c8aa0SThomas Gleixner }
439e64c8aa0SThomas Gleixner 
440fef5ba79SJeremy Fitzhardinge bool __init is_early_ioremap_ptep(pte_t *ptep)
441fef5ba79SJeremy Fitzhardinge {
442fef5ba79SJeremy Fitzhardinge 	return ptep >= &bm_pte[0] && ptep < &bm_pte[PAGE_SIZE/sizeof(pte_t)];
443fef5ba79SJeremy Fitzhardinge }
444fef5ba79SJeremy Fitzhardinge 
445e64c8aa0SThomas Gleixner void __init early_ioremap_init(void)
446e64c8aa0SThomas Gleixner {
447551889a6SIan Campbell 	pmd_t *pmd;
448e64c8aa0SThomas Gleixner 
44973159fdcSAndy Lutomirski #ifdef CONFIG_X86_64
45073159fdcSAndy Lutomirski 	BUILD_BUG_ON((fix_to_virt(0) + PAGE_SIZE) & ((1 << PMD_SHIFT) - 1));
45173159fdcSAndy Lutomirski #else
45273159fdcSAndy Lutomirski 	WARN_ON((fix_to_virt(0) + PAGE_SIZE) & ((1 << PMD_SHIFT) - 1));
45373159fdcSAndy Lutomirski #endif
45473159fdcSAndy Lutomirski 
4555b7c73e0SMark Salter 	early_ioremap_setup();
4568827247fSWang Chen 
457551889a6SIan Campbell 	pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN));
458e64c8aa0SThomas Gleixner 	memset(bm_pte, 0, sizeof(bm_pte));
459b6fbb669SIan Campbell 	pmd_populate_kernel(&init_mm, pmd, bm_pte);
460551889a6SIan Campbell 
461e64c8aa0SThomas Gleixner 	/*
462551889a6SIan Campbell 	 * The boot-ioremap range spans multiple pmds, for which
463e64c8aa0SThomas Gleixner 	 * we are not prepared:
464e64c8aa0SThomas Gleixner 	 */
465499a5f1eSJan Beulich #define __FIXADDR_TOP (-PAGE_SIZE)
466499a5f1eSJan Beulich 	BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
467499a5f1eSJan Beulich 		     != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));
468499a5f1eSJan Beulich #undef __FIXADDR_TOP
469551889a6SIan Campbell 	if (pmd != early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))) {
470e64c8aa0SThomas Gleixner 		WARN_ON(1);
471551889a6SIan Campbell 		printk(KERN_WARNING "pmd %p != %p\n",
472551889a6SIan Campbell 		       pmd, early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END)));
473e64c8aa0SThomas Gleixner 		printk(KERN_WARNING "fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",
474e64c8aa0SThomas Gleixner 			fix_to_virt(FIX_BTMAP_BEGIN));
475e64c8aa0SThomas Gleixner 		printk(KERN_WARNING "fix_to_virt(FIX_BTMAP_END):   %08lx\n",
476e64c8aa0SThomas Gleixner 			fix_to_virt(FIX_BTMAP_END));
477e64c8aa0SThomas Gleixner 
478e64c8aa0SThomas Gleixner 		printk(KERN_WARNING "FIX_BTMAP_END:       %d\n", FIX_BTMAP_END);
479e64c8aa0SThomas Gleixner 		printk(KERN_WARNING "FIX_BTMAP_BEGIN:     %d\n",
480e64c8aa0SThomas Gleixner 		       FIX_BTMAP_BEGIN);
481e64c8aa0SThomas Gleixner 	}
482e64c8aa0SThomas Gleixner }
483e64c8aa0SThomas Gleixner 
4845b7c73e0SMark Salter void __init __early_set_fixmap(enum fixed_addresses idx,
4859b987aebSMasami Hiramatsu 			       phys_addr_t phys, pgprot_t flags)
486e64c8aa0SThomas Gleixner {
487551889a6SIan Campbell 	unsigned long addr = __fix_to_virt(idx);
488551889a6SIan Campbell 	pte_t *pte;
489e64c8aa0SThomas Gleixner 
490e64c8aa0SThomas Gleixner 	if (idx >= __end_of_fixed_addresses) {
491e64c8aa0SThomas Gleixner 		BUG();
492e64c8aa0SThomas Gleixner 		return;
493e64c8aa0SThomas Gleixner 	}
494e64c8aa0SThomas Gleixner 	pte = early_ioremap_pte(addr);
4954583ed51SJeremy Fitzhardinge 
496e64c8aa0SThomas Gleixner 	if (pgprot_val(flags))
497551889a6SIan Campbell 		set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
498e64c8aa0SThomas Gleixner 	else
4994f9c11ddSJeremy Fitzhardinge 		pte_clear(&init_mm, addr, pte);
500e64c8aa0SThomas Gleixner 	__flush_tlb_one(addr);
501e64c8aa0SThomas Gleixner }
502