194 lines · c
1// SPDX-License-Identifier: GPL-2.02#include <linux/pci.h>3#include <linux/module.h>4#include <linux/of.h>5#include <linux/of_platform.h>6#include <linux/platform_device.h>7 8#include "pci.h"9 10static void pci_free_resources(struct pci_dev *dev)11{12 struct resource *res;13 14 pci_dev_for_each_resource(dev, res) {15 if (res->parent)16 release_resource(res);17 }18}19 20static int pci_pwrctl_unregister(struct device *dev, void *data)21{22 struct device_node *pci_node = data, *plat_node = dev_of_node(dev);23 24 if (dev_is_platform(dev) && plat_node && plat_node == pci_node) {25 of_device_unregister(to_platform_device(dev));26 of_node_clear_flag(plat_node, OF_POPULATED);27 }28 29 return 0;30}31 32static void pci_stop_dev(struct pci_dev *dev)33{34 pci_pme_active(dev, false);35 36 if (pci_dev_is_added(dev)) {37 device_for_each_child(dev->dev.parent, dev_of_node(&dev->dev),38 pci_pwrctl_unregister);39 device_release_driver(&dev->dev);40 pci_proc_detach_device(dev);41 pci_remove_sysfs_dev_files(dev);42 of_pci_remove_node(dev);43 44 pci_dev_assign_added(dev, false);45 }46}47 48static void pci_destroy_dev(struct pci_dev *dev)49{50 if (!dev->dev.kobj.parent)51 return;52 53 pci_npem_remove(dev);54 55 device_del(&dev->dev);56 57 down_write(&pci_bus_sem);58 list_del(&dev->bus_list);59 up_write(&pci_bus_sem);60 61 pci_doe_destroy(dev);62 pcie_aspm_exit_link_state(dev);63 pci_bridge_d3_update(dev);64 pci_free_resources(dev);65 put_device(&dev->dev);66}67 68void pci_remove_bus(struct pci_bus *bus)69{70 pci_proc_detach_bus(bus);71 72 down_write(&pci_bus_sem);73 list_del(&bus->node);74 pci_bus_release_busn_res(bus);75 up_write(&pci_bus_sem);76 pci_remove_legacy_files(bus);77 78 if (bus->ops->remove_bus)79 bus->ops->remove_bus(bus);80 81 pcibios_remove_bus(bus);82 device_unregister(&bus->dev);83}84EXPORT_SYMBOL(pci_remove_bus);85 86static void pci_stop_bus_device(struct pci_dev *dev)87{88 struct pci_bus *bus = dev->subordinate;89 struct pci_dev *child, *tmp;90 91 /*92 * Stopping an SR-IOV PF device removes all the associated VFs,93 * which will update the bus->devices list and confuse the94 * iterator. Therefore, iterate in reverse so we remove the VFs95 * first, then the PF.96 */97 if (bus) {98 list_for_each_entry_safe_reverse(child, tmp,99 &bus->devices, bus_list)100 pci_stop_bus_device(child);101 }102 103 pci_stop_dev(dev);104}105 106static void pci_remove_bus_device(struct pci_dev *dev)107{108 struct pci_bus *bus = dev->subordinate;109 struct pci_dev *child, *tmp;110 111 if (bus) {112 list_for_each_entry_safe(child, tmp,113 &bus->devices, bus_list)114 pci_remove_bus_device(child);115 116 pci_remove_bus(bus);117 dev->subordinate = NULL;118 }119 120 pci_destroy_dev(dev);121}122 123/**124 * pci_stop_and_remove_bus_device - remove a PCI device and any children125 * @dev: the device to remove126 *127 * Remove a PCI device from the device lists, informing the drivers128 * that the device has been removed. We also remove any subordinate129 * buses and children in a depth-first manner.130 *131 * For each device we remove, delete the device structure from the132 * device lists, remove the /proc entry, and notify userspace133 * (/sbin/hotplug).134 */135void pci_stop_and_remove_bus_device(struct pci_dev *dev)136{137 pci_stop_bus_device(dev);138 pci_remove_bus_device(dev);139}140EXPORT_SYMBOL(pci_stop_and_remove_bus_device);141 142void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)143{144 pci_lock_rescan_remove();145 pci_stop_and_remove_bus_device(dev);146 pci_unlock_rescan_remove();147}148EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);149 150void pci_stop_root_bus(struct pci_bus *bus)151{152 struct pci_dev *child, *tmp;153 struct pci_host_bridge *host_bridge;154 155 if (!pci_is_root_bus(bus))156 return;157 158 host_bridge = to_pci_host_bridge(bus->bridge);159 list_for_each_entry_safe_reverse(child, tmp,160 &bus->devices, bus_list)161 pci_stop_bus_device(child);162 163 /* stop the host bridge */164 device_release_driver(&host_bridge->dev);165}166EXPORT_SYMBOL_GPL(pci_stop_root_bus);167 168void pci_remove_root_bus(struct pci_bus *bus)169{170 struct pci_dev *child, *tmp;171 struct pci_host_bridge *host_bridge;172 173 if (!pci_is_root_bus(bus))174 return;175 176 host_bridge = to_pci_host_bridge(bus->bridge);177 list_for_each_entry_safe(child, tmp,178 &bus->devices, bus_list)179 pci_remove_bus_device(child);180 181#ifdef CONFIG_PCI_DOMAINS_GENERIC182 /* Release domain_nr if it was dynamically allocated */183 if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)184 pci_bus_release_domain_nr(host_bridge->dev.parent, bus->domain_nr);185#endif186 187 pci_remove_bus(bus);188 host_bridge->bus = NULL;189 190 /* remove the host bridge */191 device_del(&host_bridge->dev);192}193EXPORT_SYMBOL_GPL(pci_remove_root_bus);194