aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/pci-slot.c24
-rw-r--r--hw/phb3.c57
2 files changed, 64 insertions, 17 deletions
diff --git a/core/pci-slot.c b/core/pci-slot.c
index 721b2d9..aa4bea6 100644
--- a/core/pci-slot.c
+++ b/core/pci-slot.c
@@ -38,15 +38,21 @@ static void pci_slot_prepare_link_change(struct pci_slot *slot, bool up)
if (pci_has_cap(pd, PCIECAP_ID_AER, true)) {
aercap = pci_cap(pd, PCIECAP_ID_AER, true);
- /* Link down error */
- pci_cfg_read32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_MASK,
- &mask);
- if (up)
- mask &= ~PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
- else
- mask |= PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
- pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_MASK,
- mask);
+ /* Mask link surprise down event. The event is always
+ * masked when the associated PCI slot supports PCI
+ * surprise hotplug. We needn't toggle it when the link
+ * bounces caused by reset and just keep it always masked.
+ */
+ if (!pd->slot || !pd->slot->surprise_pluggable) {
+ pci_cfg_read32(phb, pd->bdfn,
+ aercap + PCIECAP_AER_UE_MASK, &mask);
+ if (up)
+ mask &= ~PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
+ else
+ mask |= PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
+ pci_cfg_write32(phb, pd->bdfn,
+ aercap + PCIECAP_AER_UE_MASK, mask);
+ }
/* Receiver error */
pci_cfg_read32(phb, pd->bdfn, aercap + PCIECAP_AER_CE_MASK,
diff --git a/hw/phb3.c b/hw/phb3.c
index 2a05fcc..35bc73a 100644
--- a/hw/phb3.c
+++ b/hw/phb3.c
@@ -279,10 +279,18 @@ static int64_t phb3_get_reserved_pe_number(struct phb *phb __unused)
static void phb3_root_port_init(struct phb *phb, struct pci_device *dev,
int ecap, int aercap)
{
+ struct phb3 *p = phb_to_phb3(phb);
uint16_t bdfn = dev->bdfn;
uint16_t val16;
uint32_t val32;
+ /* Mask UTL link down event if root slot supports surprise
+ * hotplug as the event should be handled by hotplug driver
+ * instead of EEH subsystem.
+ */
+ if (dev->slot && dev->slot->surprise_pluggable)
+ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN, 0xad42800000000000);
+
/* Enable SERR and parity checking */
pci_cfg_read16(phb, bdfn, PCI_CFG_CMD, &val16);
val16 |= (PCI_CFG_CMD_SERR_EN | PCI_CFG_CMD_PERR_RESP);
@@ -299,12 +307,18 @@ static void phb3_root_port_init(struct phb *phb, struct pci_device *dev,
if (!aercap) return;
- /* Mask various unrecoverable errors */
+ /* Mask various unrecoverable errors. The link surprise down
+ * event should be masked when its PCI slot support surprise
+ * hotplug. The link surprise down event should be handled by
+ * PCI hotplug driver instead of EEH subsystem.
+ */
pci_cfg_read32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, &val32);
val32 |= (PCIECAP_AER_UE_MASK_POISON_TLP |
PCIECAP_AER_UE_MASK_COMPL_TIMEOUT |
PCIECAP_AER_UE_MASK_COMPL_ABORT |
PCIECAP_AER_UE_MASK_ECRC);
+ if (dev->slot && dev->slot->surprise_pluggable)
+ val32 |= PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, val32);
/* Report various unrecoverable errors as fatal errors */
@@ -368,9 +382,18 @@ static void phb3_switch_port_init(struct phb *phb,
val16 &= ~PCICAP_EXP_DEVCTL_CE_REPORT;
pci_cfg_write16(phb, bdfn, ecap + PCICAP_EXP_DEVCTL, val16);
- /* Unmask all unrecoverable errors */
+ /* Unmask all unrecoverable errors for upstream port. For
+ * downstream port, the surprise link down is masked because
+ * it should be handled by hotplug driver instead of EEH
+ * subsystem.
+ */
if (!aercap) return;
- pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, 0x0);
+ if (dev->dev_type == PCIE_TYPE_SWITCH_DNPORT &&
+ dev->slot && dev->slot->surprise_pluggable)
+ pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK,
+ PCIECAP_AER_UE_MASK_SURPRISE_DOWN);
+ else
+ pci_cfg_write32(phb, bdfn, aercap + PCIECAP_AER_UE_MASK, 0x0);
/* Severity of unrecoverable errors */
if (dev->dev_type == PCIE_TYPE_SWITCH_UPPORT)
@@ -1963,13 +1986,22 @@ static void phb3_prepare_link_change(struct pci_slot *slot,
bool is_up)
{
struct phb3 *p = phb_to_phb3(slot->phb);
+ struct pci_device *pd = slot->pd;
uint32_t reg32;
p->has_link = is_up;
if (!is_up) {
- /* Mask PCIE port interrupts */
- out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
- 0xad42800000000000);
+ if (!pd || !pd->slot || !pd->slot->surprise_pluggable) {
+ /* Mask PCIE port interrupts */
+ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
+ 0xad42800000000000);
+
+ pci_cfg_read32(&p->phb, 0,
+ p->aercap + PCIECAP_AER_UE_MASK, &reg32);
+ reg32 |= PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
+ pci_cfg_write32(&p->phb, 0,
+ p->aercap + PCIECAP_AER_UE_MASK, reg32);
+ }
/* Mask AER receiver error */
phb3_pcicfg_read32(&p->phb, 0,
@@ -1996,8 +2028,17 @@ static void phb3_prepare_link_change(struct pci_slot *slot,
/* Clear spurrious errors and enable PCIE port interrupts */
out_be64(p->regs + UTL_PCIE_PORT_STATUS,
0xffdfffffffffffff);
- out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
- 0xad52800000000000);
+
+ if (!pd || !pd->slot || !pd->slot->surprise_pluggable) {
+ out_be64(p->regs + UTL_PCIE_PORT_IRQ_EN,
+ 0xad52800000000000);
+
+ pci_cfg_read32(&p->phb, 0,
+ p->aercap + PCIECAP_AER_UE_MASK, &reg32);
+ reg32 &= ~PCIECAP_AER_UE_MASK_SURPRISE_DOWN;
+ pci_cfg_write32(&p->phb, 0,
+ p->aercap + PCIECAP_AER_UE_MASK, reg32);
+ }
/* Don't block PCI-CFG */
p->flags &= ~PHB3_CFG_BLOCKED;