aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2006-01-17 01:14:15 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-17 02:15:28 -0500
commitb66c1a3919abb40f9bd8fb92a0d9fd77eb899c54 (patch)
treee83c11e63f760e8a3c09ab44e8c951a6df0400b7 /arch
parentbf3a00f88c926635932c91afd90b4a0907dfbe78 (diff)
[PATCH] sh: I/O routine cleanups and ioremap() overhaul
This introduces a few changes in the way that the I/O routines are defined on SH, specifically so that things like the iomap API properly wrap through the machvec for board-specific quirks. In addition to this, the old p3_ioremap() work is converted to a more generic __ioremap() that will map through the PMB if it's available, or fall back on page tables for everything else. An alpha-like IO_CONCAT is also added so we can start to clean up the board-specific io.h mess, which will be handled in board update patches.. Signed-off-by: Paul Mundt <lethal@linux-sh.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/sh/kernel/io.c41
-rw-r--r--arch/sh/kernel/io_generic.c192
-rw-r--r--arch/sh/mm/ioremap.c99
3 files changed, 196 insertions, 136 deletions
diff --git a/arch/sh/kernel/io.c b/arch/sh/kernel/io.c
index d9932f25993b..71c9fde2fd90 100644
--- a/arch/sh/kernel/io.c
+++ b/arch/sh/kernel/io.c
@@ -2,58 +2,73 @@
2 * linux/arch/sh/kernel/io.c 2 * linux/arch/sh/kernel/io.c
3 * 3 *
4 * Copyright (C) 2000 Stuart Menefy 4 * Copyright (C) 2000 Stuart Menefy
5 * Copyright (C) 2005 Paul Mundt
5 * 6 *
6 * Provide real functions which expand to whatever the header file defined. 7 * Provide real functions which expand to whatever the header file defined.
7 * Also definitions of machine independent IO functions. 8 * Also definitions of machine independent IO functions.
9 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file "COPYING" in the main directory of this archive
12 * for more details.
8 */ 13 */
9
10#include <asm/io.h>
11#include <linux/module.h> 14#include <linux/module.h>
15#include <asm/machvec.h>
16#include <asm/io.h>
12 17
13/* 18/*
14 * Copy data from IO memory space to "real" memory space. 19 * Copy data from IO memory space to "real" memory space.
15 * This needs to be optimized. 20 * This needs to be optimized.
16 */ 21 */
17void memcpy_fromio(void * to, unsigned long from, unsigned long count) 22void memcpy_fromio(void *to, volatile void __iomem *from, unsigned long count)
18{ 23{
19 char *p = to; 24 char *p = to;
20 while (count) { 25 while (count) {
21 count--; 26 count--;
22 *p = readb(from); 27 *p = readb((void __iomem *)from);
23 p++; 28 p++;
24 from++; 29 from++;
25 } 30 }
26} 31}
27 32EXPORT_SYMBOL(memcpy_fromio);
33
28/* 34/*
29 * Copy data from "real" memory space to IO memory space. 35 * Copy data from "real" memory space to IO memory space.
30 * This needs to be optimized. 36 * This needs to be optimized.
31 */ 37 */
32void memcpy_toio(unsigned long to, const void * from, unsigned long count) 38void memcpy_toio(volatile void __iomem *to, const void *from, unsigned long count)
33{ 39{
34 const char *p = from; 40 const char *p = from;
35 while (count) { 41 while (count) {
36 count--; 42 count--;
37 writeb(*p, to); 43 writeb(*p, (void __iomem *)to);
38 p++; 44 p++;
39 to++; 45 to++;
40 } 46 }
41} 47}
42 48EXPORT_SYMBOL(memcpy_toio);
49
43/* 50/*
44 * "memset" on IO memory space. 51 * "memset" on IO memory space.
45 * This needs to be optimized. 52 * This needs to be optimized.
46 */ 53 */
47void memset_io(unsigned long dst, int c, unsigned long count) 54void memset_io(volatile void __iomem *dst, int c, unsigned long count)
48{ 55{
49 while (count) { 56 while (count) {
50 count--; 57 count--;
51 writeb(c, dst); 58 writeb(c, (void __iomem *)dst);
52 dst++; 59 dst++;
53 } 60 }
54} 61}
55
56EXPORT_SYMBOL(memcpy_fromio);
57EXPORT_SYMBOL(memcpy_toio);
58EXPORT_SYMBOL(memset_io); 62EXPORT_SYMBOL(memset_io);
59 63
64void __iomem *ioport_map(unsigned long port, unsigned int nr)
65{
66 return sh_mv.mv_ioport_map(port, nr);
67}
68EXPORT_SYMBOL(ioport_map);
69
70void ioport_unmap(void __iomem *addr)
71{
72 sh_mv.mv_ioport_unmap(addr);
73}
74EXPORT_SYMBOL(ioport_unmap);
diff --git a/arch/sh/kernel/io_generic.c b/arch/sh/kernel/io_generic.c
index a911b0149d1f..28ec7487de8c 100644
--- a/arch/sh/kernel/io_generic.c
+++ b/arch/sh/kernel/io_generic.c
@@ -3,6 +3,7 @@
3 * linux/arch/sh/kernel/io_generic.c 3 * linux/arch/sh/kernel/io_generic.c
4 * 4 *
5 * Copyright (C) 2000 Niibe Yutaka 5 * Copyright (C) 2000 Niibe Yutaka
6 * Copyright (C) 2005 Paul Mundt
6 * 7 *
7 * Generic I/O routine. These can be used where a machine specific version 8 * Generic I/O routine. These can be used where a machine specific version
8 * is not required. 9 * is not required.
@@ -10,21 +11,20 @@
10 * This file is subject to the terms and conditions of the GNU General Public 11 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file "COPYING" in the main directory of this archive 12 * License. See the file "COPYING" in the main directory of this archive
12 * for more details. 13 * for more details.
13 *
14 */ 14 */
15 15#include <linux/module.h>
16#include <asm/io.h> 16#include <asm/io.h>
17#include <asm/machvec.h> 17#include <asm/machvec.h>
18#include <linux/module.h>
19 18
20#if defined(CONFIG_CPU_SH3) 19#ifdef CONFIG_CPU_SH3
20/* SH3 has a PCMCIA bug that needs a dummy read from area 6 for a
21 * workaround. */
21/* I'm not sure SH7709 has this kind of bug */ 22/* I'm not sure SH7709 has this kind of bug */
22#define SH3_PCMCIA_BUG_WORKAROUND 1 23#define dummy_read() ctrl_inb(0xba000000)
23#define DUMMY_READ_AREA6 0xba000000 24#else
25#define dummy_read()
24#endif 26#endif
25 27
26#define PORT2ADDR(x) (sh_mv.mv_isa_port2addr(x))
27
28unsigned long generic_io_base; 28unsigned long generic_io_base;
29 29
30static inline void delay(void) 30static inline void delay(void)
@@ -32,40 +32,40 @@ static inline void delay(void)
32 ctrl_inw(0xa0000000); 32 ctrl_inw(0xa0000000);
33} 33}
34 34
35unsigned char generic_inb(unsigned long port) 35u8 generic_inb(unsigned long port)
36{ 36{
37 return *(volatile unsigned char*)PORT2ADDR(port); 37 return ctrl_inb((unsigned long __force)ioport_map(port, 1));
38} 38}
39 39
40unsigned short generic_inw(unsigned long port) 40u16 generic_inw(unsigned long port)
41{ 41{
42 return *(volatile unsigned short*)PORT2ADDR(port); 42 return ctrl_inw((unsigned long __force)ioport_map(port, 2));
43} 43}
44 44
45unsigned int generic_inl(unsigned long port) 45u32 generic_inl(unsigned long port)
46{ 46{
47 return *(volatile unsigned long*)PORT2ADDR(port); 47 return ctrl_inl((unsigned long __force)ioport_map(port, 4));
48} 48}
49 49
50unsigned char generic_inb_p(unsigned long port) 50u8 generic_inb_p(unsigned long port)
51{ 51{
52 unsigned long v = *(volatile unsigned char*)PORT2ADDR(port); 52 unsigned long v = generic_inb(port);
53 53
54 delay(); 54 delay();
55 return v; 55 return v;
56} 56}
57 57
58unsigned short generic_inw_p(unsigned long port) 58u16 generic_inw_p(unsigned long port)
59{ 59{
60 unsigned long v = *(volatile unsigned short*)PORT2ADDR(port); 60 unsigned long v = generic_inw(port);
61 61
62 delay(); 62 delay();
63 return v; 63 return v;
64} 64}
65 65
66unsigned int generic_inl_p(unsigned long port) 66u32 generic_inl_p(unsigned long port)
67{ 67{
68 unsigned long v = *(volatile unsigned long*)PORT2ADDR(port); 68 unsigned long v = generic_inl(port);
69 69
70 delay(); 70 delay();
71 return v; 71 return v;
@@ -77,75 +77,70 @@ unsigned int generic_inl_p(unsigned long port)
77 * convert the port address to real address once. 77 * convert the port address to real address once.
78 */ 78 */
79 79
80void generic_insb(unsigned long port, void *buffer, unsigned long count) 80void generic_insb(unsigned long port, void *dst, unsigned long count)
81{ 81{
82 volatile unsigned char *port_addr; 82 volatile u8 *port_addr;
83 unsigned char *buf=buffer; 83 u8 *buf = dst;
84
85 port_addr = (volatile unsigned char *)PORT2ADDR(port);
86 84
87 while(count--) 85 port_addr = (volatile u8 *)ioport_map(port, 1);
88 *buf++ = *port_addr; 86 while (count--)
87 *buf++ = *port_addr;
89} 88}
90 89
91void generic_insw(unsigned long port, void *buffer, unsigned long count) 90void generic_insw(unsigned long port, void *dst, unsigned long count)
92{ 91{
93 volatile unsigned short *port_addr; 92 volatile u16 *port_addr;
94 unsigned short *buf=buffer; 93 u16 *buf = dst;
95 94
96 port_addr = (volatile unsigned short *)PORT2ADDR(port); 95 port_addr = (volatile u16 *)ioport_map(port, 2);
96 while (count--)
97 *buf++ = *port_addr;
97 98
98 while(count--) 99 dummy_read();
99 *buf++ = *port_addr;
100#ifdef SH3_PCMCIA_BUG_WORKAROUND
101 ctrl_inb (DUMMY_READ_AREA6);
102#endif
103} 100}
104 101
105void generic_insl(unsigned long port, void *buffer, unsigned long count) 102void generic_insl(unsigned long port, void *dst, unsigned long count)
106{ 103{
107 volatile unsigned long *port_addr; 104 volatile u32 *port_addr;
108 unsigned long *buf=buffer; 105 u32 *buf = dst;
109 106
110 port_addr = (volatile unsigned long *)PORT2ADDR(port); 107 port_addr = (volatile u32 *)ioport_map(port, 4);
108 while (count--)
109 *buf++ = *port_addr;
111 110
112 while(count--) 111 dummy_read();
113 *buf++ = *port_addr;
114#ifdef SH3_PCMCIA_BUG_WORKAROUND
115 ctrl_inb (DUMMY_READ_AREA6);
116#endif
117} 112}
118 113
119void generic_outb(unsigned char b, unsigned long port) 114void generic_outb(u8 b, unsigned long port)
120{ 115{
121 *(volatile unsigned char*)PORT2ADDR(port) = b; 116 ctrl_outb(b, (unsigned long __force)ioport_map(port, 1));
122} 117}
123 118
124void generic_outw(unsigned short b, unsigned long port) 119void generic_outw(u16 b, unsigned long port)
125{ 120{
126 *(volatile unsigned short*)PORT2ADDR(port) = b; 121 ctrl_outw(b, (unsigned long __force)ioport_map(port, 2));
127} 122}
128 123
129void generic_outl(unsigned int b, unsigned long port) 124void generic_outl(u32 b, unsigned long port)
130{ 125{
131 *(volatile unsigned long*)PORT2ADDR(port) = b; 126 ctrl_outl(b, (unsigned long __force)ioport_map(port, 4));
132} 127}
133 128
134void generic_outb_p(unsigned char b, unsigned long port) 129void generic_outb_p(u8 b, unsigned long port)
135{ 130{
136 *(volatile unsigned char*)PORT2ADDR(port) = b; 131 generic_outb(b, port);
137 delay(); 132 delay();
138} 133}
139 134
140void generic_outw_p(unsigned short b, unsigned long port) 135void generic_outw_p(u16 b, unsigned long port)
141{ 136{
142 *(volatile unsigned short*)PORT2ADDR(port) = b; 137 generic_outw(b, port);
143 delay(); 138 delay();
144} 139}
145 140
146void generic_outl_p(unsigned int b, unsigned long port) 141void generic_outl_p(u32 b, unsigned long port)
147{ 142{
148 *(volatile unsigned long*)PORT2ADDR(port) = b; 143 generic_outl(b, port);
149 delay(); 144 delay();
150} 145}
151 146
@@ -154,90 +149,77 @@ void generic_outl_p(unsigned int b, unsigned long port)
154 * address. However as the port address doesn't change we only need to 149 * address. However as the port address doesn't change we only need to
155 * convert the port address to real address once. 150 * convert the port address to real address once.
156 */ 151 */
157 152void generic_outsb(unsigned long port, const void *src, unsigned long count)
158void generic_outsb(unsigned long port, const void *buffer, unsigned long count)
159{ 153{
160 volatile unsigned char *port_addr; 154 volatile u8 *port_addr;
161 const unsigned char *buf=buffer; 155 const u8 *buf = src;
162 156
163 port_addr = (volatile unsigned char *)PORT2ADDR(port); 157 port_addr = (volatile u8 __force *)ioport_map(port, 1);
164 158
165 while(count--) 159 while (count--)
166 *port_addr = *buf++; 160 *port_addr = *buf++;
167} 161}
168 162
169void generic_outsw(unsigned long port, const void *buffer, unsigned long count) 163void generic_outsw(unsigned long port, const void *src, unsigned long count)
170{ 164{
171 volatile unsigned short *port_addr; 165 volatile u16 *port_addr;
172 const unsigned short *buf=buffer; 166 const u16 *buf = src;
173 167
174 port_addr = (volatile unsigned short *)PORT2ADDR(port); 168 port_addr = (volatile u16 __force *)ioport_map(port, 2);
175 169
176 while(count--) 170 while (count--)
177 *port_addr = *buf++; 171 *port_addr = *buf++;
178 172
179#ifdef SH3_PCMCIA_BUG_WORKAROUND 173 dummy_read();
180 ctrl_inb (DUMMY_READ_AREA6);
181#endif
182} 174}
183 175
184void generic_outsl(unsigned long port, const void *buffer, unsigned long count) 176void generic_outsl(unsigned long port, const void *src, unsigned long count)
185{ 177{
186 volatile unsigned long *port_addr; 178 volatile u32 *port_addr;
187 const unsigned long *buf=buffer; 179 const u32 *buf = src;
188 180
189 port_addr = (volatile unsigned long *)PORT2ADDR(port); 181 port_addr = (volatile u32 __force *)ioport_map(port, 4);
182 while (count--)
183 *port_addr = *buf++;
190 184
191 while(count--) 185 dummy_read();
192 *port_addr = *buf++;
193
194#ifdef SH3_PCMCIA_BUG_WORKAROUND
195 ctrl_inb (DUMMY_READ_AREA6);
196#endif
197}
198
199unsigned char generic_readb(unsigned long addr)
200{
201 return *(volatile unsigned char*)addr;
202} 186}
203 187
204unsigned short generic_readw(unsigned long addr) 188u8 generic_readb(void __iomem *addr)
205{ 189{
206 return *(volatile unsigned short*)addr; 190 return ctrl_inb((unsigned long __force)addr);
207} 191}
208 192
209unsigned int generic_readl(unsigned long addr) 193u16 generic_readw(void __iomem *addr)
210{ 194{
211 return *(volatile unsigned long*)addr; 195 return ctrl_inw((unsigned long __force)addr);
212} 196}
213 197
214void generic_writeb(unsigned char b, unsigned long addr) 198u32 generic_readl(void __iomem *addr)
215{ 199{
216 *(volatile unsigned char*)addr = b; 200 return ctrl_inl((unsigned long __force)addr);
217} 201}
218 202
219void generic_writew(unsigned short b, unsigned long addr) 203void generic_writeb(u8 b, void __iomem *addr)
220{ 204{
221 *(volatile unsigned short*)addr = b; 205 ctrl_outb(b, (unsigned long __force)addr);
222} 206}
223 207
224void generic_writel(unsigned int b, unsigned long addr) 208void generic_writew(u16 b, void __iomem *addr)
225{ 209{
226 *(volatile unsigned long*)addr = b; 210 ctrl_outw(b, (unsigned long __force)addr);
227} 211}
228 212
229void * generic_ioremap(unsigned long offset, unsigned long size) 213void generic_writel(u32 b, void __iomem *addr)
230{ 214{
231 return (void *) P2SEGADDR(offset); 215 ctrl_outl(b, (unsigned long __force)addr);
232} 216}
233EXPORT_SYMBOL(generic_ioremap);
234 217
235void generic_iounmap(void *addr) 218void __iomem *generic_ioport_map(unsigned long addr, unsigned int size)
236{ 219{
220 return (void __iomem *)(addr + generic_io_base);
237} 221}
238EXPORT_SYMBOL(generic_iounmap);
239 222
240unsigned long generic_isa_port2addr(unsigned long offset) 223void generic_ioport_unmap(void __iomem *addr)
241{ 224{
242 return offset + generic_io_base;
243} 225}
diff --git a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap.c
index e794e27a72f1..96fa4a999e2a 100644
--- a/arch/sh/mm/ioremap.c
+++ b/arch/sh/mm/ioremap.c
@@ -6,13 +6,19 @@
6 * 640k-1MB IO memory area on PC's 6 * 640k-1MB IO memory area on PC's
7 * 7 *
8 * (C) Copyright 1995 1996 Linus Torvalds 8 * (C) Copyright 1995 1996 Linus Torvalds
9 * (C) Copyright 2005, 2006 Paul Mundt
10 *
11 * This file is subject to the terms and conditions of the GNU General
12 * Public License. See the file "COPYING" in the main directory of this
13 * archive for more details.
9 */ 14 */
10
11#include <linux/vmalloc.h> 15#include <linux/vmalloc.h>
16#include <linux/module.h>
12#include <linux/mm.h> 17#include <linux/mm.h>
13#include <asm/io.h> 18#include <asm/io.h>
14#include <asm/page.h> 19#include <asm/page.h>
15#include <asm/pgalloc.h> 20#include <asm/pgalloc.h>
21#include <asm/addrspace.h>
16#include <asm/cacheflush.h> 22#include <asm/cacheflush.h>
17#include <asm/tlbflush.h> 23#include <asm/tlbflush.h>
18 24
@@ -80,9 +86,15 @@ int remap_area_pages(unsigned long address, unsigned long phys_addr,
80 if (address >= end) 86 if (address >= end)
81 BUG(); 87 BUG();
82 do { 88 do {
89 pud_t *pud;
83 pmd_t *pmd; 90 pmd_t *pmd;
84 pmd = pmd_alloc(&init_mm, dir, address); 91
85 error = -ENOMEM; 92 error = -ENOMEM;
93
94 pud = pud_alloc(&init_mm, dir, address);
95 if (!pud)
96 break;
97 pmd = pmd_alloc(&init_mm, pud, address);
86 if (!pmd) 98 if (!pmd)
87 break; 99 break;
88 if (remap_area_pmd(pmd, address, end - address, 100 if (remap_area_pmd(pmd, address, end - address,
@@ -97,10 +109,6 @@ int remap_area_pages(unsigned long address, unsigned long phys_addr,
97} 109}
98 110
99/* 111/*
100 * Generic mapping function (not visible outside):
101 */
102
103/*
104 * Remap an arbitrary physical address space into the kernel virtual 112 * Remap an arbitrary physical address space into the kernel virtual
105 * address space. Needed when the kernel wants to access high addresses 113 * address space. Needed when the kernel wants to access high addresses
106 * directly. 114 * directly.
@@ -109,11 +117,11 @@ int remap_area_pages(unsigned long address, unsigned long phys_addr,
109 * have to convert them into an offset in a page-aligned mapping, but the 117 * have to convert them into an offset in a page-aligned mapping, but the
110 * caller shouldn't need to know that small detail. 118 * caller shouldn't need to know that small detail.
111 */ 119 */
112void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 120void __iomem *__ioremap(unsigned long phys_addr, unsigned long size,
121 unsigned long flags)
113{ 122{
114 void * addr;
115 struct vm_struct * area; 123 struct vm_struct * area;
116 unsigned long offset, last_addr; 124 unsigned long offset, last_addr, addr, orig_addr;
117 125
118 /* Don't allow wraparound or zero size */ 126 /* Don't allow wraparound or zero size */
119 last_addr = phys_addr + size - 1; 127 last_addr = phys_addr + size - 1;
@@ -124,7 +132,7 @@ void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long fla
124 * Don't remap the low PCI/ISA area, it's always mapped.. 132 * Don't remap the low PCI/ISA area, it's always mapped..
125 */ 133 */
126 if (phys_addr >= 0xA0000 && last_addr < 0x100000) 134 if (phys_addr >= 0xA0000 && last_addr < 0x100000)
127 return phys_to_virt(phys_addr); 135 return (void __iomem *)phys_to_virt(phys_addr);
128 136
129 /* 137 /*
130 * Don't allow anybody to remap normal RAM that we're using.. 138 * Don't allow anybody to remap normal RAM that we're using..
@@ -146,16 +154,71 @@ void * p3_ioremap(unsigned long phys_addr, unsigned long size, unsigned long fla
146 if (!area) 154 if (!area)
147 return NULL; 155 return NULL;
148 area->phys_addr = phys_addr; 156 area->phys_addr = phys_addr;
149 addr = area->addr; 157 orig_addr = addr = (unsigned long)area->addr;
150 if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { 158
151 vunmap(addr); 159#ifdef CONFIG_32BIT
152 return NULL; 160 /*
161 * First try to remap through the PMB once a valid VMA has been
162 * established. Smaller allocations (or the rest of the size
163 * remaining after a PMB mapping due to the size not being
164 * perfectly aligned on a PMB size boundary) are then mapped
165 * through the UTLB using conventional page tables.
166 *
167 * PMB entries are all pre-faulted.
168 */
169 if (unlikely(size >= 0x1000000)) {
170 unsigned long mapped = pmb_remap(addr, phys_addr, size, flags);
171
172 if (likely(mapped)) {
173 addr += mapped;
174 phys_addr += mapped;
175 size -= mapped;
176 }
153 } 177 }
154 return (void *) (offset + (char *)addr); 178#endif
179
180 if (likely(size))
181 if (remap_area_pages(addr, phys_addr, size, flags)) {
182 vunmap((void *)orig_addr);
183 return NULL;
184 }
185
186 return (void __iomem *)(offset + (char *)orig_addr);
155} 187}
188EXPORT_SYMBOL(__ioremap);
156 189
157void p3_iounmap(void *addr) 190void __iounmap(void __iomem *addr)
158{ 191{
159 if (addr > high_memory) 192 unsigned long vaddr = (unsigned long __force)addr;
160 vfree((void *)(PAGE_MASK & (unsigned long)addr)); 193 struct vm_struct *p;
194
195 if (PXSEG(vaddr) < P3SEG)
196 return;
197
198#ifdef CONFIG_32BIT
199 /*
200 * Purge any PMB entries that may have been established for this
201 * mapping, then proceed with conventional VMA teardown.
202 *
203 * XXX: Note that due to the way that remove_vm_area() does
204 * matching of the resultant VMA, we aren't able to fast-forward
205 * the address past the PMB space until the end of the VMA where
206 * the page tables reside. As such, unmap_vm_area() will be
207 * forced to linearly scan over the area until it finds the page
208 * tables where PTEs that need to be unmapped actually reside,
209 * which is far from optimal. Perhaps we need to use a separate
210 * VMA for the PMB mappings?
211 * -- PFM.
212 */
213 pmb_unmap(vaddr);
214#endif
215
216 p = remove_vm_area((void *)(vaddr & PAGE_MASK));
217 if (!p) {
218 printk(KERN_ERR "%s: bad address %p\n", __FUNCTION__, addr);
219 return;
220 }
221
222 kfree(p);
161} 223}
224EXPORT_SYMBOL(__iounmap);