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