diff options
Diffstat (limited to 'drivers/acpi/pci_link.c')
| -rw-r--r-- | drivers/acpi/pci_link.c | 103 |
1 files changed, 87 insertions, 16 deletions
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, |
