aboutsummaryrefslogtreecommitdiff
path: root/core/pcie-slot.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/pcie-slot.c')
-rw-r--r--core/pcie-slot.c40
1 files changed, 32 insertions, 8 deletions
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;