diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_ctrl.c')
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 99 |
1 files changed, 48 insertions, 51 deletions
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 627e846df802..70bad847a450 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -122,22 +122,26 @@ static void remove_board(struct slot *p_slot) pciehp_green_led_off(p_slot); } +void pciehp_request(struct controller *ctrl, int action) +{ + atomic_or(action, &ctrl->pending_events); + if (!pciehp_poll_mode) + irq_wake_thread(ctrl->pcie->irq, ctrl); +} + void pciehp_queue_pushbutton_work(struct work_struct *work) { struct slot *p_slot = container_of(work, struct slot, work.work); + struct controller *ctrl = p_slot->ctrl; mutex_lock(&p_slot->lock); switch (p_slot->state) { case BLINKINGOFF_STATE: - p_slot->state = POWEROFF_STATE; - mutex_unlock(&p_slot->lock); - pciehp_disable_slot(p_slot); - return; + pciehp_request(ctrl, DISABLE_SLOT); + break; case BLINKINGON_STATE: - p_slot->state = POWERON_STATE; - mutex_unlock(&p_slot->lock); - pciehp_enable_slot(p_slot); - return; + pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + break; default: break; } @@ -186,16 +190,6 @@ void pciehp_handle_button_press(struct slot *p_slot) ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", slot_name(p_slot)); break; - case POWEROFF_STATE: - case POWERON_STATE: - /* - * Ignore if the slot is on power-on or power-off state; - * this means that the previous attention button action - * to hot-add or hot-remove is undergoing - */ - ctrl_info(ctrl, "Slot(%s): Button ignored\n", - slot_name(p_slot)); - break; default: ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", slot_name(p_slot), p_slot->state); @@ -204,6 +198,22 @@ void pciehp_handle_button_press(struct slot *p_slot) mutex_unlock(&p_slot->lock); } +void pciehp_handle_disable_request(struct slot *slot) +{ + struct controller *ctrl = slot->ctrl; + + mutex_lock(&slot->lock); + switch (slot->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&slot->work); + } + slot->state = POWEROFF_STATE; + mutex_unlock(&slot->lock); + + ctrl->request_result = pciehp_disable_slot(slot); +} + void pciehp_handle_link_change(struct slot *p_slot) { struct controller *ctrl = p_slot->ctrl; @@ -232,32 +242,6 @@ void pciehp_handle_link_change(struct slot *p_slot) } return; break; - case POWERON_STATE: - if (link_active) { - ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n", - slot_name(p_slot)); - } else { - p_slot->state = POWEROFF_STATE; - mutex_unlock(&p_slot->lock); - ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n", - slot_name(p_slot)); - pciehp_disable_slot(p_slot); - return; - } - break; - case POWEROFF_STATE: - if (link_active) { - p_slot->state = POWERON_STATE; - mutex_unlock(&p_slot->lock); - ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n", - slot_name(p_slot)); - pciehp_enable_slot(p_slot); - return; - } else { - ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n", - slot_name(p_slot)); - } - break; default: ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", slot_name(p_slot), p_slot->state); @@ -272,6 +256,12 @@ void pciehp_handle_presence_change(struct slot *slot) u8 present; mutex_lock(&slot->lock); + switch (slot->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&slot->work); + } + pciehp_get_adapter_status(slot, &present); ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot), present ? "" : "not "); @@ -279,7 +269,7 @@ void pciehp_handle_presence_change(struct slot *slot) if (present) { slot->state = POWERON_STATE; mutex_unlock(&slot->lock); - pciehp_enable_slot(slot); + ctrl->request_result = pciehp_enable_slot(slot); } else { slot->state = POWEROFF_STATE; mutex_unlock(&slot->lock); @@ -383,11 +373,17 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) mutex_lock(&p_slot->lock); switch (p_slot->state) { case BLINKINGON_STATE: - cancel_delayed_work(&p_slot->work); case OFF_STATE: - p_slot->state = POWERON_STATE; mutex_unlock(&p_slot->lock); - return pciehp_enable_slot(p_slot); + /* + * The IRQ thread becomes a no-op if the user pulls out the + * card before the thread wakes up, so initialize to -ENODEV. + */ + ctrl->request_result = -ENODEV; + pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events)); + return ctrl->request_result; case POWERON_STATE: ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", slot_name(p_slot)); @@ -415,11 +411,12 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot) mutex_lock(&p_slot->lock); switch (p_slot->state) { case BLINKINGOFF_STATE: - cancel_delayed_work(&p_slot->work); case ON_STATE: - p_slot->state = POWEROFF_STATE; mutex_unlock(&p_slot->lock); - return pciehp_disable_slot(p_slot); + pciehp_request(ctrl, DISABLE_SLOT); + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events)); + return ctrl->request_result; case POWEROFF_STATE: ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", slot_name(p_slot)); |