Merge branches 'core-fixes-for-linus', 'x86-fixes-for-linus', 'timers-fixes-for-linus...
[platform/kernel/linux-rpi.git] / lib / ioremap.c
1 /*
2  * Re-map IO memory to kernel address space so that we can access it.
3  * This is needed for high PCI addresses that aren't mapped in the
4  * 640k-1MB IO memory area on PC's
5  *
6  * (C) Copyright 1995 1996 Linus Torvalds
7  */
8 #include <linux/vmalloc.h>
9 #include <linux/mm.h>
10 #include <linux/sched.h>
11 #include <linux/io.h>
12 #include <linux/module.h>
13 #include <asm/cacheflush.h>
14 #include <asm/pgtable.h>
15
16 static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
17                 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
18 {
19         pte_t *pte;
20         u64 pfn;
21
22         pfn = phys_addr >> PAGE_SHIFT;
23         pte = pte_alloc_kernel(pmd, addr);
24         if (!pte)
25                 return -ENOMEM;
26         do {
27                 BUG_ON(!pte_none(*pte));
28                 set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
29                 pfn++;
30         } while (pte++, addr += PAGE_SIZE, addr != end);
31         return 0;
32 }
33
34 static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
35                 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
36 {
37         pmd_t *pmd;
38         unsigned long next;
39
40         phys_addr -= addr;
41         pmd = pmd_alloc(&init_mm, pud, addr);
42         if (!pmd)
43                 return -ENOMEM;
44         do {
45                 next = pmd_addr_end(addr, end);
46                 if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot))
47                         return -ENOMEM;
48         } while (pmd++, addr = next, addr != end);
49         return 0;
50 }
51
52 static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr,
53                 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
54 {
55         pud_t *pud;
56         unsigned long next;
57
58         phys_addr -= addr;
59         pud = pud_alloc(&init_mm, pgd, addr);
60         if (!pud)
61                 return -ENOMEM;
62         do {
63                 next = pud_addr_end(addr, end);
64                 if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot))
65                         return -ENOMEM;
66         } while (pud++, addr = next, addr != end);
67         return 0;
68 }
69
70 int ioremap_page_range(unsigned long addr,
71                        unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
72 {
73         pgd_t *pgd;
74         unsigned long start;
75         unsigned long next;
76         int err;
77
78         BUG_ON(addr >= end);
79
80         start = addr;
81         phys_addr -= addr;
82         pgd = pgd_offset_k(addr);
83         do {
84                 next = pgd_addr_end(addr, end);
85                 err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
86                 if (err)
87                         break;
88         } while (pgd++, addr = next, addr != end);
89
90         flush_cache_vmap(start, end);
91
92         return err;
93 }
94 EXPORT_SYMBOL_GPL(ioremap_page_range);