[Zesty,SRU] drivers/pci/hotplug: Mask PDC interrupt if required

Message ID 1491481904-19168-1-git-send-email-tim.gardner@canonical.com
State New
Headers show

Commit Message

Tim Gardner April 6, 2017, 12:31 p.m.
From: Gavin Shan <gwshan@linux.vnet.ibm.com>

BugLink: http://bugs.launchpad.net/bugs/1680328

We're supporting surprise hotplug on PCI slots behind root port
or PCIe switch downstream ports, which don't claim the capability
in hardware register (offset: PCIe cap + PCI_EXP_SLTCAP). PEX8718
is one of the examples. For those PCI slots, the PDC (Presence
Detection Change) event isn't reliable and the underly (skiboot)
firmware has best judgement.

This masks the PDC event when skiboot requests by "ibm,slot-broken-pdc"
property in PCI slot's device-tree node.

Reported-by: Hank Chang <hankmax0000@gmail.com>
Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
Tested-by: Willie Liauw <williel@supermicro.com.tw>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
(cherry picked from commit 454593e54cac126a0f49d4b65f90a5e518c51404)
Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
---
 arch/powerpc/include/asm/pnv-pci.h |  2 ++
 drivers/pci/hotplug/pnv_php.c      | 27 ++++++++++++++++++++++-----
 2 files changed, 24 insertions(+), 5 deletions(-)

Comments

Colin King April 6, 2017, 12:40 p.m. | #1
On 06/04/17 13:31, Tim Gardner wrote:
> From: Gavin Shan <gwshan@linux.vnet.ibm.com>
> 
> BugLink: http://bugs.launchpad.net/bugs/1680328
> 
> We're supporting surprise hotplug on PCI slots behind root port
> or PCIe switch downstream ports, which don't claim the capability
> in hardware register (offset: PCIe cap + PCI_EXP_SLTCAP). PEX8718
> is one of the examples. For those PCI slots, the PDC (Presence
> Detection Change) event isn't reliable and the underly (skiboot)
> firmware has best judgement.
> 
> This masks the PDC event when skiboot requests by "ibm,slot-broken-pdc"
> property in PCI slot's device-tree node.
> 
> Reported-by: Hank Chang <hankmax0000@gmail.com>
> Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
> Tested-by: Willie Liauw <williel@supermicro.com.tw>
> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
> (cherry picked from commit 454593e54cac126a0f49d4b65f90a5e518c51404)
> Signed-off-by: Tim Gardner <tim.gardner@canonical.com>
> ---
>  arch/powerpc/include/asm/pnv-pci.h |  2 ++
>  drivers/pci/hotplug/pnv_php.c      | 27 ++++++++++++++++++++++-----
>  2 files changed, 24 insertions(+), 5 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h
> index 696438f..de96810 100644
> --- a/arch/powerpc/include/asm/pnv-pci.h
> +++ b/arch/powerpc/include/asm/pnv-pci.h
> @@ -57,6 +57,8 @@ struct pnv_php_slot {
>  	uint64_t			id;
>  	char				*name;
>  	int				slot_no;
> +	unsigned int			flags;
> +#define PNV_PHP_FLAG_BROKEN_PDC		0x1
>  	struct kref			kref;
>  #define PNV_PHP_STATE_INITIALIZED	0
>  #define PNV_PHP_STATE_REGISTERED	1
> diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
> index e96973b..335a556 100644
> --- a/drivers/pci/hotplug/pnv_php.c
> +++ b/drivers/pci/hotplug/pnv_php.c
> @@ -723,7 +723,8 @@ 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) {
>  			dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n",
> @@ -774,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;
>  
> @@ -785,9 +787,18 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
>  		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 */
> @@ -801,9 +812,15 @@ static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
>  
>  	/* 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 */
> 

Clean upstream cherry pick.  Looks sane to me.

Acked-by: Colin Ian King <colin.king@canonical.com>
Stefan Bader April 6, 2017, 12:57 p.m. | #2

Tim Gardner April 11, 2017, 4:11 p.m. | #3

Patch

diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h
index 696438f..de96810 100644
--- a/arch/powerpc/include/asm/pnv-pci.h
+++ b/arch/powerpc/include/asm/pnv-pci.h
@@ -57,6 +57,8 @@  struct pnv_php_slot {
 	uint64_t			id;
 	char				*name;
 	int				slot_no;
+	unsigned int			flags;
+#define PNV_PHP_FLAG_BROKEN_PDC		0x1
 	struct kref			kref;
 #define PNV_PHP_STATE_INITIALIZED	0
 #define PNV_PHP_STATE_REGISTERED	1
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index e96973b..335a556 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -723,7 +723,8 @@  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) {
 			dev_warn(&pdev->dev, "PCI slot [%s] error %d getting presence (0x%04x), to retry the operation.\n",
@@ -774,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;
 
@@ -785,9 +787,18 @@  static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
 		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 */
@@ -801,9 +812,15 @@  static void pnv_php_init_irq(struct pnv_php_slot *php_slot, int irq)
 
 	/* 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 */