diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/acpi/osl.c | 280 |
1 files changed, 232 insertions, 48 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index d3bed219c442..0c2e445410ab 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
| @@ -95,6 +95,21 @@ 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 | struct kref ref; | ||
| 108 | }; | ||
| 109 | |||
| 110 | static LIST_HEAD(acpi_ioremaps); | ||
| 111 | static DEFINE_SPINLOCK(acpi_ioremap_lock); | ||
| 112 | |||
| 98 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ | 113 | #define OSI_STRING_LENGTH_MAX 64 /* arbitrary */ |
| 99 | static char osi_setup_string[OSI_STRING_LENGTH_MAX]; | 114 | static char osi_setup_string[OSI_STRING_LENGTH_MAX]; |
| 100 | 115 | ||
| @@ -201,38 +216,6 @@ static int __init acpi_reserve_resources(void) | |||
| 201 | } | 216 | } |
| 202 | device_initcall(acpi_reserve_resources); | 217 | device_initcall(acpi_reserve_resources); |
| 203 | 218 | ||
| 204 | acpi_status __init acpi_os_initialize(void) | ||
| 205 | { | ||
| 206 | return AE_OK; | ||
| 207 | } | ||
| 208 | |||
| 209 | acpi_status acpi_os_initialize1(void) | ||
| 210 | { | ||
| 211 | kacpid_wq = create_workqueue("kacpid"); | ||
| 212 | kacpi_notify_wq = create_workqueue("kacpi_notify"); | ||
| 213 | kacpi_hotplug_wq = create_workqueue("kacpi_hotplug"); | ||
| 214 | BUG_ON(!kacpid_wq); | ||
| 215 | BUG_ON(!kacpi_notify_wq); | ||
| 216 | BUG_ON(!kacpi_hotplug_wq); | ||
| 217 | acpi_install_interface_handler(acpi_osi_handler); | ||
| 218 | acpi_osi_setup_late(); | ||
| 219 | return AE_OK; | ||
| 220 | } | ||
| 221 | |||
| 222 | acpi_status acpi_os_terminate(void) | ||
| 223 | { | ||
| 224 | if (acpi_irq_handler) { | ||
| 225 | acpi_os_remove_interrupt_handler(acpi_irq_irq, | ||
| 226 | acpi_irq_handler); | ||
| 227 | } | ||
| 228 | |||
| 229 | destroy_workqueue(kacpid_wq); | ||
| 230 | destroy_workqueue(kacpi_notify_wq); | ||
| 231 | destroy_workqueue(kacpi_hotplug_wq); | ||
| 232 | |||
| 233 | return AE_OK; | ||
| 234 | } | ||
| 235 | |||
| 236 | void acpi_os_printf(const char *fmt, ...) | 219 | void acpi_os_printf(const char *fmt, ...) |
| 237 | { | 220 | { |
| 238 | va_list args; | 221 | va_list args; |
| @@ -278,29 +261,135 @@ acpi_physical_address __init acpi_os_get_root_pointer(void) | |||
| 278 | } | 261 | } |
| 279 | } | 262 | } |
| 280 | 263 | ||
| 264 | /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ | ||
| 265 | static struct acpi_ioremap * | ||
| 266 | acpi_map_lookup(acpi_physical_address phys, acpi_size size) | ||
| 267 | { | ||
| 268 | struct acpi_ioremap *map; | ||
| 269 | |||
| 270 | list_for_each_entry_rcu(map, &acpi_ioremaps, list) | ||
| 271 | if (map->phys <= phys && | ||
| 272 | phys + size <= map->phys + map->size) | ||
| 273 | return map; | ||
| 274 | |||
| 275 | return NULL; | ||
| 276 | } | ||
| 277 | |||
| 278 | /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ | ||
| 279 | static void __iomem * | ||
| 280 | acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) | ||
| 281 | { | ||
| 282 | struct acpi_ioremap *map; | ||
| 283 | |||
| 284 | map = acpi_map_lookup(phys, size); | ||
| 285 | if (map) | ||
| 286 | return map->virt + (phys - map->phys); | ||
| 287 | |||
| 288 | return NULL; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* Must be called with 'acpi_ioremap_lock' or RCU read 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_rcu(map, &acpi_ioremaps, list) | ||
| 298 | if (map->virt <= virt && | ||
| 299 | virt + size <= map->virt + map->size) | ||
| 300 | return map; | ||
| 301 | |||
| 302 | return NULL; | ||
| 303 | } | ||
| 304 | |||
| 281 | void __iomem *__init_refok | 305 | void __iomem *__init_refok |
| 282 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | 306 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) |
| 283 | { | 307 | { |
| 308 | struct acpi_ioremap *map, *tmp_map; | ||
| 309 | unsigned long flags, pg_sz; | ||
| 310 | void __iomem *virt; | ||
| 311 | phys_addr_t pg_off; | ||
| 312 | |||
| 284 | if (phys > ULONG_MAX) { | 313 | if (phys > ULONG_MAX) { |
| 285 | printk(KERN_ERR PREFIX "Cannot map memory that high\n"); | 314 | printk(KERN_ERR PREFIX "Cannot map memory that high\n"); |
| 286 | return NULL; | 315 | return NULL; |
| 287 | } | 316 | } |
| 288 | if (acpi_gbl_permanent_mmap) | 317 | |
| 289 | /* | 318 | if (!acpi_gbl_permanent_mmap) |
| 290 | * ioremap checks to ensure this is in reserved space | ||
| 291 | */ | ||
| 292 | return ioremap((unsigned long)phys, size); | ||
| 293 | else | ||
| 294 | return __acpi_map_table((unsigned long)phys, size); | 319 | return __acpi_map_table((unsigned long)phys, size); |
| 320 | |||
| 321 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
| 322 | if (!map) | ||
| 323 | return NULL; | ||
| 324 | |||
| 325 | pg_off = round_down(phys, PAGE_SIZE); | ||
| 326 | pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; | ||
| 327 | virt = ioremap(pg_off, pg_sz); | ||
| 328 | if (!virt) { | ||
| 329 | kfree(map); | ||
| 330 | return NULL; | ||
| 331 | } | ||
| 332 | |||
| 333 | INIT_LIST_HEAD(&map->list); | ||
| 334 | map->virt = virt; | ||
| 335 | map->phys = pg_off; | ||
| 336 | map->size = pg_sz; | ||
| 337 | kref_init(&map->ref); | ||
| 338 | |||
| 339 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
| 340 | /* Check if page has already been mapped. */ | ||
| 341 | tmp_map = acpi_map_lookup(phys, size); | ||
| 342 | if (tmp_map) { | ||
| 343 | kref_get(&tmp_map->ref); | ||
| 344 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
| 345 | iounmap(map->virt); | ||
| 346 | kfree(map); | ||
| 347 | return tmp_map->virt + (phys - tmp_map->phys); | ||
| 348 | } | ||
| 349 | list_add_tail_rcu(&map->list, &acpi_ioremaps); | ||
| 350 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
| 351 | |||
| 352 | return map->virt + (phys - map->phys); | ||
| 295 | } | 353 | } |
| 296 | EXPORT_SYMBOL_GPL(acpi_os_map_memory); | 354 | EXPORT_SYMBOL_GPL(acpi_os_map_memory); |
| 297 | 355 | ||
| 356 | static void acpi_kref_del_iomap(struct kref *ref) | ||
| 357 | { | ||
| 358 | struct acpi_ioremap *map; | ||
| 359 | |||
| 360 | map = container_of(ref, struct acpi_ioremap, ref); | ||
| 361 | list_del_rcu(&map->list); | ||
| 362 | } | ||
| 363 | |||
| 298 | void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) | 364 | void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) |
| 299 | { | 365 | { |
| 300 | if (acpi_gbl_permanent_mmap) | 366 | struct acpi_ioremap *map; |
| 301 | iounmap(virt); | 367 | unsigned long flags; |
| 302 | else | 368 | int del; |
| 369 | |||
| 370 | if (!acpi_gbl_permanent_mmap) { | ||
| 303 | __acpi_unmap_table(virt, size); | 371 | __acpi_unmap_table(virt, size); |
| 372 | return; | ||
| 373 | } | ||
| 374 | |||
| 375 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
| 376 | map = acpi_map_lookup_virt(virt, size); | ||
| 377 | if (!map) { | ||
| 378 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
| 379 | printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); | ||
| 380 | dump_stack(); | ||
| 381 | return; | ||
| 382 | } | ||
| 383 | |||
| 384 | del = kref_put(&map->ref, acpi_kref_del_iomap); | ||
| 385 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
| 386 | |||
| 387 | if (!del) | ||
| 388 | return; | ||
| 389 | |||
| 390 | synchronize_rcu(); | ||
| 391 | iounmap(map->virt); | ||
| 392 | kfree(map); | ||
| 304 | } | 393 | } |
| 305 | EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); | 394 | EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); |
| 306 | 395 | ||
| @@ -310,6 +399,44 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) | |||
| 310 | __acpi_unmap_table(virt, size); | 399 | __acpi_unmap_table(virt, size); |
| 311 | } | 400 | } |
| 312 | 401 | ||
| 402 | int acpi_os_map_generic_address(struct acpi_generic_address *addr) | ||
| 403 | { | ||
| 404 | void __iomem *virt; | ||
| 405 | |||
| 406 | if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) | ||
| 407 | return 0; | ||
| 408 | |||
| 409 | if (!addr->address || !addr->bit_width) | ||
| 410 | return -EINVAL; | ||
| 411 | |||
| 412 | virt = acpi_os_map_memory(addr->address, addr->bit_width / 8); | ||
| 413 | if (!virt) | ||
| 414 | return -EIO; | ||
| 415 | |||
| 416 | return 0; | ||
| 417 | } | ||
| 418 | EXPORT_SYMBOL_GPL(acpi_os_map_generic_address); | ||
| 419 | |||
| 420 | void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) | ||
| 421 | { | ||
| 422 | void __iomem *virt; | ||
| 423 | unsigned long flags; | ||
| 424 | acpi_size size = addr->bit_width / 8; | ||
| 425 | |||
| 426 | if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) | ||
| 427 | return; | ||
| 428 | |||
| 429 | if (!addr->address || !addr->bit_width) | ||
| 430 | return; | ||
| 431 | |||
| 432 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
| 433 | virt = acpi_map_vaddr_lookup(addr->address, size); | ||
| 434 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
| 435 | |||
| 436 | acpi_os_unmap_memory(virt, size); | ||
| 437 | } | ||
| 438 | EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address); | ||
| 439 | |||
| 313 | #ifdef ACPI_FUTURE_USAGE | 440 | #ifdef ACPI_FUTURE_USAGE |
| 314 | acpi_status | 441 | acpi_status |
| 315 | acpi_os_get_physical_address(void *virt, acpi_physical_address * phys) | 442 | acpi_os_get_physical_address(void *virt, acpi_physical_address * phys) |
| @@ -513,8 +640,15 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) | |||
| 513 | { | 640 | { |
| 514 | u32 dummy; | 641 | u32 dummy; |
| 515 | void __iomem *virt_addr; | 642 | void __iomem *virt_addr; |
| 516 | 643 | int size = width / 8, unmap = 0; | |
| 517 | virt_addr = ioremap(phys_addr, width); | 644 | |
| 645 | rcu_read_lock(); | ||
| 646 | virt_addr = acpi_map_vaddr_lookup(phys_addr, size); | ||
| 647 | rcu_read_unlock(); | ||
| 648 | if (!virt_addr) { | ||
| 649 | virt_addr = ioremap(phys_addr, size); | ||
| 650 | unmap = 1; | ||
| 651 | } | ||
| 518 | if (!value) | 652 | if (!value) |
| 519 | value = &dummy; | 653 | value = &dummy; |
| 520 | 654 | ||
| @@ -532,7 +666,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u32 * value, u32 width) | |||
| 532 | BUG(); | 666 | BUG(); |
| 533 | } | 667 | } |
| 534 | 668 | ||
| 535 | iounmap(virt_addr); | 669 | if (unmap) |
| 670 | iounmap(virt_addr); | ||
| 536 | 671 | ||
| 537 | return AE_OK; | 672 | return AE_OK; |
| 538 | } | 673 | } |
| @@ -541,8 +676,15 @@ acpi_status | |||
| 541 | acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) | 676 | acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) |
| 542 | { | 677 | { |
| 543 | void __iomem *virt_addr; | 678 | void __iomem *virt_addr; |
| 544 | 679 | int size = width / 8, unmap = 0; | |
| 545 | virt_addr = ioremap(phys_addr, width); | 680 | |
| 681 | rcu_read_lock(); | ||
| 682 | virt_addr = acpi_map_vaddr_lookup(phys_addr, size); | ||
| 683 | rcu_read_unlock(); | ||
| 684 | if (!virt_addr) { | ||
| 685 | virt_addr = ioremap(phys_addr, size); | ||
| 686 | unmap = 1; | ||
| 687 | } | ||
| 546 | 688 | ||
| 547 | switch (width) { | 689 | switch (width) { |
| 548 | case 8: | 690 | case 8: |
| @@ -558,7 +700,8 @@ acpi_os_write_memory(acpi_physical_address phys_addr, u32 value, u32 width) | |||
| 558 | BUG(); | 700 | BUG(); |
| 559 | } | 701 | } |
| 560 | 702 | ||
| 561 | iounmap(virt_addr); | 703 | if (unmap) |
| 704 | iounmap(virt_addr); | ||
| 562 | 705 | ||
| 563 | return AE_OK; | 706 | return AE_OK; |
| 564 | } | 707 | } |
| @@ -1400,5 +1543,46 @@ acpi_os_validate_address ( | |||
| 1400 | } | 1543 | } |
| 1401 | return AE_OK; | 1544 | return AE_OK; |
| 1402 | } | 1545 | } |
| 1403 | |||
| 1404 | #endif | 1546 | #endif |
| 1547 | |||
| 1548 | acpi_status __init acpi_os_initialize(void) | ||
| 1549 | { | ||
| 1550 | acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1a_event_block); | ||
| 1551 | acpi_os_map_generic_address(&acpi_gbl_FADT.xpm1b_event_block); | ||
| 1552 | acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe0_block); | ||
| 1553 | acpi_os_map_generic_address(&acpi_gbl_FADT.xgpe1_block); | ||
| 1554 | |||
| 1555 | return AE_OK; | ||
| 1556 | } | ||
| 1557 | |||
| 1558 | acpi_status acpi_os_initialize1(void) | ||
| 1559 | { | ||
| 1560 | kacpid_wq = create_workqueue("kacpid"); | ||
| 1561 | kacpi_notify_wq = create_workqueue("kacpi_notify"); | ||
| 1562 | kacpi_hotplug_wq = create_workqueue("kacpi_hotplug"); | ||
| 1563 | BUG_ON(!kacpid_wq); | ||
| 1564 | BUG_ON(!kacpi_notify_wq); | ||
| 1565 | BUG_ON(!kacpi_hotplug_wq); | ||
| 1566 | acpi_install_interface_handler(acpi_osi_handler); | ||
| 1567 | acpi_osi_setup_late(); | ||
| 1568 | return AE_OK; | ||
| 1569 | } | ||
| 1570 | |||
| 1571 | acpi_status acpi_os_terminate(void) | ||
| 1572 | { | ||
| 1573 | if (acpi_irq_handler) { | ||
| 1574 | acpi_os_remove_interrupt_handler(acpi_irq_irq, | ||
| 1575 | acpi_irq_handler); | ||
| 1576 | } | ||
| 1577 | |||
| 1578 | acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe1_block); | ||
| 1579 | acpi_os_unmap_generic_address(&acpi_gbl_FADT.xgpe0_block); | ||
| 1580 | acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1b_event_block); | ||
| 1581 | acpi_os_unmap_generic_address(&acpi_gbl_FADT.xpm1a_event_block); | ||
| 1582 | |||
| 1583 | destroy_workqueue(kacpid_wq); | ||
| 1584 | destroy_workqueue(kacpi_notify_wq); | ||
| 1585 | destroy_workqueue(kacpi_hotplug_wq); | ||
| 1586 | |||
| 1587 | return AE_OK; | ||
| 1588 | } | ||
