ALSA: memalloc: Drop snd_dma_pci_data() macro
[linux-2.6-block.git] / lib / ioremap.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
74588d8b
HS
2/*
3 * Re-map IO memory to kernel address space so that we can access it.
4 * This is needed for high PCI addresses that aren't mapped in the
5 * 640k-1MB IO memory area on PC's
6 *
7 * (C) Copyright 1995 1996 Linus Torvalds
8 */
74588d8b
HS
9#include <linux/vmalloc.h>
10#include <linux/mm.h>
e8edc6e0 11#include <linux/sched.h>
53fa6645 12#include <linux/io.h>
8bc3bcc9 13#include <linux/export.h>
74588d8b
HS
14#include <asm/cacheflush.h>
15#include <asm/pgtable.h>
16
0ddab1d2 17#ifdef CONFIG_HAVE_ARCH_HUGE_VMAP
c2febafc 18static int __read_mostly ioremap_p4d_capable;
6b637835
TK
19static int __read_mostly ioremap_pud_capable;
20static int __read_mostly ioremap_pmd_capable;
21static int __read_mostly ioremap_huge_disabled;
0ddab1d2
TK
22
23static int __init set_nohugeiomap(char *str)
24{
25 ioremap_huge_disabled = 1;
26 return 0;
27}
28early_param("nohugeiomap", set_nohugeiomap);
29
30void __init ioremap_huge_init(void)
31{
32 if (!ioremap_huge_disabled) {
0f472d04
AK
33 if (arch_ioremap_p4d_supported())
34 ioremap_p4d_capable = 1;
0ddab1d2
TK
35 if (arch_ioremap_pud_supported())
36 ioremap_pud_capable = 1;
37 if (arch_ioremap_pmd_supported())
38 ioremap_pmd_capable = 1;
39 }
40}
41
c2febafc
KS
42static inline int ioremap_p4d_enabled(void)
43{
44 return ioremap_p4d_capable;
45}
46
0ddab1d2
TK
47static inline int ioremap_pud_enabled(void)
48{
49 return ioremap_pud_capable;
50}
51
52static inline int ioremap_pmd_enabled(void)
53{
54 return ioremap_pmd_capable;
55}
56
57#else /* !CONFIG_HAVE_ARCH_HUGE_VMAP */
c2febafc 58static inline int ioremap_p4d_enabled(void) { return 0; }
0ddab1d2
TK
59static inline int ioremap_pud_enabled(void) { return 0; }
60static inline int ioremap_pmd_enabled(void) { return 0; }
61#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
62
74588d8b 63static int ioremap_pte_range(pmd_t *pmd, unsigned long addr,
ffa71f33 64 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
74588d8b
HS
65{
66 pte_t *pte;
ffa71f33 67 u64 pfn;
74588d8b
HS
68
69 pfn = phys_addr >> PAGE_SHIFT;
70 pte = pte_alloc_kernel(pmd, addr);
71 if (!pte)
72 return -ENOMEM;
73 do {
74 BUG_ON(!pte_none(*pte));
75 set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
76 pfn++;
77 } while (pte++, addr += PAGE_SIZE, addr != end);
78 return 0;
79}
80
d239865a
WD
81static int ioremap_try_huge_pmd(pmd_t *pmd, unsigned long addr,
82 unsigned long end, phys_addr_t phys_addr,
83 pgprot_t prot)
84{
85 if (!ioremap_pmd_enabled())
86 return 0;
87
88 if ((end - addr) != PMD_SIZE)
89 return 0;
90
6b95ab42
AK
91 if (!IS_ALIGNED(addr, PMD_SIZE))
92 return 0;
93
d239865a
WD
94 if (!IS_ALIGNED(phys_addr, PMD_SIZE))
95 return 0;
96
97 if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
98 return 0;
99
100 return pmd_set_huge(pmd, phys_addr, prot);
101}
102
74588d8b 103static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr,
ffa71f33 104 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
74588d8b
HS
105{
106 pmd_t *pmd;
107 unsigned long next;
108
74588d8b
HS
109 pmd = pmd_alloc(&init_mm, pud, addr);
110 if (!pmd)
111 return -ENOMEM;
112 do {
113 next = pmd_addr_end(addr, end);
e61ce6ad 114
36ddc5a7 115 if (ioremap_try_huge_pmd(pmd, addr, next, phys_addr, prot))
d239865a 116 continue;
e61ce6ad 117
36ddc5a7 118 if (ioremap_pte_range(pmd, addr, next, phys_addr, prot))
74588d8b 119 return -ENOMEM;
36ddc5a7 120 } while (pmd++, phys_addr += (next - addr), addr = next, addr != end);
74588d8b
HS
121 return 0;
122}
123
d239865a
WD
124static int ioremap_try_huge_pud(pud_t *pud, unsigned long addr,
125 unsigned long end, phys_addr_t phys_addr,
126 pgprot_t prot)
127{
128 if (!ioremap_pud_enabled())
129 return 0;
130
131 if ((end - addr) != PUD_SIZE)
132 return 0;
133
6b95ab42
AK
134 if (!IS_ALIGNED(addr, PUD_SIZE))
135 return 0;
136
d239865a
WD
137 if (!IS_ALIGNED(phys_addr, PUD_SIZE))
138 return 0;
139
140 if (pud_present(*pud) && !pud_free_pmd_page(pud, addr))
141 return 0;
142
143 return pud_set_huge(pud, phys_addr, prot);
144}
145
c2febafc 146static inline int ioremap_pud_range(p4d_t *p4d, unsigned long addr,
ffa71f33 147 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
74588d8b
HS
148{
149 pud_t *pud;
150 unsigned long next;
151
c2febafc 152 pud = pud_alloc(&init_mm, p4d, addr);
74588d8b
HS
153 if (!pud)
154 return -ENOMEM;
155 do {
156 next = pud_addr_end(addr, end);
e61ce6ad 157
36ddc5a7 158 if (ioremap_try_huge_pud(pud, addr, next, phys_addr, prot))
d239865a 159 continue;
e61ce6ad 160
36ddc5a7 161 if (ioremap_pmd_range(pud, addr, next, phys_addr, prot))
74588d8b 162 return -ENOMEM;
36ddc5a7 163 } while (pud++, phys_addr += (next - addr), addr = next, addr != end);
74588d8b
HS
164 return 0;
165}
166
8e2d4340
WD
167static int ioremap_try_huge_p4d(p4d_t *p4d, unsigned long addr,
168 unsigned long end, phys_addr_t phys_addr,
169 pgprot_t prot)
170{
171 if (!ioremap_p4d_enabled())
172 return 0;
173
174 if ((end - addr) != P4D_SIZE)
175 return 0;
176
6b95ab42
AK
177 if (!IS_ALIGNED(addr, P4D_SIZE))
178 return 0;
179
8e2d4340
WD
180 if (!IS_ALIGNED(phys_addr, P4D_SIZE))
181 return 0;
182
183 if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr))
184 return 0;
185
186 return p4d_set_huge(p4d, phys_addr, prot);
187}
188
c2febafc
KS
189static inline int ioremap_p4d_range(pgd_t *pgd, unsigned long addr,
190 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
191{
192 p4d_t *p4d;
193 unsigned long next;
194
c2febafc
KS
195 p4d = p4d_alloc(&init_mm, pgd, addr);
196 if (!p4d)
197 return -ENOMEM;
198 do {
199 next = p4d_addr_end(addr, end);
200
8e2d4340
WD
201 if (ioremap_try_huge_p4d(p4d, addr, next, phys_addr, prot))
202 continue;
c2febafc 203
36ddc5a7 204 if (ioremap_pud_range(p4d, addr, next, phys_addr, prot))
c2febafc 205 return -ENOMEM;
36ddc5a7 206 } while (p4d++, phys_addr += (next - addr), addr = next, addr != end);
c2febafc
KS
207 return 0;
208}
209
74588d8b 210int ioremap_page_range(unsigned long addr,
ffa71f33 211 unsigned long end, phys_addr_t phys_addr, pgprot_t prot)
74588d8b
HS
212{
213 pgd_t *pgd;
214 unsigned long start;
215 unsigned long next;
216 int err;
217
b39ab98e 218 might_sleep();
74588d8b
HS
219 BUG_ON(addr >= end);
220
74588d8b 221 start = addr;
74588d8b
HS
222 pgd = pgd_offset_k(addr);
223 do {
224 next = pgd_addr_end(addr, end);
36ddc5a7 225 err = ioremap_p4d_range(pgd, addr, next, phys_addr, prot);
74588d8b
HS
226 if (err)
227 break;
36ddc5a7 228 } while (pgd++, phys_addr += (next - addr), addr = next, addr != end);
74588d8b 229
db71daab 230 flush_cache_vmap(start, end);
74588d8b
HS
231
232 return err;
233}