Pull context-bitmap into release branch
[linux-2.6] / arch / s390 / mm / ioremap.c
1 /*
2  *  arch/s390/mm/ioremap.c
3  *
4  *  S390 version
5  *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
6  *    Author(s): Hartmut Penner (hp@de.ibm.com)
7  *
8  *  Derived from "arch/i386/mm/extable.c"
9  *    (C) Copyright 1995 1996 Linus Torvalds
10  *
11  * Re-map IO memory to kernel address space so that we can access it.
12  * This is needed for high PCI addresses that aren't mapped in the
13  * 640k-1MB IO memory area on PC's
14  */
15
16 #include <linux/vmalloc.h>
17 #include <linux/mm.h>
18 #include <asm/io.h>
19 #include <asm/pgalloc.h>
20 #include <asm/cacheflush.h>
21 #include <asm/tlbflush.h>
22
23 static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
24         unsigned long phys_addr, unsigned long flags)
25 {
26         unsigned long end;
27         unsigned long pfn;
28
29         address &= ~PMD_MASK;
30         end = address + size;
31         if (end > PMD_SIZE)
32                 end = PMD_SIZE;
33         if (address >= end)
34                 BUG();
35         pfn = phys_addr >> PAGE_SHIFT;
36         do {
37                 if (!pte_none(*pte)) {
38                         printk("remap_area_pte: page already exists\n");
39                         BUG();
40                 }
41                 set_pte(pte, pfn_pte(pfn, __pgprot(flags)));
42                 address += PAGE_SIZE;
43                 pfn++;
44                 pte++;
45         } while (address && (address < end));
46 }
47
48 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
49         unsigned long phys_addr, unsigned long flags)
50 {
51         unsigned long end;
52
53         address &= ~PGDIR_MASK;
54         end = address + size;
55         if (end > PGDIR_SIZE)
56                 end = PGDIR_SIZE;
57         phys_addr -= address;
58         if (address >= end)
59                 BUG();
60         do {
61                 pte_t * pte = pte_alloc_kernel(pmd, address);
62                 if (!pte)
63                         return -ENOMEM;
64                 remap_area_pte(pte, address, end - address, address + phys_addr, flags);
65                 address = (address + PMD_SIZE) & PMD_MASK;
66                 pmd++;
67         } while (address && (address < end));
68         return 0;
69 }
70
71 static int remap_area_pages(unsigned long address, unsigned long phys_addr,
72                                  unsigned long size, unsigned long flags)
73 {
74         int error;
75         pgd_t * dir;
76         unsigned long end = address + size;
77
78         phys_addr -= address;
79         dir = pgd_offset(&init_mm, address);
80         flush_cache_all();
81         if (address >= end)
82                 BUG();
83         do {
84                 pmd_t *pmd;
85                 pmd = pmd_alloc(&init_mm, dir, address);
86                 error = -ENOMEM;
87                 if (!pmd)
88                         break;
89                 if (remap_area_pmd(pmd, address, end - address,
90                                          phys_addr + address, flags))
91                         break;
92                 error = 0;
93                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
94                 dir++;
95         } while (address && (address < end));
96         flush_tlb_all();
97         return 0;
98 }
99
100 /*
101  * Generic mapping function (not visible outside):
102  */
103
104 /*
105  * Remap an arbitrary physical address space into the kernel virtual
106  * address space. Needed when the kernel wants to access high addresses
107  * directly.
108  */
109 void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
110 {
111         void * addr;
112         struct vm_struct * area;
113
114         if (phys_addr < virt_to_phys(high_memory))
115                 return phys_to_virt(phys_addr);
116         if (phys_addr & ~PAGE_MASK)
117                 return NULL;
118         size = PAGE_ALIGN(size);
119         if (!size || size > phys_addr + size)
120                 return NULL;
121         area = get_vm_area(size, VM_IOREMAP);
122         if (!area)
123                 return NULL;
124         addr = area->addr;
125         if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) {
126                 vfree(addr);
127                 return NULL;
128         }
129         return addr;
130 }
131
132 void iounmap(void *addr)
133 {
134         if (addr > high_memory)
135                 vfree(addr);
136 }