aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/pci-slot.c2
-rw-r--r--core/pcie-slot.c40
-rw-r--r--include/pci-slot.h1
-rw-r--r--platforms/ibm-fsp/firenze-pci.c10
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);