diff options
Diffstat (limited to 'arch/sh')
-rw-r--r-- | arch/sh/mm/pmb.c | 140 |
1 files changed, 96 insertions, 44 deletions
diff --git a/arch/sh/mm/pmb.c b/arch/sh/mm/pmb.c index 55d21902d707..75b8861ec624 100644 --- a/arch/sh/mm/pmb.c +++ b/arch/sh/mm/pmb.c | |||
@@ -128,13 +128,67 @@ static inline unsigned long pgprot_to_pmb_flags(pgprot_t prot) | |||
128 | return pmb_flags; | 128 | return pmb_flags; |
129 | } | 129 | } |
130 | 130 | ||
131 | static bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b) | 131 | static inline bool pmb_can_merge(struct pmb_entry *a, struct pmb_entry *b) |
132 | { | 132 | { |
133 | return (b->vpn == (a->vpn + a->size)) && | 133 | return (b->vpn == (a->vpn + a->size)) && |
134 | (b->ppn == (a->ppn + a->size)) && | 134 | (b->ppn == (a->ppn + a->size)) && |
135 | (b->flags == a->flags); | 135 | (b->flags == a->flags); |
136 | } | 136 | } |
137 | 137 | ||
138 | static bool pmb_mapping_exists(unsigned long vaddr, phys_addr_t phys, | ||
139 | unsigned long size) | ||
140 | { | ||
141 | int i; | ||
142 | |||
143 | read_lock(&pmb_rwlock); | ||
144 | |||
145 | for (i = 0; i < ARRAY_SIZE(pmb_entry_list); i++) { | ||
146 | struct pmb_entry *pmbe, *iter; | ||
147 | unsigned long span; | ||
148 | |||
149 | if (!test_bit(i, pmb_map)) | ||
150 | continue; | ||
151 | |||
152 | pmbe = &pmb_entry_list[i]; | ||
153 | |||
154 | /* | ||
155 | * See if VPN and PPN are bounded by an existing mapping. | ||
156 | */ | ||
157 | if ((vaddr < pmbe->vpn) || (vaddr >= (pmbe->vpn + pmbe->size))) | ||
158 | continue; | ||
159 | if ((phys < pmbe->ppn) || (phys >= (pmbe->ppn + pmbe->size))) | ||
160 | continue; | ||
161 | |||
162 | /* | ||
163 | * Now see if we're in range of a simple mapping. | ||
164 | */ | ||
165 | if (size <= pmbe->size) { | ||
166 | read_unlock(&pmb_rwlock); | ||
167 | return true; | ||
168 | } | ||
169 | |||
170 | span = pmbe->size; | ||
171 | |||
172 | /* | ||
173 | * Finally for sizes that involve compound mappings, walk | ||
174 | * the chain. | ||
175 | */ | ||
176 | for (iter = pmbe->link; iter; iter = iter->link) | ||
177 | span += iter->size; | ||
178 | |||
179 | /* | ||
180 | * Nothing else to do if the range requirements are met. | ||
181 | */ | ||
182 | if (size <= span) { | ||
183 | read_unlock(&pmb_rwlock); | ||
184 | return true; | ||
185 | } | ||
186 | } | ||
187 | |||
188 | read_unlock(&pmb_rwlock); | ||
189 | return false; | ||
190 | } | ||
191 | |||
138 | static bool pmb_size_valid(unsigned long size) | 192 | static bool pmb_size_valid(unsigned long size) |
139 | { | 193 | { |
140 | int i; | 194 | int i; |
@@ -272,64 +326,62 @@ int pmb_bolt_mapping(unsigned long vaddr, phys_addr_t phys, | |||
272 | unsigned long size, pgprot_t prot) | 326 | unsigned long size, pgprot_t prot) |
273 | { | 327 | { |
274 | struct pmb_entry *pmbp, *pmbe; | 328 | struct pmb_entry *pmbp, *pmbe; |
275 | unsigned long pmb_flags; | 329 | unsigned long flags, pmb_flags; |
276 | int i, mapped; | 330 | int i, mapped; |
277 | 331 | ||
278 | if (!pmb_addr_valid(vaddr, size)) | 332 | if (!pmb_addr_valid(vaddr, size)) |
279 | return -EFAULT; | 333 | return -EFAULT; |
334 | if (pmb_mapping_exists(vaddr, phys, size)) | ||
335 | return 0; | ||
280 | 336 | ||
281 | pmb_flags = pgprot_to_pmb_flags(prot); | 337 | pmb_flags = pgprot_to_pmb_flags(prot); |
282 | pmbp = NULL; | 338 | pmbp = NULL; |
283 | 339 | ||
284 | again: | 340 | do { |
285 | for (i = mapped = 0; i < ARRAY_SIZE(pmb_sizes); i++) { | 341 | for (i = mapped = 0; i < ARRAY_SIZE(pmb_sizes); i++) { |
286 | unsigned long flags; | 342 | if (size < pmb_sizes[i].size) |
287 | 343 | continue; | |
288 | if (size < pmb_sizes[i].size) | 344 | |
289 | continue; | 345 | pmbe = pmb_alloc(vaddr, phys, pmb_flags | |
290 | 346 | pmb_sizes[i].flag, PMB_NO_ENTRY); | |
291 | pmbe = pmb_alloc(vaddr, phys, pmb_flags | pmb_sizes[i].flag, | 347 | if (IS_ERR(pmbe)) { |
292 | PMB_NO_ENTRY); | 348 | pmb_unmap_entry(pmbp, mapped); |
293 | if (IS_ERR(pmbe)) { | 349 | return PTR_ERR(pmbe); |
294 | pmb_unmap_entry(pmbp, mapped); | 350 | } |
295 | return PTR_ERR(pmbe); | ||
296 | } | ||
297 | |||
298 | spin_lock_irqsave(&pmbe->lock, flags); | ||
299 | 351 | ||
300 | pmbe->size = pmb_sizes[i].size; | 352 | spin_lock_irqsave(&pmbe->lock, flags); |
301 | 353 | ||
302 | __set_pmb_entry(pmbe); | 354 | pmbe->size = pmb_sizes[i].size; |
303 | 355 | ||
304 | phys += pmbe->size; | 356 | __set_pmb_entry(pmbe); |
305 | vaddr += pmbe->size; | ||
306 | size -= pmbe->size; | ||
307 | 357 | ||
308 | /* | 358 | phys += pmbe->size; |
309 | * Link adjacent entries that span multiple PMB entries | 359 | vaddr += pmbe->size; |
310 | * for easier tear-down. | 360 | size -= pmbe->size; |
311 | */ | ||
312 | if (likely(pmbp)) { | ||
313 | spin_lock(&pmbp->lock); | ||
314 | pmbp->link = pmbe; | ||
315 | spin_unlock(&pmbp->lock); | ||
316 | } | ||
317 | 361 | ||
318 | pmbp = pmbe; | 362 | /* |
363 | * Link adjacent entries that span multiple PMB | ||
364 | * entries for easier tear-down. | ||
365 | */ | ||
366 | if (likely(pmbp)) { | ||
367 | spin_lock(&pmbp->lock); | ||
368 | pmbp->link = pmbe; | ||
369 | spin_unlock(&pmbp->lock); | ||
370 | } | ||
319 | 371 | ||
320 | /* | 372 | pmbp = pmbe; |
321 | * Instead of trying smaller sizes on every iteration | ||
322 | * (even if we succeed in allocating space), try using | ||
323 | * pmb_sizes[i].size again. | ||
324 | */ | ||
325 | i--; | ||
326 | mapped++; | ||
327 | 373 | ||
328 | spin_unlock_irqrestore(&pmbe->lock, flags); | 374 | /* |
329 | } | 375 | * Instead of trying smaller sizes on every |
376 | * iteration (even if we succeed in allocating | ||
377 | * space), try using pmb_sizes[i].size again. | ||
378 | */ | ||
379 | i--; | ||
380 | mapped++; | ||
330 | 381 | ||
331 | if (size >= SZ_16M) | 382 | spin_unlock_irqrestore(&pmbe->lock, flags); |
332 | goto again; | 383 | } |
384 | } while (size >= SZ_16M); | ||
333 | 385 | ||
334 | return 0; | 386 | return 0; |
335 | } | 387 | } |
@@ -374,7 +426,7 @@ void __iomem *pmb_remap_caller(phys_addr_t phys, unsigned long size, | |||
374 | orig_addr = vaddr = (unsigned long)area->addr; | 426 | orig_addr = vaddr = (unsigned long)area->addr; |
375 | 427 | ||
376 | ret = pmb_bolt_mapping(vaddr, phys, size, prot); | 428 | ret = pmb_bolt_mapping(vaddr, phys, size, prot); |
377 | if (ret != 0) | 429 | if (unlikely(ret != 0)) |
378 | return ERR_PTR(ret); | 430 | return ERR_PTR(ret); |
379 | 431 | ||
380 | return (void __iomem *)(offset + (char *)orig_addr); | 432 | return (void __iomem *)(offset + (char *)orig_addr); |