diff options
author | David Shaohua Li <shaohua.li@intel.com> | 2005-07-27 23:02:00 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2005-07-29 22:49:38 -0400 |
commit | 87bec66b9691522414862dd8d41e430b063735ef (patch) | |
tree | f9976d7f6bb92fe3ebeda3b5d3644ac048147e62 /drivers | |
parent | 68ac767686fd72f37a25bb4895fb4ab0080ba755 (diff) |
[ACPI] suspend/resume ACPI PCI Interrupt Links
Add reference count and disable ACPI PCI Interrupt Link
when no device still uses it.
Warn when drivers have not released Link at suspend time.
http://bugzilla.kernel.org/show_bug.cgi?id=3469
Signed-off-by: David Shaohua Li <shaohua.li@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/pci_irq.c | 85 | ||||
-rw-r--r-- | drivers/acpi/pci_link.c | 103 |
2 files changed, 146 insertions, 42 deletions
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 8093f2e00321..c536ccfc5413 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c | |||
@@ -269,7 +269,51 @@ acpi_pci_irq_del_prt (int segment, int bus) | |||
269 | /* -------------------------------------------------------------------------- | 269 | /* -------------------------------------------------------------------------- |
270 | PCI Interrupt Routing Support | 270 | PCI Interrupt Routing Support |
271 | -------------------------------------------------------------------------- */ | 271 | -------------------------------------------------------------------------- */ |
272 | typedef int (*irq_lookup_func)(struct acpi_prt_entry *, int *, int *, char **); | ||
272 | 273 | ||
274 | static int | ||
275 | acpi_pci_allocate_irq(struct acpi_prt_entry *entry, | ||
276 | int *edge_level, | ||
277 | int *active_high_low, | ||
278 | char **link) | ||
279 | { | ||
280 | int irq; | ||
281 | |||
282 | ACPI_FUNCTION_TRACE("acpi_pci_allocate_irq"); | ||
283 | |||
284 | if (entry->link.handle) { | ||
285 | irq = acpi_pci_link_allocate_irq(entry->link.handle, | ||
286 | entry->link.index, edge_level, active_high_low, link); | ||
287 | if (irq < 0) { | ||
288 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); | ||
289 | return_VALUE(-1); | ||
290 | } | ||
291 | } else { | ||
292 | irq = entry->link.index; | ||
293 | *edge_level = ACPI_LEVEL_SENSITIVE; | ||
294 | *active_high_low = ACPI_ACTIVE_LOW; | ||
295 | } | ||
296 | |||
297 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); | ||
298 | return_VALUE(irq); | ||
299 | } | ||
300 | |||
301 | static int | ||
302 | acpi_pci_free_irq(struct acpi_prt_entry *entry, | ||
303 | int *edge_level, | ||
304 | int *active_high_low, | ||
305 | char **link) | ||
306 | { | ||
307 | int irq; | ||
308 | |||
309 | ACPI_FUNCTION_TRACE("acpi_pci_free_irq"); | ||
310 | if (entry->link.handle) { | ||
311 | irq = acpi_pci_link_free_irq(entry->link.handle); | ||
312 | } else { | ||
313 | irq = entry->link.index; | ||
314 | } | ||
315 | return_VALUE(irq); | ||
316 | } | ||
273 | /* | 317 | /* |
274 | * acpi_pci_irq_lookup | 318 | * acpi_pci_irq_lookup |
275 | * success: return IRQ >= 0 | 319 | * success: return IRQ >= 0 |
@@ -282,12 +326,13 @@ acpi_pci_irq_lookup ( | |||
282 | int pin, | 326 | int pin, |
283 | int *edge_level, | 327 | int *edge_level, |
284 | int *active_high_low, | 328 | int *active_high_low, |
285 | char **link) | 329 | char **link, |
330 | irq_lookup_func func) | ||
286 | { | 331 | { |
287 | struct acpi_prt_entry *entry = NULL; | 332 | struct acpi_prt_entry *entry = NULL; |
288 | int segment = pci_domain_nr(bus); | 333 | int segment = pci_domain_nr(bus); |
289 | int bus_nr = bus->number; | 334 | int bus_nr = bus->number; |
290 | int irq; | 335 | int ret; |
291 | 336 | ||
292 | ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); | 337 | ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); |
293 | 338 | ||
@@ -301,22 +346,8 @@ acpi_pci_irq_lookup ( | |||
301 | return_VALUE(-1); | 346 | return_VALUE(-1); |
302 | } | 347 | } |
303 | 348 | ||
304 | if (entry->link.handle) { | 349 | ret = func(entry, edge_level, active_high_low, link); |
305 | irq = acpi_pci_link_get_irq(entry->link.handle, | 350 | return_VALUE(ret); |
306 | entry->link.index, edge_level, active_high_low, link); | ||
307 | if (irq < 0) { | ||
308 | ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); | ||
309 | return_VALUE(-1); | ||
310 | } | ||
311 | } else { | ||
312 | irq = entry->link.index; | ||
313 | *edge_level = ACPI_LEVEL_SENSITIVE; | ||
314 | *active_high_low = ACPI_ACTIVE_LOW; | ||
315 | } | ||
316 | |||
317 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); | ||
318 | |||
319 | return_VALUE(irq); | ||
320 | } | 351 | } |
321 | 352 | ||
322 | /* | 353 | /* |
@@ -330,7 +361,8 @@ acpi_pci_irq_derive ( | |||
330 | int pin, | 361 | int pin, |
331 | int *edge_level, | 362 | int *edge_level, |
332 | int *active_high_low, | 363 | int *active_high_low, |
333 | char **link) | 364 | char **link, |
365 | irq_lookup_func func) | ||
334 | { | 366 | { |
335 | struct pci_dev *bridge = dev; | 367 | struct pci_dev *bridge = dev; |
336 | int irq = -1; | 368 | int irq = -1; |
@@ -363,7 +395,7 @@ acpi_pci_irq_derive ( | |||
363 | } | 395 | } |
364 | 396 | ||
365 | irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), | 397 | irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), |
366 | pin, edge_level, active_high_low, link); | 398 | pin, edge_level, active_high_low, link, func); |
367 | } | 399 | } |
368 | 400 | ||
369 | if (irq < 0) { | 401 | if (irq < 0) { |
@@ -415,7 +447,7 @@ acpi_pci_irq_enable ( | |||
415 | * values override any BIOS-assigned IRQs set during boot. | 447 | * values override any BIOS-assigned IRQs set during boot. |
416 | */ | 448 | */ |
417 | irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, | 449 | irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, |
418 | &edge_level, &active_high_low, &link); | 450 | &edge_level, &active_high_low, &link, acpi_pci_allocate_irq); |
419 | 451 | ||
420 | /* | 452 | /* |
421 | * If no PRT entry was found, we'll try to derive an IRQ from the | 453 | * If no PRT entry was found, we'll try to derive an IRQ from the |
@@ -423,7 +455,7 @@ acpi_pci_irq_enable ( | |||
423 | */ | 455 | */ |
424 | if (irq < 0) | 456 | if (irq < 0) |
425 | irq = acpi_pci_irq_derive(dev, pin, &edge_level, | 457 | irq = acpi_pci_irq_derive(dev, pin, &edge_level, |
426 | &active_high_low, &link); | 458 | &active_high_low, &link, acpi_pci_allocate_irq); |
427 | 459 | ||
428 | /* | 460 | /* |
429 | * No IRQ known to the ACPI subsystem - maybe the BIOS / | 461 | * No IRQ known to the ACPI subsystem - maybe the BIOS / |
@@ -461,7 +493,9 @@ acpi_pci_irq_enable ( | |||
461 | EXPORT_SYMBOL(acpi_pci_irq_enable); | 493 | EXPORT_SYMBOL(acpi_pci_irq_enable); |
462 | 494 | ||
463 | 495 | ||
464 | #ifdef CONFIG_ACPI_DEALLOCATE_IRQ | 496 | /* FIXME: implement x86/x86_64 version */ |
497 | void __attribute__((weak)) acpi_unregister_gsi(u32 i) {} | ||
498 | |||
465 | void | 499 | void |
466 | acpi_pci_irq_disable ( | 500 | acpi_pci_irq_disable ( |
467 | struct pci_dev *dev) | 501 | struct pci_dev *dev) |
@@ -488,14 +522,14 @@ acpi_pci_irq_disable ( | |||
488 | * First we check the PCI IRQ routing table (PRT) for an IRQ. | 522 | * First we check the PCI IRQ routing table (PRT) for an IRQ. |
489 | */ | 523 | */ |
490 | gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, | 524 | gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, |
491 | &edge_level, &active_high_low, NULL); | 525 | &edge_level, &active_high_low, NULL, acpi_pci_free_irq); |
492 | /* | 526 | /* |
493 | * If no PRT entry was found, we'll try to derive an IRQ from the | 527 | * If no PRT entry was found, we'll try to derive an IRQ from the |
494 | * device's parent bridge. | 528 | * device's parent bridge. |
495 | */ | 529 | */ |
496 | if (gsi < 0) | 530 | if (gsi < 0) |
497 | gsi = acpi_pci_irq_derive(dev, pin, | 531 | gsi = acpi_pci_irq_derive(dev, pin, |
498 | &edge_level, &active_high_low, NULL); | 532 | &edge_level, &active_high_low, NULL, acpi_pci_free_irq); |
499 | if (gsi < 0) | 533 | if (gsi < 0) |
500 | return_VOID; | 534 | return_VOID; |
501 | 535 | ||
@@ -511,4 +545,3 @@ acpi_pci_irq_disable ( | |||
511 | 545 | ||
512 | return_VOID; | 546 | return_VOID; |
513 | } | 547 | } |
514 | #endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ | ||
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 6ad0e77df9b3..6a29610edc11 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c | |||
@@ -68,6 +68,10 @@ static struct acpi_driver acpi_pci_link_driver = { | |||
68 | }, | 68 | }, |
69 | }; | 69 | }; |
70 | 70 | ||
71 | /* | ||
72 | * If a link is initialized, we never change its active and initialized | ||
73 | * later even the link is disable. Instead, we just repick the active irq | ||
74 | */ | ||
71 | struct acpi_pci_link_irq { | 75 | struct acpi_pci_link_irq { |
72 | u8 active; /* Current IRQ */ | 76 | u8 active; /* Current IRQ */ |
73 | u8 edge_level; /* All IRQs */ | 77 | u8 edge_level; /* All IRQs */ |
@@ -76,8 +80,7 @@ struct acpi_pci_link_irq { | |||
76 | u8 possible_count; | 80 | u8 possible_count; |
77 | u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; | 81 | u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; |
78 | u8 initialized:1; | 82 | u8 initialized:1; |
79 | u8 suspend_resume:1; | 83 | u8 reserved:7; |
80 | u8 reserved:6; | ||
81 | }; | 84 | }; |
82 | 85 | ||
83 | struct acpi_pci_link { | 86 | struct acpi_pci_link { |
@@ -85,12 +88,14 @@ struct acpi_pci_link { | |||
85 | struct acpi_device *device; | 88 | struct acpi_device *device; |
86 | acpi_handle handle; | 89 | acpi_handle handle; |
87 | struct acpi_pci_link_irq irq; | 90 | struct acpi_pci_link_irq irq; |
91 | int refcnt; | ||
88 | }; | 92 | }; |
89 | 93 | ||
90 | static struct { | 94 | static struct { |
91 | int count; | 95 | int count; |
92 | struct list_head entries; | 96 | struct list_head entries; |
93 | } acpi_link; | 97 | } acpi_link; |
98 | DECLARE_MUTEX(acpi_link_lock); | ||
94 | 99 | ||
95 | 100 | ||
96 | /* -------------------------------------------------------------------------- | 101 | /* -------------------------------------------------------------------------- |
@@ -532,12 +537,12 @@ static int acpi_pci_link_allocate( | |||
532 | 537 | ||
533 | ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); | 538 | ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); |
534 | 539 | ||
535 | if (link->irq.suspend_resume) { | 540 | if (link->irq.initialized) { |
536 | acpi_pci_link_set(link, link->irq.active); | 541 | if (link->refcnt == 0) |
537 | link->irq.suspend_resume = 0; | 542 | /* This means the link is disabled but initialized */ |
538 | } | 543 | acpi_pci_link_set(link, link->irq.active); |
539 | if (link->irq.initialized) | ||
540 | return_VALUE(0); | 544 | return_VALUE(0); |
545 | } | ||
541 | 546 | ||
542 | /* | 547 | /* |
543 | * search for active IRQ in list of possible IRQs. | 548 | * search for active IRQ in list of possible IRQs. |
@@ -596,13 +601,13 @@ static int acpi_pci_link_allocate( | |||
596 | } | 601 | } |
597 | 602 | ||
598 | /* | 603 | /* |
599 | * acpi_pci_link_get_irq | 604 | * acpi_pci_link_allocate_irq |
600 | * success: return IRQ >= 0 | 605 | * success: return IRQ >= 0 |
601 | * failure: return -1 | 606 | * failure: return -1 |
602 | */ | 607 | */ |
603 | 608 | ||
604 | int | 609 | int |
605 | acpi_pci_link_get_irq ( | 610 | acpi_pci_link_allocate_irq ( |
606 | acpi_handle handle, | 611 | acpi_handle handle, |
607 | int index, | 612 | int index, |
608 | int *edge_level, | 613 | int *edge_level, |
@@ -613,7 +618,7 @@ acpi_pci_link_get_irq ( | |||
613 | struct acpi_device *device = NULL; | 618 | struct acpi_device *device = NULL; |
614 | struct acpi_pci_link *link = NULL; | 619 | struct acpi_pci_link *link = NULL; |
615 | 620 | ||
616 | ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq"); | 621 | ACPI_FUNCTION_TRACE("acpi_pci_link_allocate_irq"); |
617 | 622 | ||
618 | result = acpi_bus_get_device(handle, &device); | 623 | result = acpi_bus_get_device(handle, &device); |
619 | if (result) { | 624 | if (result) { |
@@ -633,21 +638,70 @@ acpi_pci_link_get_irq ( | |||
633 | return_VALUE(-1); | 638 | return_VALUE(-1); |
634 | } | 639 | } |
635 | 640 | ||
636 | if (acpi_pci_link_allocate(link)) | 641 | down(&acpi_link_lock); |
642 | if (acpi_pci_link_allocate(link)) { | ||
643 | up(&acpi_link_lock); | ||
637 | return_VALUE(-1); | 644 | return_VALUE(-1); |
645 | } | ||
638 | 646 | ||
639 | if (!link->irq.active) { | 647 | if (!link->irq.active) { |
648 | up(&acpi_link_lock); | ||
640 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); | 649 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); |
641 | return_VALUE(-1); | 650 | return_VALUE(-1); |
642 | } | 651 | } |
652 | link->refcnt ++; | ||
653 | up(&acpi_link_lock); | ||
643 | 654 | ||
644 | if (edge_level) *edge_level = link->irq.edge_level; | 655 | if (edge_level) *edge_level = link->irq.edge_level; |
645 | if (active_high_low) *active_high_low = link->irq.active_high_low; | 656 | if (active_high_low) *active_high_low = link->irq.active_high_low; |
646 | if (name) *name = acpi_device_bid(link->device); | 657 | if (name) *name = acpi_device_bid(link->device); |
658 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
659 | "Link %s is referenced\n", acpi_device_bid(link->device))); | ||
647 | return_VALUE(link->irq.active); | 660 | return_VALUE(link->irq.active); |
648 | } | 661 | } |
649 | 662 | ||
663 | /* | ||
664 | * We don't change link's irq information here. After it is reenabled, we | ||
665 | * continue use the info | ||
666 | */ | ||
667 | int | ||
668 | acpi_pci_link_free_irq(acpi_handle handle) | ||
669 | { | ||
670 | struct acpi_device *device = NULL; | ||
671 | struct acpi_pci_link *link = NULL; | ||
672 | acpi_status result; | ||
673 | |||
674 | ACPI_FUNCTION_TRACE("acpi_pci_link_free_irq"); | ||
675 | |||
676 | result = acpi_bus_get_device(handle, &device); | ||
677 | if (result) { | ||
678 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n")); | ||
679 | return_VALUE(-1); | ||
680 | } | ||
650 | 681 | ||
682 | link = (struct acpi_pci_link *) acpi_driver_data(device); | ||
683 | if (!link) { | ||
684 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); | ||
685 | return_VALUE(-1); | ||
686 | } | ||
687 | |||
688 | down(&acpi_link_lock); | ||
689 | if (!link->irq.initialized) { | ||
690 | up(&acpi_link_lock); | ||
691 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link isn't initialized\n")); | ||
692 | return_VALUE(-1); | ||
693 | } | ||
694 | |||
695 | link->refcnt --; | ||
696 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
697 | "Link %s is dereferenced\n", acpi_device_bid(link->device))); | ||
698 | |||
699 | if (link->refcnt == 0) { | ||
700 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); | ||
701 | } | ||
702 | up(&acpi_link_lock); | ||
703 | return_VALUE(link->irq.active); | ||
704 | } | ||
651 | /* -------------------------------------------------------------------------- | 705 | /* -------------------------------------------------------------------------- |
652 | Driver Interface | 706 | Driver Interface |
653 | -------------------------------------------------------------------------- */ | 707 | -------------------------------------------------------------------------- */ |
@@ -677,6 +731,7 @@ acpi_pci_link_add ( | |||
677 | strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); | 731 | strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); |
678 | acpi_driver_data(device) = link; | 732 | acpi_driver_data(device) = link; |
679 | 733 | ||
734 | down(&acpi_link_lock); | ||
680 | result = acpi_pci_link_get_possible(link); | 735 | result = acpi_pci_link_get_possible(link); |
681 | if (result) | 736 | if (result) |
682 | goto end; | 737 | goto end; |
@@ -712,6 +767,7 @@ acpi_pci_link_add ( | |||
712 | end: | 767 | end: |
713 | /* disable all links -- to be activated on use */ | 768 | /* disable all links -- to be activated on use */ |
714 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); | 769 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); |
770 | up(&acpi_link_lock); | ||
715 | 771 | ||
716 | if (result) | 772 | if (result) |
717 | kfree(link); | 773 | kfree(link); |
@@ -726,19 +782,32 @@ irqrouter_suspend( | |||
726 | { | 782 | { |
727 | struct list_head *node = NULL; | 783 | struct list_head *node = NULL; |
728 | struct acpi_pci_link *link = NULL; | 784 | struct acpi_pci_link *link = NULL; |
785 | int ret = 0; | ||
729 | 786 | ||
730 | ACPI_FUNCTION_TRACE("irqrouter_suspend"); | 787 | ACPI_FUNCTION_TRACE("irqrouter_suspend"); |
731 | 788 | ||
732 | list_for_each(node, &acpi_link.entries) { | 789 | list_for_each(node, &acpi_link.entries) { |
733 | link = list_entry(node, struct acpi_pci_link, node); | 790 | link = list_entry(node, struct acpi_pci_link, node); |
734 | if (!link) { | 791 | if (!link) { |
735 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); | 792 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
793 | "Invalid link context\n")); | ||
736 | continue; | 794 | continue; |
737 | } | 795 | } |
738 | if (link->irq.active && link->irq.initialized) | 796 | if (link->irq.initialized && link->refcnt != 0 |
739 | link->irq.suspend_resume = 1; | 797 | /* We ignore legacy IDE device irq */ |
798 | && link->irq.active != 14 && link->irq.active !=15) { | ||
799 | printk(KERN_WARNING PREFIX | ||
800 | "%d drivers with interrupt %d neglected to call" | ||
801 | " pci_disable_device at .suspend\n", | ||
802 | link->refcnt, | ||
803 | link->irq.active); | ||
804 | printk(KERN_WARNING PREFIX | ||
805 | "Fix the driver, or rmmod before suspend\n"); | ||
806 | link->refcnt = 0; | ||
807 | ret = -EINVAL; | ||
808 | } | ||
740 | } | 809 | } |
741 | return_VALUE(0); | 810 | return_VALUE(ret); |
742 | } | 811 | } |
743 | 812 | ||
744 | 813 | ||
@@ -756,8 +825,9 @@ acpi_pci_link_remove ( | |||
756 | 825 | ||
757 | link = (struct acpi_pci_link *) acpi_driver_data(device); | 826 | link = (struct acpi_pci_link *) acpi_driver_data(device); |
758 | 827 | ||
759 | /* TBD: Acquire/release lock */ | 828 | down(&acpi_link_lock); |
760 | list_del(&link->node); | 829 | list_del(&link->node); |
830 | up(&acpi_link_lock); | ||
761 | 831 | ||
762 | kfree(link); | 832 | kfree(link); |
763 | 833 | ||
@@ -849,6 +919,7 @@ int __init acpi_irq_balance_set(char *str) | |||
849 | __setup("acpi_irq_balance", acpi_irq_balance_set); | 919 | __setup("acpi_irq_balance", acpi_irq_balance_set); |
850 | 920 | ||
851 | 921 | ||
922 | /* FIXME: we will remove this interface after all drivers call pci_disable_device */ | ||
852 | static struct sysdev_class irqrouter_sysdev_class = { | 923 | static struct sysdev_class irqrouter_sysdev_class = { |
853 | set_kset_name("irqrouter"), | 924 | set_kset_name("irqrouter"), |
854 | .suspend = irqrouter_suspend, | 925 | .suspend = irqrouter_suspend, |