return pciehp_reset_slot(slot, probe);
}
+/**
+ * pciehp_check_presence() - synthesize event if presence has changed
+ *
+ * On probe and resume, an explicit presence check is necessary to bring up an
+ * occupied slot or bring down an unoccupied slot. This can't be triggered by
+ * events in the Slot Status register, they may be stale and are therefore
+ * cleared. Secondly, sending an interrupt for "events that occur while
+ * interrupt generation is disabled [when] interrupt generation is subsequently
+ * enabled" is optional per PCIe r4.0, sec 6.7.3.4.
+ */
+static void pciehp_check_presence(struct controller *ctrl)
+{
+ struct slot *slot = ctrl->slot;
+ u8 occupied;
+
+ down_read(&ctrl->reset_lock);
+ mutex_lock(&slot->lock);
+
+ pciehp_get_adapter_status(slot, &occupied);
+ if ((occupied && (slot->state == OFF_STATE ||
+ slot->state == BLINKINGON_STATE)) ||
+ (!occupied && (slot->state == ON_STATE ||
+ slot->state == BLINKINGOFF_STATE)))
+ pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
+
+ mutex_unlock(&slot->lock);
+ up_read(&ctrl->reset_lock);
+}
+
static int pciehp_probe(struct pcie_device *dev)
{
int rc;
struct controller *ctrl;
struct slot *slot;
- u8 occupied, poweron;
/* If this is not a "hotplug" service, we have no business here. */
if (dev->service != PCIE_PORT_SERVICE_HP)
goto err_out_shutdown_notification;
}
- /* Check if slot is occupied */
- down_read(&ctrl->reset_lock);
- mutex_lock(&slot->lock);
- pciehp_get_adapter_status(slot, &occupied);
- pciehp_get_power_status(slot, &poweron);
- if ((occupied && (slot->state == OFF_STATE ||
- slot->state == BLINKINGON_STATE)) ||
- (!occupied && (slot->state == ON_STATE ||
- slot->state == BLINKINGOFF_STATE)))
- pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
- /* If empty slot's power status is on, turn power off */
- if (!occupied && poweron && POWER_CTRL(ctrl))
- pciehp_power_off_slot(slot);
- mutex_unlock(&slot->lock);
- up_read(&ctrl->reset_lock);
+ pciehp_check_presence(ctrl);
return 0;
static int pciehp_resume(struct pcie_device *dev)
{
- struct controller *ctrl;
- struct slot *slot;
- u8 status;
-
- ctrl = get_service_data(dev);
- slot = ctrl->slot;
+ struct controller *ctrl = get_service_data(dev);
- /* Check if slot is occupied */
- pciehp_get_adapter_status(slot, &status);
- mutex_lock(&slot->lock);
- if ((status && (slot->state == OFF_STATE ||
- slot->state == BLINKINGON_STATE)) ||
- (!status && (slot->state == ON_STATE ||
- slot->state == BLINKINGOFF_STATE)))
- pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
- mutex_unlock(&slot->lock);
+ pciehp_check_presence(ctrl);
return 0;
}
{
struct controller *ctrl;
u32 slot_cap, link_cap;
+ u8 occupied, poweron;
struct pci_dev *pdev = dev->port;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (pcie_init_slot(ctrl))
goto abort_ctrl;
+ /*
+ * If empty slot's power status is on, turn power off. The IRQ isn't
+ * requested yet, so avoid triggering a notification with this command.
+ */
+ if (POWER_CTRL(ctrl)) {
+ pciehp_get_adapter_status(ctrl->slot, &occupied);
+ pciehp_get_power_status(ctrl->slot, &poweron);
+ if (!occupied && poweron) {
+ pcie_disable_notification(ctrl);
+ pciehp_power_off_slot(ctrl->slot);
+ }
+ }
+
return ctrl;
abort_ctrl: