diff options
Diffstat (limited to 'drivers/acpi/osl.c')
-rw-r--r-- | drivers/acpi/osl.c | 139 |
1 files changed, 79 insertions, 60 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 4a6753009d79..45ad4ffef533 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -76,7 +76,6 @@ EXPORT_SYMBOL(acpi_in_debugger); | |||
76 | extern char line_buf[80]; | 76 | extern char line_buf[80]; |
77 | #endif /*ENABLE_DEBUGGER */ | 77 | #endif /*ENABLE_DEBUGGER */ |
78 | 78 | ||
79 | static unsigned int acpi_irq_irq; | ||
80 | static acpi_osd_handler acpi_irq_handler; | 79 | static acpi_osd_handler acpi_irq_handler; |
81 | static void *acpi_irq_context; | 80 | static void *acpi_irq_context; |
82 | static struct workqueue_struct *kacpid_wq; | 81 | static struct workqueue_struct *kacpid_wq; |
@@ -105,11 +104,11 @@ struct acpi_ioremap { | |||
105 | void __iomem *virt; | 104 | void __iomem *virt; |
106 | acpi_physical_address phys; | 105 | acpi_physical_address phys; |
107 | acpi_size size; | 106 | acpi_size size; |
108 | struct kref ref; | 107 | unsigned long refcount; |
109 | }; | 108 | }; |
110 | 109 | ||
111 | static LIST_HEAD(acpi_ioremaps); | 110 | static LIST_HEAD(acpi_ioremaps); |
112 | static DEFINE_SPINLOCK(acpi_ioremap_lock); | 111 | static DEFINE_MUTEX(acpi_ioremap_lock); |
113 | 112 | ||
114 | static void __init acpi_osi_setup_late(void); | 113 | static void __init acpi_osi_setup_late(void); |
115 | 114 | ||
@@ -285,6 +284,22 @@ acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) | |||
285 | return NULL; | 284 | return NULL; |
286 | } | 285 | } |
287 | 286 | ||
287 | void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size) | ||
288 | { | ||
289 | struct acpi_ioremap *map; | ||
290 | void __iomem *virt = NULL; | ||
291 | |||
292 | mutex_lock(&acpi_ioremap_lock); | ||
293 | map = acpi_map_lookup(phys, size); | ||
294 | if (map) { | ||
295 | virt = map->virt + (phys - map->phys); | ||
296 | map->refcount++; | ||
297 | } | ||
298 | mutex_unlock(&acpi_ioremap_lock); | ||
299 | return virt; | ||
300 | } | ||
301 | EXPORT_SYMBOL_GPL(acpi_os_get_iomem); | ||
302 | |||
288 | /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ | 303 | /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ |
289 | static struct acpi_ioremap * | 304 | static struct acpi_ioremap * |
290 | acpi_map_lookup_virt(void __iomem *virt, acpi_size size) | 305 | acpi_map_lookup_virt(void __iomem *virt, acpi_size size) |
@@ -302,8 +317,7 @@ acpi_map_lookup_virt(void __iomem *virt, acpi_size size) | |||
302 | void __iomem *__init_refok | 317 | void __iomem *__init_refok |
303 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | 318 | acpi_os_map_memory(acpi_physical_address phys, acpi_size size) |
304 | { | 319 | { |
305 | struct acpi_ioremap *map, *tmp_map; | 320 | struct acpi_ioremap *map; |
306 | unsigned long flags; | ||
307 | void __iomem *virt; | 321 | void __iomem *virt; |
308 | acpi_physical_address pg_off; | 322 | acpi_physical_address pg_off; |
309 | acpi_size pg_sz; | 323 | acpi_size pg_sz; |
@@ -316,14 +330,25 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | |||
316 | if (!acpi_gbl_permanent_mmap) | 330 | if (!acpi_gbl_permanent_mmap) |
317 | return __acpi_map_table((unsigned long)phys, size); | 331 | return __acpi_map_table((unsigned long)phys, size); |
318 | 332 | ||
333 | mutex_lock(&acpi_ioremap_lock); | ||
334 | /* Check if there's a suitable mapping already. */ | ||
335 | map = acpi_map_lookup(phys, size); | ||
336 | if (map) { | ||
337 | map->refcount++; | ||
338 | goto out; | ||
339 | } | ||
340 | |||
319 | map = kzalloc(sizeof(*map), GFP_KERNEL); | 341 | map = kzalloc(sizeof(*map), GFP_KERNEL); |
320 | if (!map) | 342 | if (!map) { |
343 | mutex_unlock(&acpi_ioremap_lock); | ||
321 | return NULL; | 344 | return NULL; |
345 | } | ||
322 | 346 | ||
323 | pg_off = round_down(phys, PAGE_SIZE); | 347 | pg_off = round_down(phys, PAGE_SIZE); |
324 | pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; | 348 | pg_sz = round_up(phys + size, PAGE_SIZE) - pg_off; |
325 | virt = acpi_os_ioremap(pg_off, pg_sz); | 349 | virt = acpi_os_ioremap(pg_off, pg_sz); |
326 | if (!virt) { | 350 | if (!virt) { |
351 | mutex_unlock(&acpi_ioremap_lock); | ||
327 | kfree(map); | 352 | kfree(map); |
328 | return NULL; | 353 | return NULL; |
329 | } | 354 | } |
@@ -332,62 +357,51 @@ acpi_os_map_memory(acpi_physical_address phys, acpi_size size) | |||
332 | map->virt = virt; | 357 | map->virt = virt; |
333 | map->phys = pg_off; | 358 | map->phys = pg_off; |
334 | map->size = pg_sz; | 359 | map->size = pg_sz; |
335 | kref_init(&map->ref); | 360 | map->refcount = 1; |
336 | 361 | ||
337 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | ||
338 | /* Check if page has already been mapped. */ | ||
339 | tmp_map = acpi_map_lookup(phys, size); | ||
340 | if (tmp_map) { | ||
341 | kref_get(&tmp_map->ref); | ||
342 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
343 | iounmap(map->virt); | ||
344 | kfree(map); | ||
345 | return tmp_map->virt + (phys - tmp_map->phys); | ||
346 | } | ||
347 | list_add_tail_rcu(&map->list, &acpi_ioremaps); | 362 | list_add_tail_rcu(&map->list, &acpi_ioremaps); |
348 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
349 | 363 | ||
364 | out: | ||
365 | mutex_unlock(&acpi_ioremap_lock); | ||
350 | return map->virt + (phys - map->phys); | 366 | return map->virt + (phys - map->phys); |
351 | } | 367 | } |
352 | EXPORT_SYMBOL_GPL(acpi_os_map_memory); | 368 | EXPORT_SYMBOL_GPL(acpi_os_map_memory); |
353 | 369 | ||
354 | static void acpi_kref_del_iomap(struct kref *ref) | 370 | static void acpi_os_drop_map_ref(struct acpi_ioremap *map) |
355 | { | 371 | { |
356 | struct acpi_ioremap *map; | 372 | if (!--map->refcount) |
373 | list_del_rcu(&map->list); | ||
374 | } | ||
357 | 375 | ||
358 | map = container_of(ref, struct acpi_ioremap, ref); | 376 | static void acpi_os_map_cleanup(struct acpi_ioremap *map) |
359 | list_del_rcu(&map->list); | 377 | { |
378 | if (!map->refcount) { | ||
379 | synchronize_rcu(); | ||
380 | iounmap(map->virt); | ||
381 | kfree(map); | ||
382 | } | ||
360 | } | 383 | } |
361 | 384 | ||
362 | void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) | 385 | void __ref acpi_os_unmap_memory(void __iomem *virt, acpi_size size) |
363 | { | 386 | { |
364 | struct acpi_ioremap *map; | 387 | struct acpi_ioremap *map; |
365 | unsigned long flags; | ||
366 | int del; | ||
367 | 388 | ||
368 | if (!acpi_gbl_permanent_mmap) { | 389 | if (!acpi_gbl_permanent_mmap) { |
369 | __acpi_unmap_table(virt, size); | 390 | __acpi_unmap_table(virt, size); |
370 | return; | 391 | return; |
371 | } | 392 | } |
372 | 393 | ||
373 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | 394 | mutex_lock(&acpi_ioremap_lock); |
374 | map = acpi_map_lookup_virt(virt, size); | 395 | map = acpi_map_lookup_virt(virt, size); |
375 | if (!map) { | 396 | if (!map) { |
376 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | 397 | mutex_unlock(&acpi_ioremap_lock); |
377 | printk(KERN_ERR PREFIX "%s: bad address %p\n", __func__, virt); | 398 | WARN(true, PREFIX "%s: bad address %p\n", __func__, virt); |
378 | dump_stack(); | ||
379 | return; | 399 | return; |
380 | } | 400 | } |
401 | acpi_os_drop_map_ref(map); | ||
402 | mutex_unlock(&acpi_ioremap_lock); | ||
381 | 403 | ||
382 | del = kref_put(&map->ref, acpi_kref_del_iomap); | 404 | acpi_os_map_cleanup(map); |
383 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | ||
384 | |||
385 | if (!del) | ||
386 | return; | ||
387 | |||
388 | synchronize_rcu(); | ||
389 | iounmap(map->virt); | ||
390 | kfree(map); | ||
391 | } | 405 | } |
392 | EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); | 406 | EXPORT_SYMBOL_GPL(acpi_os_unmap_memory); |
393 | 407 | ||
@@ -397,7 +411,7 @@ void __init early_acpi_os_unmap_memory(void __iomem *virt, acpi_size size) | |||
397 | __acpi_unmap_table(virt, size); | 411 | __acpi_unmap_table(virt, size); |
398 | } | 412 | } |
399 | 413 | ||
400 | int acpi_os_map_generic_address(struct acpi_generic_address *addr) | 414 | static int acpi_os_map_generic_address(struct acpi_generic_address *addr) |
401 | { | 415 | { |
402 | void __iomem *virt; | 416 | void __iomem *virt; |
403 | 417 | ||
@@ -413,13 +427,10 @@ int acpi_os_map_generic_address(struct acpi_generic_address *addr) | |||
413 | 427 | ||
414 | return 0; | 428 | return 0; |
415 | } | 429 | } |
416 | EXPORT_SYMBOL_GPL(acpi_os_map_generic_address); | ||
417 | 430 | ||
418 | void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) | 431 | static void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) |
419 | { | 432 | { |
420 | void __iomem *virt; | 433 | struct acpi_ioremap *map; |
421 | unsigned long flags; | ||
422 | acpi_size size = addr->bit_width / 8; | ||
423 | 434 | ||
424 | if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) | 435 | if (addr->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) |
425 | return; | 436 | return; |
@@ -427,13 +438,17 @@ void acpi_os_unmap_generic_address(struct acpi_generic_address *addr) | |||
427 | if (!addr->address || !addr->bit_width) | 438 | if (!addr->address || !addr->bit_width) |
428 | return; | 439 | return; |
429 | 440 | ||
430 | spin_lock_irqsave(&acpi_ioremap_lock, flags); | 441 | mutex_lock(&acpi_ioremap_lock); |
431 | virt = acpi_map_vaddr_lookup(addr->address, size); | 442 | map = acpi_map_lookup(addr->address, addr->bit_width / 8); |
432 | spin_unlock_irqrestore(&acpi_ioremap_lock, flags); | 443 | if (!map) { |
444 | mutex_unlock(&acpi_ioremap_lock); | ||
445 | return; | ||
446 | } | ||
447 | acpi_os_drop_map_ref(map); | ||
448 | mutex_unlock(&acpi_ioremap_lock); | ||
433 | 449 | ||
434 | acpi_os_unmap_memory(virt, size); | 450 | acpi_os_map_cleanup(map); |
435 | } | 451 | } |
436 | EXPORT_SYMBOL_GPL(acpi_os_unmap_generic_address); | ||
437 | 452 | ||
438 | #ifdef ACPI_FUTURE_USAGE | 453 | #ifdef ACPI_FUTURE_USAGE |
439 | acpi_status | 454 | acpi_status |
@@ -516,11 +531,15 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, | |||
516 | acpi_irq_stats_init(); | 531 | acpi_irq_stats_init(); |
517 | 532 | ||
518 | /* | 533 | /* |
519 | * Ignore the GSI from the core, and use the value in our copy of the | 534 | * ACPI interrupts different from the SCI in our copy of the FADT are |
520 | * FADT. It may not be the same if an interrupt source override exists | 535 | * not supported. |
521 | * for the SCI. | ||
522 | */ | 536 | */ |
523 | gsi = acpi_gbl_FADT.sci_interrupt; | 537 | if (gsi != acpi_gbl_FADT.sci_interrupt) |
538 | return AE_BAD_PARAMETER; | ||
539 | |||
540 | if (acpi_irq_handler) | ||
541 | return AE_ALREADY_ACQUIRED; | ||
542 | |||
524 | if (acpi_gsi_to_irq(gsi, &irq) < 0) { | 543 | if (acpi_gsi_to_irq(gsi, &irq) < 0) { |
525 | printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n", | 544 | printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n", |
526 | gsi); | 545 | gsi); |
@@ -531,20 +550,20 @@ acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, | |||
531 | acpi_irq_context = context; | 550 | acpi_irq_context = context; |
532 | if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { | 551 | if (request_irq(irq, acpi_irq, IRQF_SHARED, "acpi", acpi_irq)) { |
533 | printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); | 552 | printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); |
553 | acpi_irq_handler = NULL; | ||
534 | return AE_NOT_ACQUIRED; | 554 | return AE_NOT_ACQUIRED; |
535 | } | 555 | } |
536 | acpi_irq_irq = irq; | ||
537 | 556 | ||
538 | return AE_OK; | 557 | return AE_OK; |
539 | } | 558 | } |
540 | 559 | ||
541 | acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) | 560 | acpi_status acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) |
542 | { | 561 | { |
543 | if (irq) { | 562 | if (irq != acpi_gbl_FADT.sci_interrupt) |
544 | free_irq(irq, acpi_irq); | 563 | return AE_BAD_PARAMETER; |
545 | acpi_irq_handler = NULL; | 564 | |
546 | acpi_irq_irq = 0; | 565 | free_irq(irq, acpi_irq); |
547 | } | 566 | acpi_irq_handler = NULL; |
548 | 567 | ||
549 | return AE_OK; | 568 | return AE_OK; |
550 | } | 569 | } |
@@ -1603,7 +1622,7 @@ acpi_status __init acpi_os_initialize1(void) | |||
1603 | acpi_status acpi_os_terminate(void) | 1622 | acpi_status acpi_os_terminate(void) |
1604 | { | 1623 | { |
1605 | if (acpi_irq_handler) { | 1624 | if (acpi_irq_handler) { |
1606 | acpi_os_remove_interrupt_handler(acpi_irq_irq, | 1625 | acpi_os_remove_interrupt_handler(acpi_gbl_FADT.sci_interrupt, |
1607 | acpi_irq_handler); | 1626 | acpi_irq_handler); |
1608 | } | 1627 | } |
1609 | 1628 | ||