Commit 74588d8ba34ff1bda027cfa737972af01ab00c8b
Committed by
Linus Torvalds
1 parent
bc03613dec
Exists in
master
and in
4 other branches
[PATCH] Generic ioremap_page_range: implementation
This patch adds a generic implementation of ioremap_page_range() in lib/ioremap.c based on the i386 implementation. It differs from the i386 version in the following ways: * The PTE flags are passed as a pgprot_t argument and must be determined up front by the arch-specific code. No additional PTE flags are added. * Uses set_pte_at() instead of set_pte() [bunk@stusta.de: warning fix] ]dhowells@redhat.com: nommu build fix] Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com> Cc: Richard Henderson <rth@twiddle.net> Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru> Cc: Russell King <rmk@arm.linux.org.uk> Cc: Mikael Starvik <starvik@axis.com> Cc: Andi Kleen <ak@suse.de> Cc: <linux-m32r@ml.linux-m32r.org> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: Kyle McMartin <kyle@parisc-linux.org> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Cc: Paul Mundt <lethal@linux-sh.org> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Showing 3 changed files with 99 additions and 0 deletions Side-by-side Diff
include/linux/io.h
... | ... | @@ -19,9 +19,13 @@ |
19 | 19 | #define _LINUX_IO_H |
20 | 20 | |
21 | 21 | #include <asm/io.h> |
22 | +#include <asm/page.h> | |
22 | 23 | |
23 | 24 | void __iowrite32_copy(void __iomem *to, const void *from, size_t count); |
24 | 25 | void __iowrite64_copy(void __iomem *to, const void *from, size_t count); |
26 | + | |
27 | +int ioremap_page_range(unsigned long addr, unsigned long end, | |
28 | + unsigned long phys_addr, pgprot_t prot); | |
25 | 29 | |
26 | 30 | #endif /* _LINUX_IO_H */ |
lib/Makefile
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/io.h> | |
9 | +#include <linux/vmalloc.h> | |
10 | +#include <linux/mm.h> | |
11 | + | |
12 | +#include <asm/cacheflush.h> | |
13 | +#include <asm/pgtable.h> | |
14 | + | |
15 | +static int ioremap_pte_range(pmd_t *pmd, unsigned long addr, | |
16 | + unsigned long end, unsigned long phys_addr, pgprot_t prot) | |
17 | +{ | |
18 | + pte_t *pte; | |
19 | + unsigned long pfn; | |
20 | + | |
21 | + pfn = phys_addr >> PAGE_SHIFT; | |
22 | + pte = pte_alloc_kernel(pmd, addr); | |
23 | + if (!pte) | |
24 | + return -ENOMEM; | |
25 | + do { | |
26 | + BUG_ON(!pte_none(*pte)); | |
27 | + set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot)); | |
28 | + pfn++; | |
29 | + } while (pte++, addr += PAGE_SIZE, addr != end); | |
30 | + return 0; | |
31 | +} | |
32 | + | |
33 | +static inline int ioremap_pmd_range(pud_t *pud, unsigned long addr, | |
34 | + unsigned long end, unsigned long phys_addr, pgprot_t prot) | |
35 | +{ | |
36 | + pmd_t *pmd; | |
37 | + unsigned long next; | |
38 | + | |
39 | + phys_addr -= addr; | |
40 | + pmd = pmd_alloc(&init_mm, pud, addr); | |
41 | + if (!pmd) | |
42 | + return -ENOMEM; | |
43 | + do { | |
44 | + next = pmd_addr_end(addr, end); | |
45 | + if (ioremap_pte_range(pmd, addr, next, phys_addr + addr, prot)) | |
46 | + return -ENOMEM; | |
47 | + } while (pmd++, addr = next, addr != end); | |
48 | + return 0; | |
49 | +} | |
50 | + | |
51 | +static inline int ioremap_pud_range(pgd_t *pgd, unsigned long addr, | |
52 | + unsigned long end, unsigned long phys_addr, pgprot_t prot) | |
53 | +{ | |
54 | + pud_t *pud; | |
55 | + unsigned long next; | |
56 | + | |
57 | + phys_addr -= addr; | |
58 | + pud = pud_alloc(&init_mm, pgd, addr); | |
59 | + if (!pud) | |
60 | + return -ENOMEM; | |
61 | + do { | |
62 | + next = pud_addr_end(addr, end); | |
63 | + if (ioremap_pmd_range(pud, addr, next, phys_addr + addr, prot)) | |
64 | + return -ENOMEM; | |
65 | + } while (pud++, addr = next, addr != end); | |
66 | + return 0; | |
67 | +} | |
68 | + | |
69 | +int ioremap_page_range(unsigned long addr, | |
70 | + unsigned long end, unsigned long phys_addr, pgprot_t prot) | |
71 | +{ | |
72 | + pgd_t *pgd; | |
73 | + unsigned long start; | |
74 | + unsigned long next; | |
75 | + int err; | |
76 | + | |
77 | + BUG_ON(addr >= end); | |
78 | + | |
79 | + flush_cache_all(); | |
80 | + | |
81 | + start = addr; | |
82 | + phys_addr -= addr; | |
83 | + pgd = pgd_offset_k(addr); | |
84 | + do { | |
85 | + next = pgd_addr_end(addr, end); | |
86 | + err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot); | |
87 | + if (err) | |
88 | + break; | |
89 | + } while (pgd++, addr = next, addr != end); | |
90 | + | |
91 | + flush_tlb_all(); | |
92 | + | |
93 | + return err; | |
94 | +} |