diff options
Diffstat (limited to 'drivers/pci/remove.c')
-rw-r--r-- | drivers/pci/remove.c | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c new file mode 100644 index 000000000000..96f077f9a659 --- /dev/null +++ b/drivers/pci/remove.c | |||
@@ -0,0 +1,118 @@ | |||
1 | #include <linux/pci.h> | ||
2 | #include <linux/module.h> | ||
3 | #include "pci.h" | ||
4 | |||
5 | static void pci_free_resources(struct pci_dev *dev) | ||
6 | { | ||
7 | int i; | ||
8 | |||
9 | msi_remove_pci_irq_vectors(dev); | ||
10 | |||
11 | pci_cleanup_rom(dev); | ||
12 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
13 | struct resource *res = dev->resource + i; | ||
14 | if (res->parent) | ||
15 | release_resource(res); | ||
16 | } | ||
17 | } | ||
18 | |||
19 | static void pci_destroy_dev(struct pci_dev *dev) | ||
20 | { | ||
21 | pci_proc_detach_device(dev); | ||
22 | pci_remove_sysfs_dev_files(dev); | ||
23 | device_unregister(&dev->dev); | ||
24 | |||
25 | /* Remove the device from the device lists, and prevent any further | ||
26 | * list accesses from this device */ | ||
27 | spin_lock(&pci_bus_lock); | ||
28 | list_del(&dev->bus_list); | ||
29 | list_del(&dev->global_list); | ||
30 | dev->bus_list.next = dev->bus_list.prev = NULL; | ||
31 | dev->global_list.next = dev->global_list.prev = NULL; | ||
32 | spin_unlock(&pci_bus_lock); | ||
33 | |||
34 | pci_free_resources(dev); | ||
35 | pci_dev_put(dev); | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * pci_remove_device_safe - remove an unused hotplug device | ||
40 | * @dev: the device to remove | ||
41 | * | ||
42 | * Delete the device structure from the device lists and | ||
43 | * notify userspace (/sbin/hotplug), but only if the device | ||
44 | * in question is not being used by a driver. | ||
45 | * Returns 0 on success. | ||
46 | */ | ||
47 | int pci_remove_device_safe(struct pci_dev *dev) | ||
48 | { | ||
49 | if (pci_dev_driver(dev)) | ||
50 | return -EBUSY; | ||
51 | pci_destroy_dev(dev); | ||
52 | return 0; | ||
53 | } | ||
54 | EXPORT_SYMBOL(pci_remove_device_safe); | ||
55 | |||
56 | void pci_remove_bus(struct pci_bus *pci_bus) | ||
57 | { | ||
58 | pci_proc_detach_bus(pci_bus); | ||
59 | |||
60 | spin_lock(&pci_bus_lock); | ||
61 | list_del(&pci_bus->node); | ||
62 | spin_unlock(&pci_bus_lock); | ||
63 | pci_remove_legacy_files(pci_bus); | ||
64 | class_device_remove_file(&pci_bus->class_dev, | ||
65 | &class_device_attr_cpuaffinity); | ||
66 | sysfs_remove_link(&pci_bus->class_dev.kobj, "bridge"); | ||
67 | class_device_unregister(&pci_bus->class_dev); | ||
68 | } | ||
69 | EXPORT_SYMBOL(pci_remove_bus); | ||
70 | |||
71 | /** | ||
72 | * pci_remove_bus_device - remove a PCI device and any children | ||
73 | * @dev: the device to remove | ||
74 | * | ||
75 | * Remove a PCI device from the device lists, informing the drivers | ||
76 | * that the device has been removed. We also remove any subordinate | ||
77 | * buses and children in a depth-first manner. | ||
78 | * | ||
79 | * For each device we remove, delete the device structure from the | ||
80 | * device lists, remove the /proc entry, and notify userspace | ||
81 | * (/sbin/hotplug). | ||
82 | */ | ||
83 | void pci_remove_bus_device(struct pci_dev *dev) | ||
84 | { | ||
85 | if (dev->subordinate) { | ||
86 | struct pci_bus *b = dev->subordinate; | ||
87 | |||
88 | pci_remove_behind_bridge(dev); | ||
89 | pci_remove_bus(b); | ||
90 | dev->subordinate = NULL; | ||
91 | } | ||
92 | |||
93 | pci_destroy_dev(dev); | ||
94 | } | ||
95 | |||
96 | /** | ||
97 | * pci_remove_behind_bridge - remove all devices behind a PCI bridge | ||
98 | * @dev: PCI bridge device | ||
99 | * | ||
100 | * Remove all devices on the bus, except for the parent bridge. | ||
101 | * This also removes any child buses, and any devices they may | ||
102 | * contain in a depth-first manner. | ||
103 | */ | ||
104 | void pci_remove_behind_bridge(struct pci_dev *dev) | ||
105 | { | ||
106 | struct list_head *l, *n; | ||
107 | |||
108 | if (dev->subordinate) { | ||
109 | list_for_each_safe(l, n, &dev->subordinate->devices) { | ||
110 | struct pci_dev *dev = pci_dev_b(l); | ||
111 | |||
112 | pci_remove_bus_device(dev); | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | EXPORT_SYMBOL(pci_remove_bus_device); | ||
118 | EXPORT_SYMBOL(pci_remove_behind_bridge); | ||