xref: /openbmc/linux/drivers/pci/remove.c (revision 407d1a51)
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