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
10static 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(new: res);
17 }
18}
19
20static 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(ofdev: pdev);
34 put_device(dev: &pdev->dev);
35
36 of_node_clear_flag(n: np, OF_POPULATED);
37}
38
39static void pci_stop_dev(struct pci_dev *dev)
40{
41 pci_pme_active(dev, enable: false);
42
43 if (!pci_dev_test_and_clear_added(dev))
44 return;
45
46 device_release_driver(dev: &dev->dev);
47 pci_proc_detach_device(dev);
48 pci_remove_sysfs_dev_files(pdev: dev);
49 of_pci_remove_node(pdev: dev);
50}
51
52static 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(pdev: dev);
58 pci_npem_remove(dev);
59
60 device_del(dev: &dev->dev);
61
62 down_write(sem: &pci_bus_sem);
63 list_del(entry: &dev->bus_list);
64 up_write(sem: &pci_bus_sem);
65
66 pci_doe_destroy(pdev: dev);
67 pcie_aspm_exit_link_state(pdev: dev);
68 pci_bridge_d3_update(dev);
69 pci_pwrctrl_unregister(dev: &dev->dev);
70 pci_free_resources(dev);
71 put_device(dev: &dev->dev);
72}
73
74void pci_remove_bus(struct pci_bus *bus)
75{
76 pci_proc_detach_bus(bus);
77
78 down_write(sem: &pci_bus_sem);
79 list_del(entry: &bus->node);
80 pci_bus_release_busn_res(b: bus);
81 up_write(sem: &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(dev: &bus->dev);
89}
90EXPORT_SYMBOL(pci_remove_bus);
91
92static 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(dev: child);
107 }
108
109 pci_stop_dev(dev);
110}
111
112static 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(dev: 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 */
141void 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}
147EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
148
149void 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}
155EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
156
157void 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(pbus: 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(dev: child);
169
170 of_pci_remove_host_bridge_node(bridge: host_bridge);
171
172 /* stop the host bridge */
173 device_release_driver(dev: &host_bridge->dev);
174}
175EXPORT_SYMBOL_GPL(pci_stop_root_bus);
176
177void 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(pbus: 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(dev: 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(dev: &host_bridge->dev);
201}
202EXPORT_SYMBOL_GPL(pci_remove_root_bus);
203