Branch data 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 : 0 : 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 : 0 : pfn = phys_addr >> PAGE_SHIFT;
23 [ # # ][ # # ]: 0 : pte = pte_alloc_kernel(pmd, addr);
24 [ # # ]: 0 : if (!pte)
25 : : return -ENOMEM;
26 : : do {
27 [ # # ]: 0 : BUG_ON(!pte_none(*pte));
28 : 0 : set_pte_at(&init_mm, addr, pte, pfn_pte(pfn, prot));
29 : 0 : pfn++;
30 [ # # ]: 0 : } 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 [ # # ]: 0 : if (!pmd)
43 : : return -ENOMEM;
44 : : do {
45 : : next = pmd_addr_end(addr, end);
46 [ # # ]: 0 : 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 [ # # ]: 0 : if (!pud)
61 : : return -ENOMEM;
62 : : do {
63 : : next = pud_addr_end(addr, end);
64 [ # # ]: 0 : 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 : 0 : 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 [ # # ]: 0 : BUG_ON(addr >= end);
79 : :
80 : : start = addr;
81 : 0 : phys_addr -= addr;
82 : 0 : pgd = pgd_offset_k(addr);
83 : : do {
84 [ # # ]: 0 : next = pgd_addr_end(addr, end);
85 : : err = ioremap_pud_range(pgd, addr, next, phys_addr+addr, prot);
86 [ # # ]: 0 : if (err)
87 : : break;
88 [ # # ]: 0 : } while (pgd++, addr = next, addr != end);
89 : :
90 : : flush_cache_vmap(start, end);
91 : :
92 : 0 : return err;
93 : : }
94 : : EXPORT_SYMBOL_GPL(ioremap_page_range);
|