diff options
Diffstat (limited to 'drivers/pci/hotplug/pnv_php.c')
-rw-r--r-- | drivers/pci/hotplug/pnv_php.c | 81 |
1 files changed, 64 insertions, 17 deletions
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c index d2961ef39a3a..7c203198b582 100644 --- a/drivers/pci/hotplug/pnv_php.c +++ b/drivers/pci/hotplug/pnv_php.c | |||
@@ -35,9 +35,11 @@ static void pnv_php_register(struct device_node *dn); | |||
35 | static void pnv_php_unregister_one(struct device_node *dn); | 35 | static void pnv_php_unregister_one(struct device_node *dn); |
36 | static void pnv_php_unregister(struct device_node *dn); | 36 | static void pnv_php_unregister(struct device_node *dn); |
37 | 37 | ||
38 | static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) | 38 | static void pnv_php_disable_irq(struct pnv_php_slot *php_slot, |
39 | bool disable_device) | ||
39 | { | 40 | { |
40 | struct pci_dev *pdev = php_slot->pdev; | 41 | struct pci_dev *pdev = php_slot->pdev; |
42 | int irq = php_slot->irq; | ||
41 | u16 ctrl; | 43 | u16 ctrl; |
42 | 44 | ||
43 | if (php_slot->irq > 0) { | 45 | if (php_slot->irq > 0) { |
@@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) | |||
56 | php_slot->wq = NULL; | 58 | php_slot->wq = NULL; |
57 | } | 59 | } |
58 | 60 | ||
59 | if (pdev->msix_enabled) | 61 | if (disable_device || irq > 0) { |
60 | pci_disable_msix(pdev); | 62 | if (pdev->msix_enabled) |
61 | else if (pdev->msi_enabled) | 63 | pci_disable_msix(pdev); |
62 | pci_disable_msi(pdev); | 64 | else if (pdev->msi_enabled) |
65 | pci_disable_msi(pdev); | ||
66 | |||
67 | pci_disable_device(pdev); | ||
68 | } | ||
63 | } | 69 | } |
64 | 70 | ||
65 | static void pnv_php_free_slot(struct kref *kref) | 71 | static void pnv_php_free_slot(struct kref *kref) |
@@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kref *kref) | |||
68 | struct pnv_php_slot, kref); | 74 | struct pnv_php_slot, kref); |
69 | 75 | ||
70 | WARN_ON(!list_empty(&php_slot->children)); | 76 | WARN_ON(!list_empty(&php_slot->children)); |
71 | pnv_php_disable_irq(php_slot); | 77 | pnv_php_disable_irq(php_slot, false); |
72 | kfree(php_slot->name); | 78 | kfree(php_slot->name); |
73 | kfree(php_slot); | 79 | kfree(php_slot); |
74 | } | 80 | } |
@@ -76,7 +82,7 @@ static void pnv_php_free_slot(struct kref *kref) | |||
76 | static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot) | 82 | static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot) |
77 | { | 83 | { |
78 | 84 | ||
79 | if (WARN_ON(!php_slot)) | 85 | if (!php_slot) |
80 | return; | 86 | return; |
81 | 87 | ||
82 | kref_put(&php_slot->kref, pnv_php_free_slot); | 88 | kref_put(&php_slot->kref, pnv_php_free_slot); |
@@ -430,9 +436,21 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan) | |||
430 | if (ret) | 436 | if (ret) |
431 | return ret; | 437 | return ret; |
432 | 438 | ||
433 | /* Proceed if there have nothing behind the slot */ | 439 | /* |
434 | if (presence == OPAL_PCI_SLOT_EMPTY) | 440 | * Proceed if there have nothing behind the slot. However, |
441 | * we should leave the slot in registered state at the | ||
442 | * beginning. Otherwise, the PCI devices inserted afterwards | ||
443 | * won't be probed and populated. | ||
444 | */ | ||
445 | if (presence == OPAL_PCI_SLOT_EMPTY) { | ||
446 | if (!php_slot->power_state_check) { | ||
447 | php_slot->power_state_check = true; | ||
448 | |||
449 | return 0; | ||
450 | } | ||
451 | |||
435 | goto scan; | 452 | goto scan; |
453 | } | ||
436 | 454 | ||
437 | /* | 455 | /* |
438 | * If the power supply to the slot is off, we can't detect | 456 | * If the power supply to the slot is off, we can't detect |
@@ -705,10 +723,15 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data) | |||
705 | if (sts & PCI_EXP_SLTSTA_DLLSC) { | 723 | if (sts & PCI_EXP_SLTSTA_DLLSC) { |
706 | pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); | 724 | pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts); |
707 | added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); | 725 | added = !!(lsts & PCI_EXP_LNKSTA_DLLLA); |
708 | } else if (sts & PCI_EXP_SLTSTA_PDC) { | 726 | } else if (!(php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) && |
727 | (sts & PCI_EXP_SLTSTA_PDC)) { | ||
709 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); | 728 | ret = pnv_pci_get_presence_state(php_slot->id, &presence); |
710 | if (!ret) | 729 | if (ret) { |
730 | dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n", | ||
731 | php_slot->name, ret, sts); | ||
711 | return IRQ_HANDLED; | 732 | return IRQ_HANDLED; |
733 | } | ||
734 | |||
712 | added = !!(presence == OPAL_PCI_SLOT_PRESENT); | 735 | added = !!(presence == OPAL_PCI_SLOT_PRESENT); |
713 | } else { | 736 | } else { |
714 | return IRQ_NONE; | 737 | return IRQ_NONE; |
@@ -752,6 +775,7 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data) | |||
752 | static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) | 775 | static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) |
753 | { | 776 | { |
754 | struct pci_dev *pdev = php_slot->pdev; | 777 | struct pci_dev *pdev = php_slot->pdev; |
778 | u32 broken_pdc = 0; | ||
755 | u16 sts, ctrl; | 779 | u16 sts, ctrl; |
756 | int ret; | 780 | int ret; |
757 | 781 | ||
@@ -759,29 +783,44 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) | |||
759 | php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); | 783 | php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name); |
760 | if (!php_slot->wq) { | 784 | if (!php_slot->wq) { |
761 | dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); | 785 | dev_warn(&pdev->dev, "Cannot alloc workqueue\n"); |
762 | pnv_php_disable_irq(php_slot); | 786 | pnv_php_disable_irq(php_slot, true); |
763 | return; | 787 | return; |
764 | } | 788 | } |
765 | 789 | ||
790 | /* Check PDC (Presence Detection Change) is broken or not */ | ||
791 | ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc", | ||
792 | &broken_pdc); | ||
793 | if (!ret && broken_pdc) | ||
794 | php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC; | ||
795 | |||
766 | /* Clear pending interrupts */ | 796 | /* Clear pending interrupts */ |
767 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); | 797 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts); |
768 | sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); | 798 | if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) |
799 | sts |= PCI_EXP_SLTSTA_DLLSC; | ||
800 | else | ||
801 | sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); | ||
769 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); | 802 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts); |
770 | 803 | ||
771 | /* Request the interrupt */ | 804 | /* Request the interrupt */ |
772 | ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, | 805 | ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED, |
773 | php_slot->name, php_slot); | 806 | php_slot->name, php_slot); |
774 | if (ret) { | 807 | if (ret) { |
775 | pnv_php_disable_irq(php_slot); | 808 | pnv_php_disable_irq(php_slot, true); |
776 | dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq); | 809 | dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq); |
777 | return; | 810 | return; |
778 | } | 811 | } |
779 | 812 | ||
780 | /* Enable the interrupts */ | 813 | /* Enable the interrupts */ |
781 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); | 814 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl); |
782 | ctrl |= (PCI_EXP_SLTCTL_HPIE | | 815 | if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) { |
783 | PCI_EXP_SLTCTL_PDCE | | 816 | ctrl &= ~PCI_EXP_SLTCTL_PDCE; |
784 | PCI_EXP_SLTCTL_DLLSCE); | 817 | ctrl |= (PCI_EXP_SLTCTL_HPIE | |
818 | PCI_EXP_SLTCTL_DLLSCE); | ||
819 | } else { | ||
820 | ctrl |= (PCI_EXP_SLTCTL_HPIE | | ||
821 | PCI_EXP_SLTCTL_PDCE | | ||
822 | PCI_EXP_SLTCTL_DLLSCE); | ||
823 | } | ||
785 | pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); | 824 | pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl); |
786 | 825 | ||
787 | /* The interrupt is initialized successfully when @irq is valid */ | 826 | /* The interrupt is initialized successfully when @irq is valid */ |
@@ -793,6 +832,14 @@ static void pnv_php_enable_irq(struct pnv_php_slot *php_slot) | |||
793 | struct pci_dev *pdev = php_slot->pdev; | 832 | struct pci_dev *pdev = php_slot->pdev; |
794 | int irq, ret; | 833 | int irq, ret; |
795 | 834 | ||
835 | /* | ||
836 | * The MSI/MSIx interrupt might have been occupied by other | ||
837 | * drivers. Don't populate the surprise hotplug capability | ||
838 | * in that case. | ||
839 | */ | ||
840 | if (pci_dev_msi_enabled(pdev)) | ||
841 | return; | ||
842 | |||
796 | ret = pci_enable_device(pdev); | 843 | ret = pci_enable_device(pdev); |
797 | if (ret) { | 844 | if (ret) { |
798 | dev_warn(&pdev->dev, "Error %d enabling device\n", ret); | 845 | dev_warn(&pdev->dev, "Error %d enabling device\n", ret); |