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