aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug/pnv_php.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/hotplug/pnv_php.c')
-rw-r--r--drivers/pci/hotplug/pnv_php.c81
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);
35static void pnv_php_unregister_one(struct device_node *dn); 35static void pnv_php_unregister_one(struct device_node *dn);
36static void pnv_php_unregister(struct device_node *dn); 36static void pnv_php_unregister(struct device_node *dn);
37 37
38static void pnv_php_disable_irq(struct pnv_php_slot *php_slot) 38static 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
65static void pnv_php_free_slot(struct kref *kref) 71static 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)
76static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot) 82static 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)
752static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq) 775static 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);