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