17328c8f4SBjorn Helgaas // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds #include <linux/pci.h>
31da177e4SLinus Torvalds #include <linux/module.h>
41da177e4SLinus Torvalds #include "pci.h"
51da177e4SLinus Torvalds
pci_free_resources(struct pci_dev * dev)604480094SRafael J. Wysocki static void pci_free_resources(struct pci_dev *dev)
704480094SRafael J. Wysocki {
809cc9006SMika Westerberg struct resource *res;
904480094SRafael J. Wysocki
1009cc9006SMika Westerberg pci_dev_for_each_resource(dev, res) {
1104480094SRafael J. Wysocki if (res->parent)
1204480094SRafael J. Wysocki release_resource(res);
1304480094SRafael J. Wysocki }
1404480094SRafael J. Wysocki }
1504480094SRafael J. Wysocki
pci_stop_dev(struct pci_dev * dev)1624f8aa9bSSatoru Takeuchi static void pci_stop_dev(struct pci_dev *dev)
171da177e4SLinus Torvalds {
18249bfb83SRafael J. Wysocki pci_pme_active(dev, false);
19249bfb83SRafael J. Wysocki
2044bda4b7SHari Vyas if (pci_dev_is_added(dev)) {
21f42c35eaSKrzysztof Wilczyński
2216b6c8bbSAlex Williamson device_release_driver(&dev->dev);
231da177e4SLinus Torvalds pci_proc_detach_device(dev);
241da177e4SLinus Torvalds pci_remove_sysfs_dev_files(dev);
25*407d1a51SLizhi Hou of_pci_remove_node(dev);
2644bda4b7SHari Vyas
2744bda4b7SHari Vyas pci_dev_assign_added(dev, false);
28091ca9f0SRajesh Shah }
2924f8aa9bSSatoru Takeuchi }
3024f8aa9bSSatoru Takeuchi
pci_destroy_dev(struct pci_dev * dev)3124f8aa9bSSatoru Takeuchi static void pci_destroy_dev(struct pci_dev *dev)
3224f8aa9bSSatoru Takeuchi {
338a4c5c32SRafael J. Wysocki if (!dev->dev.kobj.parent)
348a4c5c32SRafael J. Wysocki return;
358a4c5c32SRafael J. Wysocki
36c4a0a5d9SRafael J. Wysocki device_del(&dev->dev);
37c4a0a5d9SRafael J. Wysocki
3804480094SRafael J. Wysocki down_write(&pci_bus_sem);
3904480094SRafael J. Wysocki list_del(&dev->bus_list);
4004480094SRafael J. Wysocki up_write(&pci_bus_sem);
4104480094SRafael J. Wysocki
42ac048403SLukas Wunner pci_doe_destroy(dev);
43aeae4f3eSLukas Wunner pcie_aspm_exit_link_state(dev);
441ed276a7SLukas Wunner pci_bridge_d3_update(dev);
4504480094SRafael J. Wysocki pci_free_resources(dev);
46e723f0b4SJiang Liu put_device(&dev->dev);
471da177e4SLinus Torvalds }
481da177e4SLinus Torvalds
pci_remove_bus(struct pci_bus * bus)49d563e2ccSBjorn Helgaas void pci_remove_bus(struct pci_bus *bus)
501da177e4SLinus Torvalds {
51d563e2ccSBjorn Helgaas pci_proc_detach_bus(bus);
521da177e4SLinus Torvalds
53d71374daSZhang Yanmin down_write(&pci_bus_sem);
54d563e2ccSBjorn Helgaas list_del(&bus->node);
55d563e2ccSBjorn Helgaas pci_bus_release_busn_res(bus);
56d71374daSZhang Yanmin up_write(&pci_bus_sem);
57d563e2ccSBjorn Helgaas pci_remove_legacy_files(bus);
58057bd2e0SThierry Reding
59057bd2e0SThierry Reding if (bus->ops->remove_bus)
60057bd2e0SThierry Reding bus->ops->remove_bus(bus);
61057bd2e0SThierry Reding
6210a95747SJiang Liu pcibios_remove_bus(bus);
63d563e2ccSBjorn Helgaas device_unregister(&bus->dev);
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds EXPORT_SYMBOL(pci_remove_bus);
661da177e4SLinus Torvalds
pci_stop_bus_device(struct pci_dev * dev)673891b6acSYinghai Lu static void pci_stop_bus_device(struct pci_dev *dev)
683891b6acSYinghai Lu {
693891b6acSYinghai Lu struct pci_bus *bus = dev->subordinate;
703891b6acSYinghai Lu struct pci_dev *child, *tmp;
713891b6acSYinghai Lu
723891b6acSYinghai Lu /*
733891b6acSYinghai Lu * Stopping an SR-IOV PF device removes all the associated VFs,
743891b6acSYinghai Lu * which will update the bus->devices list and confuse the
753891b6acSYinghai Lu * iterator. Therefore, iterate in reverse so we remove the VFs
763891b6acSYinghai Lu * first, then the PF.
773891b6acSYinghai Lu */
783891b6acSYinghai Lu if (bus) {
793891b6acSYinghai Lu list_for_each_entry_safe_reverse(child, tmp,
803891b6acSYinghai Lu &bus->devices, bus_list)
813891b6acSYinghai Lu pci_stop_bus_device(child);
823891b6acSYinghai Lu }
833891b6acSYinghai Lu
843891b6acSYinghai Lu pci_stop_dev(dev);
853891b6acSYinghai Lu }
863891b6acSYinghai Lu
pci_remove_bus_device(struct pci_dev * dev)873891b6acSYinghai Lu static void pci_remove_bus_device(struct pci_dev *dev)
883891b6acSYinghai Lu {
893891b6acSYinghai Lu struct pci_bus *bus = dev->subordinate;
903891b6acSYinghai Lu struct pci_dev *child, *tmp;
913891b6acSYinghai Lu
923891b6acSYinghai Lu if (bus) {
933891b6acSYinghai Lu list_for_each_entry_safe(child, tmp,
943891b6acSYinghai Lu &bus->devices, bus_list)
953891b6acSYinghai Lu pci_remove_bus_device(child);
963891b6acSYinghai Lu
973891b6acSYinghai Lu pci_remove_bus(bus);
983891b6acSYinghai Lu dev->subordinate = NULL;
993891b6acSYinghai Lu }
1003891b6acSYinghai Lu
1013891b6acSYinghai Lu pci_destroy_dev(dev);
1023891b6acSYinghai Lu }
1033891b6acSYinghai Lu
1041da177e4SLinus Torvalds /**
105210647afSYinghai Lu * pci_stop_and_remove_bus_device - remove a PCI device and any children
1061da177e4SLinus Torvalds * @dev: the device to remove
1071da177e4SLinus Torvalds *
1081da177e4SLinus Torvalds * Remove a PCI device from the device lists, informing the drivers
1091da177e4SLinus Torvalds * that the device has been removed. We also remove any subordinate
1101da177e4SLinus Torvalds * buses and children in a depth-first manner.
1111da177e4SLinus Torvalds *
1121da177e4SLinus Torvalds * For each device we remove, delete the device structure from the
1131da177e4SLinus Torvalds * device lists, remove the /proc entry, and notify userspace
1141da177e4SLinus Torvalds * (/sbin/hotplug).
1151da177e4SLinus Torvalds */
pci_stop_and_remove_bus_device(struct pci_dev * dev)116210647afSYinghai Lu void pci_stop_and_remove_bus_device(struct pci_dev *dev)
11779cc9601SYinghai Lu {
1183891b6acSYinghai Lu pci_stop_bus_device(dev);
1193891b6acSYinghai Lu pci_remove_bus_device(dev);
120282e1d65SBjorn Helgaas }
121210647afSYinghai Lu EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
122cdfcc572SYinghai Lu
pci_stop_and_remove_bus_device_locked(struct pci_dev * dev)1239d16947bSRafael J. Wysocki void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
1249d16947bSRafael J. Wysocki {
1259d16947bSRafael J. Wysocki pci_lock_rescan_remove();
1269d16947bSRafael J. Wysocki pci_stop_and_remove_bus_device(dev);
1279d16947bSRafael J. Wysocki pci_unlock_rescan_remove();
1289d16947bSRafael J. Wysocki }
1299d16947bSRafael J. Wysocki EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
1309d16947bSRafael J. Wysocki
pci_stop_root_bus(struct pci_bus * bus)131cdfcc572SYinghai Lu void pci_stop_root_bus(struct pci_bus *bus)
132cdfcc572SYinghai Lu {
133cdfcc572SYinghai Lu struct pci_dev *child, *tmp;
134cdfcc572SYinghai Lu struct pci_host_bridge *host_bridge;
135cdfcc572SYinghai Lu
136cdfcc572SYinghai Lu if (!pci_is_root_bus(bus))
137cdfcc572SYinghai Lu return;
138cdfcc572SYinghai Lu
139cdfcc572SYinghai Lu host_bridge = to_pci_host_bridge(bus->bridge);
140cdfcc572SYinghai Lu list_for_each_entry_safe_reverse(child, tmp,
141cdfcc572SYinghai Lu &bus->devices, bus_list)
142cdfcc572SYinghai Lu pci_stop_bus_device(child);
143cdfcc572SYinghai Lu
144cdfcc572SYinghai Lu /* stop the host bridge */
145e3b439e1SYinghai Lu device_release_driver(&host_bridge->dev);
146cdfcc572SYinghai Lu }
147e6b29deaSRay Jui EXPORT_SYMBOL_GPL(pci_stop_root_bus);
148cdfcc572SYinghai Lu
pci_remove_root_bus(struct pci_bus * bus)149cdfcc572SYinghai Lu void pci_remove_root_bus(struct pci_bus *bus)
150cdfcc572SYinghai Lu {
151cdfcc572SYinghai Lu struct pci_dev *child, *tmp;
152cdfcc572SYinghai Lu struct pci_host_bridge *host_bridge;
153cdfcc572SYinghai Lu
154cdfcc572SYinghai Lu if (!pci_is_root_bus(bus))
155cdfcc572SYinghai Lu return;
156cdfcc572SYinghai Lu
157cdfcc572SYinghai Lu host_bridge = to_pci_host_bridge(bus->bridge);
158cdfcc572SYinghai Lu list_for_each_entry_safe(child, tmp,
159cdfcc572SYinghai Lu &bus->devices, bus_list)
160cdfcc572SYinghai Lu pci_remove_bus_device(child);
161cdfcc572SYinghai Lu
162c14f7cccSPali Rohár #ifdef CONFIG_PCI_DOMAINS_GENERIC
163c14f7cccSPali Rohár /* Release domain_nr if it was dynamically allocated */
164c14f7cccSPali Rohár if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
165c14f7cccSPali Rohár pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
166c14f7cccSPali Rohár #endif
167c14f7cccSPali Rohár
16830ba2d09SRob Herring pci_remove_bus(bus);
16930ba2d09SRob Herring host_bridge->bus = NULL;
17030ba2d09SRob Herring
171cdfcc572SYinghai Lu /* remove the host bridge */
1729885440bSRob Herring device_del(&host_bridge->dev);
173cdfcc572SYinghai Lu }
174e6b29deaSRay Jui EXPORT_SYMBOL_GPL(pci_remove_root_bus);
175