| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #include <linux/pci.h> | 
 | 2 | #include <linux/module.h> | 
| Shaohua Li | 7d715a6 | 2008-02-25 09:46:41 +0800 | [diff] [blame] | 3 | #include <linux/pci-aspm.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4 | #include "pci.h" | 
 | 5 |  | 
 | 6 | static void pci_free_resources(struct pci_dev *dev) | 
 | 7 | { | 
 | 8 | 	int i; | 
 | 9 |  | 
 | 10 |  	msi_remove_pci_irq_vectors(dev); | 
 | 11 |  | 
 | 12 | 	pci_cleanup_rom(dev); | 
 | 13 | 	for (i = 0; i < PCI_NUM_RESOURCES; i++) { | 
 | 14 | 		struct resource *res = dev->resource + i; | 
 | 15 | 		if (res->parent) | 
 | 16 | 			release_resource(res); | 
 | 17 | 	} | 
 | 18 | } | 
 | 19 |  | 
| Satoru Takeuchi | 24f8aa9 | 2006-09-12 10:16:36 -0700 | [diff] [blame] | 20 | static void pci_stop_dev(struct pci_dev *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | { | 
| Greg Kroah-Hartman | 8a1bc90 | 2008-02-14 14:56:56 -0800 | [diff] [blame] | 22 | 	if (dev->is_added) { | 
| Rajesh Shah | 091ca9f | 2005-04-28 00:25:49 -0700 | [diff] [blame] | 23 | 		pci_proc_detach_device(dev); | 
 | 24 | 		pci_remove_sysfs_dev_files(dev); | 
 | 25 | 		device_unregister(&dev->dev); | 
| Greg Kroah-Hartman | 8a1bc90 | 2008-02-14 14:56:56 -0800 | [diff] [blame] | 26 | 		dev->is_added = 0; | 
| Rajesh Shah | 091ca9f | 2005-04-28 00:25:49 -0700 | [diff] [blame] | 27 | 	} | 
| Shaohua Li | 7d715a6 | 2008-02-25 09:46:41 +0800 | [diff] [blame] | 28 |  | 
 | 29 | 	if (dev->bus->self) | 
 | 30 | 		pcie_aspm_exit_link_state(dev); | 
| Satoru Takeuchi | 24f8aa9 | 2006-09-12 10:16:36 -0700 | [diff] [blame] | 31 | } | 
 | 32 |  | 
 | 33 | static void pci_destroy_dev(struct pci_dev *dev) | 
 | 34 | { | 
 | 35 | 	pci_stop_dev(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 36 |  | 
 | 37 | 	/* Remove the device from the device lists, and prevent any further | 
 | 38 | 	 * list accesses from this device */ | 
| Zhang Yanmin | d71374d | 2006-06-02 12:35:43 +0800 | [diff] [blame] | 39 | 	down_write(&pci_bus_sem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 40 | 	list_del(&dev->bus_list); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | 	dev->bus_list.next = dev->bus_list.prev = NULL; | 
| Zhang Yanmin | d71374d | 2006-06-02 12:35:43 +0800 | [diff] [blame] | 42 | 	up_write(&pci_bus_sem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 |  | 
 | 44 | 	pci_free_resources(dev); | 
 | 45 | 	pci_dev_put(dev); | 
 | 46 | } | 
 | 47 |  | 
 | 48 | /** | 
 | 49 |  * pci_remove_device_safe - remove an unused hotplug device | 
 | 50 |  * @dev: the device to remove | 
 | 51 |  * | 
 | 52 |  * Delete the device structure from the device lists and  | 
 | 53 |  * notify userspace (/sbin/hotplug), but only if the device | 
 | 54 |  * in question is not being used by a driver. | 
 | 55 |  * Returns 0 on success. | 
 | 56 |  */ | 
| Adrian Bunk | 54c762f | 2005-12-22 01:08:52 +0100 | [diff] [blame] | 57 | #if 0 | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | int pci_remove_device_safe(struct pci_dev *dev) | 
 | 59 | { | 
 | 60 | 	if (pci_dev_driver(dev)) | 
 | 61 | 		return -EBUSY; | 
 | 62 | 	pci_destroy_dev(dev); | 
 | 63 | 	return 0; | 
 | 64 | } | 
| Adrian Bunk | 54c762f | 2005-12-22 01:08:52 +0100 | [diff] [blame] | 65 | #endif  /*  0  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 |  | 
 | 67 | void pci_remove_bus(struct pci_bus *pci_bus) | 
 | 68 | { | 
 | 69 | 	pci_proc_detach_bus(pci_bus); | 
 | 70 |  | 
| Zhang Yanmin | d71374d | 2006-06-02 12:35:43 +0800 | [diff] [blame] | 71 | 	down_write(&pci_bus_sem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 72 | 	list_del(&pci_bus->node); | 
| Zhang Yanmin | d71374d | 2006-06-02 12:35:43 +0800 | [diff] [blame] | 73 | 	up_write(&pci_bus_sem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 74 | 	pci_remove_legacy_files(pci_bus); | 
| Greg Kroah-Hartman | fd7d1ce | 2007-05-22 22:47:54 -0400 | [diff] [blame] | 75 | 	device_remove_file(&pci_bus->dev, &dev_attr_cpuaffinity); | 
 | 76 | 	device_unregister(&pci_bus->dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 77 | } | 
 | 78 | EXPORT_SYMBOL(pci_remove_bus); | 
 | 79 |  | 
 | 80 | /** | 
 | 81 |  * pci_remove_bus_device - remove a PCI device and any children | 
 | 82 |  * @dev: the device to remove | 
 | 83 |  * | 
 | 84 |  * Remove a PCI device from the device lists, informing the drivers | 
 | 85 |  * that the device has been removed.  We also remove any subordinate | 
 | 86 |  * buses and children in a depth-first manner. | 
 | 87 |  * | 
 | 88 |  * For each device we remove, delete the device structure from the | 
 | 89 |  * device lists, remove the /proc entry, and notify userspace | 
 | 90 |  * (/sbin/hotplug). | 
 | 91 |  */ | 
 | 92 | void pci_remove_bus_device(struct pci_dev *dev) | 
 | 93 | { | 
 | 94 | 	if (dev->subordinate) { | 
 | 95 | 		struct pci_bus *b = dev->subordinate; | 
 | 96 |  | 
 | 97 | 		pci_remove_behind_bridge(dev); | 
 | 98 | 		pci_remove_bus(b); | 
 | 99 | 		dev->subordinate = NULL; | 
 | 100 | 	} | 
 | 101 |  | 
 | 102 | 	pci_destroy_dev(dev); | 
 | 103 | } | 
 | 104 |  | 
 | 105 | /** | 
 | 106 |  * pci_remove_behind_bridge - remove all devices behind a PCI bridge | 
 | 107 |  * @dev: PCI bridge device | 
 | 108 |  * | 
 | 109 |  * Remove all devices on the bus, except for the parent bridge. | 
 | 110 |  * This also removes any child buses, and any devices they may | 
 | 111 |  * contain in a depth-first manner. | 
 | 112 |  */ | 
 | 113 | void pci_remove_behind_bridge(struct pci_dev *dev) | 
 | 114 | { | 
 | 115 | 	struct list_head *l, *n; | 
 | 116 |  | 
 | 117 | 	if (dev->subordinate) { | 
 | 118 | 		list_for_each_safe(l, n, &dev->subordinate->devices) { | 
 | 119 | 			struct pci_dev *dev = pci_dev_b(l); | 
 | 120 |  | 
 | 121 | 			pci_remove_bus_device(dev); | 
 | 122 | 		} | 
 | 123 | 	} | 
 | 124 | } | 
 | 125 |  | 
| Satoru Takeuchi | 24f8aa9 | 2006-09-12 10:16:36 -0700 | [diff] [blame] | 126 | static void pci_stop_bus_devices(struct pci_bus *bus) | 
 | 127 | { | 
 | 128 | 	struct list_head *l, *n; | 
 | 129 |  | 
 | 130 | 	list_for_each_safe(l, n, &bus->devices) { | 
 | 131 | 		struct pci_dev *dev = pci_dev_b(l); | 
 | 132 | 		pci_stop_bus_device(dev); | 
 | 133 | 	} | 
 | 134 | } | 
 | 135 |  | 
 | 136 | /** | 
 | 137 |  * pci_stop_bus_device - stop a PCI device and any children | 
 | 138 |  * @dev: the device to stop | 
 | 139 |  * | 
 | 140 |  * Stop a PCI device (detach the driver, remove from the global list | 
 | 141 |  * and so on). This also stop any subordinate buses and children in a | 
 | 142 |  * depth-first manner. | 
 | 143 |  */ | 
 | 144 | void pci_stop_bus_device(struct pci_dev *dev) | 
 | 145 | { | 
 | 146 | 	if (dev->subordinate) | 
 | 147 | 		pci_stop_bus_devices(dev->subordinate); | 
 | 148 |  | 
 | 149 | 	pci_stop_dev(dev); | 
 | 150 | } | 
 | 151 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 152 | EXPORT_SYMBOL(pci_remove_bus_device); | 
 | 153 | EXPORT_SYMBOL(pci_remove_behind_bridge); | 
| Satoru Takeuchi | 24f8aa9 | 2006-09-12 10:16:36 -0700 | [diff] [blame] | 154 | EXPORT_SYMBOL_GPL(pci_stop_bus_device); |