162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/pci.h>
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci#include "pci.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_cistatic void pci_free_resources(struct pci_dev *dev)
762306a36Sopenharmony_ci{
862306a36Sopenharmony_ci	struct resource *res;
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci	pci_dev_for_each_resource(dev, res) {
1162306a36Sopenharmony_ci		if (res->parent)
1262306a36Sopenharmony_ci			release_resource(res);
1362306a36Sopenharmony_ci	}
1462306a36Sopenharmony_ci}
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic void pci_stop_dev(struct pci_dev *dev)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	pci_pme_active(dev, false);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	if (pci_dev_is_added(dev)) {
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci		device_release_driver(&dev->dev);
2362306a36Sopenharmony_ci		pci_proc_detach_device(dev);
2462306a36Sopenharmony_ci		pci_remove_sysfs_dev_files(dev);
2562306a36Sopenharmony_ci		of_pci_remove_node(dev);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci		pci_dev_assign_added(dev, false);
2862306a36Sopenharmony_ci	}
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void pci_destroy_dev(struct pci_dev *dev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	if (!dev->dev.kobj.parent)
3462306a36Sopenharmony_ci		return;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	device_del(&dev->dev);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	down_write(&pci_bus_sem);
3962306a36Sopenharmony_ci	list_del(&dev->bus_list);
4062306a36Sopenharmony_ci	up_write(&pci_bus_sem);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	pci_doe_destroy(dev);
4362306a36Sopenharmony_ci	pcie_aspm_exit_link_state(dev);
4462306a36Sopenharmony_ci	pci_bridge_d3_update(dev);
4562306a36Sopenharmony_ci	pci_free_resources(dev);
4662306a36Sopenharmony_ci	put_device(&dev->dev);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_civoid pci_remove_bus(struct pci_bus *bus)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	pci_proc_detach_bus(bus);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	down_write(&pci_bus_sem);
5462306a36Sopenharmony_ci	list_del(&bus->node);
5562306a36Sopenharmony_ci	pci_bus_release_busn_res(bus);
5662306a36Sopenharmony_ci	up_write(&pci_bus_sem);
5762306a36Sopenharmony_ci	pci_remove_legacy_files(bus);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (bus->ops->remove_bus)
6062306a36Sopenharmony_ci		bus->ops->remove_bus(bus);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	pcibios_remove_bus(bus);
6362306a36Sopenharmony_ci	device_unregister(&bus->dev);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ciEXPORT_SYMBOL(pci_remove_bus);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic void pci_stop_bus_device(struct pci_dev *dev)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct pci_bus *bus = dev->subordinate;
7062306a36Sopenharmony_ci	struct pci_dev *child, *tmp;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/*
7362306a36Sopenharmony_ci	 * Stopping an SR-IOV PF device removes all the associated VFs,
7462306a36Sopenharmony_ci	 * which will update the bus->devices list and confuse the
7562306a36Sopenharmony_ci	 * iterator.  Therefore, iterate in reverse so we remove the VFs
7662306a36Sopenharmony_ci	 * first, then the PF.
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	if (bus) {
7962306a36Sopenharmony_ci		list_for_each_entry_safe_reverse(child, tmp,
8062306a36Sopenharmony_ci						 &bus->devices, bus_list)
8162306a36Sopenharmony_ci			pci_stop_bus_device(child);
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	pci_stop_dev(dev);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void pci_remove_bus_device(struct pci_dev *dev)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct pci_bus *bus = dev->subordinate;
9062306a36Sopenharmony_ci	struct pci_dev *child, *tmp;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (bus) {
9362306a36Sopenharmony_ci		list_for_each_entry_safe(child, tmp,
9462306a36Sopenharmony_ci					 &bus->devices, bus_list)
9562306a36Sopenharmony_ci			pci_remove_bus_device(child);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		pci_remove_bus(bus);
9862306a36Sopenharmony_ci		dev->subordinate = NULL;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	pci_destroy_dev(dev);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci/**
10562306a36Sopenharmony_ci * pci_stop_and_remove_bus_device - remove a PCI device and any children
10662306a36Sopenharmony_ci * @dev: the device to remove
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Remove a PCI device from the device lists, informing the drivers
10962306a36Sopenharmony_ci * that the device has been removed.  We also remove any subordinate
11062306a36Sopenharmony_ci * buses and children in a depth-first manner.
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * For each device we remove, delete the device structure from the
11362306a36Sopenharmony_ci * device lists, remove the /proc entry, and notify userspace
11462306a36Sopenharmony_ci * (/sbin/hotplug).
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_civoid pci_stop_and_remove_bus_device(struct pci_dev *dev)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	pci_stop_bus_device(dev);
11962306a36Sopenharmony_ci	pci_remove_bus_device(dev);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ciEXPORT_SYMBOL(pci_stop_and_remove_bus_device);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_civoid pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	pci_lock_rescan_remove();
12662306a36Sopenharmony_ci	pci_stop_and_remove_bus_device(dev);
12762306a36Sopenharmony_ci	pci_unlock_rescan_remove();
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_civoid pci_stop_root_bus(struct pci_bus *bus)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	struct pci_dev *child, *tmp;
13462306a36Sopenharmony_ci	struct pci_host_bridge *host_bridge;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!pci_is_root_bus(bus))
13762306a36Sopenharmony_ci		return;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	host_bridge = to_pci_host_bridge(bus->bridge);
14062306a36Sopenharmony_ci	list_for_each_entry_safe_reverse(child, tmp,
14162306a36Sopenharmony_ci					 &bus->devices, bus_list)
14262306a36Sopenharmony_ci		pci_stop_bus_device(child);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* stop the host bridge */
14562306a36Sopenharmony_ci	device_release_driver(&host_bridge->dev);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_stop_root_bus);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_civoid pci_remove_root_bus(struct pci_bus *bus)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct pci_dev *child, *tmp;
15262306a36Sopenharmony_ci	struct pci_host_bridge *host_bridge;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (!pci_is_root_bus(bus))
15562306a36Sopenharmony_ci		return;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	host_bridge = to_pci_host_bridge(bus->bridge);
15862306a36Sopenharmony_ci	list_for_each_entry_safe(child, tmp,
15962306a36Sopenharmony_ci				 &bus->devices, bus_list)
16062306a36Sopenharmony_ci		pci_remove_bus_device(child);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci#ifdef CONFIG_PCI_DOMAINS_GENERIC
16362306a36Sopenharmony_ci	/* Release domain_nr if it was dynamically allocated */
16462306a36Sopenharmony_ci	if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
16562306a36Sopenharmony_ci		pci_bus_release_domain_nr(bus, host_bridge->dev.parent);
16662306a36Sopenharmony_ci#endif
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	pci_remove_bus(bus);
16962306a36Sopenharmony_ci	host_bridge->bus = NULL;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* remove the host bridge */
17262306a36Sopenharmony_ci	device_del(&host_bridge->dev);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_remove_root_bus);
175