diff options
-rw-r--r-- | core/pci-slot.c | 2 | ||||
-rw-r--r-- | core/pcie-slot.c | 40 | ||||
-rw-r--r-- | include/pci-slot.h | 1 | ||||
-rw-r--r-- | platforms/ibm-fsp/firenze-pci.c | 10 |
4 files changed, 45 insertions, 8 deletions
diff --git a/core/pci-slot.c b/core/pci-slot.c index 8735598..721b2d9 100644 --- a/core/pci-slot.c +++ b/core/pci-slot.c @@ -116,6 +116,8 @@ void pci_slot_add_dt_properties(struct pci_slot *slot, dt_add_property_cells(np, "ibm,reset-by-firmware", 1); dt_add_property_cells(np, "ibm,slot-pluggable", slot->pluggable); + dt_add_property_cells(np, "ibm,slot-surprise-pluggable", + slot->surprise_pluggable); dt_add_property_cells(np, "ibm,slot-power-ctl", slot->power_ctl); dt_add_property_cells(np, "ibm,slot-power-led-ctlled", slot->power_led_ctl); diff --git a/core/pcie-slot.c b/core/pcie-slot.c index fed3462..2484570 100644 --- a/core/pcie-slot.c +++ b/core/pcie-slot.c @@ -196,7 +196,8 @@ static int64_t pcie_slot_set_attention_state(struct pci_slot *slot, return OPAL_SUCCESS; } -static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val) +static int64_t pcie_slot_set_power_state_ext(struct pci_slot *slot, uint8_t val, + bool surprise_check) { struct phb *phb = slot->phb; struct pci_device *pd = slot->pd; @@ -214,6 +215,15 @@ static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val) return OPAL_SUCCESS; } + /* The power supply to the slot should be always on when surprise + * hotplug is claimed. For this case, update with the requested + * power state and bail immediately. + */ + if (surprise_check && slot->surprise_pluggable) { + slot->power_state = val; + return OPAL_SUCCESS; + } + pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_START); slot->power_state = val; ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); @@ -239,6 +249,11 @@ static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val) return OPAL_ASYNC_COMPLETION; } +static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val) +{ + return pcie_slot_set_power_state_ext(slot, val, true); +} + static int64_t pcie_slot_sm_poll_link(struct pci_slot *slot) { struct phb *phb = slot->phb; @@ -383,10 +398,10 @@ static int64_t pcie_slot_sm_freset(struct pci_slot *slot) } /* In power on state, power it off */ - if (power_state == PCI_SLOT_POWER_ON && - slot->ops.set_power_state) { + if (power_state == PCI_SLOT_POWER_ON) { PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n"); - slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF); + pcie_slot_set_power_state_ext(slot, + PCI_SLOT_POWER_OFF, false); pci_slot_set_state(slot, PCI_SLOT_STATE_FRESET_POWER_OFF); return pci_slot_set_sm_timeout(slot, msecs_to_tb(50)); @@ -394,8 +409,7 @@ static int64_t pcie_slot_sm_freset(struct pci_slot *slot) /* No power state change, fall through */ case PCI_SLOT_STATE_FRESET_POWER_OFF: PCIE_SLOT_DBG(slot, "FRESET: Power is off, turn on\n"); - if (slot->ops.set_power_state) - slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON); + pcie_slot_set_power_state_ext(slot, PCI_SLOT_POWER_ON, false); pci_slot_set_state(slot, PCI_SLOT_STATE_HRESET_START); return pci_slot_set_sm_timeout(slot, msecs_to_tb(50)); default: @@ -428,8 +442,7 @@ struct pci_slot *pcie_slot_create(struct phb *phb, struct pci_device *pd) &slot->slot_cap); } - if ((slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_SURP) && - (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_CAP)) + if (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_CAP) slot->pluggable = 1; if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL) { @@ -451,6 +464,17 @@ struct pci_slot *pcie_slot_create(struct phb *phb, struct pci_device *pd) slot->attn_led_ctl = PCI_SLOT_ATTN_LED_CTL_KERNEL; slot->wired_lanes = ((slot->link_cap & PCICAP_EXP_LCAP_MAXWDTH) >> 4); + /* The surprise hotplug capability is claimed when it's supported + * in the slot's capability bits or link state change reporting is + * supported in PCIe link capability. It means the surprise hotplug + * relies on presence or link state change events. In order for the + * link state change event to be properly raised during surprise hot + * add/remove, the power supply to the slot should be always on. + */ + if ((slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_SURP) || + (slot->link_cap & PCICAP_EXP_LCAP_DL_ACT_REP)) + slot->surprise_pluggable = 1; + /* Standard slot operations */ slot->ops.get_presence_state = pcie_slot_get_presence_state; slot->ops.get_link_state = pcie_slot_get_link_state; diff --git a/include/pci-slot.h b/include/pci-slot.h index cf22432..694a448 100644 --- a/include/pci-slot.h +++ b/include/pci-slot.h @@ -161,6 +161,7 @@ struct pci_slot { /* Slot information */ uint8_t pluggable; + uint8_t surprise_pluggable; uint8_t power_ctl; uint8_t power_led_ctl; uint8_t attn_led_ctl; diff --git a/platforms/ibm-fsp/firenze-pci.c b/platforms/ibm-fsp/firenze-pci.c index 1d83409..2b8c0c6 100644 --- a/platforms/ibm-fsp/firenze-pci.c +++ b/platforms/ibm-fsp/firenze-pci.c @@ -653,6 +653,16 @@ static int64_t firenze_pci_slot_set_power_state(struct pci_slot *slot, if (slot->power_state == val) return OPAL_SUCCESS; + /* Update with the requested power state and bail immediately when + * surprise hotplug is supported on the slot. It keeps the power + * supply to the slot on and it guarentees the link state change + * events will be raised properly during surprise hot add/remove. + */ + if (slot->surprise_pluggable) { + slot->power_state = val; + return OPAL_SUCCESS; + } + slot->power_state = val; pci_slot_set_state(slot, FIRENZE_PCI_SLOT_SPOWER_START); |