1 // SPDX-License-Identifier: GPL-2.0 2 #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 10 static 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 20 static void pci_pwrctrl_unregister(struct device *dev) 21 { 22 struct device_node *np; 23 struct platform_device *pdev; 24 25 np = dev_of_node(dev); 26 if (!np) 27 return; 28 29 pdev = of_find_device_by_node(np); 30 if (!pdev) 31 return; 32 33 of_device_unregister(pdev); 34 of_node_clear_flag(np, OF_POPULATED); 35 } 36 37 static void pci_stop_dev(struct pci_dev *dev) 38 { 39 pci_pme_active(dev, false); 40 41 if (!pci_dev_test_and_clear_added(dev)) 42 return; 43 44 pci_pwrctrl_unregister(&dev->dev); 45 device_release_driver(&dev->dev); 46 pci_proc_detach_device(dev); 47 pci_remove_sysfs_dev_files(dev); 48 of_pci_remove_node(dev); 49 } 50 51 static void pci_destroy_dev(struct pci_dev *dev) 52 { 53 if (pci_dev_test_and_set_removed(dev)) 54 return; 55 56 pci_npem_remove(dev); 57 58 device_del(&dev->dev); 59 60 down_write(&pci_bus_sem); 61 list_del(&dev->bus_list); 62 up_write(&pci_bus_sem); 63 64 pci_doe_destroy(dev); 65 pcie_aspm_exit_link_state(dev); 66 pci_bridge_d3_update(dev); 67 pci_free_resources(dev); 68 put_device(&dev->dev); 69 } 70 71 void pci_remove_bus(struct pci_bus *bus) 72 { 73 pci_proc_detach_bus(bus); 74 75 down_write(&pci_bus_sem); 76 list_del(&bus->node); 77 pci_bus_release_busn_res(bus); 78 up_write(&pci_bus_sem); 79 pci_remove_legacy_files(bus); 80 81 if (bus->ops->remove_bus) 82 bus->ops->remove_bus(bus); 83 84 pcibios_remove_bus(bus); 85 device_unregister(&bus->dev); 86 } 87 EXPORT_SYMBOL(pci_remove_bus); 88 89 static void pci_stop_bus_device(struct pci_dev *dev) 90 { 91 struct pci_bus *bus = dev->subordinate; 92 struct pci_dev *child, *tmp; 93 94 /* 95 * Stopping an SR-IOV PF device removes all the associated VFs, 96 * which will update the bus->devices list and confuse the 97 * iterator. Therefore, iterate in reverse so we remove the VFs 98 * first, then the PF. 99 */ 100 if (bus) { 101 list_for_each_entry_safe_reverse(child, tmp, 102 &bus->devices, bus_list) 103 pci_stop_bus_device(child); 104 } 105 106 pci_stop_dev(dev); 107 } 108 109 static void pci_remove_bus_device(struct pci_dev *dev) 110 { 111 struct pci_bus *bus = dev->subordinate; 112 struct pci_dev *child, *tmp; 113 114 if (bus) { 115 list_for_each_entry_safe(child, tmp, 116 &bus->devices, bus_list) 117 pci_remove_bus_device(child); 118 119 pci_remove_bus(bus); 120 dev->subordinate = NULL; 121 } 122 123 pci_destroy_dev(dev); 124 } 125 126 /** 127 * pci_stop_and_remove_bus_device - remove a PCI device and any children 128 * @dev: the device to remove 129 * 130 * Remove a PCI device from the device lists, informing the drivers 131 * that the device has been removed. We also remove any subordinate 132 * buses and children in a depth-first manner. 133 * 134 * For each device we remove, delete the device structure from the 135 * device lists, remove the /proc entry, and notify userspace 136 * (/sbin/hotplug). 137 */ 138 void pci_stop_and_remove_bus_device(struct pci_dev *dev) 139 { 140 pci_stop_bus_device(dev); 141 pci_remove_bus_device(dev); 142 } 143 EXPORT_SYMBOL(pci_stop_and_remove_bus_device); 144 145 void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev) 146 { 147 pci_lock_rescan_remove(); 148 pci_stop_and_remove_bus_device(dev); 149 pci_unlock_rescan_remove(); 150 } 151 EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked); 152 153 void pci_stop_root_bus(struct pci_bus *bus) 154 { 155 struct pci_dev *child, *tmp; 156 struct pci_host_bridge *host_bridge; 157 158 if (!pci_is_root_bus(bus)) 159 return; 160 161 host_bridge = to_pci_host_bridge(bus->bridge); 162 list_for_each_entry_safe_reverse(child, tmp, 163 &bus->devices, bus_list) 164 pci_stop_bus_device(child); 165 166 /* stop the host bridge */ 167 device_release_driver(&host_bridge->dev); 168 } 169 EXPORT_SYMBOL_GPL(pci_stop_root_bus); 170 171 void pci_remove_root_bus(struct pci_bus *bus) 172 { 173 struct pci_dev *child, *tmp; 174 struct pci_host_bridge *host_bridge; 175 176 if (!pci_is_root_bus(bus)) 177 return; 178 179 host_bridge = to_pci_host_bridge(bus->bridge); 180 list_for_each_entry_safe(child, tmp, 181 &bus->devices, bus_list) 182 pci_remove_bus_device(child); 183 184 #ifdef CONFIG_PCI_DOMAINS_GENERIC 185 /* Release domain_nr if it was dynamically allocated */ 186 if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET) 187 pci_bus_release_domain_nr(host_bridge->dev.parent, bus->domain_nr); 188 #endif 189 190 pci_remove_bus(bus); 191 host_bridge->bus = NULL; 192 193 /* remove the host bridge */ 194 device_del(&host_bridge->dev); 195 } 196 EXPORT_SYMBOL_GPL(pci_remove_root_bus); 197