summaryrefslogtreecommitdiff
path: root/drivers/pci/hotplug
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-03-01 10:10:16 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2017-03-01 10:10:16 -0800
commitb286cedd473006b33d5ae076afac509e6b2c3bf4 (patch)
treef45cbb0337238771d6ebe2220f9d5b4f02771903 /drivers/pci/hotplug
parent522214d9be9c9f00f34ed89cb95e901b7ac31c59 (diff)
parent9f3768e02335ddd6ebe1d85d5cb3a68ee6264004 (diff)
downloadlwn-b286cedd473006b33d5ae076afac509e6b2c3bf4.tar.gz
lwn-b286cedd473006b33d5ae076afac509e6b2c3bf4.zip
Merge tag 'powerpc-4.11-2' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull more powerpc updates from Michael Ellerman: "Highlights include: - an update of the disassembly code used by xmon to the latest versions in binutils. We've received permission from all the authors of the relevant binutils changes to relicense their changes to the relevant files from GPLv3 to GPLv2, for inclusion in Linux. Thanks to Peter Bergner for doing the leg work to get permission from everyone. - addition of the "architected" Power9 CPU table entry, allowing us to boot in Power9 architected mode under a hypervisor. - updates to the Power9 PMU code. - implementation of clear_bit_unlock_is_negative_byte() to optimise unlock_page(). - Freescale updates from Scott: "Highlights include 8xx breakpoints and perf, t1042rdb display support, and board updates." Thanks to: Al Viro, Andrew Donnellan, Aneesh Kumar K.V, Balbir Singh, Douglas Miller, Frédéric Weisbecker, Gavin Shan, Madhavan Srinivasan, Michael Roth, Nathan Fontenot, Naveen N. Rao, Nicholas Piggin, Peter Bergner, Paul E. McKenney, Rashmica Gupta, Russell Currey, Sahil Mehta, Stewart Smith" * tag 'powerpc-4.11-2' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux: (48 commits) powerpc: Remove leftover cputime_to_nsecs call causing build error powerpc/mm/hash: Always clear UPRT and Host Radix bits when setting up CPU powerpc/optprobes: Fix TOC handling in optprobes trampoline powerpc/pseries: Advertise Hot Plug Event support to firmware cxl: fix nested locking hang during EEH hotplug powerpc/xmon: Dump memory in CPU endian format powerpc/pseries: Revert 'Auto-online hotplugged memory' powerpc/powernv: Make PCI non-optional powerpc/64: Implement clear_bit_unlock_is_negative_byte() powerpc/powernv: Remove unused variable in pnv_pci_sriov_disable() powerpc/kernel: Remove error message in pcibios_setup_phb_resources() powerpc/mm: Fix typo in set_pte_at() pci/hotplug/pnv-php: Disable MSI and PCI device properly pci/hotplug/pnv-php: Disable surprise hotplug capability on conflicts pci/hotplug/pnv-php: Remove WARN_ON() in pnv_php_put_slot() powerpc: Add POWER9 architected mode to cputable powerpc/perf: use is_kernel_addr macro in perf_get_misc_flags() powerpc/perf: Avoid FAB_*_MATCH checks for power9 powerpc/perf: Add restrictions to PMC5 in power9 DD1 powerpc/perf: Use Instruction Counter value ...
Diffstat (limited to 'drivers/pci/hotplug')
-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);
static void pnv_php_unregister_one(struct device_node *dn);
static void pnv_php_unregister(struct device_node *dn);
-static void pnv_php_disable_irq(struct pnv_php_slot *php_slot)
+static void pnv_php_disable_irq(struct pnv_php_slot *php_slot,
+ bool disable_device)
{
struct pci_dev *pdev = php_slot->pdev;
+ int irq = php_slot->irq;
u16 ctrl;
if (php_slot->irq > 0) {
@@ -56,10 +58,14 @@ static void pnv_php_disable_irq(struct pnv_php_slot *php_slot)
php_slot->wq = NULL;
}
- if (pdev->msix_enabled)
- pci_disable_msix(pdev);
- else if (pdev->msi_enabled)
- pci_disable_msi(pdev);
+ if (disable_device || irq > 0) {
+ if (pdev->msix_enabled)
+ pci_disable_msix(pdev);
+ else if (pdev->msi_enabled)
+ pci_disable_msi(pdev);
+
+ pci_disable_device(pdev);
+ }
}
static void pnv_php_free_slot(struct kref *kref)
@@ -68,7 +74,7 @@ static void pnv_php_free_slot(struct kref *kref)
struct pnv_php_slot, kref);
WARN_ON(!list_empty(&php_slot->children));
- pnv_php_disable_irq(php_slot);
+ pnv_php_disable_irq(php_slot, false);
kfree(php_slot->name);
kfree(php_slot);
}
@@ -76,7 +82,7 @@ static void pnv_php_free_slot(struct kref *kref)
static inline void pnv_php_put_slot(struct pnv_php_slot *php_slot)
{
- if (WARN_ON(!php_slot))
+ if (!php_slot)
return;
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)
if (ret)
return ret;
- /* Proceed if there have nothing behind the slot */
- if (presence == OPAL_PCI_SLOT_EMPTY)
+ /*
+ * Proceed if there have nothing behind the slot. However,
+ * we should leave the slot in registered state at the
+ * beginning. Otherwise, the PCI devices inserted afterwards
+ * won't be probed and populated.
+ */
+ if (presence == OPAL_PCI_SLOT_EMPTY) {
+ if (!php_slot->power_state_check) {
+ php_slot->power_state_check = true;
+
+ return 0;
+ }
+
goto scan;
+ }
/*
* 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)
if (sts & PCI_EXP_SLTSTA_DLLSC) {
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lsts);
added = !!(lsts & PCI_EXP_LNKSTA_DLLLA);
- } else if (sts & PCI_EXP_SLTSTA_PDC) {
+ } else if (!(php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) &&
+ (sts & PCI_EXP_SLTSTA_PDC)) {
ret = pnv_pci_get_presence_state(php_slot->id, &presence);
- if (!ret)
+ if (ret) {
+ dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n",
+ php_slot->name, ret, sts);
return IRQ_HANDLED;
+ }
+
added = !!(presence == OPAL_PCI_SLOT_PRESENT);
} else {
return IRQ_NONE;
@@ -752,6 +775,7 @@ static irqreturn_t pnv_php_interrupt(int irq, void *data)
static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
{
struct pci_dev *pdev = php_slot->pdev;
+ u32 broken_pdc = 0;
u16 sts, ctrl;
int ret;
@@ -759,29 +783,44 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
if (!php_slot->wq) {
dev_warn(&pdev->dev, "Cannot alloc workqueue\n");
- pnv_php_disable_irq(php_slot);
+ pnv_php_disable_irq(php_slot, true);
return;
}
+ /* Check PDC (Presence Detection Change) is broken or not */
+ ret = of_property_read_u32(php_slot->dn, "ibm,slot-broken-pdc",
+ &broken_pdc);
+ if (!ret && broken_pdc)
+ php_slot->flags |= PNV_PHP_FLAG_BROKEN_PDC;
+
/* Clear pending interrupts */
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &sts);
- sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
+ if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC)
+ sts |= PCI_EXP_SLTSTA_DLLSC;
+ else
+ sts |= (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, sts);
/* Request the interrupt */
ret = request_irq(irq, pnv_php_interrupt, IRQF_SHARED,
php_slot->name, php_slot);
if (ret) {
- pnv_php_disable_irq(php_slot);
+ pnv_php_disable_irq(php_slot, true);
dev_warn(&pdev->dev, "Error %d enabling IRQ %d\n", ret, irq);
return;
}
/* Enable the interrupts */
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &ctrl);
- ctrl |= (PCI_EXP_SLTCTL_HPIE |
- PCI_EXP_SLTCTL_PDCE |
- PCI_EXP_SLTCTL_DLLSCE);
+ if (php_slot->flags & PNV_PHP_FLAG_BROKEN_PDC) {
+ ctrl &= ~PCI_EXP_SLTCTL_PDCE;
+ ctrl |= (PCI_EXP_SLTCTL_HPIE |
+ PCI_EXP_SLTCTL_DLLSCE);
+ } else {
+ ctrl |= (PCI_EXP_SLTCTL_HPIE |
+ PCI_EXP_SLTCTL_PDCE |
+ PCI_EXP_SLTCTL_DLLSCE);
+ }
pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, ctrl);
/* 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)
struct pci_dev *pdev = php_slot->pdev;
int irq, ret;
+ /*
+ * The MSI/MSIx interrupt might have been occupied by other
+ * drivers. Don't populate the surprise hotplug capability
+ * in that case.
+ */
+ if (pci_dev_msi_enabled(pdev))
+ return;
+
ret = pci_enable_device(pdev);
if (ret) {
dev_warn(&pdev->dev, "Error %d enabling device\n", ret);