| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Derived from "arch/powerpc/platforms/pseries/pci_dlpar.c" | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> | 
					
						
							|  |  |  |  * Copyright (C) 2005 International Business Machines | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Updates, 2005, John Rose <johnrose@austin.ibm.com> | 
					
						
							|  |  |  |  * Updates, 2005, Linas Vepstas <linas@austin.ibm.com> | 
					
						
							|  |  |  |  * Updates, 2013, Gavin Shan <shangw@linux.vnet.ibm.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify | 
					
						
							|  |  |  |  * it under the terms of the GNU General Public License as published by | 
					
						
							|  |  |  |  * the Free Software Foundation; either version 2 of the License, or | 
					
						
							|  |  |  |  * (at your option) any later version. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/export.h>
 | 
					
						
							|  |  |  | #include <asm/pci-bridge.h>
 | 
					
						
							|  |  |  | #include <asm/ppc-pci.h>
 | 
					
						
							|  |  |  | #include <asm/firmware.h>
 | 
					
						
							|  |  |  | #include <asm/eeh.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:53 +08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * pcibios_release_device - release PCI device | 
					
						
							|  |  |  |  * @dev: PCI device | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The function is called before releasing the indicated PCI device. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void pcibios_release_device(struct pci_dev *dev) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:55 +08:00
										 |  |  | 	eeh_remove_device(dev); | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:53 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:55 +08:00
										 |  |  |  * pcibios_remove_pci_devices - remove all devices under this bus | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  |  * @bus: the indicated PCI bus | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Remove all of the PCI devices under this bus both from the | 
					
						
							|  |  |  |  * linux pci device tree, and from the powerpc EEH address cache. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:55 +08:00
										 |  |  | void pcibios_remove_pci_devices(struct pci_bus *bus) | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pci_dev *dev, *tmp; | 
					
						
							|  |  |  | 	struct pci_bus *child_bus; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* First go down child busses */ | 
					
						
							|  |  |  | 	list_for_each_entry(child_bus, &bus->children, node) | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:55 +08:00
										 |  |  | 		pcibios_remove_pci_devices(child_bus); | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pr_debug("PCI: Removing devices on bus %04x:%02x\n", | 
					
						
							|  |  |  | 		 pci_domain_nr(bus),  bus->number); | 
					
						
							|  |  |  | 	list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:55 +08:00
										 |  |  | 		pr_debug("   Removing %s...\n", pci_name(dev)); | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | 		pci_stop_and_remove_bus_device(dev); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * pcibios_add_pci_devices - adds new pci devices to bus | 
					
						
							|  |  |  |  * @bus: the indicated PCI bus | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This routine will find and fixup new pci devices under | 
					
						
							|  |  |  |  * the indicated bus. This routine presumes that there | 
					
						
							|  |  |  |  * might already be some devices under this bridge, so | 
					
						
							|  |  |  |  * it carefully tries to add only new devices.  (And that | 
					
						
							|  |  |  |  * is how this routine differs from other, similar pcibios | 
					
						
							|  |  |  |  * routines.) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void pcibios_add_pci_devices(struct pci_bus * bus) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:57 +08:00
										 |  |  | 	int slotno, mode, pass, max; | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | 	struct pci_dev *dev; | 
					
						
							|  |  |  | 	struct device_node *dn = pci_bus_to_OF_node(bus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	eeh_add_device_tree_early(dn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mode = PCI_PROBE_NORMAL; | 
					
						
							|  |  |  | 	if (ppc_md.pci_probe_mode) | 
					
						
							|  |  |  | 		mode = ppc_md.pci_probe_mode(bus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mode == PCI_PROBE_DEVTREE) { | 
					
						
							|  |  |  | 		/* use ofdt-based probe */ | 
					
						
							|  |  |  | 		of_rescan_bus(dn, bus); | 
					
						
							|  |  |  | 	} else if (mode == PCI_PROBE_NORMAL) { | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:57 +08:00
										 |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Use legacy probe. In the partial hotplug case, we | 
					
						
							|  |  |  | 		 * probably have grandchildren devices unplugged. So | 
					
						
							|  |  |  | 		 * we don't check the return value from pci_scan_slot() in | 
					
						
							|  |  |  | 		 * order for fully rescan all the way down to pick them up. | 
					
						
							|  |  |  | 		 * They can have been removed during partial hotplug. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | 		slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); | 
					
						
							| 
									
										
										
										
											2013-07-24 10:24:57 +08:00
										 |  |  | 		pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); | 
					
						
							| 
									
										
										
										
											2013-06-20 13:20:52 +08:00
										 |  |  | 		pcibios_setup_bus_devices(bus); | 
					
						
							|  |  |  | 		max = bus->busn_res.start; | 
					
						
							|  |  |  | 		for (pass = 0; pass < 2; pass++) { | 
					
						
							|  |  |  | 			list_for_each_entry(dev, &bus->devices, bus_list) { | 
					
						
							|  |  |  | 				if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || | 
					
						
							|  |  |  | 				    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) | 
					
						
							|  |  |  | 					max = pci_scan_bridge(bus, dev, | 
					
						
							|  |  |  | 							      max, pass); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	pcibios_finish_adding_to_bus(bus); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); |