aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mm
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2007-06-04 01:15:36 -0400
committerPaul Mackerras <paulus@samba.org>2007-06-14 08:29:56 -0400
commit3d5134ee8341bffc4f539049abb9e90d469b448d (patch)
tree037958e0daa97b4ef350908a53182167ee2c8a03 /arch/powerpc/mm
parentc19c03fc749147f565e807fa65f1729066800571 (diff)
[POWERPC] Rewrite IO allocation & mapping on powerpc64
This rewrites pretty much from scratch the handling of MMIO and PIO space allocations on powerpc64. The main goals are: - Get rid of imalloc and use more common code where possible - Simplify the current mess so that PIO space is allocated and mapped in a single place for PCI bridges - Handle allocation constraints of PIO for all bridges including hot plugged ones within the 2GB space reserved for IO ports, so that devices on hotplugged busses will now work with drivers that assume IO ports fit in an int. - Cleanup and separate tracking of the ISA space in the reserved low 64K of IO space. No ISA -> Nothing mapped there. I booted a cell blade with IDE on PIO and MMIO and a dual G5 so far, that's it :-) With this patch, all allocations are done using the code in mm/vmalloc.c, though we use the low level __get_vm_area with explicit start/stop constraints in order to manage separate areas for vmalloc/vmap, ioremap, and PCI IOs. This greatly simplifies a lot of things, as you can see in the diffstat of that patch :-) A new pair of functions pcibios_map/unmap_io_space() now replace all of the previous code that used to manipulate PCI IOs space. The allocation is done at mapping time, which is now called from scan_phb's, just before the devices are probed (instead of after, which is by itself a bug fix). The only other caller is the PCI hotplug code for hot adding PCI-PCI bridges (slots). imalloc is gone, as is the "sub-allocation" thing, but I do beleive that hotplug should still work in the sense that the space allocation is always done by the PHB, but if you unmap a child bus of this PHB (which seems to be possible), then the code should properly tear down all the HPTE mappings for that area of the PHB allocated IO space. I now always reserve the first 64K of IO space for the bridge with the ISA bus on it. I have moved the code for tracking ISA in a separate file which should also make it smarter if we ever are capable of hot unplugging or re-plugging an ISA bridge. This should have a side effect on platforms like powermac where VGA IOs will no longer work. This is done on purpose though as they would have worked semi-randomly before. The idea at this point is to isolate drivers that might need to access those and fix them by providing a proper function to obtain an offset to the legacy IOs of a given bus. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/mm')
-rw-r--r--arch/powerpc/mm/Makefile3
-rw-r--r--arch/powerpc/mm/imalloc.c314
-rw-r--r--arch/powerpc/mm/mmu_decl.h12
-rw-r--r--arch/powerpc/mm/pgtable_64.c204
-rw-r--r--arch/powerpc/mm/tlb_64.c56
5 files changed, 106 insertions, 483 deletions
diff --git a/arch/powerpc/mm/Makefile b/arch/powerpc/mm/Makefile
index 4f839c6a9768..7e4d27ad3dee 100644
--- a/arch/powerpc/mm/Makefile
+++ b/arch/powerpc/mm/Makefile
@@ -11,8 +11,7 @@ obj-$(CONFIG_PPC32) += init_32.o pgtable_32.o mmu_context_32.o
11hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o 11hash-$(CONFIG_PPC_NATIVE) := hash_native_64.o
12obj-$(CONFIG_PPC64) += init_64.o pgtable_64.o mmu_context_64.o \ 12obj-$(CONFIG_PPC64) += init_64.o pgtable_64.o mmu_context_64.o \
13 hash_utils_64.o hash_low_64.o tlb_64.o \ 13 hash_utils_64.o hash_low_64.o tlb_64.o \
14 slb_low.o slb.o stab.o mmap.o imalloc.o \ 14 slb_low.o slb.o stab.o mmap.o $(hash-y)
15 $(hash-y)
16obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o hash_low_32.o tlb_32.o 15obj-$(CONFIG_PPC_STD_MMU_32) += ppc_mmu_32.o hash_low_32.o tlb_32.o
17obj-$(CONFIG_40x) += 4xx_mmu.o 16obj-$(CONFIG_40x) += 4xx_mmu.o
18obj-$(CONFIG_44x) += 44x_mmu.o 17obj-$(CONFIG_44x) += 44x_mmu.o
diff --git a/arch/powerpc/mm/imalloc.c b/arch/powerpc/mm/imalloc.c
deleted file mode 100644
index 9eddf37303d7..000000000000
--- a/arch/powerpc/mm/imalloc.c
+++ /dev/null
@@ -1,314 +0,0 @@
1/*
2 * c 2001 PPC 64 Team, IBM Corp
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 */
9
10#include <linux/slab.h>
11#include <linux/vmalloc.h>
12
13#include <asm/uaccess.h>
14#include <asm/pgalloc.h>
15#include <asm/pgtable.h>
16#include <linux/mutex.h>
17#include <asm/cacheflush.h>
18
19#include "mmu_decl.h"
20
21static DEFINE_MUTEX(imlist_mutex);
22struct vm_struct * imlist = NULL;
23
24static int get_free_im_addr(unsigned long size, unsigned long *im_addr)
25{
26 unsigned long addr;
27 struct vm_struct **p, *tmp;
28
29 addr = ioremap_bot;
30 for (p = &imlist; (tmp = *p) ; p = &tmp->next) {
31 if (size + addr < (unsigned long) tmp->addr)
32 break;
33 if ((unsigned long)tmp->addr >= ioremap_bot)
34 addr = tmp->size + (unsigned long) tmp->addr;
35 if (addr >= IMALLOC_END-size)
36 return 1;
37 }
38 *im_addr = addr;
39
40 return 0;
41}
42
43/* Return whether the region described by v_addr and size is a subset
44 * of the region described by parent
45 */
46static inline int im_region_is_subset(unsigned long v_addr, unsigned long size,
47 struct vm_struct *parent)
48{
49 return (int) (v_addr >= (unsigned long) parent->addr &&
50 v_addr < (unsigned long) parent->addr + parent->size &&
51 size < parent->size);
52}
53
54/* Return whether the region described by v_addr and size is a superset
55 * of the region described by child
56 */
57static int im_region_is_superset(unsigned long v_addr, unsigned long size,
58 struct vm_struct *child)
59{
60 struct vm_struct parent;
61
62 parent.addr = (void *) v_addr;
63 parent.size = size;
64
65 return im_region_is_subset((unsigned long) child->addr, child->size,
66 &parent);
67}
68
69/* Return whether the region described by v_addr and size overlaps
70 * the region described by vm. Overlapping regions meet the
71 * following conditions:
72 * 1) The regions share some part of the address space
73 * 2) The regions aren't identical
74 * 3) Neither region is a subset of the other
75 */
76static int im_region_overlaps(unsigned long v_addr, unsigned long size,
77 struct vm_struct *vm)
78{
79 if (im_region_is_superset(v_addr, size, vm))
80 return 0;
81
82 return (v_addr + size > (unsigned long) vm->addr + vm->size &&
83 v_addr < (unsigned long) vm->addr + vm->size) ||
84 (v_addr < (unsigned long) vm->addr &&
85 v_addr + size > (unsigned long) vm->addr);
86}
87
88/* Determine imalloc status of region described by v_addr and size.
89 * Can return one of the following:
90 * IM_REGION_UNUSED - Entire region is unallocated in imalloc space.
91 * IM_REGION_SUBSET - Region is a subset of a region that is already
92 * allocated in imalloc space.
93 * vm will be assigned to a ptr to the parent region.
94 * IM_REGION_EXISTS - Exact region already allocated in imalloc space.
95 * vm will be assigned to a ptr to the existing imlist
96 * member.
97 * IM_REGION_OVERLAPS - Region overlaps an allocated region in imalloc space.
98 * IM_REGION_SUPERSET - Region is a superset of a region that is already
99 * allocated in imalloc space.
100 */
101static int im_region_status(unsigned long v_addr, unsigned long size,
102 struct vm_struct **vm)
103{
104 struct vm_struct *tmp;
105
106 for (tmp = imlist; tmp; tmp = tmp->next)
107 if (v_addr < (unsigned long) tmp->addr + tmp->size)
108 break;
109
110 *vm = NULL;
111 if (tmp) {
112 if (im_region_overlaps(v_addr, size, tmp))
113 return IM_REGION_OVERLAP;
114
115 *vm = tmp;
116 if (im_region_is_subset(v_addr, size, tmp)) {
117 /* Return with tmp pointing to superset */
118 return IM_REGION_SUBSET;
119 }
120 if (im_region_is_superset(v_addr, size, tmp)) {
121 /* Return with tmp pointing to first subset */
122 return IM_REGION_SUPERSET;
123 }
124 else if (v_addr == (unsigned long) tmp->addr &&
125 size == tmp->size) {
126 /* Return with tmp pointing to exact region */
127 return IM_REGION_EXISTS;
128 }
129 }
130
131 return IM_REGION_UNUSED;
132}
133
134static struct vm_struct * split_im_region(unsigned long v_addr,
135 unsigned long size, struct vm_struct *parent)
136{
137 struct vm_struct *vm1 = NULL;
138 struct vm_struct *vm2 = NULL;
139 struct vm_struct *new_vm = NULL;
140
141 vm1 = kmalloc(sizeof(*vm1), GFP_KERNEL);
142 if (vm1 == NULL) {
143 printk(KERN_ERR "%s() out of memory\n", __FUNCTION__);
144 return NULL;
145 }
146
147 if (v_addr == (unsigned long) parent->addr) {
148 /* Use existing parent vm_struct to represent child, allocate
149 * new one for the remainder of parent range
150 */
151 vm1->size = parent->size - size;
152 vm1->addr = (void *) (v_addr + size);
153 vm1->next = parent->next;
154
155 parent->size = size;
156 parent->next = vm1;
157 new_vm = parent;
158 } else if (v_addr + size == (unsigned long) parent->addr +
159 parent->size) {
160 /* Allocate new vm_struct to represent child, use existing
161 * parent one for remainder of parent range
162 */
163 vm1->size = size;
164 vm1->addr = (void *) v_addr;
165 vm1->next = parent->next;
166 new_vm = vm1;
167
168 parent->size -= size;
169 parent->next = vm1;
170 } else {
171 /* Allocate two new vm_structs for the new child and
172 * uppermost remainder, and use existing parent one for the
173 * lower remainder of parent range
174 */
175 vm2 = kmalloc(sizeof(*vm2), GFP_KERNEL);
176 if (vm2 == NULL) {
177 printk(KERN_ERR "%s() out of memory\n", __FUNCTION__);
178 kfree(vm1);
179 return NULL;
180 }
181
182 vm1->size = size;
183 vm1->addr = (void *) v_addr;
184 vm1->next = vm2;
185 new_vm = vm1;
186
187 vm2->size = ((unsigned long) parent->addr + parent->size) -
188 (v_addr + size);
189 vm2->addr = (void *) v_addr + size;
190 vm2->next = parent->next;
191
192 parent->size = v_addr - (unsigned long) parent->addr;
193 parent->next = vm1;
194 }
195
196 return new_vm;
197}
198
199static struct vm_struct * __add_new_im_area(unsigned long req_addr,
200 unsigned long size)
201{
202 struct vm_struct **p, *tmp, *area;
203
204 for (p = &imlist; (tmp = *p) ; p = &tmp->next) {
205 if (req_addr + size <= (unsigned long)tmp->addr)
206 break;
207 }
208
209 area = kmalloc(sizeof(*area), GFP_KERNEL);
210 if (!area)
211 return NULL;
212 area->flags = 0;
213 area->addr = (void *)req_addr;
214 area->size = size;
215 area->next = *p;
216 *p = area;
217
218 return area;
219}
220
221static struct vm_struct * __im_get_area(unsigned long req_addr,
222 unsigned long size,
223 int criteria)
224{
225 struct vm_struct *tmp;
226 int status;
227
228 status = im_region_status(req_addr, size, &tmp);
229 if ((criteria & status) == 0) {
230 return NULL;
231 }
232
233 switch (status) {
234 case IM_REGION_UNUSED:
235 tmp = __add_new_im_area(req_addr, size);
236 break;
237 case IM_REGION_SUBSET:
238 tmp = split_im_region(req_addr, size, tmp);
239 break;
240 case IM_REGION_EXISTS:
241 /* Return requested region */
242 break;
243 case IM_REGION_SUPERSET:
244 /* Return first existing subset of requested region */
245 break;
246 default:
247 printk(KERN_ERR "%s() unexpected imalloc region status\n",
248 __FUNCTION__);
249 tmp = NULL;
250 }
251
252 return tmp;
253}
254
255struct vm_struct * im_get_free_area(unsigned long size)
256{
257 struct vm_struct *area;
258 unsigned long addr;
259
260 mutex_lock(&imlist_mutex);
261 if (get_free_im_addr(size, &addr)) {
262 printk(KERN_ERR "%s() cannot obtain addr for size 0x%lx\n",
263 __FUNCTION__, size);
264 area = NULL;
265 goto next_im_done;
266 }
267
268 area = __im_get_area(addr, size, IM_REGION_UNUSED);
269 if (area == NULL) {
270 printk(KERN_ERR
271 "%s() cannot obtain area for addr 0x%lx size 0x%lx\n",
272 __FUNCTION__, addr, size);
273 }
274next_im_done:
275 mutex_unlock(&imlist_mutex);
276 return area;
277}
278
279struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size,
280 int criteria)
281{
282 struct vm_struct *area;
283
284 mutex_lock(&imlist_mutex);
285 area = __im_get_area(v_addr, size, criteria);
286 mutex_unlock(&imlist_mutex);
287 return area;
288}
289
290void im_free(void * addr)
291{
292 struct vm_struct **p, *tmp;
293
294 if (!addr)
295 return;
296 if ((unsigned long) addr & ~PAGE_MASK) {
297 printk(KERN_ERR "Trying to %s bad address (%p)\n", __FUNCTION__, addr);
298 return;
299 }
300 mutex_lock(&imlist_mutex);
301 for (p = &imlist ; (tmp = *p) ; p = &tmp->next) {
302 if (tmp->addr == addr) {
303 *p = tmp->next;
304 unmap_kernel_range((unsigned long)tmp->addr,
305 tmp->size);
306 kfree(tmp);
307 mutex_unlock(&imlist_mutex);
308 return;
309 }
310 }
311 mutex_unlock(&imlist_mutex);
312 printk(KERN_ERR "Trying to %s nonexistent area (%p)\n", __FUNCTION__,
313 addr);
314}
diff --git a/arch/powerpc/mm/mmu_decl.h b/arch/powerpc/mm/mmu_decl.h
index 2558c34eedaa..f7a4066a57ea 100644
--- a/arch/powerpc/mm/mmu_decl.h
+++ b/arch/powerpc/mm/mmu_decl.h
@@ -90,16 +90,4 @@ static inline void flush_HPTE(unsigned context, unsigned long va,
90 else 90 else
91 _tlbie(va); 91 _tlbie(va);
92} 92}
93#else /* CONFIG_PPC64 */
94/* imalloc region types */
95#define IM_REGION_UNUSED 0x1
96#define IM_REGION_SUBSET 0x2
97#define IM_REGION_EXISTS 0x4
98#define IM_REGION_OVERLAP 0x8
99#define IM_REGION_SUPERSET 0x10
100
101extern struct vm_struct * im_get_free_area(unsigned long size);
102extern struct vm_struct * im_get_area(unsigned long v_addr, unsigned long size,
103 int region_type);
104extern void im_free(void *addr);
105#endif 93#endif
diff --git a/arch/powerpc/mm/pgtable_64.c b/arch/powerpc/mm/pgtable_64.c
index fa5c828d3876..a895de73beae 100644
--- a/arch/powerpc/mm/pgtable_64.c
+++ b/arch/powerpc/mm/pgtable_64.c
@@ -34,41 +34,27 @@
34#include <linux/stddef.h> 34#include <linux/stddef.h>
35#include <linux/vmalloc.h> 35#include <linux/vmalloc.h>
36#include <linux/init.h> 36#include <linux/init.h>
37#include <linux/delay.h>
38#include <linux/bootmem.h>
39#include <linux/highmem.h>
40#include <linux/idr.h>
41#include <linux/nodemask.h>
42#include <linux/module.h>
43 37
44#include <asm/pgalloc.h> 38#include <asm/pgalloc.h>
45#include <asm/page.h> 39#include <asm/page.h>
46#include <asm/prom.h> 40#include <asm/prom.h>
47#include <asm/lmb.h>
48#include <asm/rtas.h>
49#include <asm/io.h> 41#include <asm/io.h>
50#include <asm/mmu_context.h> 42#include <asm/mmu_context.h>
51#include <asm/pgtable.h> 43#include <asm/pgtable.h>
52#include <asm/mmu.h> 44#include <asm/mmu.h>
53#include <asm/uaccess.h>
54#include <asm/smp.h> 45#include <asm/smp.h>
55#include <asm/machdep.h> 46#include <asm/machdep.h>
56#include <asm/tlb.h> 47#include <asm/tlb.h>
57#include <asm/eeh.h>
58#include <asm/processor.h> 48#include <asm/processor.h>
59#include <asm/mmzone.h>
60#include <asm/cputable.h> 49#include <asm/cputable.h>
61#include <asm/sections.h> 50#include <asm/sections.h>
62#include <asm/system.h> 51#include <asm/system.h>
63#include <asm/iommu.h>
64#include <asm/abs_addr.h> 52#include <asm/abs_addr.h>
65#include <asm/vdso.h>
66#include <asm/firmware.h> 53#include <asm/firmware.h>
67 54
68#include "mmu_decl.h" 55#include "mmu_decl.h"
69 56
70unsigned long ioremap_bot = IMALLOC_BASE; 57unsigned long ioremap_bot = IOREMAP_BASE;
71static unsigned long phbs_io_bot = PHBS_IO_BASE;
72 58
73/* 59/*
74 * map_io_page currently only called by __ioremap 60 * map_io_page currently only called by __ioremap
@@ -102,8 +88,8 @@ static int map_io_page(unsigned long ea, unsigned long pa, int flags)
102 * entry in the hardware page table. 88 * entry in the hardware page table.
103 * 89 *
104 */ 90 */
105 if (htab_bolt_mapping(ea, ea + PAGE_SIZE, pa, flags, 91 if (htab_bolt_mapping(ea, (unsigned long)ea + PAGE_SIZE,
106 mmu_io_psize)) { 92 pa, flags, mmu_io_psize)) {
107 printk(KERN_ERR "Failed to do bolted mapping IO " 93 printk(KERN_ERR "Failed to do bolted mapping IO "
108 "memory at %016lx !\n", pa); 94 "memory at %016lx !\n", pa);
109 return -ENOMEM; 95 return -ENOMEM;
@@ -113,8 +99,11 @@ static int map_io_page(unsigned long ea, unsigned long pa, int flags)
113} 99}
114 100
115 101
116static void __iomem * __ioremap_com(phys_addr_t addr, unsigned long pa, 102/**
117 unsigned long ea, unsigned long size, 103 * __ioremap_at - Low level function to establish the page tables
104 * for an IO mapping
105 */
106void __iomem * __ioremap_at(phys_addr_t pa, void *ea, unsigned long size,
118 unsigned long flags) 107 unsigned long flags)
119{ 108{
120 unsigned long i; 109 unsigned long i;
@@ -122,17 +111,35 @@ static void __iomem * __ioremap_com(phys_addr_t addr, unsigned long pa,
122 if ((flags & _PAGE_PRESENT) == 0) 111 if ((flags & _PAGE_PRESENT) == 0)
123 flags |= pgprot_val(PAGE_KERNEL); 112 flags |= pgprot_val(PAGE_KERNEL);
124 113
114 WARN_ON(pa & ~PAGE_MASK);
115 WARN_ON(((unsigned long)ea) & ~PAGE_MASK);
116 WARN_ON(size & ~PAGE_MASK);
117
125 for (i = 0; i < size; i += PAGE_SIZE) 118 for (i = 0; i < size; i += PAGE_SIZE)
126 if (map_io_page(ea+i, pa+i, flags)) 119 if (map_io_page((unsigned long)ea+i, pa+i, flags))
127 return NULL; 120 return NULL;
128 121
129 return (void __iomem *) (ea + (addr & ~PAGE_MASK)); 122 return (void __iomem *)ea;
123}
124
125/**
126 * __iounmap_from - Low level function to tear down the page tables
127 * for an IO mapping. This is used for mappings that
128 * are manipulated manually, like partial unmapping of
129 * PCI IOs or ISA space.
130 */
131void __iounmap_at(void *ea, unsigned long size)
132{
133 WARN_ON(((unsigned long)ea) & ~PAGE_MASK);
134 WARN_ON(size & ~PAGE_MASK);
135
136 unmap_kernel_range((unsigned long)ea, size);
130} 137}
131 138
132void __iomem * __ioremap(phys_addr_t addr, unsigned long size, 139void __iomem * __ioremap(phys_addr_t addr, unsigned long size,
133 unsigned long flags) 140 unsigned long flags)
134{ 141{
135 unsigned long pa, ea; 142 phys_addr_t paligned;
136 void __iomem *ret; 143 void __iomem *ret;
137 144
138 /* 145 /*
@@ -144,27 +151,30 @@ void __iomem * __ioremap(phys_addr_t addr, unsigned long size,
144 * IMALLOC_END 151 * IMALLOC_END
145 * 152 *
146 */ 153 */
147 pa = addr & PAGE_MASK; 154 paligned = addr & PAGE_MASK;
148 size = PAGE_ALIGN(addr + size) - pa; 155 size = PAGE_ALIGN(addr + size) - paligned;
149 156
150 if ((size == 0) || (pa == 0)) 157 if ((size == 0) || (paligned == 0))
151 return NULL; 158 return NULL;
152 159
153 if (mem_init_done) { 160 if (mem_init_done) {
154 struct vm_struct *area; 161 struct vm_struct *area;
155 area = im_get_free_area(size); 162
163 area = __get_vm_area(size, VM_IOREMAP,
164 ioremap_bot, IOREMAP_END);
156 if (area == NULL) 165 if (area == NULL)
157 return NULL; 166 return NULL;
158 ea = (unsigned long)(area->addr); 167 ret = __ioremap_at(paligned, area->addr, size, flags);
159 ret = __ioremap_com(addr, pa, ea, size, flags);
160 if (!ret) 168 if (!ret)
161 im_free(area->addr); 169 vunmap(area->addr);
162 } else { 170 } else {
163 ea = ioremap_bot; 171 ret = __ioremap_at(paligned, (void *)ioremap_bot, size, flags);
164 ret = __ioremap_com(addr, pa, ea, size, flags);
165 if (ret) 172 if (ret)
166 ioremap_bot += size; 173 ioremap_bot += size;
167 } 174 }
175
176 if (ret)
177 ret += addr & ~PAGE_MASK;
168 return ret; 178 return ret;
169} 179}
170 180
@@ -187,61 +197,9 @@ void __iomem * ioremap_flags(phys_addr_t addr, unsigned long size,
187} 197}
188 198
189 199
190#define IS_PAGE_ALIGNED(_val) ((_val) == ((_val) & PAGE_MASK))
191
192int __ioremap_explicit(phys_addr_t pa, unsigned long ea,
193 unsigned long size, unsigned long flags)
194{
195 struct vm_struct *area;
196 void __iomem *ret;
197
198 /* For now, require page-aligned values for pa, ea, and size */
199 if (!IS_PAGE_ALIGNED(pa) || !IS_PAGE_ALIGNED(ea) ||
200 !IS_PAGE_ALIGNED(size)) {
201 printk(KERN_ERR "unaligned value in %s\n", __FUNCTION__);
202 return 1;
203 }
204
205 if (!mem_init_done) {
206 /* Two things to consider in this case:
207 * 1) No records will be kept (imalloc, etc) that the region
208 * has been remapped
209 * 2) It won't be easy to iounmap() the region later (because
210 * of 1)
211 */
212 ;
213 } else {
214 area = im_get_area(ea, size,
215 IM_REGION_UNUSED|IM_REGION_SUBSET|IM_REGION_EXISTS);
216 if (area == NULL) {
217 /* Expected when PHB-dlpar is in play */
218 return 1;
219 }
220 if (ea != (unsigned long) area->addr) {
221 printk(KERN_ERR "unexpected addr return from "
222 "im_get_area\n");
223 return 1;
224 }
225 }
226
227 ret = __ioremap_com(pa, pa, ea, size, flags);
228 if (ret == NULL) {
229 printk(KERN_ERR "ioremap_explicit() allocation failure !\n");
230 return 1;
231 }
232 if (ret != (void *) ea) {
233 printk(KERN_ERR "__ioremap_com() returned unexpected addr\n");
234 return 1;
235 }
236
237 return 0;
238}
239
240/* 200/*
241 * Unmap an IO region and remove it from imalloc'd list. 201 * Unmap an IO region and remove it from imalloc'd list.
242 * Access to IO memory should be serialized by driver. 202 * Access to IO memory should be serialized by driver.
243 *
244 * XXX what about calls before mem_init_done (ie python_countermeasures())
245 */ 203 */
246void __iounmap(volatile void __iomem *token) 204void __iounmap(volatile void __iomem *token)
247{ 205{
@@ -250,9 +208,14 @@ void __iounmap(volatile void __iomem *token)
250 if (!mem_init_done) 208 if (!mem_init_done)
251 return; 209 return;
252 210
253 addr = (void *) ((unsigned long __force) token & PAGE_MASK); 211 addr = (void *) ((unsigned long __force)
254 212 PCI_FIX_ADDR(token) & PAGE_MASK);
255 im_free(addr); 213 if ((unsigned long)addr < ioremap_bot) {
214 printk(KERN_WARNING "Attempt to iounmap early bolted mapping"
215 " at 0x%p\n", addr);
216 return;
217 }
218 vunmap(addr);
256} 219}
257 220
258void iounmap(volatile void __iomem *token) 221void iounmap(volatile void __iomem *token)
@@ -263,77 +226,8 @@ void iounmap(volatile void __iomem *token)
263 __iounmap(token); 226 __iounmap(token);
264} 227}
265 228
266static int iounmap_subset_regions(unsigned long addr, unsigned long size)
267{
268 struct vm_struct *area;
269
270 /* Check whether subsets of this region exist */
271 area = im_get_area(addr, size, IM_REGION_SUPERSET);
272 if (area == NULL)
273 return 1;
274
275 while (area) {
276 iounmap((void __iomem *) area->addr);
277 area = im_get_area(addr, size,
278 IM_REGION_SUPERSET);
279 }
280
281 return 0;
282}
283
284int __iounmap_explicit(volatile void __iomem *start, unsigned long size)
285{
286 struct vm_struct *area;
287 unsigned long addr;
288 int rc;
289
290 addr = (unsigned long __force) start & PAGE_MASK;
291
292 /* Verify that the region either exists or is a subset of an existing
293 * region. In the latter case, split the parent region to create
294 * the exact region
295 */
296 area = im_get_area(addr, size,
297 IM_REGION_EXISTS | IM_REGION_SUBSET);
298 if (area == NULL) {
299 /* Determine whether subset regions exist. If so, unmap */
300 rc = iounmap_subset_regions(addr, size);
301 if (rc) {
302 printk(KERN_ERR
303 "%s() cannot unmap nonexistent range 0x%lx\n",
304 __FUNCTION__, addr);
305 return 1;
306 }
307 } else {
308 iounmap((void __iomem *) area->addr);
309 }
310 /*
311 * FIXME! This can't be right:
312 iounmap(area->addr);
313 * Maybe it should be "iounmap(area);"
314 */
315 return 0;
316}
317
318EXPORT_SYMBOL(ioremap); 229EXPORT_SYMBOL(ioremap);
319EXPORT_SYMBOL(ioremap_flags); 230EXPORT_SYMBOL(ioremap_flags);
320EXPORT_SYMBOL(__ioremap); 231EXPORT_SYMBOL(__ioremap);
321EXPORT_SYMBOL(iounmap); 232EXPORT_SYMBOL(iounmap);
322EXPORT_SYMBOL(__iounmap); 233EXPORT_SYMBOL(__iounmap);
323
324static DEFINE_SPINLOCK(phb_io_lock);
325
326void __iomem * reserve_phb_iospace(unsigned long size)
327{
328 void __iomem *virt_addr;
329
330 if (phbs_io_bot >= IMALLOC_BASE)
331 panic("reserve_phb_iospace(): phb io space overflow\n");
332
333 spin_lock(&phb_io_lock);
334 virt_addr = (void __iomem *) phbs_io_bot;
335 phbs_io_bot += size;
336 spin_unlock(&phb_io_lock);
337
338 return virt_addr;
339}
diff --git a/arch/powerpc/mm/tlb_64.c b/arch/powerpc/mm/tlb_64.c
index 2bfc4d7e1aa2..fdecb7f764d6 100644
--- a/arch/powerpc/mm/tlb_64.c
+++ b/arch/powerpc/mm/tlb_64.c
@@ -239,3 +239,59 @@ void pte_free_finish(void)
239 pte_free_submit(*batchp); 239 pte_free_submit(*batchp);
240 *batchp = NULL; 240 *batchp = NULL;
241} 241}
242
243/**
244 * __flush_hash_table_range - Flush all HPTEs for a given address range
245 * from the hash table (and the TLB). But keeps
246 * the linux PTEs intact.
247 *
248 * @mm : mm_struct of the target address space (generally init_mm)
249 * @start : starting address
250 * @end : ending address (not included in the flush)
251 *
252 * This function is mostly to be used by some IO hotplug code in order
253 * to remove all hash entries from a given address range used to map IO
254 * space on a removed PCI-PCI bidge without tearing down the full mapping
255 * since 64K pages may overlap with other bridges when using 64K pages
256 * with 4K HW pages on IO space.
257 *
258 * Because of that usage pattern, it's only available with CONFIG_HOTPLUG
259 * and is implemented for small size rather than speed.
260 */
261#ifdef CONFIG_HOTPLUG
262
263void __flush_hash_table_range(struct mm_struct *mm, unsigned long start,
264 unsigned long end)
265{
266 unsigned long flags;
267
268 start = _ALIGN_DOWN(start, PAGE_SIZE);
269 end = _ALIGN_UP(end, PAGE_SIZE);
270
271 BUG_ON(!mm->pgd);
272
273 /* Note: Normally, we should only ever use a batch within a
274 * PTE locked section. This violates the rule, but will work
275 * since we don't actually modify the PTEs, we just flush the
276 * hash while leaving the PTEs intact (including their reference
277 * to being hashed). This is not the most performance oriented
278 * way to do things but is fine for our needs here.
279 */
280 local_irq_save(flags);
281 arch_enter_lazy_mmu_mode();
282 for (; start < end; start += PAGE_SIZE) {
283 pte_t *ptep = find_linux_pte(mm->pgd, start);
284 unsigned long pte;
285
286 if (ptep == NULL)
287 continue;
288 pte = pte_val(*ptep);
289 if (!(pte & _PAGE_HASHPTE))
290 continue;
291 hpte_need_flush(mm, start, ptep, pte, 0);
292 }
293 arch_leave_lazy_mmu_mode();
294 local_irq_restore(flags);
295}
296
297#endif /* CONFIG_HOTPLUG */