diff options
-rw-r--r-- | drivers/acpi/osl.c | 128 |
1 files changed, 113 insertions, 15 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 58842fb9fd9..bd72129e35f 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -95,6 +95,20 @@ struct acpi_res_list { | |||
95 | static LIST_HEAD(resource_list_head); | 95 | static LIST_HEAD(resource_list_head); |
96 | static DEFINE_SPINLOCK(acpi_res_lock); | 96 | static DEFINE_SPINLOCK(acpi_res_lock); |
97 | 97 | ||
98 | /* | ||
99 | * This list of permanent mappings is for memory that may be accessed from | ||
100 | * interrupt context, where we can't do the ioremap(). | ||
101 | */ | ||
102 | struct acpi_ioremap { | ||
103 | struct list_head list; | ||
104 | void __iomem *virt; | ||
105 | acpi_physical_address phys; | ||
106 | acpi_size size; | ||
107 | }; | ||
108 | |||
109 | static LIST_HEAD(acpi_ioremaps); | ||
110 | static DEFINE_SPINLOCK(acpi_ioremap_lock); | ||
111 | |||
98 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ | 112 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ |
99 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; | 113 | static char osi_additional_string[OSI_STRING_LENGTH_MAX]; |
100 | 114 | ||
@@ -260,29 +274,95 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) | |||
260 | } | 274 | } |
261 | } | 275 | } |
262 | 276 | ||
277 | /* Must be called with 'acpi_ioremap_lock' lock held. */ | ||
278 | static void __iomem * | ||
279 | acpi_map_vaddr_lookup(acpi_physical_address phys, acpi_size size) | ||
280 | { | ||
281 | struct acpi_ioremap *map; | ||
282 | |||
283 | list_for_each_entry(map, &acpi_ioremaps, list) | ||
284 | if (map->phys <= phys && | ||
285 | phys + size <= map->phys + map->size) | ||
286 | return map->virt + (phys - map->phys); | ||
287 | |||
288 | return NULL; | ||
289 | } | ||
290 | |||
291 | /* Must be called with 'acpi_ioremap_lock' lock held. */ | ||
292 | static struct acpi_ioremap * | ||
293 | acpi_map_lookup_virt(void __iomem *virt, acpi_size size) | ||
294 | { | ||
295 | struct acpi_ioremap *map; | ||
296 | |||
297 | list_for_each_entry(map, &acpi_ioremaps, list) | ||
298 | if (map->virt == virt && map->size == size) | ||
299 | return map; | ||
300 | |||
301 | return NULL; | ||
302 | } | ||
303 | |||
263 | void __iomem *__init_refok | 304 | void __iomem *__init_refok |
264 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | 305 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) |
265 | { | 306 | { |
307 | struct acpi_ioremap *map; | ||
308 | unsigned long flags; | ||
309 | void __iomem *virt; | ||
310 | |||
266 | if (phys > ULONG_MAX) { | 311 | if (phys > ULONG_MAX) { |
267 | printk(KERN_ERR PREFIX "Cannot map memory that high\n"); | 312 | printk(KERN_ERR PREFIX "Cannot map memory that high\n"); |
268 | return NULL; | 313 | return NULL; |
269 | } | 314 | } |
270 | if (acpi_gbl_permanent_mmap) | 315 | |
271 | /* | 316 | if (!acpi_gbl_permanent_mmap) |
272 | * ioremap checks to ensure this is in reserved space | ||
273 | */ | ||
274 | return ioremap((unsigned long)phys, size); | ||
275 | else | ||
276 | return __acpi_map_table((unsigned long)phys, size); | 317 | return __acpi_map_table((unsigned long)phys, size); |
318 | |||
319 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
320 | if (!map) | ||
321 | return NULL; | ||
322 | |||
323 | virt = ioremap(phys, size); | ||
324 | if (!virt) { | ||
325 | kfree(map); | ||
326 | return NULL; | ||
327 | } | ||
328 | |||
329 | INIT_LIST_HEAD(&map->list); | ||
330 | map->virt = virt; | ||
331 | map->phys = phys; | ||
332 | map->size = size; | ||
333 | |||
334 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
335 | list_add_tail(&map->list, &acpi_ioremaps); | ||
336 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
337 | |||
338 | return virt; | ||
277 | } | 339 | } |
278 | EXPORT_SYMBOL_GPL(acpi_os_map_memory); | 340 | EXPORT_SYMBOL_GPL(acpi_os_map_memory); |
279 | 341 | ||
280 | void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) | 342 | void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) |
281 | { | 343 | { |
282 | if (acpi_gbl_permanent_mmap) | 344 | struct acpi_ioremap *map; |
283 | iounmap(virt); | 345 | unsigned long flags; |
284 | else | 346 | |
347 | if (!acpi_gbl_permanent_mmap) { | ||
285 | __acpi_unmap_table(virt, size); | 348 | __acpi_unmap_table(virt, size); |
349 | return; | ||
350 | } | ||
351 | |||
352 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
353 | map = acpi_map_lookup_virt(virt, size); | ||
354 | if (!map) { | ||
355 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
356 | printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); | ||
357 | dump_stack(); | ||
358 | return; | ||
359 | } | ||
360 | |||
361 | list_del(&map->list); | ||
362 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
363 | |||
364 | iounmap(map->virt); | ||
365 | kfree(map); | ||
286 | } | 366 | } |
287 | EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); | 367 | EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); |
288 | 368 | ||
@@ -495,8 +575,16 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) | |||
495 | { | 575 | { |
496 | u32 dummy; | 576 | u32 dummy; |
497 | void __iomem *virt_addr; | 577 | void __iomem *virt_addr; |
498 | 578 | int size = width / 8, unmap = 0; | |
499 | virt_addr = ioremap(phys_addr, width / 8); | 579 | unsigned long flags; |
580 | |||
581 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
582 | virt_addr = acpi_map_vaddr_lookup(phys_addr, size); | ||
583 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
584 | if (!virt_addr) { | ||
585 | virt_addr = ioremap(phys_addr, size); | ||
586 | unmap = 1; | ||
587 | } | ||
500 | if (!value) | 588 | if (!value) |
501 | value = &dummy; | 589 | value = &dummy; |
502 | 590 | ||
@@ -514,7 +602,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) | |||
514 | BUG(); | 602 | BUG(); |
515 | } | 603 | } |
516 | 604 | ||
517 | iounmap(virt_addr); | 605 | if (unmap) |
606 | iounmap(virt_addr); | ||
518 | 607 | ||
519 | return AE_OK; | 608 | return AE_OK; |
520 | } | 609 | } |
@@ -523,8 +612,16 @@ acpi_status | |||
523 | acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) | 612 | acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) |
524 | { | 613 | { |
525 | void __iomem *virt_addr; | 614 | void __iomem *virt_addr; |
526 | 615 | int size = width / 8, unmap = 0; | |
527 | virt_addr = ioremap(phys_addr, width / 8); | 616 | unsigned long flags; |
617 | |||
618 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
619 | virt_addr = acpi_map_vaddr_lookup(phys_addr, size); | ||
620 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
621 | if (!virt_addr) { | ||
622 | virt_addr = ioremap(phys_addr, size); | ||
623 | unmap = 1; | ||
624 | } | ||
528 | 625 | ||
529 | switch (width) { | 626 | switch (width) { |
530 | case 8: | 627 | case 8: |
@@ -540,7 +637,8 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) | |||
540 | BUG(); | 637 | BUG(); |
541 | } | 638 | } |
542 | 639 | ||
543 | iounmap(virt_addr); | 640 | if (unmap) |
641 | iounmap(virt_addr); | ||
544 | 642 | ||
545 | return AE_OK; | 643 | return AE_OK; |
546 | } | 644 | } |