diff options
Diffstat (limited to 'drivers/acpi/pci_link.c')
-rw-r--r-- | drivers/acpi/pci_link.c | 127 |
1 files changed, 107 insertions, 20 deletions
diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 6ad0e77df9b3..834c2ceff1aa 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,81 @@ 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 | } | ||
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 | } | ||
650 | 694 | ||
695 | #ifdef FUTURE_USE | ||
696 | /* | ||
697 | * The Link reference count allows us to _DISable an unused link | ||
698 | * and suspend time, and set it again on resume. | ||
699 | * However, 2.6.12 still has irq_router.resume | ||
700 | * which blindly restores the link state. | ||
701 | * So we disable the reference count method | ||
702 | * to prevent duplicate acpi_pci_link_set() | ||
703 | * which would harm some systems | ||
704 | */ | ||
705 | link->refcnt --; | ||
706 | #endif | ||
707 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
708 | "Link %s is dereferenced\n", acpi_device_bid(link->device))); | ||
709 | |||
710 | if (link->refcnt == 0) { | ||
711 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); | ||
712 | } | ||
713 | up(&acpi_link_lock); | ||
714 | return_VALUE(link->irq.active); | ||
715 | } | ||
651 | /* -------------------------------------------------------------------------- | 716 | /* -------------------------------------------------------------------------- |
652 | Driver Interface | 717 | Driver Interface |
653 | -------------------------------------------------------------------------- */ | 718 | -------------------------------------------------------------------------- */ |
@@ -677,6 +742,7 @@ acpi_pci_link_add ( | |||
677 | strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); | 742 | strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); |
678 | acpi_driver_data(device) = link; | 743 | acpi_driver_data(device) = link; |
679 | 744 | ||
745 | down(&acpi_link_lock); | ||
680 | result = acpi_pci_link_get_possible(link); | 746 | result = acpi_pci_link_get_possible(link); |
681 | if (result) | 747 | if (result) |
682 | goto end; | 748 | goto end; |
@@ -712,6 +778,7 @@ acpi_pci_link_add ( | |||
712 | end: | 778 | end: |
713 | /* disable all links -- to be activated on use */ | 779 | /* disable all links -- to be activated on use */ |
714 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); | 780 | acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); |
781 | up(&acpi_link_lock); | ||
715 | 782 | ||
716 | if (result) | 783 | if (result) |
717 | kfree(link); | 784 | kfree(link); |
@@ -720,24 +787,42 @@ end: | |||
720 | } | 787 | } |
721 | 788 | ||
722 | static int | 789 | static int |
723 | irqrouter_suspend( | 790 | acpi_pci_link_resume( |
724 | struct sys_device *dev, | 791 | struct acpi_pci_link *link) |
725 | u32 state) | 792 | { |
793 | ACPI_FUNCTION_TRACE("acpi_pci_link_resume"); | ||
794 | |||
795 | if (link->refcnt && link->irq.active && link->irq.initialized) | ||
796 | return_VALUE(acpi_pci_link_set(link, link->irq.active)); | ||
797 | else | ||
798 | return_VALUE(0); | ||
799 | } | ||
800 | |||
801 | /* | ||
802 | * FIXME: this is a workaround to avoid nasty warning. It will be removed | ||
803 | * after every device calls pci_disable_device in .resume. | ||
804 | */ | ||
805 | int acpi_in_resume; | ||
806 | static int | ||
807 | irqrouter_resume( | ||
808 | struct sys_device *dev) | ||
726 | { | 809 | { |
727 | struct list_head *node = NULL; | 810 | struct list_head *node = NULL; |
728 | struct acpi_pci_link *link = NULL; | 811 | struct acpi_pci_link *link = NULL; |
729 | 812 | ||
730 | ACPI_FUNCTION_TRACE("irqrouter_suspend"); | 813 | ACPI_FUNCTION_TRACE("irqrouter_resume"); |
731 | 814 | ||
815 | acpi_in_resume = 1; | ||
732 | list_for_each(node, &acpi_link.entries) { | 816 | list_for_each(node, &acpi_link.entries) { |
733 | link = list_entry(node, struct acpi_pci_link, node); | 817 | link = list_entry(node, struct acpi_pci_link, node); |
734 | if (!link) { | 818 | if (!link) { |
735 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); | 819 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
820 | "Invalid link context\n")); | ||
736 | continue; | 821 | continue; |
737 | } | 822 | } |
738 | if (link->irq.active && link->irq.initialized) | 823 | acpi_pci_link_resume(link); |
739 | link->irq.suspend_resume = 1; | ||
740 | } | 824 | } |
825 | acpi_in_resume = 0; | ||
741 | return_VALUE(0); | 826 | return_VALUE(0); |
742 | } | 827 | } |
743 | 828 | ||
@@ -756,8 +841,9 @@ acpi_pci_link_remove ( | |||
756 | 841 | ||
757 | link = (struct acpi_pci_link *) acpi_driver_data(device); | 842 | link = (struct acpi_pci_link *) acpi_driver_data(device); |
758 | 843 | ||
759 | /* TBD: Acquire/release lock */ | 844 | down(&acpi_link_lock); |
760 | list_del(&link->node); | 845 | list_del(&link->node); |
846 | up(&acpi_link_lock); | ||
761 | 847 | ||
762 | kfree(link); | 848 | kfree(link); |
763 | 849 | ||
@@ -849,9 +935,10 @@ int __init acpi_irq_balance_set(char *str) | |||
849 | __setup("acpi_irq_balance", acpi_irq_balance_set); | 935 | __setup("acpi_irq_balance", acpi_irq_balance_set); |
850 | 936 | ||
851 | 937 | ||
938 | /* FIXME: we will remove this interface after all drivers call pci_disable_device */ | ||
852 | static struct sysdev_class irqrouter_sysdev_class = { | 939 | static struct sysdev_class irqrouter_sysdev_class = { |
853 | set_kset_name("irqrouter"), | 940 | set_kset_name("irqrouter"), |
854 | .suspend = irqrouter_suspend, | 941 | .resume = irqrouter_resume, |
855 | }; | 942 | }; |
856 | 943 | ||
857 | 944 | ||