diff options
Diffstat (limited to 'drivers/pci/hotplug/pciehp_ctrl.c')
| -rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 562 |
1 files changed, 246 insertions, 316 deletions
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index ec0b4c11ccd9..bcc938d4420f 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PCI Express Hot Plug Controller Driver * @@ -8,60 +9,35 @@ * * All rights reserved. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or - * NON INFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> * */ -#include <linux/module.h> +#define dev_fmt(fmt) "pciehp: " fmt + #include <linux/kernel.h> #include <linux/types.h> -#include <linux/slab.h> +#include <linux/pm_runtime.h> #include <linux/pci.h> + #include "../pci.h" #include "pciehp.h" -static void interrupt_event_handler(struct work_struct *work); - -void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type) -{ - struct event_info *info; - - info = kmalloc(sizeof(*info), GFP_ATOMIC); - if (!info) { - ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type); - return; - } - - INIT_WORK(&info->work, interrupt_event_handler); - info->event_type = event_type; - info->p_slot = p_slot; - queue_work(p_slot->wq, &info->work); -} - /* The following routines constitute the bulk of the hotplug controller logic */ -static void set_slot_off(struct controller *ctrl, struct slot *pslot) +#define SAFE_REMOVAL true +#define SURPRISE_REMOVAL false + +static void set_slot_off(struct controller *ctrl) { - /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ + /* + * Turn off slot, turn on attention indicator, turn off power + * indicator + */ if (POWER_CTRL(ctrl)) { - pciehp_power_off_slot(pslot); + pciehp_power_off_slot(ctrl); /* * After turning power off, we must wait for at least 1 second @@ -71,78 +47,73 @@ static void set_slot_off(struct controller *ctrl, struct slot *pslot) msleep(1000); } - pciehp_green_led_off(pslot); - pciehp_set_attention_status(pslot, 1); + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + PCI_EXP_SLTCTL_ATTN_IND_ON); } /** * board_added - Called after a board has been added to the system. - * @p_slot: &slot where board is added + * @ctrl: PCIe hotplug controller where board is added * * Turns power on for the board. * Configures board. */ -static int board_added(struct slot *p_slot) +static int board_added(struct controller *ctrl) { int retval = 0; - struct controller *ctrl = p_slot->ctrl; struct pci_bus *parent = ctrl->pcie->port->subordinate; if (POWER_CTRL(ctrl)) { /* Power on slot */ - retval = pciehp_power_on_slot(p_slot); + retval = pciehp_power_on_slot(ctrl); if (retval) return retval; } - pciehp_green_led_blink(p_slot); + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, + INDICATOR_NOOP); /* Check link training status */ retval = pciehp_check_link_status(ctrl); - if (retval) { - ctrl_err(ctrl, "Failed to check link status\n"); + if (retval) goto err_exit; - } /* Check for a power fault */ - if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { - ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot)); + if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) { + ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); retval = -EIO; goto err_exit; } - retval = pciehp_configure_device(p_slot); + retval = pciehp_configure_device(ctrl); if (retval) { - ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", - pci_domain_nr(parent), parent->number); - if (retval != -EEXIST) + if (retval != -EEXIST) { + ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", + pci_domain_nr(parent), parent->number); goto err_exit; + } } - pciehp_green_led_on(p_slot); - pciehp_set_attention_status(p_slot, 0); + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, + PCI_EXP_SLTCTL_ATTN_IND_OFF); return 0; err_exit: - set_slot_off(ctrl, p_slot); + set_slot_off(ctrl); return retval; } /** - * remove_board - Turns off slot and LEDs - * @p_slot: slot where board is being removed + * remove_board - Turn off slot and Power Indicator + * @ctrl: PCIe hotplug controller where board is being removed + * @safe_removal: whether the board is safely removed (versus surprise removed) */ -static int remove_board(struct slot *p_slot) +static void remove_board(struct controller *ctrl, bool safe_removal) { - int retval; - struct controller *ctrl = p_slot->ctrl; - - retval = pciehp_unconfigure_device(p_slot); - if (retval) - return retval; + pciehp_unconfigure_device(ctrl, safe_removal); if (POWER_CTRL(ctrl)) { - pciehp_power_off_slot(p_slot); + pciehp_power_off_slot(ctrl); /* * After turning power off, we must wait for at least 1 second @@ -150,121 +121,67 @@ static int remove_board(struct slot *p_slot) * removed from the slot/adapter. */ msleep(1000); - } - /* turn off Green LED */ - pciehp_green_led_off(p_slot); - return 0; -} - -struct power_work_info { - struct slot *p_slot; - struct work_struct work; - unsigned int req; -#define DISABLE_REQ 0 -#define ENABLE_REQ 1 -}; - -/** - * pciehp_power_thread - handle pushbutton events - * @work: &struct work_struct describing work to be done - * - * Scheduled procedure to handle blocking stuff for the pushbuttons. - * Handles all pending events and exits. - */ -static void pciehp_power_thread(struct work_struct *work) -{ - struct power_work_info *info = - container_of(work, struct power_work_info, work); - struct slot *p_slot = info->p_slot; - int ret; - - switch (info->req) { - case DISABLE_REQ: - mutex_lock(&p_slot->hotplug_lock); - pciehp_disable_slot(p_slot); - mutex_unlock(&p_slot->hotplug_lock); - mutex_lock(&p_slot->lock); - p_slot->state = STATIC_STATE; - mutex_unlock(&p_slot->lock); - break; - case ENABLE_REQ: - mutex_lock(&p_slot->hotplug_lock); - ret = pciehp_enable_slot(p_slot); - mutex_unlock(&p_slot->hotplug_lock); - if (ret) - pciehp_green_led_off(p_slot); - mutex_lock(&p_slot->lock); - p_slot->state = STATIC_STATE; - mutex_unlock(&p_slot->lock); - break; - default: - break; + /* Ignore link or presence changes caused by power off */ + atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC), + &ctrl->pending_events); } - kfree(info); -} + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); -static void pciehp_queue_power_work(struct slot *p_slot, int req) -{ - struct power_work_info *info; + /* Don't carry LBMS indications across */ + pcie_reset_lbms(ctrl->pcie->port); +} - p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE; +static int pciehp_enable_slot(struct controller *ctrl); +static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal); - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - ctrl_err(p_slot->ctrl, "no memory to queue %s request\n", - (req == ENABLE_REQ) ? "poweron" : "poweroff"); - return; - } - info->p_slot = p_slot; - INIT_WORK(&info->work, pciehp_power_thread); - info->req = req; - queue_work(p_slot->wq, &info->work); +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 = container_of(work, struct controller, + button_work.work); - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { case BLINKINGOFF_STATE: - pciehp_queue_power_work(p_slot, DISABLE_REQ); + pciehp_request(ctrl, DISABLE_SLOT); break; case BLINKINGON_STATE: - pciehp_queue_power_work(p_slot, ENABLE_REQ); + pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); break; default: break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->state_lock); } -/* - * Note: This function must be called with slot->lock held - */ -static void handle_button_press_event(struct slot *p_slot) +void pciehp_handle_button_press(struct controller *ctrl) { - struct controller *ctrl = p_slot->ctrl; - u8 getstatus; - - switch (p_slot->state) { - case STATIC_STATE: - pciehp_get_power_status(p_slot, &getstatus); - if (getstatus) { - p_slot->state = BLINKINGOFF_STATE; - ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", - slot_name(p_slot)); + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case OFF_STATE: + case ON_STATE: + if (ctrl->state == ON_STATE) { + ctrl->state = BLINKINGOFF_STATE; + ctrl_info(ctrl, "Slot(%s): Button press: will power off in 5 sec\n", + slot_name(ctrl)); } else { - p_slot->state = BLINKINGON_STATE; - ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", - slot_name(p_slot)); + ctrl->state = BLINKINGON_STATE; + ctrl_info(ctrl, "Slot(%s): Button press: will power on in 5 sec\n", + slot_name(ctrl)); } - /* blink green LED and turn off amber */ - pciehp_green_led_blink(p_slot); - pciehp_set_attention_status(p_slot, 0); - queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); + /* blink power indicator and turn off attention */ + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + schedule_delayed_work(&ctrl->button_work, 5 * HZ); break; case BLINKINGOFF_STATE: case BLINKINGON_STATE: @@ -273,243 +190,256 @@ static void handle_button_press_event(struct slot *p_slot) * press the attention again before the 5 sec. limit * expires to cancel hot-add or hot-remove */ - ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); - cancel_delayed_work(&p_slot->work); - if (p_slot->state == BLINKINGOFF_STATE) - pciehp_green_led_on(p_slot); - else - pciehp_green_led_off(p_slot); - pciehp_set_attention_status(p_slot, 0); - ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", - slot_name(p_slot)); - p_slot->state = STATIC_STATE; - 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)); + cancel_delayed_work(&ctrl->button_work); + if (ctrl->state == BLINKINGOFF_STATE) { + ctrl->state = ON_STATE; + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + ctrl_info(ctrl, "Slot(%s): Button press: canceling request to power off\n", + slot_name(ctrl)); + } else { + ctrl->state = OFF_STATE; + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + ctrl_info(ctrl, "Slot(%s): Button press: canceling request to power on\n", + slot_name(ctrl)); + } break; default: - ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", - slot_name(p_slot), p_slot->state); + ctrl_err(ctrl, "Slot(%s): Button press: ignoring invalid state %#x\n", + slot_name(ctrl), ctrl->state); break; } + mutex_unlock(&ctrl->state_lock); } -/* - * Note: This function must be called with slot->lock held - */ -static void handle_link_event(struct slot *p_slot, u32 event) +void pciehp_handle_disable_request(struct controller *ctrl) { - struct controller *ctrl = p_slot->ctrl; - - switch (p_slot->state) { + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { case BLINKINGON_STATE: case BLINKINGOFF_STATE: - cancel_delayed_work(&p_slot->work); - /* Fall through */ - case STATIC_STATE: - pciehp_queue_power_work(p_slot, event == INT_LINK_UP ? - ENABLE_REQ : DISABLE_REQ); - break; - case POWERON_STATE: - if (event == INT_LINK_UP) { - ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n", - slot_name(p_slot)); - } else { - ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n", - slot_name(p_slot)); - pciehp_queue_power_work(p_slot, DISABLE_REQ); - } - break; - case POWEROFF_STATE: - if (event == INT_LINK_UP) { - ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n", - slot_name(p_slot)); - pciehp_queue_power_work(p_slot, ENABLE_REQ); - } 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); + cancel_delayed_work(&ctrl->button_work); break; } + ctrl->state = POWEROFF_STATE; + mutex_unlock(&ctrl->state_lock); + + ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL); } -static void interrupt_event_handler(struct work_struct *work) +void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) { - struct event_info *info = container_of(work, struct event_info, work); - struct slot *p_slot = info->p_slot; - struct controller *ctrl = p_slot->ctrl; - - mutex_lock(&p_slot->lock); - switch (info->event_type) { - case INT_BUTTON_PRESS: - handle_button_press_event(p_slot); - break; - case INT_POWER_FAULT: - if (!POWER_CTRL(ctrl)) - break; - pciehp_set_attention_status(p_slot, 1); - pciehp_green_led_off(p_slot); - break; - case INT_PRESENCE_ON: - pciehp_queue_power_work(p_slot, ENABLE_REQ); + int present, link_active; + + /* + * If the slot is on and presence or link has changed, turn it off. + * Even if it's occupied again, we cannot assume the card is the same. + */ + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: + cancel_delayed_work(&ctrl->button_work); + fallthrough; + case ON_STATE: + ctrl->state = POWEROFF_STATE; + mutex_unlock(&ctrl->state_lock); + if (events & PCI_EXP_SLTSTA_DLLSC) + ctrl_info(ctrl, "Slot(%s): Link Down\n", + slot_name(ctrl)); + if (events & PCI_EXP_SLTSTA_PDC) + ctrl_info(ctrl, "Slot(%s): Card not present\n", + slot_name(ctrl)); + pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); break; - case INT_PRESENCE_OFF: - /* - * Regardless of surprise capability, we need to - * definitely remove a card that has been pulled out! - */ - pciehp_queue_power_work(p_slot, DISABLE_REQ); + default: + mutex_unlock(&ctrl->state_lock); break; - case INT_LINK_UP: - case INT_LINK_DOWN: - handle_link_event(p_slot, info->event_type); + } + + /* Turn the slot on if it's occupied or link is up */ + mutex_lock(&ctrl->state_lock); + present = pciehp_card_present(ctrl); + link_active = pciehp_check_link_active(ctrl); + if (present <= 0 && link_active <= 0) { + if (ctrl->state == BLINKINGON_STATE) { + ctrl->state = OFF_STATE; + cancel_delayed_work(&ctrl->button_work); + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); + ctrl_info(ctrl, "Slot(%s): Card not present\n", + slot_name(ctrl)); + } + mutex_unlock(&ctrl->state_lock); + return; + } + + switch (ctrl->state) { + case BLINKINGON_STATE: + cancel_delayed_work(&ctrl->button_work); + fallthrough; + case OFF_STATE: + ctrl->state = POWERON_STATE; + mutex_unlock(&ctrl->state_lock); + if (present) + ctrl_info(ctrl, "Slot(%s): Card present\n", + slot_name(ctrl)); + if (link_active) + ctrl_info(ctrl, "Slot(%s): Link Up\n", + slot_name(ctrl)); + ctrl->request_result = pciehp_enable_slot(ctrl); break; default: + mutex_unlock(&ctrl->state_lock); break; } - mutex_unlock(&p_slot->lock); - - kfree(info); } -/* - * Note: This function must be called with slot->hotplug_lock held - */ -int pciehp_enable_slot(struct slot *p_slot) +static int __pciehp_enable_slot(struct controller *ctrl) { u8 getstatus = 0; - struct controller *ctrl = p_slot->ctrl; - pciehp_get_adapter_status(p_slot, &getstatus); - if (!getstatus) { - ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot)); - return -ENODEV; - } - if (MRL_SENS(p_slot->ctrl)) { - pciehp_get_latch_status(p_slot, &getstatus); + if (MRL_SENS(ctrl)) { + pciehp_get_latch_status(ctrl, &getstatus); if (getstatus) { ctrl_info(ctrl, "Slot(%s): Latch open\n", - slot_name(p_slot)); + slot_name(ctrl)); return -ENODEV; } } - if (POWER_CTRL(p_slot->ctrl)) { - pciehp_get_power_status(p_slot, &getstatus); + if (POWER_CTRL(ctrl)) { + pciehp_get_power_status(ctrl, &getstatus); if (getstatus) { ctrl_info(ctrl, "Slot(%s): Already enabled\n", - slot_name(p_slot)); + slot_name(ctrl)); return 0; } } - return board_added(p_slot); + return board_added(ctrl); } -/* - * Note: This function must be called with slot->hotplug_lock held - */ -int pciehp_disable_slot(struct slot *p_slot) +static int pciehp_enable_slot(struct controller *ctrl) { - u8 getstatus = 0; - struct controller *ctrl = p_slot->ctrl; + int ret; + + pm_runtime_get_sync(&ctrl->pcie->port->dev); + ret = __pciehp_enable_slot(ctrl); + if (ret && ATTN_BUTTN(ctrl)) + /* may be blinking */ + pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); + pm_runtime_put(&ctrl->pcie->port->dev); - if (!p_slot->ctrl) - return 1; + mutex_lock(&ctrl->state_lock); + ctrl->state = ret ? OFF_STATE : ON_STATE; + mutex_unlock(&ctrl->state_lock); - if (POWER_CTRL(p_slot->ctrl)) { - pciehp_get_power_status(p_slot, &getstatus); + return ret; +} + +static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal) +{ + u8 getstatus = 0; + + if (POWER_CTRL(ctrl)) { + pciehp_get_power_status(ctrl, &getstatus); if (!getstatus) { ctrl_info(ctrl, "Slot(%s): Already disabled\n", - slot_name(p_slot)); + slot_name(ctrl)); return -EINVAL; } } - return remove_board(p_slot); + remove_board(ctrl, safe_removal); + return 0; } -int pciehp_sysfs_enable_slot(struct slot *p_slot) +static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal) { - int retval = -ENODEV; - struct controller *ctrl = p_slot->ctrl; + int ret; + + pm_runtime_get_sync(&ctrl->pcie->port->dev); + ret = __pciehp_disable_slot(ctrl, safe_removal); + pm_runtime_put(&ctrl->pcie->port->dev); + + mutex_lock(&ctrl->state_lock); + ctrl->state = OFF_STATE; + mutex_unlock(&ctrl->state_lock); + + return ret; +} + +int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { case BLINKINGON_STATE: - cancel_delayed_work(&p_slot->work); - case STATIC_STATE: - p_slot->state = POWERON_STATE; - mutex_unlock(&p_slot->lock); - mutex_lock(&p_slot->hotplug_lock); - retval = pciehp_enable_slot(p_slot); - mutex_unlock(&p_slot->hotplug_lock); - mutex_lock(&p_slot->lock); - p_slot->state = STATIC_STATE; - break; + case OFF_STATE: + mutex_unlock(&ctrl->state_lock); + /* + * 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) && + !ctrl->ist_running); + return ctrl->request_result; case POWERON_STATE: ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", - slot_name(p_slot)); + slot_name(ctrl)); break; case BLINKINGOFF_STATE: + case ON_STATE: case POWEROFF_STATE: ctrl_info(ctrl, "Slot(%s): Already enabled\n", - slot_name(p_slot)); + slot_name(ctrl)); break; default: ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", - slot_name(p_slot), p_slot->state); + slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->state_lock); - return retval; + return -ENODEV; } -int pciehp_sysfs_disable_slot(struct slot *p_slot) +int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) { - int retval = -ENODEV; - struct controller *ctrl = p_slot->ctrl; + struct controller *ctrl = to_ctrl(hotplug_slot); - mutex_lock(&p_slot->lock); - switch (p_slot->state) { + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { case BLINKINGOFF_STATE: - cancel_delayed_work(&p_slot->work); - case STATIC_STATE: - p_slot->state = POWEROFF_STATE; - mutex_unlock(&p_slot->lock); - mutex_lock(&p_slot->hotplug_lock); - retval = pciehp_disable_slot(p_slot); - mutex_unlock(&p_slot->hotplug_lock); - mutex_lock(&p_slot->lock); - p_slot->state = STATIC_STATE; - break; + case ON_STATE: + mutex_unlock(&ctrl->state_lock); + pciehp_request(ctrl, DISABLE_SLOT); + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events) && + !ctrl->ist_running); + return ctrl->request_result; case POWEROFF_STATE: ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", - slot_name(p_slot)); + slot_name(ctrl)); break; case BLINKINGON_STATE: + case OFF_STATE: case POWERON_STATE: ctrl_info(ctrl, "Slot(%s): Already disabled\n", - slot_name(p_slot)); + slot_name(ctrl)); break; default: ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", - slot_name(p_slot), p_slot->state); + slot_name(ctrl), ctrl->state); break; } - mutex_unlock(&p_slot->lock); + mutex_unlock(&ctrl->state_lock); - return retval; + return -ENODEV; } |
