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