projects/linux·blob master · refs

187 lines · 4.3 KiB

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