1 /* 2 * PCI Dynamic LPAR, PCI Hot Plug and PCI EEH recovery code 3 * for RPA-compliant PPC64 platform. 4 * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> 5 * Copyright (C) 2005 International Business Machines 6 * 7 * Updates, 2005, John Rose <johnrose@austin.ibm.com> 8 * Updates, 2005, Linas Vepstas <linas@austin.ibm.com> 9 * 10 * All rights reserved. 11 * 12 * This program is free software; you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation; either version 2 of the License, or (at 15 * your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, but 18 * WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 20 * NON INFRINGEMENT. See the GNU General Public License for more 21 * details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this program; if not, write to the Free Software 25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 */ 27 28 #undef DEBUG 29 30 #include <linux/pci.h> 31 #include <asm/pci-bridge.h> 32 #include <asm/ppc-pci.h> 33 #include <asm/firmware.h> 34 #include <asm/eeh.h> 35 36 static struct pci_bus * 37 find_bus_among_children(struct pci_bus *bus, 38 struct device_node *dn) 39 { 40 struct pci_bus *child = NULL; 41 struct list_head *tmp; 42 struct device_node *busdn; 43 44 busdn = pci_bus_to_OF_node(bus); 45 if (busdn == dn) 46 return bus; 47 48 list_for_each(tmp, &bus->children) { 49 child = find_bus_among_children(pci_bus_b(tmp), dn); 50 if (child) 51 break; 52 }; 53 return child; 54 } 55 56 struct pci_bus * 57 pcibios_find_pci_bus(struct device_node *dn) 58 { 59 struct pci_dn *pdn = dn->data; 60 61 if (!pdn || !pdn->phb || !pdn->phb->bus) 62 return NULL; 63 64 return find_bus_among_children(pdn->phb->bus, dn); 65 } 66 EXPORT_SYMBOL_GPL(pcibios_find_pci_bus); 67 68 /** 69 * pcibios_remove_pci_devices - remove all devices under this bus 70 * 71 * Remove all of the PCI devices under this bus both from the 72 * linux pci device tree, and from the powerpc EEH address cache. 73 */ 74 void pcibios_remove_pci_devices(struct pci_bus *bus) 75 { 76 struct pci_dev *dev, *tmp; 77 struct pci_bus *child_bus; 78 79 /* First go down child busses */ 80 list_for_each_entry(child_bus, &bus->children, node) 81 pcibios_remove_pci_devices(child_bus); 82 83 pr_debug("PCI: Removing devices on bus %04x:%02x\n", 84 pci_domain_nr(bus), bus->number); 85 list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) { 86 pr_debug(" * Removing %s...\n", pci_name(dev)); 87 eeh_remove_bus_device(dev); 88 pci_remove_bus_device(dev); 89 } 90 } 91 EXPORT_SYMBOL_GPL(pcibios_remove_pci_devices); 92 93 /** 94 * pcibios_add_pci_devices - adds new pci devices to bus 95 * 96 * This routine will find and fixup new pci devices under 97 * the indicated bus. This routine presumes that there 98 * might already be some devices under this bridge, so 99 * it carefully tries to add only new devices. (And that 100 * is how this routine differs from other, similar pcibios 101 * routines.) 102 */ 103 void pcibios_add_pci_devices(struct pci_bus * bus) 104 { 105 int slotno, num, mode, pass, max; 106 struct pci_dev *dev; 107 struct device_node *dn = pci_bus_to_OF_node(bus); 108 109 eeh_add_device_tree_early(dn); 110 111 mode = PCI_PROBE_NORMAL; 112 if (ppc_md.pci_probe_mode) 113 mode = ppc_md.pci_probe_mode(bus); 114 115 if (mode == PCI_PROBE_DEVTREE) { 116 /* use ofdt-based probe */ 117 of_rescan_bus(dn, bus); 118 } else if (mode == PCI_PROBE_NORMAL) { 119 /* use legacy probe */ 120 slotno = PCI_SLOT(PCI_DN(dn->child)->devfn); 121 num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); 122 if (!num) 123 return; 124 pcibios_setup_bus_devices(bus); 125 max = bus->secondary; 126 for (pass=0; pass < 2; pass++) 127 list_for_each_entry(dev, &bus->devices, bus_list) { 128 if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || 129 dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) 130 max = pci_scan_bridge(bus, dev, max, pass); 131 } 132 } 133 pcibios_finish_adding_to_bus(bus); 134 } 135 EXPORT_SYMBOL_GPL(pcibios_add_pci_devices); 136 137 struct pci_controller * __devinit init_phb_dynamic(struct device_node *dn) 138 { 139 struct pci_controller *phb; 140 141 pr_debug("PCI: Initializing new hotplug PHB %s\n", dn->full_name); 142 143 phb = pcibios_alloc_controller(dn); 144 if (!phb) 145 return NULL; 146 rtas_setup_phb(phb); 147 pci_process_bridge_OF_ranges(phb, dn, 0); 148 149 pci_devs_phb_init_dynamic(phb); 150 151 if (dn->child) 152 eeh_add_device_tree_early(dn); 153 154 scan_phb(phb); 155 pcibios_finish_adding_to_bus(phb->bus); 156 157 return phb; 158 } 159 EXPORT_SYMBOL_GPL(init_phb_dynamic); 160 161 /* RPA-specific bits for removing PHBs */ 162 int remove_phb_dynamic(struct pci_controller *phb) 163 { 164 struct pci_bus *b = phb->bus; 165 struct resource *res; 166 int rc, i; 167 168 pr_debug("PCI: Removing PHB %04x:%02x... \n", 169 pci_domain_nr(b), b->number); 170 171 /* We cannot to remove a root bus that has children */ 172 if (!(list_empty(&b->children) && list_empty(&b->devices))) 173 return -EBUSY; 174 175 /* We -know- there aren't any child devices anymore at this stage 176 * and thus, we can safely unmap the IO space as it's not in use 177 */ 178 res = &phb->io_resource; 179 if (res->flags & IORESOURCE_IO) { 180 rc = pcibios_unmap_io_space(b); 181 if (rc) { 182 printk(KERN_ERR "%s: failed to unmap IO on bus %s\n", 183 __func__, b->name); 184 return 1; 185 } 186 } 187 188 /* Unregister the bridge device from sysfs and remove the PCI bus */ 189 device_unregister(b->bridge); 190 phb->bus = NULL; 191 pci_remove_bus(b); 192 193 /* Now release the IO resource */ 194 if (res->flags & IORESOURCE_IO) 195 release_resource(res); 196 197 /* Release memory resources */ 198 for (i = 0; i < 3; ++i) { 199 res = &phb->mem_resources[i]; 200 if (!(res->flags & IORESOURCE_MEM)) 201 continue; 202 release_resource(res); 203 } 204 205 /* Free pci_controller data structure */ 206 pcibios_free_controller(phb); 207 208 return 0; 209 } 210 EXPORT_SYMBOL_GPL(remove_phb_dynamic); 211