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