diff options
Diffstat (limited to 'drivers/pci/remove.c')
| -rw-r--r-- | drivers/pci/remove.c | 94 |
1 files changed, 77 insertions, 17 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 8fc54b7327bc..417a9ea59117 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -1,43 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 #include <linux/pci.h> #include <linux/module.h> -#include <linux/pci-aspm.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> + #include "pci.h" static void pci_free_resources(struct pci_dev *dev) { - int i; - - msi_remove_pci_irq_vectors(dev); + struct resource *res; - pci_cleanup_rom(dev); - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - struct resource *res = dev->resource + i; + pci_dev_for_each_resource(dev, res) { if (res->parent) release_resource(res); } } +static void pci_pwrctrl_unregister(struct device *dev) +{ + struct device_node *np; + struct platform_device *pdev; + + np = dev_of_node(dev); + if (!np) + return; + + pdev = of_find_device_by_node(np); + if (!pdev) + return; + + of_device_unregister(pdev); + put_device(&pdev->dev); + + of_node_clear_flag(np, OF_POPULATED); +} + static void pci_stop_dev(struct pci_dev *dev) { pci_pme_active(dev, false); - if (dev->is_added) { - pci_proc_detach_device(dev); - pci_remove_sysfs_dev_files(dev); - device_del(&dev->dev); - dev->is_added = 0; - } + if (!pci_dev_test_and_clear_added(dev)) + return; - if (dev->bus->self) - pcie_aspm_exit_link_state(dev); + device_release_driver(&dev->dev); + pci_proc_detach_device(dev); + pci_remove_sysfs_dev_files(dev); + of_pci_remove_node(dev); } static void pci_destroy_dev(struct pci_dev *dev) { + if (pci_dev_test_and_set_removed(dev)) + return; + + pci_doe_sysfs_teardown(dev); + pci_npem_remove(dev); + + /* + * While device is in D0 drop the device from TSM link operations + * including unbind and disconnect (IDE + SPDM teardown). + */ + pci_tsm_destroy(dev); + + device_del(&dev->dev); + down_write(&pci_bus_sem); list_del(&dev->bus_list); up_write(&pci_bus_sem); + pci_doe_destroy(dev); + pci_ide_destroy(dev); + pcie_aspm_exit_link_state(dev); + pci_bridge_d3_update(dev); + pci_pwrctrl_unregister(&dev->dev); pci_free_resources(dev); put_device(&dev->dev); } @@ -51,6 +87,10 @@ void pci_remove_bus(struct pci_bus *bus) pci_bus_release_busn_res(bus); up_write(&pci_bus_sem); pci_remove_legacy_files(bus); + + if (bus->ops->remove_bus) + bus->ops->remove_bus(bus); + pcibios_remove_bus(bus); device_unregister(&bus->dev); } @@ -107,11 +147,20 @@ static void pci_remove_bus_device(struct pci_dev *dev) */ void pci_stop_and_remove_bus_device(struct pci_dev *dev) { + lockdep_assert_held(&pci_rescan_remove_lock); pci_stop_bus_device(dev); pci_remove_bus_device(dev); } EXPORT_SYMBOL(pci_stop_and_remove_bus_device); +void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev) +{ + pci_lock_rescan_remove(); + pci_stop_and_remove_bus_device(dev); + pci_unlock_rescan_remove(); +} +EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked); + void pci_stop_root_bus(struct pci_bus *bus) { struct pci_dev *child, *tmp; @@ -125,9 +174,12 @@ void pci_stop_root_bus(struct pci_bus *bus) &bus->devices, bus_list) pci_stop_bus_device(child); + of_pci_remove_host_bridge_node(host_bridge); + /* stop the host bridge */ - device_del(&host_bridge->dev); + device_release_driver(&host_bridge->dev); } +EXPORT_SYMBOL_GPL(pci_stop_root_bus); void pci_remove_root_bus(struct pci_bus *bus) { @@ -141,9 +193,17 @@ void pci_remove_root_bus(struct pci_bus *bus) list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) pci_remove_bus_device(child); + +#ifdef CONFIG_PCI_DOMAINS_GENERIC + /* Release domain_nr if it was dynamically allocated */ + if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET) + pci_bus_release_domain_nr(host_bridge->dev.parent, bus->domain_nr); +#endif + pci_remove_bus(bus); host_bridge->bus = NULL; /* remove the host bridge */ - put_device(&host_bridge->dev); + device_del(&host_bridge->dev); } +EXPORT_SYMBOL_GPL(pci_remove_root_bus); |
