File Index
Files and directories
Sign in
FILE

remove.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pci/remove.c C
  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_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
 39static 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
 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(dev);
 58	pci_npem_remove(dev);
 59
 60	/*
 61	 * While device is in D0 drop the device from TSM link operations
 62	 * including unbind and disconnect (IDE + SPDM teardown).
 63	 */
 64	pci_tsm_destroy(dev);
 65
 66	device_del(&dev->dev);
 67
 68	down_write(&pci_bus_sem);
 69	list_del(&dev->bus_list);
 70	up_write(&pci_bus_sem);
 71
 72	pci_doe_destroy(dev);
 73	pci_ide_destroy(dev);
 74	pcie_aspm_exit_link_state(dev);
 75	pci_bridge_d3_update(dev);
 76	pci_pwrctrl_unregister(&dev->dev);
 77	pci_free_resources(dev);
 78	put_device(&dev->dev);
 79}
 80
 81void pci_remove_bus(struct pci_bus *bus)
 82{
 83	pci_proc_detach_bus(bus);
 84
 85	down_write(&pci_bus_sem);
 86	list_del(&bus->node);
 87	pci_bus_release_busn_res(bus);
 88	up_write(&pci_bus_sem);
 89	pci_remove_legacy_files(bus);
 90
 91	if (bus->ops->remove_bus)
 92		bus->ops->remove_bus(bus);
 93
 94	pcibios_remove_bus(bus);
 95	device_unregister(&bus->dev);
 96}
 97EXPORT_SYMBOL(pci_remove_bus);
 98
 99static void pci_stop_bus_device(struct pci_dev *dev)
100{
101	struct pci_bus *bus = dev->subordinate;
102	struct pci_dev *child, *tmp;
103
104	/*
105	 * Stopping an SR-IOV PF device removes all the associated VFs,
106	 * which will update the bus->devices list and confuse the
107	 * iterator.  Therefore, iterate in reverse so we remove the VFs
108	 * first, then the PF.
109	 */
110	if (bus) {
111		list_for_each_entry_safe_reverse(child, tmp,
112						 &bus->devices, bus_list)
113			pci_stop_bus_device(child);
114	}
115
116	pci_stop_dev(dev);
117}
118
119static void pci_remove_bus_device(struct pci_dev *dev)
120{
121	struct pci_bus *bus = dev->subordinate;
122	struct pci_dev *child, *tmp;
123
124	if (bus) {
125		list_for_each_entry_safe(child, tmp,
126					 &bus->devices, bus_list)
127			pci_remove_bus_device(child);
128
129		pci_remove_bus(bus);
130		dev->subordinate = NULL;
131	}
132
133	pci_destroy_dev(dev);
134}
135
136/**
137 * pci_stop_and_remove_bus_device - remove a PCI device and any children
138 * @dev: the device to remove
139 *
140 * Remove a PCI device from the device lists, informing the drivers
141 * that the device has been removed.  We also remove any subordinate
142 * buses and children in a depth-first manner.
143 *
144 * For each device we remove, delete the device structure from the
145 * device lists, remove the /proc entry, and notify userspace
146 * (/sbin/hotplug).
147 */
148void pci_stop_and_remove_bus_device(struct pci_dev *dev)
149{
150	lockdep_assert_held(&pci_rescan_remove_lock);
151	pci_stop_bus_device(dev);
152	pci_remove_bus_device(dev);
153}
154EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
155
156void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev)
157{
158	pci_lock_rescan_remove();
159	pci_stop_and_remove_bus_device(dev);
160	pci_unlock_rescan_remove();
161}
162EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
163
164void pci_stop_root_bus(struct pci_bus *bus)
165{
166	struct pci_dev *child, *tmp;
167	struct pci_host_bridge *host_bridge;
168
169	if (!pci_is_root_bus(bus))
170		return;
171
172	host_bridge = to_pci_host_bridge(bus->bridge);
173	list_for_each_entry_safe_reverse(child, tmp,
174					 &bus->devices, bus_list)
175		pci_stop_bus_device(child);
176
177	of_pci_remove_host_bridge_node(host_bridge);
178
179	/* stop the host bridge */
180	device_release_driver(&host_bridge->dev);
181}
182EXPORT_SYMBOL_GPL(pci_stop_root_bus);
183
184void pci_remove_root_bus(struct pci_bus *bus)
185{
186	struct pci_dev *child, *tmp;
187	struct pci_host_bridge *host_bridge;
188
189	if (!pci_is_root_bus(bus))
190		return;
191
192	host_bridge = to_pci_host_bridge(bus->bridge);
193	list_for_each_entry_safe(child, tmp,
194				 &bus->devices, bus_list)
195		pci_remove_bus_device(child);
196
197#ifdef CONFIG_PCI_DOMAINS_GENERIC
198	/* Release domain_nr if it was dynamically allocated */
199	if (host_bridge->domain_nr == PCI_DOMAIN_NR_NOT_SET)
200		pci_bus_release_domain_nr(host_bridge->dev.parent, bus->domain_nr);
201#endif
202
203	pci_remove_bus(bus);
204	host_bridge->bus = NULL;
205
206	/* remove the host bridge */
207	device_del(&host_bridge->dev);
208}
209EXPORT_SYMBOL_GPL(pci_remove_root_bus);
~/ Read-only file preview.