update atp870u driver to 0.78 from D-Link source
[linux-2.4.git] / arch / s390x / 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 <asm/io.h>
18 #include <asm/pgalloc.h>
19
20 static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size,
21         unsigned long phys_addr, unsigned long flags)
22 {
23         unsigned long end;
24
25         address &= ~PMD_MASK;
26         end = address + size;
27         if (end > PMD_SIZE)
28                 end = PMD_SIZE;
29         if (address >= end)
30                 BUG();
31         do {
32                 if (!pte_none(*pte)) {
33                         printk("remap_area_pte: page already exists\n");
34                         BUG();
35                 }
36                 set_pte(pte, mk_pte_phys(phys_addr,
37                                          __pgprot(_PAGE_PRESENT | flags)));
38                 address += PAGE_SIZE;
39                 phys_addr += PAGE_SIZE;
40                 pte++;
41         } while (address && (address < end));
42 }
43
44 static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size,
45         unsigned long phys_addr, unsigned long flags)
46 {
47         unsigned long end;
48
49         address &= ~PGDIR_MASK;
50         end = address + size;
51         if (end > PGDIR_SIZE)
52                 end = PGDIR_SIZE;
53         phys_addr -= address;
54         if (address >= end)
55                 BUG();
56         do {
57                 pte_t * pte = pte_alloc(&init_mm, pmd, address);
58                 if (!pte)
59                         return -ENOMEM;
60                 remap_area_pte(pte, address, end - address, address + phys_addr, flags);
61                 address = (address + PMD_SIZE) & PMD_MASK;
62                 pmd++;
63         } while (address && (address < end));
64         return 0;
65 }
66
67 static int remap_area_pages(unsigned long address, unsigned long phys_addr,
68                                  unsigned long size, unsigned long flags)
69 {
70         int error;
71         pgd_t * dir;
72         unsigned long end = address + size;
73
74         phys_addr -= address;
75         dir = pgd_offset(&init_mm, address);
76         flush_cache_all();
77         if (address >= end)
78                 BUG();
79         spin_lock(&init_mm.page_table_lock);
80         do {
81                 pmd_t *pmd;
82                 pmd = pmd_alloc(&init_mm, dir, address);
83                 error = -ENOMEM;
84                 if (!pmd)
85                         break;
86                 if (remap_area_pmd(pmd, address, end - address,
87                                          phys_addr + address, flags))
88                         break;
89                 error = 0;
90                 address = (address + PGDIR_SIZE) & PGDIR_MASK;
91                 dir++;
92         } while (address && (address < end));
93         spin_unlock(&init_mm.page_table_lock);
94         flush_tlb_all();
95         return 0;
96 }
97
98 /*
99  * Generic mapping function (not visible outside):
100  */
101
102 /*
103  * Remap an arbitrary physical address space into the kernel virtual
104  * address space. Needed when the kernel wants to access high addresses
105  * directly.
106  */
107 void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags)
108 {
109         void * addr;
110         struct vm_struct * area;
111
112         if (phys_addr < virt_to_phys(high_memory))
113                 return phys_to_virt(phys_addr);
114         if (phys_addr & ~PAGE_MASK)
115                 return NULL;
116         size = PAGE_ALIGN(size);
117         if (!size || size > phys_addr + size)
118                 return NULL;
119         area = get_vm_area(size, VM_IOREMAP);
120         if (!area)
121                 return NULL;
122         addr = area->addr;
123         if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) {
124                 vfree(addr);
125                 return NULL;
126         }
127         return addr;
128 }
129
130 void iounmap(void *addr)
131 {
132         if (addr > high_memory)
133                 return vfree(addr);
134 }