1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/pci.h>
3 #include <linux/module.h>
4 #include <linux/of.h>
5 #include <linux/of_platform.h>
6 #include <linux/platform_device.h>
7
8 #include "pci.h"
9
pci_free_resources(struct pci_dev * dev)10 static void pci_free_resources(struct pci_dev *dev)
11 {
12 struct resource *res;
13
14 pci_dev_for_each_resource(dev, res) {
15 if (res->parent)
16 release_resource(res);
17 }
18 }
19
pci_pwrctrl_unregister(struct device * dev)20 static void pci_pwrctrl_unregister(struct device *dev)
21 {
22 struct device_node *np;
23 struct platform_device *pdev;
24
25 np = dev_of_node(dev);
26 if (!np)
27 return;
28
29 pdev = of_find_device_by_node(np);
30 if (!pdev)
31 return;
32
33 of_device_unregister(pdev);
34 put_device(&pdev->dev);
35
36 of_node_clear_flag(np, OF_POPULATED);
37 }
38
pci_stop_dev(struct pci_dev * dev)39 static void pci_stop_dev(struct pci_dev *dev)
40 {
41 pci_pme_active(dev, false);
42
43 if (!pci_dev_test_and_clear_added(dev))
44 return;
45
46 device_release_driver(&dev->dev);
47 pci_proc_detach_device(dev);
48 pci_remove_sysfs_dev_files(dev);
49 of_pci_remove_node(dev);
50 }
51
pci_destroy_dev(struct pci_dev * dev)52 static void pci_destroy_dev(struct pci_dev *dev)
53 {
54 if (pci_dev_test_and_set_removed(dev))
55 return;
56
57 pci_doe_sysfs_teardown(dev);
58 pci_npem_remove(dev);
59
60 device_del(&dev->dev);
61
62 down_write(&pci_bus_sem);
63 list_del(&dev->bus_list);
64 up_write(&pci_bus_sem);
65
66 pci_doe_destroy(dev);
67 pcie_aspm_exit_link_state(dev);
68 pci_bridge_d3_update(dev);
69 pci_pwrctrl_unregister(&dev->dev);
70 pci_free_resources(dev);
71 put_device(&dev->dev);
72 }
73
pci_remove_bus(struct pci_bus * bus)74 void pci_remove_bus(struct pci_bus *bus)
75 {
76 pci_proc_detach_bus(bus);
77
78 down_write(&pci_bus_sem);
79 list_del(&bus->node);
80 pci_bus_release_busn_res(bus);
81 up_write(&pci_bus_sem);
82 pci_remove_legacy_files(bus);
83
84 if (bus->ops->remove_bus)
85 bus->ops->remove_bus(bus);
86
87 pcibios_remove_bus(bus);
88 device_unregister(&bus->dev);
89 }
90 EXPORT_SYMBOL(pci_remove_bus);
91
pci_stop_bus_device(struct pci_dev * dev)92 static void pci_stop_bus_device(struct pci_dev *dev)
93 {
94 struct pci_bus *bus = dev->subordinate;
95 struct pci_dev *child, *tmp;
96
97 /*
98 * Stopping an SR-IOV PF device removes all the associated VFs,
99 * which will update the bus->devices list and confuse the
100 * iterator. Therefore, iterate in reverse so we remove the VFs
101 * first, then the PF.
102 */
103 if (bus) {
104 list_for_each_entry_safe_reverse(child, tmp,
105 &bus->devices, bus_list)
106 pci_stop_bus_device(child);
107 }
108
109 pci_stop_dev(dev);
110 }
111
pci_remove_bus_device(struct pci_dev * dev)112 static void pci_remove_bus_device(struct pci_dev *dev)
113 {
114 struct pci_bus *bus = dev->subordinate;
115 struct pci_dev *child, *tmp;
116
117 if (bus) {
118 list_for_each_entry_safe(child, tmp,
119 &bus->devices, bus_list)
120 pci_remove_bus_device(child);
121
122 pci_remove_bus(bus);
123 dev->subordinate = NULL;
124 }
125
126 pci_destroy_dev(dev);
127 }
128
129 /**
130 * pci_stop_and_remove_bus_device - remove a PCI device and any children
131 * @dev: the device to remove
132 *
133 * Remove a PCI device from the device lists, informing the drivers
134 * that the device has been removed. We also remove any subordinate
135 * buses and children in a depth-first manner.
136 *
137 * For each device we remove, delete the device structure from the
138 * device lists, remove the /proc entry, and notify userspace
139 * (/sbin/hotplug).
140 */
pci_stop_and_remove_bus_device(struct pci_dev * dev)141 void pci_stop_and_remove_bus_device(struct pci_dev *dev)
142 {
143 lockdep_assert_held(&pci_rescan_remove_lock);
144 pci_stop_bus_device(dev);
145 pci_remove_bus_device(dev);
146 }
147 EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
148
pci_stop_and_remove_bus_device_locked(struct pci_dev * dev)149 void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
150 {
151 pci_lock_rescan_remove();
152 pci_stop_and_remove_bus_device(dev);
153 pci_unlock_rescan_remove();
154 }
155 EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
156
pci_stop_root_bus(struct pci_bus * bus)157 void pci_stop_root_bus(struct pci_bus *bus)
158 {
159 struct pci_dev *child, *tmp;
160 struct pci_host_bridge *host_bridge;
161
162 if (!pci_is_root_bus(bus))
163 return;
164
165 host_bridge = to_pci_host_bridge(bus->bridge);
166 list_for_each_entry_safe_reverse(child, tmp,
167 &bus->devices, bus_list)
168 pci_stop_bus_device(child);
169
170 of_pci_remove_host_bridge_node(host_bridge);
171
172 /* stop the host bridge */
173 device_release_driver(&host_bridge->dev);
174 }
175 EXPORT_SYMBOL_GPL(pci_stop_root_bus);
176
pci_remove_root_bus(struct pci_bus * bus)177 void pci_remove_root_bus(struct pci_bus *bus)
178 {
179 struct pci_dev *child, *tmp;
180 struct pci_host_bridge *host_bridge;
181
182 if (!pci_is_root_bus(bus))
183 return;
184
185 host_bridge = to_pci_host_bridge(bus->bridge);
186 list_for_each_entry_safe(child, tmp,
187 &bus->devices, bus_list)
188 pci_remove_bus_device(child);
189
190 #ifdef CONFIG_PCI_DOMAINS_GENERIC
191 /* Release domain_nr if it was dynamically allocated */
192 if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
193 pci_bus_release_domain_nr(host_bridge->dev.parent, bus->domain_nr);
194 #endif
195
196 pci_remove_bus(bus);
197 host_bridge->bus = NULL;
198
199 /* remove the host bridge */
200 device_del(&host_bridge->dev);
201 }
202 EXPORT_SYMBOL_GPL(pci_remove_root_bus);
203