wcxb: Disable presence detect reporting on upstream port during PCIe hard reset.

When the card goes through a reset the PCIe link will be brought down. Some
slots will report this change upstream to the root port which will believe that
the card has been hotplugged out of the system.

This fixes cases on some systems where, during a firmware update, the card gets
removed from the system's logical PCI tree with messages like the following in
the kernel log:

  pciehp 0000:00:1c.0:pcie04: Card not present on Slot(259)
  pciehp 0000:00:1c.0:pcie04: Card present on Slot(259)

Internal-Issue-ID: DAHDI-1091
Signed-off-by: Shaun Ruffell <sruffell@digium.com>
This commit is contained in:
Shaun Ruffell
2014-03-19 15:15:11 -05:00
parent e4fce6849a
commit e62f0f1835

View File

@@ -45,10 +45,13 @@
/* The definition for Surprise Down was added in Linux 3.6 in (a0dee2e PCI: misc
* pci_reg additions). It may be backported though so we won't check for the
* version. */
* version. Same with PCI_EXP_SLTCTL_PDCE. */
#ifndef PCI_ERR_UNC_SURPDN
#define PCI_ERR_UNC_SURPDN 0x20
#endif
#ifndef PCI_EXP_SLTCTL_PDCE
#define PCI_EXP_SLTCTL_PDCE 0x8
#endif
/* FPGA Status definitions */
#define OCT_CPU_RESET (1 << 0)
@@ -602,27 +605,41 @@ static void _wcxb_pcie_hard_reset(struct wcxb *xb)
{
struct pci_dev *const parent = xb->pdev->bus->self;
u32 aer_mask;
int pos;
u16 sltctl;
int pos_err;
int pos_exp;
if (!wcxb_is_pcie(xb))
return;
pos = pci_find_ext_capability(parent, PCI_EXT_CAP_ID_ERR);
if (pos) {
pci_read_config_dword(parent, pos + PCI_ERR_UNCOR_MASK,
pos_err = pci_find_ext_capability(parent, PCI_EXT_CAP_ID_ERR);
if (pos_err) {
pci_read_config_dword(parent, pos_err + PCI_ERR_UNCOR_MASK,
&aer_mask);
pci_write_config_dword(parent, pos + PCI_ERR_UNCOR_MASK,
pci_write_config_dword(parent, pos_err + PCI_ERR_UNCOR_MASK,
aer_mask | PCI_ERR_UNC_SURPDN);
}
/* Also disable any presence change reporting. */
pos_exp = pci_find_capability(parent, PCI_CAP_ID_EXP);
if (pos_exp) {
pci_read_config_word(parent, pos_exp + PCI_EXP_SLTCTL,
&sltctl);
pci_write_config_word(parent, pos_exp + PCI_EXP_SLTCTL,
sltctl & ~PCI_EXP_SLTCTL_PDCE);
}
_wcxb_hard_reset(xb);
if (pos) {
pci_write_config_dword(parent, pos + PCI_ERR_UNCOR_MASK,
if (pos_exp)
pci_write_config_word(parent, pos_exp + PCI_EXP_SLTCTL, sltctl);
if (pos_err) {
pci_write_config_dword(parent, pos_err + PCI_ERR_UNCOR_MASK,
aer_mask);
/* Clear the error as well from the status register. */
pci_write_config_dword(parent, pos + PCI_ERR_UNCOR_STATUS,
pci_write_config_dword(parent, pos_err + PCI_ERR_UNCOR_STATUS,
PCI_ERR_UNC_SURPDN);
}