Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
88278ca2 | 2 | /* |
1da177e4 LT |
3 | * ioport.c: Simple io mapping allocator. |
4 | * | |
5 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | |
6 | * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) | |
7 | * | |
8 | * 1996: sparc_free_io, 1999: ioremap()/iounmap() by Pete Zaitcev. | |
9 | * | |
10 | * 2000/01/29 | |
11 | * <rth> zait: as long as pci_alloc_consistent produces something addressable, | |
12 | * things are ok. | |
13 | * <zaitcev> rth: no, it is relevant, because get_free_pages returns you a | |
14 | * pointer into the big page mapping | |
15 | * <rth> zait: so what? | |
16 | * <rth> zait: remap_it_my_way(virt_to_phys(get_free_page())) | |
17 | * <zaitcev> Hmm | |
18 | * <zaitcev> Suppose I did this remap_it_my_way(virt_to_phys(get_free_page())). | |
19 | * So far so good. | |
20 | * <zaitcev> Now, driver calls pci_free_consistent(with result of | |
21 | * remap_it_my_way()). | |
22 | * <zaitcev> How do you find the address to pass to free_pages()? | |
23 | * <rth> zait: walk the page tables? It's only two or three level after all. | |
24 | * <rth> zait: you have to walk them anyway to remove the mapping. | |
25 | * <zaitcev> Hmm | |
26 | * <zaitcev> Sounds reasonable | |
27 | */ | |
28 | ||
3ca9fab4 | 29 | #include <linux/module.h> |
1da177e4 LT |
30 | #include <linux/sched.h> |
31 | #include <linux/kernel.h> | |
32 | #include <linux/errno.h> | |
33 | #include <linux/types.h> | |
34 | #include <linux/ioport.h> | |
35 | #include <linux/mm.h> | |
36 | #include <linux/slab.h> | |
37 | #include <linux/pci.h> /* struct pci_dev */ | |
38 | #include <linux/proc_fs.h> | |
e7a088f9 | 39 | #include <linux/seq_file.h> |
0912a5db | 40 | #include <linux/scatterlist.h> |
9f4df96b | 41 | #include <linux/dma-map-ops.h> |
263291fa | 42 | #include <linux/of.h> |
1da177e4 LT |
43 | |
44 | #include <asm/io.h> | |
45 | #include <asm/vaddrs.h> | |
46 | #include <asm/oplib.h> | |
576c352e | 47 | #include <asm/prom.h> |
1da177e4 LT |
48 | #include <asm/page.h> |
49 | #include <asm/pgalloc.h> | |
50 | #include <asm/dma.h> | |
e0039348 DM |
51 | #include <asm/iommu.h> |
52 | #include <asm/io-unit.h> | |
8401707f | 53 | #include <asm/leon.h> |
1da177e4 | 54 | |
1da177e4 LT |
55 | static void __iomem *_sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz); |
56 | static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys, | |
57 | unsigned long size, char *name); | |
58 | static void _sparc_free_io(struct resource *res); | |
59 | ||
c61c65cd AB |
60 | static void register_proc_sparc_ioport(void); |
61 | ||
1da177e4 LT |
62 | /* This points to the next to use virtual memory for DVMA mappings */ |
63 | static struct resource _sparc_dvma = { | |
64 | .name = "sparc_dvma", .start = DVMA_VADDR, .end = DVMA_END - 1 | |
65 | }; | |
66 | /* This points to the start of I/O mappings, cluable from outside. */ | |
67 | /*ext*/ struct resource sparc_iomap = { | |
68 | .name = "sparc_iomap", .start = IOBASE_VADDR, .end = IOBASE_END - 1 | |
69 | }; | |
70 | ||
71 | /* | |
72 | * Our mini-allocator... | |
73 | * Boy this is gross! We need it because we must map I/O for | |
74 | * timers and interrupt controller before the kmalloc is available. | |
75 | */ | |
76 | ||
77 | #define XNMLN 15 | |
78 | #define XNRES 10 /* SS-10 uses 8 */ | |
79 | ||
80 | struct xresource { | |
81 | struct resource xres; /* Must be first */ | |
82 | int xflag; /* 1 == used */ | |
83 | char xname[XNMLN+1]; | |
84 | }; | |
85 | ||
86 | static struct xresource xresv[XNRES]; | |
87 | ||
88 | static struct xresource *xres_alloc(void) { | |
89 | struct xresource *xrp; | |
90 | int n; | |
91 | ||
92 | xrp = xresv; | |
93 | for (n = 0; n < XNRES; n++) { | |
94 | if (xrp->xflag == 0) { | |
95 | xrp->xflag = 1; | |
96 | return xrp; | |
97 | } | |
98 | xrp++; | |
99 | } | |
100 | return NULL; | |
101 | } | |
102 | ||
103 | static void xres_free(struct xresource *xrp) { | |
104 | xrp->xflag = 0; | |
105 | } | |
106 | ||
107 | /* | |
108 | * These are typically used in PCI drivers | |
109 | * which are trying to be cross-platform. | |
110 | * | |
111 | * Bus type is always zero on IIep. | |
112 | */ | |
b3ada9d0 | 113 | void __iomem *ioremap(phys_addr_t offset, size_t size) |
1da177e4 LT |
114 | { |
115 | char name[14]; | |
116 | ||
117 | sprintf(name, "phys_%08x", (u32)offset); | |
b3ada9d0 | 118 | return _sparc_alloc_io(0, (unsigned long)offset, size, name); |
1da177e4 | 119 | } |
6943f3da | 120 | EXPORT_SYMBOL(ioremap); |
1da177e4 LT |
121 | |
122 | /* | |
08f80073 | 123 | * Complementary to ioremap(). |
1da177e4 LT |
124 | */ |
125 | void iounmap(volatile void __iomem *virtual) | |
126 | { | |
127 | unsigned long vaddr = (unsigned long) virtual & PAGE_MASK; | |
128 | struct resource *res; | |
129 | ||
a0e997c2 GU |
130 | /* |
131 | * XXX Too slow. Can have 8192 DVMA pages on sun4m in the worst case. | |
132 | * This probably warrants some sort of hashing. | |
133 | */ | |
134 | if ((res = lookup_resource(&sparc_iomap, vaddr)) == NULL) { | |
1da177e4 LT |
135 | printk("free_io/iounmap: cannot free %lx\n", vaddr); |
136 | return; | |
137 | } | |
138 | _sparc_free_io(res); | |
139 | ||
140 | if ((char *)res >= (char*)xresv && (char *)res < (char *)&xresv[XNRES]) { | |
141 | xres_free((struct xresource *)res); | |
142 | } else { | |
143 | kfree(res); | |
144 | } | |
145 | } | |
6943f3da | 146 | EXPORT_SYMBOL(iounmap); |
1da177e4 | 147 | |
3ca9fab4 DM |
148 | void __iomem *of_ioremap(struct resource *res, unsigned long offset, |
149 | unsigned long size, char *name) | |
150 | { | |
151 | return _sparc_alloc_io(res->flags & 0xF, | |
152 | res->start + offset, | |
153 | size, name); | |
154 | } | |
155 | EXPORT_SYMBOL(of_ioremap); | |
156 | ||
e3a411a3 | 157 | void of_iounmap(struct resource *res, void __iomem *base, unsigned long size) |
3ca9fab4 DM |
158 | { |
159 | iounmap(base); | |
160 | } | |
161 | EXPORT_SYMBOL(of_iounmap); | |
162 | ||
1da177e4 LT |
163 | /* |
164 | * Meat of mapping | |
165 | */ | |
166 | static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys, | |
167 | unsigned long size, char *name) | |
168 | { | |
169 | static int printed_full; | |
170 | struct xresource *xres; | |
171 | struct resource *res; | |
172 | char *tack; | |
173 | int tlen; | |
174 | void __iomem *va; /* P3 diag */ | |
175 | ||
176 | if (name == NULL) name = "???"; | |
177 | ||
c31f7651 | 178 | if ((xres = xres_alloc()) != NULL) { |
1da177e4 LT |
179 | tack = xres->xname; |
180 | res = &xres->xres; | |
181 | } else { | |
182 | if (!printed_full) { | |
183 | printk("ioremap: done with statics, switching to malloc\n"); | |
184 | printed_full = 1; | |
185 | } | |
186 | tlen = strlen(name); | |
187 | tack = kmalloc(sizeof (struct resource) + tlen + 1, GFP_KERNEL); | |
188 | if (tack == NULL) return NULL; | |
189 | memset(tack, 0, sizeof(struct resource)); | |
190 | res = (struct resource *) tack; | |
191 | tack += sizeof (struct resource); | |
192 | } | |
193 | ||
bb07972f | 194 | strscpy(tack, name, XNMLN+1); |
1da177e4 LT |
195 | res->name = tack; |
196 | ||
197 | va = _sparc_ioremap(res, busno, phys, size); | |
198 | /* printk("ioremap(0x%x:%08lx[0x%lx])=%p\n", busno, phys, size, va); */ /* P3 diag */ | |
199 | return va; | |
200 | } | |
201 | ||
202 | /* | |
203 | */ | |
204 | static void __iomem * | |
205 | _sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz) | |
206 | { | |
207 | unsigned long offset = ((unsigned long) pa) & (~PAGE_MASK); | |
208 | ||
209 | if (allocate_resource(&sparc_iomap, res, | |
210 | (offset + sz + PAGE_SIZE-1) & PAGE_MASK, | |
211 | sparc_iomap.start, sparc_iomap.end, PAGE_SIZE, NULL, NULL) != 0) { | |
212 | /* Usually we cannot see printks in this case. */ | |
213 | prom_printf("alloc_io_res(%s): cannot occupy\n", | |
214 | (res->name != NULL)? res->name: "???"); | |
215 | prom_halt(); | |
216 | } | |
217 | ||
218 | pa &= PAGE_MASK; | |
9701b264 | 219 | srmmu_mapiorange(bus, pa, res->start, resource_size(res)); |
1da177e4 | 220 | |
d75fc8bb | 221 | return (void __iomem *)(unsigned long)(res->start + offset); |
1da177e4 LT |
222 | } |
223 | ||
224 | /* | |
08f80073 | 225 | * Complementary to _sparc_ioremap(). |
1da177e4 LT |
226 | */ |
227 | static void _sparc_free_io(struct resource *res) | |
228 | { | |
229 | unsigned long plen; | |
230 | ||
28f65c11 | 231 | plen = resource_size(res); |
30d4d1ff | 232 | BUG_ON((plen & (PAGE_SIZE-1)) != 0); |
9701b264 | 233 | srmmu_unmapiorange(res->start, plen); |
1da177e4 LT |
234 | release_resource(res); |
235 | } | |
236 | ||
ce65d36f | 237 | unsigned long sparc_dma_alloc_resource(struct device *dev, size_t len) |
53b7670e CH |
238 | { |
239 | struct resource *res; | |
240 | ||
241 | res = kzalloc(sizeof(*res), GFP_KERNEL); | |
242 | if (!res) | |
243 | return 0; | |
48cc8f7a | 244 | res->name = dev->of_node->full_name; |
53b7670e CH |
245 | |
246 | if (allocate_resource(&_sparc_dvma, res, len, _sparc_dvma.start, | |
247 | _sparc_dvma.end, PAGE_SIZE, NULL, NULL) != 0) { | |
248 | printk("%s: cannot occupy 0x%zx", __func__, len); | |
249 | kfree(res); | |
250 | return 0; | |
251 | } | |
252 | ||
253 | return res->start; | |
254 | } | |
255 | ||
ce65d36f | 256 | bool sparc_dma_free_resource(void *cpu_addr, size_t size) |
53b7670e CH |
257 | { |
258 | unsigned long addr = (unsigned long)cpu_addr; | |
259 | struct resource *res; | |
260 | ||
261 | res = lookup_resource(&_sparc_dvma, addr); | |
262 | if (!res) { | |
263 | printk("%s: cannot free %p\n", __func__, cpu_addr); | |
264 | return false; | |
265 | } | |
266 | ||
267 | if ((addr & (PAGE_SIZE - 1)) != 0) { | |
268 | printk("%s: unaligned va %p\n", __func__, cpu_addr); | |
269 | return false; | |
270 | } | |
271 | ||
272 | size = PAGE_ALIGN(size); | |
273 | if (resource_size(res) != size) { | |
274 | printk("%s: region 0x%lx asked 0x%zx\n", | |
275 | __func__, (long)resource_size(res), size); | |
276 | return false; | |
277 | } | |
278 | ||
279 | release_resource(res); | |
280 | kfree(res); | |
281 | return true; | |
282 | } | |
283 | ||
1da177e4 LT |
284 | #ifdef CONFIG_SBUS |
285 | ||
63237eeb | 286 | void sbus_set_sbus64(struct device *dev, int x) |
8fae097d | 287 | { |
1da177e4 LT |
288 | printk("sbus_set_sbus64: unsupported\n"); |
289 | } | |
6943f3da | 290 | EXPORT_SYMBOL(sbus_set_sbus64); |
1da177e4 | 291 | |
f8e4d32c | 292 | static int __init sparc_register_ioport(void) |
576c352e | 293 | { |
576c352e DM |
294 | register_proc_sparc_ioport(); |
295 | ||
576c352e | 296 | return 0; |
576c352e DM |
297 | } |
298 | ||
f8e4d32c DM |
299 | arch_initcall(sparc_register_ioport); |
300 | ||
1da177e4 LT |
301 | #endif /* CONFIG_SBUS */ |
302 | ||
837e80b3 CH |
303 | /* |
304 | * IIep is write-through, not flushing on cpu to device transfer. | |
305 | * | |
306 | * On LEON systems without cache snooping, the entire D-CACHE must be flushed to | |
307 | * make DMA to cacheable memory coherent. | |
308 | */ | |
56e35f9c CH |
309 | void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, |
310 | enum dma_data_direction dir) | |
1da177e4 | 311 | { |
0fb3436b | 312 | if (dir != DMA_TO_DEVICE && |
837e80b3 CH |
313 | sparc_cpu_model == sparc_leon && |
314 | !sparc_leon3_snooping_enabled()) | |
315 | leon_flush_dcache_all(); | |
1da177e4 | 316 | } |
ee664a92 | 317 | |
1da177e4 LT |
318 | #ifdef CONFIG_PROC_FS |
319 | ||
e7a088f9 | 320 | static int sparc_io_proc_show(struct seq_file *m, void *v) |
1da177e4 | 321 | { |
e7a088f9 | 322 | struct resource *root = m->private, *r; |
1da177e4 LT |
323 | const char *nm; |
324 | ||
e7a088f9 | 325 | for (r = root->child; r != NULL; r = r->sibling) { |
c31f7651 | 326 | if ((nm = r->name) == NULL) nm = "???"; |
e7a088f9 | 327 | seq_printf(m, "%016llx-%016llx: %s\n", |
685143ac GKH |
328 | (unsigned long long)r->start, |
329 | (unsigned long long)r->end, nm); | |
1da177e4 LT |
330 | } |
331 | ||
e7a088f9 | 332 | return 0; |
1da177e4 | 333 | } |
1da177e4 LT |
334 | #endif /* CONFIG_PROC_FS */ |
335 | ||
c61c65cd | 336 | static void register_proc_sparc_ioport(void) |
1da177e4 LT |
337 | { |
338 | #ifdef CONFIG_PROC_FS | |
3f3942ac CH |
339 | proc_create_single_data("io_map", 0, NULL, sparc_io_proc_show, |
340 | &sparc_iomap); | |
341 | proc_create_single_data("dvma_map", 0, NULL, sparc_io_proc_show, | |
342 | &_sparc_dvma); | |
1da177e4 LT |
343 | #endif |
344 | } |