2018-01-27 04:22:04 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* PCI Express Hot Plug Controller Driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
|
|
|
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
|
|
|
* Copyright (C) 2001 IBM Corp.
|
|
|
|
* Copyright (C) 2003-2004 Intel Corporation
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2005-08-17 06:16:10 +08:00
|
|
|
* Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include "../pci.h"
|
|
|
|
#include "pciehp.h"
|
|
|
|
|
PCI: pciehp: Differentiate between surprise and safe removal
When removing PCI devices below a hotplug bridge, pciehp marks them as
disconnected if the card is no longer present in the slot or it quiesces
them if the card is still present (by disabling INTx interrupts, bus
mastering and SERR# reporting).
To detect whether the card is still present, pciehp checks the Presence
Detect State bit in the Slot Status register. The problem with this
approach is that even if the card is present, the link to it may be
down, and it that case it would be better to mark the devices as
disconnected instead of trying to quiesce them. Moreover, if the card
in the slot was quickly replaced by another one, the Presence Detect
State bit would be set, yet trying to quiesce the new card's devices
would be wrong and the correct thing to do is to mark the previous
card's devices as disconnected.
Instead of looking at the Presence Detect State bit, it is better to
differentiate whether the card was surprise removed versus safely
removed (via sysfs or an Attention Button press). On surprise removal,
the devices should be marked as disconnected, whereas on safe removal it
is correct to quiesce the devices.
The knowledge whether a surprise removal or a safe removal is at hand
does exist further up in the call stack: A surprise removal is
initiated by pciehp_handle_presence_or_link_change(), a safe removal by
pciehp_handle_disable_request().
Pass that information down to pciehp_unconfigure_device() and use it in
lieu of the Presence Detect State bit. While there, add kernel-doc to
pciehp_unconfigure_device() and pciehp_configure_device().
Tested-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Keith Busch <keith.busch@intel.com>
2018-07-31 13:50:37 +08:00
|
|
|
/**
|
|
|
|
* pciehp_configure_device() - enumerate PCI devices below a hotplug bridge
|
2018-09-19 03:46:17 +08:00
|
|
|
* @ctrl: PCIe hotplug controller
|
PCI: pciehp: Differentiate between surprise and safe removal
When removing PCI devices below a hotplug bridge, pciehp marks them as
disconnected if the card is no longer present in the slot or it quiesces
them if the card is still present (by disabling INTx interrupts, bus
mastering and SERR# reporting).
To detect whether the card is still present, pciehp checks the Presence
Detect State bit in the Slot Status register. The problem with this
approach is that even if the card is present, the link to it may be
down, and it that case it would be better to mark the devices as
disconnected instead of trying to quiesce them. Moreover, if the card
in the slot was quickly replaced by another one, the Presence Detect
State bit would be set, yet trying to quiesce the new card's devices
would be wrong and the correct thing to do is to mark the previous
card's devices as disconnected.
Instead of looking at the Presence Detect State bit, it is better to
differentiate whether the card was surprise removed versus safely
removed (via sysfs or an Attention Button press). On surprise removal,
the devices should be marked as disconnected, whereas on safe removal it
is correct to quiesce the devices.
The knowledge whether a surprise removal or a safe removal is at hand
does exist further up in the call stack: A surprise removal is
initiated by pciehp_handle_presence_or_link_change(), a safe removal by
pciehp_handle_disable_request().
Pass that information down to pciehp_unconfigure_device() and use it in
lieu of the Presence Detect State bit. While there, add kernel-doc to
pciehp_unconfigure_device() and pciehp_configure_device().
Tested-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Keith Busch <keith.busch@intel.com>
2018-07-31 13:50:37 +08:00
|
|
|
*
|
|
|
|
* Enumerate PCI devices below a hotplug bridge and add them to the system.
|
|
|
|
* Return 0 on success, %-EEXIST if the devices are already enumerated or
|
|
|
|
* %-ENODEV if enumeration failed.
|
|
|
|
*/
|
2018-09-19 03:46:17 +08:00
|
|
|
int pciehp_configure_device(struct controller *ctrl)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-01 08:20:06 +08:00
|
|
|
struct pci_dev *dev;
|
2018-09-19 03:46:17 +08:00
|
|
|
struct pci_dev *bridge = ctrl->pcie->port;
|
2010-01-22 17:02:26 +08:00
|
|
|
struct pci_bus *parent = bridge->subordinate;
|
2014-01-15 03:03:14 +08:00
|
|
|
int num, ret = 0;
|
2005-11-01 08:20:06 +08:00
|
|
|
|
2014-01-15 03:03:14 +08:00
|
|
|
pci_lock_rescan_remove();
|
|
|
|
|
2009-09-15 16:26:56 +08:00
|
|
|
dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
|
2005-11-01 08:20:06 +08:00
|
|
|
if (dev) {
|
2017-10-14 02:35:46 +08:00
|
|
|
/*
|
|
|
|
* The device is already there. Either configured by the
|
|
|
|
* boot firmware or a previous hotplug event.
|
|
|
|
*/
|
|
|
|
ctrl_dbg(ctrl, "Device %s already exists at %04x:%02x:00, skipping hot-add\n",
|
2014-04-19 08:13:50 +08:00
|
|
|
pci_name(dev), pci_domain_nr(parent), parent->number);
|
2006-05-12 10:22:24 +08:00
|
|
|
pci_dev_put(dev);
|
2014-02-12 08:36:51 +08:00
|
|
|
ret = -EEXIST;
|
2014-01-15 03:03:14 +08:00
|
|
|
goto out;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-09-15 16:26:56 +08:00
|
|
|
num = pci_scan_slot(parent, PCI_DEVFN(0, 0));
|
2005-11-01 08:20:06 +08:00
|
|
|
if (num == 0) {
|
2008-09-05 11:11:26 +08:00
|
|
|
ctrl_err(ctrl, "No new device found\n");
|
2014-01-15 03:03:14 +08:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto out;
|
2005-11-01 08:20:06 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-10-21 04:38:54 +08:00
|
|
|
for_each_pci_bridge(dev, parent)
|
|
|
|
pci_hp_add_bridge(dev);
|
2010-01-22 17:02:26 +08:00
|
|
|
|
|
|
|
pci_assign_unassigned_bridge_resources(bridge);
|
2014-08-29 02:13:51 +08:00
|
|
|
pcie_bus_configure_settings(parent);
|
2005-11-01 08:20:06 +08:00
|
|
|
pci_bus_add_devices(parent);
|
2010-01-22 17:02:26 +08:00
|
|
|
|
2014-01-15 03:03:14 +08:00
|
|
|
out:
|
|
|
|
pci_unlock_rescan_remove();
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
PCI: pciehp: Differentiate between surprise and safe removal
When removing PCI devices below a hotplug bridge, pciehp marks them as
disconnected if the card is no longer present in the slot or it quiesces
them if the card is still present (by disabling INTx interrupts, bus
mastering and SERR# reporting).
To detect whether the card is still present, pciehp checks the Presence
Detect State bit in the Slot Status register. The problem with this
approach is that even if the card is present, the link to it may be
down, and it that case it would be better to mark the devices as
disconnected instead of trying to quiesce them. Moreover, if the card
in the slot was quickly replaced by another one, the Presence Detect
State bit would be set, yet trying to quiesce the new card's devices
would be wrong and the correct thing to do is to mark the previous
card's devices as disconnected.
Instead of looking at the Presence Detect State bit, it is better to
differentiate whether the card was surprise removed versus safely
removed (via sysfs or an Attention Button press). On surprise removal,
the devices should be marked as disconnected, whereas on safe removal it
is correct to quiesce the devices.
The knowledge whether a surprise removal or a safe removal is at hand
does exist further up in the call stack: A surprise removal is
initiated by pciehp_handle_presence_or_link_change(), a safe removal by
pciehp_handle_disable_request().
Pass that information down to pciehp_unconfigure_device() and use it in
lieu of the Presence Detect State bit. While there, add kernel-doc to
pciehp_unconfigure_device() and pciehp_configure_device().
Tested-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Keith Busch <keith.busch@intel.com>
2018-07-31 13:50:37 +08:00
|
|
|
/**
|
|
|
|
* pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge
|
2018-09-19 03:46:17 +08:00
|
|
|
* @ctrl: PCIe hotplug controller
|
PCI: pciehp: Differentiate between surprise and safe removal
When removing PCI devices below a hotplug bridge, pciehp marks them as
disconnected if the card is no longer present in the slot or it quiesces
them if the card is still present (by disabling INTx interrupts, bus
mastering and SERR# reporting).
To detect whether the card is still present, pciehp checks the Presence
Detect State bit in the Slot Status register. The problem with this
approach is that even if the card is present, the link to it may be
down, and it that case it would be better to mark the devices as
disconnected instead of trying to quiesce them. Moreover, if the card
in the slot was quickly replaced by another one, the Presence Detect
State bit would be set, yet trying to quiesce the new card's devices
would be wrong and the correct thing to do is to mark the previous
card's devices as disconnected.
Instead of looking at the Presence Detect State bit, it is better to
differentiate whether the card was surprise removed versus safely
removed (via sysfs or an Attention Button press). On surprise removal,
the devices should be marked as disconnected, whereas on safe removal it
is correct to quiesce the devices.
The knowledge whether a surprise removal or a safe removal is at hand
does exist further up in the call stack: A surprise removal is
initiated by pciehp_handle_presence_or_link_change(), a safe removal by
pciehp_handle_disable_request().
Pass that information down to pciehp_unconfigure_device() and use it in
lieu of the Presence Detect State bit. While there, add kernel-doc to
pciehp_unconfigure_device() and pciehp_configure_device().
Tested-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Cc: Keith Busch <keith.busch@intel.com>
2018-07-31 13:50:37 +08:00
|
|
|
* @presence: whether the card is still present in the slot;
|
|
|
|
* true for safe removal via sysfs or an Attention Button press,
|
|
|
|
* false for surprise removal
|
|
|
|
*
|
|
|
|
* Unbind PCI devices below a hotplug bridge from their drivers and remove
|
|
|
|
* them from the system. Safely removed devices are quiesced. Surprise
|
|
|
|
* removed devices are marked as such to prevent further accesses.
|
|
|
|
*/
|
2018-09-19 03:46:17 +08:00
|
|
|
void pciehp_unconfigure_device(struct controller *ctrl, bool presence)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-01-15 11:12:19 +08:00
|
|
|
struct pci_dev *dev, *temp;
|
2018-09-19 03:46:17 +08:00
|
|
|
struct pci_bus *parent = ctrl->pcie->port->subordinate;
|
2007-12-20 18:46:33 +08:00
|
|
|
u16 command;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-09-15 16:26:56 +08:00
|
|
|
ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n",
|
|
|
|
__func__, pci_domain_nr(parent), parent->number);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-08-19 22:29:00 +08:00
|
|
|
if (!presence)
|
|
|
|
pci_walk_bus(parent, pci_dev_set_disconnected, NULL);
|
|
|
|
|
2014-01-15 03:03:14 +08:00
|
|
|
pci_lock_rescan_remove();
|
|
|
|
|
2013-07-20 03:14:16 +08:00
|
|
|
/*
|
|
|
|
* Stopping an SR-IOV PF device removes all the associated VFs,
|
|
|
|
* which will update the bus->devices list and confuse the
|
|
|
|
* iterator. Therefore, iterate in reverse so we remove the VFs
|
|
|
|
* first, then the PF. We do the same in pci_stop_bus_device().
|
|
|
|
*/
|
|
|
|
list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
|
|
|
|
bus_list) {
|
2013-01-15 11:12:19 +08:00
|
|
|
pci_dev_get(dev);
|
|
|
|
pci_stop_and_remove_bus_device(dev);
|
2007-12-20 18:46:33 +08:00
|
|
|
/*
|
|
|
|
* Ensure that no new Requests will be generated from
|
|
|
|
* the device.
|
|
|
|
*/
|
|
|
|
if (presence) {
|
2013-01-15 11:12:19 +08:00
|
|
|
pci_read_config_word(dev, PCI_COMMAND, &command);
|
2007-12-20 18:46:33 +08:00
|
|
|
command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
|
|
|
|
command |= PCI_COMMAND_INTX_DISABLE;
|
2013-01-15 11:12:19 +08:00
|
|
|
pci_write_config_word(dev, PCI_COMMAND, command);
|
2007-12-20 18:46:33 +08:00
|
|
|
}
|
2013-01-15 11:12:19 +08:00
|
|
|
pci_dev_put(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-08-10 07:09:37 +08:00
|
|
|
|
2014-01-15 03:03:14 +08:00
|
|
|
pci_unlock_rescan_remove();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|