Line data Source code
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/export.h>
13 : #include <asm/cacheflush.h>
14 : #include <asm/pgtable.h>
15 :
16 10 : 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 10 : pfn = phys_addr >> PAGE_SHIFT;
23 20 : pte = pte_alloc_kernel(pmd, addr);
24 10 : if (!pte)
25 : return -ENOMEM;
26 : do {
27 : BUG_ON(!pte_none(*pte));
28 3538 : set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
29 3538 : pfn++;
30 3538 : } 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 10 : if (!pmd)
43 : return -ENOMEM;
44 : do {
45 : next = pmd_addr_end(addr, end);
46 10 : 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 10 : if (!pud)
61 : return -ENOMEM;
62 : do {
63 : next = pud_addr_end(addr, end);
64 10 : 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 6 : 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 6 : phys_addr -= addr;
82 6 : pgd = pgd_offset_k(addr);
83 : do {
84 10 : next = pgd_addr_end(addr, end);
85 : err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
86 10 : if (err)
87 : break;
88 10 : } while (pgd++, addr = next, addr != end);
89 :
90 : flush_cache_vmap(start, end);
91 :
92 6 : return err;
93 : }
94 : EXPORT_SYMBOL_GPL(ioremap_page_range);
|