xref: /OK3568_Linux_fs/kernel/drivers/pci/remove.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <linux/pci.h>
3*4882a593Smuzhiyun #include <linux/module.h>
4*4882a593Smuzhiyun #include "pci.h"
5*4882a593Smuzhiyun 
pci_free_resources(struct pci_dev * dev)6*4882a593Smuzhiyun static void pci_free_resources(struct pci_dev *dev)
7*4882a593Smuzhiyun {
8*4882a593Smuzhiyun 	int i;
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun 	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
11*4882a593Smuzhiyun 		struct resource *res = dev->resource + i;
12*4882a593Smuzhiyun 		if (res->parent)
13*4882a593Smuzhiyun 			release_resource(res);
14*4882a593Smuzhiyun 	}
15*4882a593Smuzhiyun }
16*4882a593Smuzhiyun 
pci_stop_dev(struct pci_dev * dev)17*4882a593Smuzhiyun static void pci_stop_dev(struct pci_dev *dev)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun 	pci_pme_active(dev, false);
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun 	if (pci_dev_is_added(dev)) {
22*4882a593Smuzhiyun 		device_release_driver(&dev->dev);
23*4882a593Smuzhiyun 		pci_proc_detach_device(dev);
24*4882a593Smuzhiyun 		pci_remove_sysfs_dev_files(dev);
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 		pci_dev_assign_added(dev, false);
27*4882a593Smuzhiyun 	}
28*4882a593Smuzhiyun }
29*4882a593Smuzhiyun 
pci_destroy_dev(struct pci_dev * dev)30*4882a593Smuzhiyun static void pci_destroy_dev(struct pci_dev *dev)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun 	if (!dev->dev.kobj.parent)
33*4882a593Smuzhiyun 		return;
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun 	device_del(&dev->dev);
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	down_write(&pci_bus_sem);
38*4882a593Smuzhiyun 	list_del(&dev->bus_list);
39*4882a593Smuzhiyun 	up_write(&pci_bus_sem);
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun 	pcie_aspm_exit_link_state(dev);
42*4882a593Smuzhiyun 	pci_bridge_d3_update(dev);
43*4882a593Smuzhiyun 	pci_free_resources(dev);
44*4882a593Smuzhiyun 	put_device(&dev->dev);
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun 
pci_remove_bus(struct pci_bus * bus)47*4882a593Smuzhiyun void pci_remove_bus(struct pci_bus *bus)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	pci_proc_detach_bus(bus);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	down_write(&pci_bus_sem);
52*4882a593Smuzhiyun 	list_del(&bus->node);
53*4882a593Smuzhiyun 	pci_bus_release_busn_res(bus);
54*4882a593Smuzhiyun 	up_write(&pci_bus_sem);
55*4882a593Smuzhiyun 	pci_remove_legacy_files(bus);
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	if (bus->ops->remove_bus)
58*4882a593Smuzhiyun 		bus->ops->remove_bus(bus);
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	pcibios_remove_bus(bus);
61*4882a593Smuzhiyun 	device_unregister(&bus->dev);
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun EXPORT_SYMBOL(pci_remove_bus);
64*4882a593Smuzhiyun 
pci_stop_bus_device(struct pci_dev * dev)65*4882a593Smuzhiyun static void pci_stop_bus_device(struct pci_dev *dev)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun 	struct pci_bus *bus = dev->subordinate;
68*4882a593Smuzhiyun 	struct pci_dev *child, *tmp;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	/*
71*4882a593Smuzhiyun 	 * Stopping an SR-IOV PF device removes all the associated VFs,
72*4882a593Smuzhiyun 	 * which will update the bus->devices list and confuse the
73*4882a593Smuzhiyun 	 * iterator.  Therefore, iterate in reverse so we remove the VFs
74*4882a593Smuzhiyun 	 * first, then the PF.
75*4882a593Smuzhiyun 	 */
76*4882a593Smuzhiyun 	if (bus) {
77*4882a593Smuzhiyun 		list_for_each_entry_safe_reverse(child, tmp,
78*4882a593Smuzhiyun 						 &bus->devices, bus_list)
79*4882a593Smuzhiyun 			pci_stop_bus_device(child);
80*4882a593Smuzhiyun 	}
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	pci_stop_dev(dev);
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun 
pci_remove_bus_device(struct pci_dev * dev)85*4882a593Smuzhiyun static void pci_remove_bus_device(struct pci_dev *dev)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	struct pci_bus *bus = dev->subordinate;
88*4882a593Smuzhiyun 	struct pci_dev *child, *tmp;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	if (bus) {
91*4882a593Smuzhiyun 		list_for_each_entry_safe(child, tmp,
92*4882a593Smuzhiyun 					 &bus->devices, bus_list)
93*4882a593Smuzhiyun 			pci_remove_bus_device(child);
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 		pci_remove_bus(bus);
96*4882a593Smuzhiyun 		dev->subordinate = NULL;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	pci_destroy_dev(dev);
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun /**
103*4882a593Smuzhiyun  * pci_stop_and_remove_bus_device - remove a PCI device and any children
104*4882a593Smuzhiyun  * @dev: the device to remove
105*4882a593Smuzhiyun  *
106*4882a593Smuzhiyun  * Remove a PCI device from the device lists, informing the drivers
107*4882a593Smuzhiyun  * that the device has been removed.  We also remove any subordinate
108*4882a593Smuzhiyun  * buses and children in a depth-first manner.
109*4882a593Smuzhiyun  *
110*4882a593Smuzhiyun  * For each device we remove, delete the device structure from the
111*4882a593Smuzhiyun  * device lists, remove the /proc entry, and notify userspace
112*4882a593Smuzhiyun  * (/sbin/hotplug).
113*4882a593Smuzhiyun  */
pci_stop_and_remove_bus_device(struct pci_dev * dev)114*4882a593Smuzhiyun void pci_stop_and_remove_bus_device(struct pci_dev *dev)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	pci_stop_bus_device(dev);
117*4882a593Smuzhiyun 	pci_remove_bus_device(dev);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
120*4882a593Smuzhiyun 
pci_stop_and_remove_bus_device_locked(struct pci_dev * dev)121*4882a593Smuzhiyun void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	pci_lock_rescan_remove();
124*4882a593Smuzhiyun 	pci_stop_and_remove_bus_device(dev);
125*4882a593Smuzhiyun 	pci_unlock_rescan_remove();
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
128*4882a593Smuzhiyun 
pci_stop_root_bus(struct pci_bus * bus)129*4882a593Smuzhiyun void pci_stop_root_bus(struct pci_bus *bus)
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	struct pci_dev *child, *tmp;
132*4882a593Smuzhiyun 	struct pci_host_bridge *host_bridge;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (!pci_is_root_bus(bus))
135*4882a593Smuzhiyun 		return;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	host_bridge = to_pci_host_bridge(bus->bridge);
138*4882a593Smuzhiyun 	list_for_each_entry_safe_reverse(child, tmp,
139*4882a593Smuzhiyun 					 &bus->devices, bus_list)
140*4882a593Smuzhiyun 		pci_stop_bus_device(child);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	/* stop the host bridge */
143*4882a593Smuzhiyun 	device_release_driver(&host_bridge->dev);
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pci_stop_root_bus);
146*4882a593Smuzhiyun 
pci_remove_root_bus(struct pci_bus * bus)147*4882a593Smuzhiyun void pci_remove_root_bus(struct pci_bus *bus)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	struct pci_dev *child, *tmp;
150*4882a593Smuzhiyun 	struct pci_host_bridge *host_bridge;
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	if (!pci_is_root_bus(bus))
153*4882a593Smuzhiyun 		return;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	host_bridge = to_pci_host_bridge(bus->bridge);
156*4882a593Smuzhiyun 	list_for_each_entry_safe(child, tmp,
157*4882a593Smuzhiyun 				 &bus->devices, bus_list)
158*4882a593Smuzhiyun 		pci_remove_bus_device(child);
159*4882a593Smuzhiyun 	pci_remove_bus(bus);
160*4882a593Smuzhiyun 	host_bridge->bus = NULL;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/* remove the host bridge */
163*4882a593Smuzhiyun 	device_del(&host_bridge->dev);
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pci_remove_root_bus);
166